Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <combaseapi.h>
- #include <mmdeviceapi.h>
- #include <audioclient.h>
- internal
- struct
- {
- B32 sound_initialized;
- U32 target_frames_ahead;
- U32 core_audio_buffer_size;
- IAudioRenderClient *render_client;
- IAudioClock *audio_clock;
- WAVEFORMATEX *format;
- U64 last_written_frame_number;
- U32 stream_latency;
- AudioBuffer buffer;
- AudioFormat output_format;
- } CoreAudioState;
- HRESULT init_device(IMMDevice *device)
- {
- HRESULT result;
- CoreAudioState.sound_initialized = false;
- DWORD device_state;
- result = device->GetState(&device_state);
- if (result < 0 || device_state != DEVICE_STATE_ACTIVE)
- {
- return result;
- }
- IID audio_client_id = __uuidof(IAudioClient); // IID_IAudioClient
- IAudioClient *audio_client;
- result = device->Activate(
- audio_client_id,
- CLSCTX_ALL,
- NULL,
- (void **)&audio_client);
- if (result < 0)
- {
- return result;
- }
- WAVEFORMATEX *actual_format = NULL;
- result = audio_client->GetMixFormat(&actual_format);
- if (result < 0)
- {
- return result;
- }
- U32 frames_per_sec = actual_format->nSamplesPerSec;
- U32 samples_per_sec = frames_per_sec * actual_format->nChannels;
- U32 bytes_per_sec = samples_per_sec * (actual_format->wBitsPerSample / 8);
- // TODO: check that format is acceptable
- result = audio_client->Initialize(
- AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_RATEADJUST,
- (U32)((F32)CoreAudioState.target_frames_ahead / (F32)frames_per_sec * 10000000.0f),
- 0,
- actual_format,
- NULL);
- if (result < 0)
- {
- return result;
- }
- U32 core_audio_buffer_size;
- result = audio_client->GetBufferSize(&core_audio_buffer_size);
- if (result < 0)
- {
- return result;
- }
- IAudioClock *audio_clock;
- IID audio_clock_id = __uuidof(IAudioClock);
- result = audio_client->GetService(
- audio_clock_id,
- (void **)&audio_clock);
- if (result < 0)
- {
- return result;
- }
- U64 bytes_per_second;
- result = audio_clock->GetFrequency(&bytes_per_second);
- if (result < 0)
- {
- return result;
- }
- if (bytes_per_second != bytes_per_sec)
- {
- return -1;
- }
- IAudioRenderClient *render_client;
- IID render_client_id = __uuidof(IAudioRenderClient);
- result = audio_client->GetService(
- render_client_id,
- (void **)&render_client);
- if (result < 0)
- {
- return result;
- }
- REFERENCE_TIME latency; // 100ns units
- result = audio_client->GetStreamLatency(&latency);
- if (result < 0)
- {
- return result;
- }
- CoreAudioState.core_audio_buffer_size = core_audio_buffer_size;
- CoreAudioState.audio_clock = audio_clock;
- CoreAudioState.render_client = render_client;
- CoreAudioState.format = actual_format;
- CoreAudioState.stream_latency = (U32)(latency * frames_per_sec / 10000000); // latency in frames instead of 100ns ticks
- CoreAudioState.last_written_frame_number = 0;
- CoreAudioState.target_frames_ahead = min(core_audio_buffer_size, 2 * frames_per_sec / 60);
- CoreAudioState.buffer.channel_count = actual_format->nChannels;
- CoreAudioState.buffer.frames_per_second = frames_per_sec;
- CoreAudioState.buffer.format = AudioFormat_F32;
- CoreAudioState.output_format = AudioFormat_None;
- switch (CoreAudioState.format->wFormatTag)
- {
- case WAVE_FORMAT_PCM:
- {
- S32 bits = CoreAudioState.format->wBitsPerSample;
- if ( 8 == bits) CoreAudioState.output_format = AudioFormat_S8;
- if (16 == bits) CoreAudioState.output_format = AudioFormat_S16;
- if (24 == bits) CoreAudioState.output_format = AudioFormat_S24;
- if (32 == bits) CoreAudioState.output_format = AudioFormat_S32;
- if (48 == bits) CoreAudioState.output_format = AudioFormat_S48;
- } break;
- case WAVE_FORMAT_EXTENSIBLE:
- {
- WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE *)CoreAudioState.format;
- GUID float_sub_format;
- INIT_WAVEFORMATEX_GUID(&float_sub_format, 3);
- S32 bits = CoreAudioState.format->wBitsPerSample;
- if ( 8 == bits) CoreAudioState.output_format = AudioFormat_S8;
- if (16 == bits) CoreAudioState.output_format = AudioFormat_S16;
- if (24 == bits) CoreAudioState.output_format = AudioFormat_S24;
- if (32 == bits) CoreAudioState.output_format = AudioFormat_S32;
- if (48 == bits) CoreAudioState.output_format = AudioFormat_S48;
- if (ext->SubFormat == float_sub_format) CoreAudioState.output_format = AudioFormat_F32;
- } break;
- case WAVE_FORMAT_IEEE_FLOAT:
- {
- if (32 == CoreAudioState.format->wBitsPerSample) CoreAudioState.output_format = AudioFormat_F32;
- } break;
- default:
- {
- fail("unhandled case");
- } break;
- }
- // TODO use a MemoryBlock instead of VirtualAlloc
- if (CoreAudioState.buffer.interleafed_samples)
- {
- VirtualFree(CoreAudioState.buffer.interleafed_samples, 0, MEM_RELEASE);
- }
- CoreAudioState.buffer.interleafed_samples = VirtualAlloc(0, CoreAudioState.target_frames_ahead * sizeof(F32) * CoreAudioState.buffer.channel_count, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- result = audio_client->Start();
- if (result < 0)
- {
- return result;
- }
- CoreAudioState.sound_initialized = true;
- return S_OK;
- }
- class DeviceChangeListener : IMMNotificationClient
- {
- ULONG refs = 1;
- IMMDeviceEnumerator *device_enumerator;
- public:
- DeviceChangeListener(IMMDeviceEnumerator *device_enumerator)
- {
- this->device_enumerator = device_enumerator;
- }
- #pragma warning(push)
- #pragma warning(disable:4100)
- HRESULT OnDefaultDeviceChanged(
- EDataFlow flow,
- ERole role,
- LPCWSTR pwstrDefaultDeviceId)
- {
- HRESULT result;
- if (flow == eRender && role == eConsole)
- {
- IMMDevice *device;
- result = device_enumerator->GetDefaultAudioEndpoint(
- eRender,
- eConsole,
- &device);
- if (result < 0)
- {
- return result;
- }
- return init_device(device);
- }
- return S_OK;
- }
- HRESULT OnDeviceAdded(LPCWSTR pwstrDeviceId) {return S_OK;}
- HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId) {return S_OK;}
- HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {return S_OK;}
- HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) {return S_OK;}
- #pragma warning(pop)
- ULONG AddRef()
- {
- return InterlockedIncrement(&refs);
- }
- HRESULT QueryInterface(REFIID riid, VOID **ppvInterface)
- {
- if (IID_IUnknown == riid || __uuidof(IMMNotificationClient) == riid)
- {
- AddRef();
- *ppvInterface = (VOID *)this;
- }
- else
- {
- *ppvInterface = NULL;
- return E_NOINTERFACE;
- }
- return S_OK;
- }
- ULONG Release()
- {
- ULONG ulRef = InterlockedDecrement(&refs);
- if (0 == ulRef)
- {
- delete this;
- }
- return ulRef;
- }
- };
- B32 init_coreaudio()
- {
- HRESULT result;
- result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
- if (result < 0)
- {
- return false;
- }
- const CLSID nonsens_a = __uuidof(MMDeviceEnumerator);
- const IID nonsens_b = __uuidof(IMMDeviceEnumerator);
- IMMDeviceEnumerator *device_enumerator;
- result = CoCreateInstance(
- nonsens_a,
- NULL,
- CLSCTX_ALL,
- nonsens_b,
- (void**)&device_enumerator);
- if (result < 0)
- {
- return false;
- }
- IMMDevice *device;
- result = device_enumerator->GetDefaultAudioEndpoint(
- eRender,
- eConsole,
- &device);
- if (result < 0)
- {
- return false;
- }
- static DeviceChangeListener device_change_listener(device_enumerator); // oof
- device_enumerator->RegisterEndpointNotificationCallback(
- (IMMNotificationClient *)&device_change_listener);
- return init_device(device);
- }
- AudioBuffer *get_audio_buffer()
- {
- HRESULT result;
- if (!CoreAudioState.sound_initialized)
- {
- return NULL;
- }
- U64 read_cursor;
- result = CoreAudioState.audio_clock->GetPosition(&read_cursor, NULL);
- if (result < 0)
- {
- return NULL;
- }
- read_cursor /= CoreAudioState.format->nChannels;
- read_cursor /= CoreAudioState.format->wBitsPerSample / 8;
- S32 frames_ahead;
- if (CoreAudioState.last_written_frame_number <= read_cursor)
- {
- CoreAudioState.last_written_frame_number = read_cursor;
- frames_ahead = 0;
- }
- else
- {
- U64 diff = CoreAudioState.last_written_frame_number - read_cursor;
- if (diff < CoreAudioState.core_audio_buffer_size)
- {
- frames_ahead = (S32) diff;
- }
- else
- {
- frames_ahead = CoreAudioState.core_audio_buffer_size;
- }
- }
- S32 num_frames = CoreAudioState.target_frames_ahead - frames_ahead;
- CoreAudioState.buffer.frame_count = num_frames;
- return &CoreAudioState.buffer;
- }
- B32 finalize_audio(AudioBuffer *in_buffer)
- {
- HRESULT result;
- assert(in_buffer->channel_count == CoreAudioState.format->nChannels);
- BYTE *out_buffer;
- result = CoreAudioState.render_client->GetBuffer(in_buffer->frame_count, &out_buffer);
- if (result < 0)
- {
- return false;
- }
- size_t sample_count = in_buffer->frame_count * in_buffer->channel_count;
- F32 *read_cursor = (F32 *)in_buffer->interleafed_samples;
- switch (CoreAudioState.output_format)
- {
- case AudioFormat_S8:
- {
- S8 *write_cursor = (S8 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- *write_cursor++ = round_F32_to_S8(*read_cursor++ * s8_max);
- }
- } break;
- case AudioFormat_S16:
- {
- S16 *write_cursor = (S16 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- *write_cursor++ = round_F32_to_S16(*read_cursor++ * s16_max);
- }
- } break;
- case AudioFormat_S24:
- {
- U8 *write_cursor = (U8 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- S32 sample = round_F32_to_S32(*read_cursor++ * s32_max);
- *write_cursor++ = (U8)(sample >> 8);
- *write_cursor++ = (U8)(sample >> 16);
- *write_cursor++ = (U8)(sample >> 24);
- }
- } break;
- case AudioFormat_S32:
- {
- S32 *write_cursor = (S32 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- *write_cursor++ = round_F32_to_S32(*read_cursor++ * s32_max);
- }
- } break;
- case AudioFormat_S48:
- {
- U16 *write_cursor = (U16 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- S64 sample = round_F32_to_S64(*read_cursor++ * s64_max);
- *write_cursor++ = (U16)(sample >> 16);
- *write_cursor++ = (U16)(sample >> 32);
- *write_cursor++ = (U16)(sample >> 48);
- }
- } break;
- case AudioFormat_F32:
- {
- F32 *write_cursor = (F32 *)out_buffer;
- for (size_t i = 0; i < sample_count; ++i)
- {
- *write_cursor++ = *read_cursor++;
- }
- } break;
- default:
- {
- fail("unhandled case");
- } break;
- }
- result = CoreAudioState.render_client->ReleaseBuffer(in_buffer->frame_count, 0);
- if (result < 0)
- {
- return false;
- }
- CoreAudioState.last_written_frame_number += in_buffer->frame_count;
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement