//////////////////////////////// // NOTE(allen): Quick Mutex // TODO(allen): intrinsics wrappers #include function u32 AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend) { // NOTE(casey): Returns the original value _prior_ to adding u32 Result = _InterlockedExchangeAdd((long volatile*)Value, (long)Addend); return(Result); } global volatile u32 win32_audio_ticket = 0; global volatile u32 win32_audio_serving = 0; function void win32_audio_begin_ticket_mutex(void) { u32 Ticket = AtomicAddU32AndReturnOriginal(&win32_audio_ticket, 1); while(Ticket != win32_audio_serving) {_mm_pause();} } function void win32_audio_end_ticket_mutex(void) { AtomicAddU32AndReturnOriginal(&win32_audio_serving, 1); } //////////////////////////////// // NOTE(allen): Fallback Mixers function void win32_default_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count){ } function void win32_default_mix_destination(i16 *dst, f32 *src, u32 sample_count){ u32 opl = sample_count*2; for(u32 i = 0; i < sample_count; i += 1){ dst[i] = (i16)src[i]; } } //////////////////////////////// // NOTE(allen): Win32 Audio System API internal system_set_source_mixer_sig(){ win32_audio_begin_ticket_mutex(); win32vars.audio_mix_ctx = ctx; win32vars.audio_mix_sources = mix_func; win32_audio_end_ticket_mutex(); } internal system_set_destination_mixer_sig(){ win32_audio_begin_ticket_mutex(); win32vars.audio_mix_destination = mix_func; win32_audio_end_ticket_mutex(); } //////////////////////////////// // NOTE(allen): Win32 Audio Loop function b32 win32_submit_audio(HWAVEOUT WaveOut, WAVEHDR *Header, u32 SampleCount, f32 *mix_buffer){ b32 Result = false; // NOTE(allen): prep buffers u32 mix_buffer_size = SampleCount*2*sizeof(f32); memset(mix_buffer, 0, mix_buffer_size); i16 *Samples = (i16 *)Header->lpData; // NOTE(allen): prep mixer pointers win32_audio_begin_ticket_mutex(); void *audio_mix_ctx = win32vars.audio_mix_ctx; Audio_Mix_Sources_Function *audio_mix_sources = win32vars.audio_mix_sources; Audio_Mix_Destination_Function *audio_mix_destination = win32vars.audio_mix_destination; win32_audio_end_ticket_mutex(); if (audio_mix_sources == 0){ audio_mix_sources = win32_default_mix_sources; } if (audio_mix_destination == 0){ audio_mix_destination = win32_default_mix_destination; } // NOTE(allen): mix audio_mix_sources(audio_mix_ctx, mix_buffer, SampleCount); audio_mix_destination(Samples, mix_buffer, SampleCount); // NOTE(allen): send final samples to win32 DWORD Error = waveOutPrepareHeader(WaveOut, Header, sizeof(*Header)); if(Error == MMSYSERR_NOERROR) { Error = waveOutWrite(WaveOut, Header, sizeof(*Header)); if(Error == MMSYSERR_NOERROR) { // NOTE(casey): Success Result = true; } } return(Result); } function DWORD WINAPI win32_audio_loop(void *Passthrough){ // // NOTE(casey): Set up our audio output buffer // u32 SamplesPerSecond = 48000; u32 SamplesPerBuffer = 16*SamplesPerSecond/1000; u32 ChannelCount = 2; u32 BytesPerChannelValue = 2; u32 BytesPerSample = ChannelCount*BytesPerChannelValue; u32 BufferCount = 3; u32 BufferSize = SamplesPerBuffer*BytesPerSample; u32 HeaderSize = sizeof(WAVEHDR); u32 TotalBufferSize = (BufferSize+HeaderSize); u32 MixBufferSize = (SamplesPerBuffer*ChannelCount*sizeof(f32)); u32 TotalAudioMemorySize = BufferCount*TotalBufferSize + MixBufferSize; // // NOTE(casey): Initialize audio out // HWAVEOUT WaveOut = {}; WAVEFORMATEX Format = {}; Format.wFormatTag = WAVE_FORMAT_PCM; Format.nChannels = (WORD)ChannelCount; Format.wBitsPerSample = (WORD)(8*BytesPerChannelValue); Format.nSamplesPerSec = SamplesPerSecond; Format.nBlockAlign = (Format.nChannels*Format.wBitsPerSample)/8; Format.nAvgBytesPerSec = Format.nBlockAlign * Format.nSamplesPerSec; b32 quit = false; void *MixBuffer = 0; void *AudioBufferMemory = 0; if(waveOutOpen(&WaveOut, WAVE_MAPPER, &Format, GetCurrentThreadId(), 0, CALLBACK_THREAD) == MMSYSERR_NOERROR) { AudioBufferMemory = VirtualAlloc(0, TotalAudioMemorySize, MEM_COMMIT, PAGE_READWRITE); if(AudioBufferMemory) { u8 *At = (u8 *)AudioBufferMemory; MixBuffer = At; At += MixBufferSize; for(u32 BufferIndex = 0; BufferIndex < BufferCount; ++BufferIndex) { WAVEHDR *Header = (WAVEHDR *)At; Header->lpData = (char *)(Header + 1); Header->dwBufferLength = BufferSize; At += TotalBufferSize; win32_submit_audio(WaveOut, Header, SamplesPerBuffer, (f32*)MixBuffer); } } else { // TODO(allen): audio error quit = true; } } else { // TODO(allen): audio error quit = true; } // // NOTE(casey): Serve audio forever (until we are told to stop) // SetTimer(0, 0, 100, 0); for (;!quit;) { MSG Message = {}; GetMessage(&Message, 0, 0, 0); if(Message.message == MM_WOM_DONE) { WAVEHDR *Header = (WAVEHDR *)Message.lParam; if(Header->dwFlags & WHDR_DONE) { Header->dwFlags &= ~WHDR_DONE; } waveOutUnprepareHeader(WaveOut, Header, sizeof(*Header)); win32_submit_audio(WaveOut, Header, SamplesPerBuffer, (f32*)MixBuffer); } } if(WaveOut) { waveOutClose(WaveOut); } if(AudioBufferMemory) { VirtualFree(AudioBufferMemory, 0, MEM_RELEASE); } return(0); } function DWORD win32_audio_init(void){ DWORD thread_id = 0; HANDLE handle = CreateThread(0, 0, win32_audio_loop, 0, 0, &thread_id); CloseHandle(handle); return(thread_id); }