#include "SDL2_buffer_audio_out.hpp" //############################################################################ internal short SampleSineWave(LaserBoy_audio_config* audio_config) { int HalfWaveCounter = audio_config->WavePeriod / 2; return audio_config->ToneVolume * sin(TAU * audio_config->sample_index / HalfWaveCounter); } //############################################################################ internal void LaserBoy_write_to_audio_buffer(LaserBoy_audio_buffer* audio_buffer, short (*GetSample)(LaserBoy_audio_config*) // pointer to function ) { int part_1_size = audio_buffer->read_from - audio_buffer->write_to; int part_2_size = 0; if(audio_buffer->read_from < audio_buffer->write_to) { // Fill to the end of the buffer and loop back around and fill to the read // cursor. part_1_size = audio_buffer->size_in_bytes - audio_buffer->write_to; part_2_size = audio_buffer->read_from; } LaserBoy_audio_config* audio_config = audio_buffer->audio_config; int part_1_samples = part_1_size / audio_config->bytes_per_sample; int part_2_samples = part_2_size / audio_config->bytes_per_sample; int bytes_written = part_1_size + part_2_size; short* buffer = (short*)&audio_buffer->buffer[audio_buffer->write_to]; for(int sample_index = 0; sample_index < part_1_samples; sample_index++) { short single_sample = (*GetSample)(audio_config); for(int i = 0; i < audio_buffer->audio_config->num_channels; i++) *buffer++ = single_sample; audio_config->sample_index++; } buffer = (short*)audio_buffer->buffer; for(int sample_index = 0; sample_index < part_2_samples; sample_index++) { short single_sample = (*GetSample)(audio_config); for(int i = 0; i < audio_buffer->audio_config->num_channels; i++) *buffer++ = single_sample; audio_config->sample_index++; } audio_buffer->write_to = (audio_buffer->write_to + bytes_written) % audio_buffer->size_in_bytes; } //############################################################################ internal void LaserBoy_audio_callback(void* anonymous, u_char* audio_buffer_bytes, int byte_length) { LaserBoy_audio_buffer* audio_buffer = (LaserBoy_audio_buffer*)anonymous; // Keep track of two regions. part_1 contains everything from the current // read_from up until, potentially, the end of the buffer. part_2 only // exists if we need to circle back around. It contains all the data from the // beginning of the buffer up until sufficient bytes are read to meet byte_length. int part_1_size = byte_length; int part_2_size = 0; if(audio_buffer->read_from + byte_length > audio_buffer->size_in_bytes) { // Handle looping back from the beginning. part_1_size = audio_buffer->size_in_bytes - audio_buffer->read_from; part_2_size = byte_length - part_1_size; } SDL_memcpy(audio_buffer_bytes, (audio_buffer->buffer + audio_buffer->read_from), part_1_size ); SDL_memcpy(&audio_buffer_bytes[part_1_size], audio_buffer->buffer, part_2_size ); audio_buffer->read_from = (audio_buffer->read_from + byte_length) % audio_buffer->size_in_bytes; } //############################################################################ internal void LaserBoy_init_audio_device(LaserBoy_audio_buffer* audio_buffer) { SDL_AudioSpec audio_spec_wanted = {}; SDL_AudioSpec audio_spec_available = {}; const int device_count = SDL_GetNumAudioDevices(0); for(int i = 0; i < device_count; ++i) cout << SDL_GetAudioDeviceName(i, 0) << endl; audio_spec_wanted.freq = audio_buffer->audio_config->sample_rate; audio_spec_wanted.format = AUDIO_S16LSB; audio_spec_wanted.channels = audio_buffer->audio_config->num_channels; audio_spec_wanted.samples = 8192 / audio_spec_wanted.channels; audio_spec_wanted.callback = &LaserBoy_audio_callback; audio_spec_wanted.userdata = audio_buffer; audio_buffer->device_ID = SDL_OpenAudioDevice( // "ICUSBAUDIO7D, USB Audio", // "Logitech USB Headset, USB Audio", "HDA NVidia, SAMSUNG", 0, &audio_spec_wanted, &audio_spec_available, SDL_AUDIO_TRANSPARENTLY_CONVERT_FORMAT ); if(audio_spec_wanted.format != audio_spec_available.format) { cout << "Unable to obtain expected audio settings: " << SDL_GetError() << endl; exit(1); } cout << endl; cout << "audio_spec_wanted.freq " << audio_spec_wanted.freq << endl; cout << "audio_spec_wanted.format " << audio_spec_wanted.format << endl; cout << "audio_spec_wanted.channels " << (int)audio_spec_wanted.channels << endl; cout << "audio_spec_wanted.samples " << audio_spec_wanted.samples << endl; cout << "audio_spec_wanted.size " << audio_spec_wanted.size << endl; cout << endl; cout << "audio_spec_available.freq " << audio_spec_available.freq << endl; cout << "audio_spec_available.format " << audio_spec_available.format << endl; cout << "audio_spec_available.channels " << (int)audio_spec_available.channels << endl; cout << "audio_spec_available.samples " << audio_spec_available.samples << endl; cout << "audio_spec_available.size " << audio_spec_available.size << endl; // Start playing the audio buffer SDL_PauseAudioDevice(audio_buffer->device_ID, 0); } //############################################################################ internal int LaserBoy_audio_thread(void* anonymous) { LaserBoy_audio_thread_context* audio_thread = (LaserBoy_audio_thread_context*)anonymous; while (audio_thread->audio_thread_state->running) { SDL_LockAudioDevice (audio_thread->audio_buffer->device_ID); LaserBoy_write_to_audio_buffer(audio_thread->audio_buffer, &SampleSineWave); SDL_UnlockAudioDevice (audio_thread->audio_buffer->device_ID); } return 0; } //############################################################################ int main() { LaserBoy_audio_config audio_config = {}; LaserBoy_audio_buffer audio_buffer = {}; LaserBoy_audio_thread_state audio_thread_state = {}; LaserBoy_audio_thread_context audio_thread_context = {}; if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { cout << "Unable to initialized SDL: " << SDL_GetError() << endl; return 1; } SDL_Window* Window = SDL_CreateWindow("Circular Audio Buffer Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL ); if(!Window) { cout << "Unable to initialize window: " << SDL_GetError() << endl; return 1; } audio_config.sample_rate = 48000; audio_config.num_channels = CHANNELS; audio_config.bytes_per_sample = audio_config.num_channels * sizeof(short); audio_config.sample_index = 0; audio_config.ToneHz = 110; audio_config.ToneVolume = 3000; audio_config.WavePeriod = audio_config.sample_rate / audio_config.ToneHz; audio_buffer.size_in_bytes = audio_config.sample_rate * audio_config.bytes_per_sample; audio_buffer.buffer = new u_char[audio_buffer.size_in_bytes]; audio_buffer.read_from = 0; audio_buffer.write_to = audio_config.bytes_per_sample; audio_buffer.audio_config = &audio_config; memset(audio_buffer.buffer, 0, audio_buffer.size_in_bytes); LaserBoy_init_audio_device(&audio_buffer); audio_thread_state.running = true; audio_thread_context.audio_buffer = &audio_buffer; audio_thread_context.audio_thread_state = &audio_thread_state; SDL_Thread* audio_thread = SDL_CreateThread(LaserBoy_audio_thread, "Audio", (void*)&audio_thread_context); while(audio_thread_state.running) { while(SDL_PollEvent(&audio_thread_state.last_event)) if(audio_thread_state.last_event.type == SDL_QUIT) audio_thread_state.running = false; } SDL_WaitThread(audio_thread, NULL); SDL_DestroyWindow(Window); SDL_CloseAudioDevice(audio_buffer.device_ID); SDL_Quit(); delete[] audio_buffer.buffer; return 0; } //############################################################################ ////////////////////////////////////////////////////////////////////////////// //############################################################################