From b24ba7d602209eafbab056218aa50281e4ebdeee Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 8 Nov 2020 17:33:08 +1000 Subject: [PATCH] AAudio: Add support for configuring content type and input preset. --- miniaudio.h | 1003 +++++++++++++++++++++++++++------------------------ 1 file changed, 531 insertions(+), 472 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index 0e4357c0..b61b2fea 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -3160,6 +3160,7 @@ typedef enum ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ @@ -3775,6 +3776,8 @@ struct ma_context ma_proc AAudioStreamBuilder_setDataCallback; ma_proc AAudioStreamBuilder_setErrorCallback; ma_proc AAudioStreamBuilder_setPerformanceMode; + ma_proc AAudioStreamBuilder_setContentType; + ma_proc AAudioStreamBuilder_setInputPreset; ma_proc AAudioStreamBuilder_openStream; ma_proc AAudioStream_close; ma_proc AAudioStream_getState; @@ -4217,7 +4220,7 @@ The context can be configured via the `pConfig` argument. The config object is i can then be set directly on the structure. Below are the members of the `ma_context_config` object. logCallback - Callback for handling log messages from miniaudio. + Callback for handling log messages from miniaudio. threadPriority The desired priority to use for the audio thread. Allowable values include the following: @@ -4791,7 +4794,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c ma_share_mode_shared and reinitializing. wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. + WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. wasapi.noDefaultQualitySRC WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. @@ -4820,7 +4823,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c pulse.pStreamNameCapture PulseAudio only. Sets the stream name for capture. - + coreaudio.allowNominalSampleRateChange Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate @@ -6320,7 +6323,7 @@ IMPLEMENTATION It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for supporting different assembly dialects. - + What's basically happening is that we're saving and restoring the ebx register manually. */ #if defined(DRFLAC_X86) && defined(__PIC__) @@ -6755,12 +6758,12 @@ static ma_uint32 g_maStandardSampleRatePriorities[] = { static ma_format g_maFormatPriorities[] = { ma_format_s16, /* Most common */ ma_format_f32, - + /*ma_format_s24_32,*/ /* Clean alignment */ ma_format_s32, - + ma_format_s24, /* Unclean alignment */ - + ma_format_u8 /* Low quality */ }; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) @@ -7811,10 +7814,10 @@ static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) if (x & 1) { count += 1; } - + x = x >> 1; } - + return count; } @@ -10170,7 +10173,7 @@ static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uin if (pContext == NULL) { return; } - + #if defined(MA_LOG_LEVEL) if (logLevel <= MA_LOG_LEVEL) { ma_log_proc onLog; @@ -10214,7 +10217,7 @@ int ma_vscprintf(const char* format, va_list args) result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); ma_free(pTempBuffer, NULL); - + if (result != -1) { break; /* Got it. */ } @@ -10279,7 +10282,7 @@ static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_ui #else vsprintf(pFormattedMessage, pFormat, args); #endif - + ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage); ma_free(pFormattedMessage, pAllocationCallbacks); } @@ -10563,7 +10566,7 @@ static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { float masterVolumeFactor; - + masterVolumeFactor = pDevice->masterVolumeFactor; if (pDevice->onData) { @@ -10738,7 +10741,7 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m MA_ASSERT(frameCountInDeviceFormat > 0); MA_ASSERT(pFramesInDeviceFormat != NULL); MA_ASSERT(pRB != NULL); - + /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ for (;;) { ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); @@ -10798,7 +10801,7 @@ static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, MA_ASSERT(frameCount > 0); MA_ASSERT(pFramesInInternalFormat != NULL); MA_ASSERT(pRB != NULL); - + /* Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for the whole frameCount frames we just use silence instead for the input data. @@ -10832,7 +10835,7 @@ static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, break; /* Underrun. */ } } - + /* We're done with the captured samples. */ result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames); if (result != MA_SUCCESS) { @@ -10960,7 +10963,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice, /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint32 capturedDeviceFramesRemaining; ma_uint32 capturedDeviceFramesProcessed; @@ -10988,7 +10991,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice, ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - + /* Convert capture data from device format to client format. */ result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); if (result != MA_SUCCESS) { @@ -11186,7 +11189,7 @@ static ma_result ma_device_init__custom(ma_context* pContext, const ma_device_co descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorPlayback.periodCount = pConfig->periods; - + MA_ZERO_OBJECT(&descriptorCapture); descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; descriptorCapture.shareMode = pConfig->capture.shareMode; @@ -11197,7 +11200,7 @@ static ma_result ma_device_init__custom(ma_context* pContext, const ma_device_co descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorCapture.periodCount = pConfig->periods; - + result = pContext->custom.onDeviceInit(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { return result; /* Failed to initialize the device. */ @@ -12760,7 +12763,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m if (isThisDevice) { ma_device_stop(pThis->pDevice); } - + return S_OK; } @@ -13089,7 +13092,7 @@ static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_ } pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); return pDefaultDeviceID; } @@ -13177,7 +13180,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); if (SUCCEEDED(hr)) { ma_result result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo); - + ma_IAudioClient_Release(pAudioClient); return result; } else { @@ -13196,7 +13199,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte ma_uint32 iDevice; LPWSTR pDefaultDeviceID = NULL; ma_IMMDeviceCollection* pDeviceCollection = NULL; - + MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); @@ -13215,7 +13218,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte for (iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; ma_IMMDevice* pMMDevice; - + MA_ZERO_OBJECT(&deviceInfo); hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); @@ -13378,10 +13381,10 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e #else /* UWP - + The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate over devices without using MMDevice, I'm restricting devices to defaults. - + Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ */ if (callback) { @@ -13416,7 +13419,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev ma_result result; ma_IMMDevice* pMMDevice = NULL; LPWSTR pDefaultDeviceID = NULL; - + result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); if (result != MA_SUCCESS) { return result; @@ -13623,7 +13626,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device I do not know how to query the device's native format on UWP so for now I'm just disabling support for exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() until you find one that works. - + TODO: Add support for exclusive mode to UWP. */ hr = S_FALSE; @@ -13679,7 +13682,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } else { result = MA_FORMAT_NOT_SUPPORTED; } - + errorMsg = "[WASAPI] Native format not supported."; goto done; } @@ -13726,7 +13729,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device break; } } - + if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); @@ -13818,7 +13821,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } else { #if defined(MA_DEBUG_OUTPUT) printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - #endif + #endif } } else { #if defined(MA_DEBUG_OUTPUT) @@ -13968,7 +13971,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev data.usingDefaultChannels = pDevice->capture.usingDefaultChannels; data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap; } - + data.sampleRateIn = pDevice->sampleRate; data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; @@ -14281,7 +14284,7 @@ static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_ MA_ASSERT(pDevice != NULL); MA_ASSERT(pFrameCount != NULL); - + *pFrameCount = 0; if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { @@ -14319,7 +14322,7 @@ static ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_de if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { return pDevice->wasapi.hasDefaultCaptureDeviceChanged; } - + return MA_FALSE; } @@ -14337,7 +14340,7 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { c89atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_FALSE); } - + #ifdef MA_DEBUG_OUTPUT printf("=== CHANGING DEVICE ===\n"); @@ -14360,7 +14363,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) /* It's possible for the main loop to get stuck if the device is disconnected. - + In loopback mode it's possible for WaitForSingleObject() to get stuck in a deadlock when nothing is being played. When nothing is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device. */ @@ -14550,7 +14553,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) } framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture; - + if (framesAvailableCapture > 0) { mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture); hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); @@ -14589,7 +14592,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice); pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice); - + /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */ if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) { ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed); @@ -14674,7 +14677,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) } ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); - + mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess; outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess; outputDataInClientFormatConsumed = 0; @@ -14800,7 +14803,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) } framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture; - + if (framesAvailableCapture > 0) { mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture); hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); @@ -15634,7 +15637,7 @@ typedef struct static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) { ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; + ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); @@ -15958,7 +15961,7 @@ static ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_co if (periodSizeInMilliseconds == 0) { periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate); } - + /* DirectSound should use a latency of about 20ms per period for low latency mode. */ if (pDevice->usingDefaultBufferSize) { if (pConfig->performanceProfile == ma_performance_profile_low_latency) { @@ -16130,7 +16133,7 @@ static ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_co /* From MSDN: - + The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. @@ -16165,14 +16168,14 @@ static ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_co /* Meaning of dwFlags (from MSDN): - + DSBCAPS_CTRLPOSITIONNOTIFY The buffer has position notification capability. - + DSBCAPS_GLOBALFOCUS With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to another application, even if the new application uses DirectSound. - + DSBCAPS_GETCURRENTPOSITION2 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the @@ -16231,7 +16234,7 @@ static ma_result ma_device_main_loop__dsound(ma_device* pDevice) return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE); } } - + while (ma_device_get_state(pDevice) == MA_STATE_STARTED) { switch (pDevice->type) { @@ -16426,7 +16429,7 @@ static ma_result ma_device_main_loop__dsound(ma_device* pDevice) outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; } - + hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); if (FAILED(hr)) { @@ -16439,7 +16442,7 @@ static ma_result ma_device_main_loop__dsound(ma_device* pDevice) virtualWriteCursorInBytesPlayback = 0; virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; } - + /* We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds a bit of a buffer to prevent the playback buffer from getting starved. @@ -16637,7 +16640,7 @@ static ma_result ma_device_main_loop__dsound(ma_device* pDevice) virtualWriteCursorInBytesPlayback = 0; virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; } - + /* We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds a bit of a buffer to prevent the playback buffer from getting starved. @@ -17042,7 +17045,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, /* Name / Description - + Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. @@ -17256,7 +17259,7 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); @@ -17266,7 +17269,7 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); @@ -17324,7 +17327,7 @@ static ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_con if (periodSizeInMilliseconds == 0) { periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate); } - + /* WinMM has horrible latency. */ if (pDevice->usingDefaultBufferSize) { if (pConfig->performanceProfile == ma_performance_profile_low_latency) { @@ -17421,7 +17424,7 @@ static ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_con /* The heap allocated data is allocated like so: - + [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] */ heapSize = 0; @@ -17761,7 +17764,7 @@ static ma_result ma_device_main_loop__winmm(ma_device* pDevice) ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - + MA_ASSERT(pDevice != NULL); /* The capture device needs to be started immediately. */ @@ -17802,7 +17805,7 @@ static ma_result ma_device_main_loop__winmm(ma_device* pDevice) /* The process is: device_read -> convert -> callback -> convert -> device_write */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint32 capturedDeviceFramesRemaining; ma_uint32 capturedDeviceFramesProcessed; @@ -18668,7 +18671,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s } else { /* We're trying to open a specific device. There's a few things to consider here: - + miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). @@ -18815,7 +18818,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the description. - + The value in DESC seems to be split into two lines, with the first line being the name of the device and the second line being a description of the device. I don't like having the description be across two lines because it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line @@ -19494,7 +19497,7 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_dev ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", ma_result_from_errno(-resultALSA)); } - + internalFormat = ma_format_from_alsa(formatALSA); if (internalFormat == ma_format_unknown) { ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks); @@ -19523,16 +19526,16 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_dev It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable resampling. - + To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly faster rate. - + miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - + I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. */ @@ -19897,7 +19900,7 @@ static ma_result ma_device_main_loop__alsa(ma_device* pDevice) /* The process is: device_read -> convert -> callback -> convert -> device_write */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; @@ -20052,7 +20055,7 @@ static ma_result ma_device_main_loop__alsa(ma_device* pDevice) /* To silence a warning. Will never hit this. */ case ma_device_type_loopback: default: break; - } + } } /* Here is where the device needs to be stopped. */ @@ -21631,7 +21634,7 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi callbackData.pDeviceInfo = pDeviceInfo; callbackData.foundDevice = MA_FALSE; - + result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); if (deviceType == ma_device_type_playback) { @@ -21755,7 +21758,7 @@ static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, vo } framesProcessed += framesMapped; - + } else { /* Nothing was mapped. Just abort. */ break; @@ -21785,7 +21788,7 @@ static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stre ma_uint64 framesMapped; void* pMappedPCMFrames; int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { + if (pulseResult < 0) { result = ma_result_from_pulse(pulseResult); goto done; } @@ -22872,7 +22875,7 @@ static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_conf /* The buffer size in frames can change. */ periods = pConfig->periods; periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { const char** ppPorts; @@ -23009,7 +23012,7 @@ static ma_result ma_device_start__jack(ma_device* pDevice) ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); } - + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppServerPorts == NULL) { @@ -23043,7 +23046,7 @@ static ma_result ma_device_stop__jack(ma_device* pDevice) if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR); } - + onStop = pDevice->onStop; if (onStop) { onStop(pDevice); @@ -23341,14 +23344,14 @@ static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBas { MA_ASSERT(pDescription != NULL); MA_ASSERT(pFormatOut != NULL); - + *pFormatOut = ma_format_unknown; /* Safety. */ - + /* There's a few things miniaudio doesn't support. */ if (pDescription->mFormatID != kAudioFormatLinearPCM) { return MA_FORMAT_NOT_SUPPORTED; } - + /* We don't support any non-packed formats that are aligned high. */ if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { return MA_FORMAT_NOT_SUPPORTED; @@ -23358,7 +23361,7 @@ static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBas if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { return MA_FORMAT_NOT_SUPPORTED; } - + /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { return MA_FORMAT_NOT_SUPPORTED; @@ -23397,7 +23400,7 @@ static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBas } } } - + /* Getting here means the format is not supported. */ return MA_FORMAT_NOT_SUPPORTED; } @@ -23471,7 +23474,7 @@ static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - + #if 0 /* Introduced in a later version of macOS. */ case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; @@ -23492,7 +23495,7 @@ static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; #endif - + default: return MA_CHANNEL_NONE; } } @@ -23500,7 +23503,7 @@ static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) { MA_ASSERT(pChannelLayout != NULL); - + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { UInt32 iChannel; for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { @@ -23548,7 +23551,7 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* { ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap); } break; - + case kAudioChannelLayoutTag_Octagonal: { pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; @@ -23569,16 +23572,16 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelMap[1] = MA_CHANNEL_RIGHT; pChannelMap[0] = MA_CHANNEL_LEFT; } break; - + /* TODO: Add support for more tags here. */ - + default: { ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap); } break; } } - + return MA_SUCCESS; } @@ -23596,7 +23599,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 /* Safety. */ *pDeviceCount = 0; *ppDeviceObjectIDs = NULL; - + propAddressDevices.mSelector = kAudioHardwarePropertyDevices; propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; propAddressDevices.mElement = kAudioObjectPropertyElementMaster; @@ -23605,18 +23608,18 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 if (status != noErr) { return ma_result_from_OSStatus(status); } - + pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); if (pDeviceObjectIDs == NULL) { return MA_OUT_OF_MEMORY; } - + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); if (status != noErr) { ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } - + *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); *ppDeviceObjectIDs = pDeviceObjectIDs; @@ -23640,7 +23643,7 @@ static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, Aud if (status != noErr) { return ma_result_from_OSStatus(status); } - + return MA_SUCCESS; } @@ -23655,11 +23658,11 @@ static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID obje if (result != MA_SUCCESS) { return result; } - + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } - + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); return MA_SUCCESS; } @@ -23682,11 +23685,11 @@ static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID obj if (status != noErr) { return ma_result_from_OSStatus(status); } - + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } - + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); return MA_SUCCESS; } @@ -23705,17 +23708,17 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; propAddress.mScope = scope; propAddress.mElement = kAudioObjectPropertyElementMaster; - + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return MA_FALSE; } - + pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks); if (pBufferList == NULL) { return MA_FALSE; /* Out of memory. */ } - + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); if (status != noErr) { ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks); @@ -23726,7 +23729,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb if (pBufferList->mNumberBuffers > 0) { isSupported = MA_TRUE; } - + ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks); return isSupported; } @@ -23752,7 +23755,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au MA_ASSERT(pContext != NULL); MA_ASSERT(pDescriptionCount != NULL); MA_ASSERT(ppDescriptions != NULL); - + /* TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. @@ -23760,23 +23763,23 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; - + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } - + pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pDescriptions == NULL) { return MA_OUT_OF_MEMORY; } - + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); if (status != noErr) { ma_free(pDescriptions, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } - + *pDescriptionCount = dataSize / sizeof(*pDescriptions); *ppDescriptions = pDescriptions; return MA_SUCCESS; @@ -23792,29 +23795,29 @@ static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioOb MA_ASSERT(pContext != NULL); MA_ASSERT(ppChannelLayout != NULL); - + *ppChannelLayout = NULL; /* Safety. */ - + propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; - + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } - + pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pChannelLayout == NULL) { return MA_OUT_OF_MEMORY; } - + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); if (status != noErr) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } - + *ppChannelLayout = pChannelLayout; return MA_SUCCESS; } @@ -23826,14 +23829,14 @@ static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObj MA_ASSERT(pContext != NULL); MA_ASSERT(pChannelCount != NULL); - + *pChannelCount = 0; /* Safety. */ result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); if (result != MA_SUCCESS) { return result; } - + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { *pChannelCount = pChannelLayout->mNumberChannelDescriptions; } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { @@ -23841,7 +23844,7 @@ static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObj } else { *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); } - + ma_free(pChannelLayout, &pContext->allocationCallbacks); return MA_SUCCESS; } @@ -23853,18 +23856,18 @@ static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjec ma_result result; MA_ASSERT(pContext != NULL); - + result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); if (result != MA_SUCCESS) { return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ } - + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } - + ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } @@ -23880,31 +23883,31 @@ static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObje MA_ASSERT(pContext != NULL); MA_ASSERT(pSampleRateRangesCount != NULL); MA_ASSERT(ppSampleRateRanges != NULL); - + /* Safety. */ *pSampleRateRangesCount = 0; *ppSampleRateRanges = NULL; - + propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; - + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } - + pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pSampleRateRanges == NULL) { return MA_OUT_OF_MEMORY; } - + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); if (status != noErr) { ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } - + *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); *ppSampleRateRanges = pSampleRateRanges; return MA_SUCCESS; @@ -23919,19 +23922,19 @@ static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext MA_ASSERT(pContext != NULL); MA_ASSERT(pSampleRateOut != NULL); - + *pSampleRateOut = 0; /* Safety. */ - + result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); if (result != MA_SUCCESS) { return result; } - + if (sampleRateRangeCount == 0) { ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_ERROR; /* Should never hit this case should we? */ } - + if (sampleRateIn == 0) { /* Search in order of miniaudio's preferred priority. */ UInt32 iMALSampleRate; @@ -23947,13 +23950,13 @@ static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext } } } - + /* If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this case we just fall back to the first one reported by Core Audio. */ MA_ASSERT(sampleRateRangeCount > 0); - + *pSampleRateOut = pSampleRateRanges[0].mMinimum; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; @@ -23974,21 +23977,21 @@ static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext } else { absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; } - + if (currentAbsoluteDifference > absoluteDifference) { currentAbsoluteDifference = absoluteDifference; iCurrentClosestRange = iRange; } } } - + MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - + *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; } - + /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ /*return MA_ERROR;*/ @@ -24004,9 +24007,9 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC MA_ASSERT(pContext != NULL); MA_ASSERT(pBufferSizeInFramesOut != NULL); - + *pBufferSizeInFramesOut = 0; /* Safety. */ - + propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; @@ -24016,7 +24019,7 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC if (status != noErr) { return ma_result_from_OSStatus(status); } - + /* This is just a clamp. */ if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; @@ -24048,16 +24051,16 @@ static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; - + ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - + /* Get the actual size of the buffer. */ dataSize = sizeof(*pPeriodSizeInOut); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); if (status != noErr) { return ma_result_from_OSStatus(status); } - + *pPeriodSizeInOut = chosenBufferSizeInFrames; return MA_SUCCESS; } @@ -24089,7 +24092,7 @@ static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_t *pDeviceObjectID = defaultDeviceObjectID; return MA_SUCCESS; } - + /* If we get here it means we couldn't find the device. */ return MA_NO_DEVICE; } @@ -24101,7 +24104,7 @@ static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type devi /* Safety. */ *pDeviceObjectID = 0; - + if (pDeviceID == NULL) { /* Default device. */ return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); @@ -24116,15 +24119,15 @@ static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type devi if (result != MA_SUCCESS) { return result; } - + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - + char uid[256]; if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { continue; } - + if (deviceType == ma_device_type_playback) { if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { if (strcmp(uid, pDeviceID->coreaudio) == 0) { @@ -24146,7 +24149,7 @@ static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type devi ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); } - + /* If we get here it means we couldn't find the device. */ return MA_NO_DEVICE; } @@ -24168,17 +24171,17 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec if (result != MA_SUCCESS) { return result; } - + desiredSampleRate = sampleRate; if (usingDefaultSampleRate) { desiredSampleRate = pOrigFormat->mSampleRate; } - + desiredChannelCount = channels; if (usingDefaultChannels) { desiredChannelCount = pOrigFormat->mChannelsPerFrame; } - + desiredFormat = format; if (usingDefaultFormat) { result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); @@ -24186,13 +24189,13 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec desiredFormat = g_maFormatPriorities[0]; } } - + /* If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. */ MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - + hasSupportedFormat = MA_FALSE; for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { ma_format format; @@ -24203,13 +24206,13 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec break; } } - + if (!hasSupportedFormat) { ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); return MA_FORMAT_NOT_SUPPORTED; } - - + + for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; ma_format thisSampleFormat; @@ -24221,9 +24224,9 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { continue; /* The format is not supported by miniaudio. Skip. */ } - + ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - + /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ if (thisDeviceFormat.mSampleRate != desiredSampleRate) { /* @@ -24325,7 +24328,7 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec } } } - + *pFormat = bestDeviceFormatSoFar; ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); @@ -24342,7 +24345,7 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au ma_result result; MA_ASSERT(pContext != NULL); - + if (deviceType == ma_device_type_playback) { deviceScope = kAudioUnitScope_Input; deviceBus = MA_COREAUDIO_OUTPUT_BUS; @@ -24350,23 +24353,23 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au deviceScope = kAudioUnitScope_Output; deviceBus = MA_COREAUDIO_INPUT_BUS; } - + status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); if (status != noErr) { return ma_result_from_OSStatus(status); } - + pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks); if (pChannelLayout == NULL) { return MA_OUT_OF_MEMORY; } - + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); if (status != noErr) { ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } - + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks); @@ -24397,7 +24400,7 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m AudioObjectID defaultDeviceObjectIDCapture; ma_result result; UInt32 iDevice; - + ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ @@ -24405,7 +24408,7 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m if (result != MA_SUCCESS) { return result; } - + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; ma_device_info info; @@ -24422,7 +24425,7 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m if (deviceObjectID == defaultDeviceObjectIDPlayback) { info.isDefault = MA_TRUE; } - + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { break; } @@ -24431,26 +24434,26 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m if (deviceObjectID == defaultDeviceObjectIDCapture) { info.isDefault = MA_TRUE; } - + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { break; } } } - + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); #else ma_device_info info; NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { return MA_SUCCESS; } } - + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { @@ -24458,7 +24461,7 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m } } #endif - + return MA_SUCCESS; } @@ -24472,7 +24475,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ if (shareMode == ma_share_mode_exclusive) { return MA_SHARE_MODE_NOT_SUPPORTED; } - + #if defined(MA_APPLE_DESKTOP) /* Desktop */ { @@ -24483,34 +24486,34 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ UInt32 iStreamDescription; UInt32 sampleRateRangeCount; AudioValueRange* pSampleRateRanges; - + ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MA_SUCCESS) { return result; } - + result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); if (result != MA_SUCCESS) { return result; } - + result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); if (result != MA_SUCCESS) { return result; } - + if (deviceObjectID == defaultDeviceObjectID) { pDeviceInfo->isDefault = MA_TRUE; } - + /* Formats. */ result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); if (result != MA_SUCCESS) { return result; } - + for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { ma_format format; ma_bool32 formatExists = MA_FALSE; @@ -24520,9 +24523,9 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ if (result != MA_SUCCESS) { continue; } - + MA_ASSERT(format != ma_format_unknown); - + /* Make sure the format isn't already in the output list. */ for (iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) { if (pDeviceInfo->formats[iOutputFormat] == format) { @@ -24530,29 +24533,29 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ break; } } - + if (!formatExists) { pDeviceInfo->formats[pDeviceInfo->formatCount++] = format; } } - + ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - - + + /* Channels. */ result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels); if (result != MA_SUCCESS) { return result; } pDeviceInfo->maxChannels = pDeviceInfo->minChannels; - - + + /* Sample rates. */ result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); if (result != MA_SUCCESS) { return result; } - + if (sampleRateRangeCount > 0) { UInt32 iSampleRate; pDeviceInfo->minSampleRate = UINT32_MAX; @@ -24578,7 +24581,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ AudioUnitElement formatElement; AudioStreamBasicDescription bestFormat; UInt32 propSize; - + /* We want to ensure we use a consistent device name to device enumeration. */ if (pDeviceID != NULL) { ma_bool32 found = MA_FALSE; @@ -24601,7 +24604,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ } } } - + if (!found) { return MA_DOES_NOT_EXIST; } @@ -24612,8 +24615,8 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } } - - + + /* Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to @@ -24624,40 +24627,40 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; - + component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (component == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - + status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } - + formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - + propSize = sizeof(bestFormat); status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); return ma_result_from_OSStatus(status); } - + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); audioUnit = NULL; - - + + pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame; pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame; - + pDeviceInfo->formatCount = 1; result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]); if (result != MA_SUCCESS) { return result; } - + /* It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do this we just get the shared instance and inspect. @@ -24671,7 +24674,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ } } #endif - + (void)pDeviceInfo; /* Unused. */ return MA_SUCCESS; } @@ -24693,7 +24696,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { layout = ma_stream_layout_deinterleaved; } - + if (layout == ma_stream_layout_interleaved) { /* For now we can assume everything is interleaved. */ UInt32 iBuffer; @@ -24707,7 +24710,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData); } } - + #if defined(MA_DEBUG_OUTPUT) printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); #endif @@ -24727,7 +24730,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl } else { /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ - + /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something very strange has happened and we're not going to support it. @@ -24735,7 +24738,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { ma_uint8 tempBuffer[4096]; UInt32 iBuffer; - + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); ma_uint32 framesRemaining = frameCountPerBuffer; @@ -24747,25 +24750,25 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl if (framesToRead > framesRemaining) { framesToRead = framesRemaining; } - + if (pDevice->type == ma_device_type_duplex) { ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB); } else { ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer); } - + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); } - + ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - + framesRemaining -= framesToRead; } } } } - + (void)pActionFlags; (void)pTimeStamp; (void)busNumber; @@ -24783,16 +24786,16 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla OSStatus status; MA_ASSERT(pDevice != NULL); - + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; MA_ASSERT(pRenderedBufferList); - + /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ layout = ma_stream_layout_interleaved; if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { layout = ma_stream_layout_deinterleaved; } - + #if defined(MA_DEBUG_OUTPUT) printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers); #endif @@ -24802,13 +24805,13 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a problem when a future call to this callback specifies a larger number of frames. - + To work around this we need to explicitly set the size of each buffer to their respective size in bytes. */ for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferSizeInBytes; } - + status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { #if defined(MA_DEBUG_OUTPUT) @@ -24816,7 +24819,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla #endif return status; } - + if (layout == ma_stream_layout_interleaved) { for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { @@ -24835,25 +24838,25 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla */ ma_uint8 silentBuffer[4096]; ma_uint32 framesRemaining; - + MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - + framesRemaining = frameCount; while (framesRemaining > 0) { ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); if (framesToSend > framesRemaining) { framesToSend = framesRemaining; } - + if (pDevice->type == ma_device_type_duplex) { ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB); } else { ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer); } - + framesRemaining -= framesToSend; } - + #if defined(MA_DEBUG_OUTPUT) printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); #endif @@ -24862,7 +24865,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla } else { /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - + /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something very strange has happened and we're not going to support it. @@ -24878,11 +24881,11 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla if (framesToSend > framesRemaining) { framesToSend = framesRemaining; } - + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); } - + ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); if (pDevice->type == ma_device_type_duplex) { @@ -24910,7 +24913,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); - + /* There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) @@ -24922,7 +24925,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio if (onStop) { onStop(pDevice); } - + ma_event_signal(&pDevice->coreaudio.stopEvent); } else { UInt32 isRunning; @@ -24931,16 +24934,16 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio if (status != noErr) { return; /* Don't really know what to do in this case... just ignore it, I suppose... */ } - + if (!isRunning) { ma_stop_proc onStop; /* The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - + 1) When the device is unplugged, this will be called _before_ the default device change notification. 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - + For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. */ if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || @@ -24955,17 +24958,17 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { return; } - + /* Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most likely be successful in switching to the new device. - + TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted. */ return; } - + /* Getting here means we need to stop the device. */ onStop = pDevice->onStop; if (onStop) { @@ -24988,12 +24991,12 @@ static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) { ma_device_type deviceType; - + /* Not sure if I really need to check this, but it makes me feel better. */ if (addressCount == 0) { return noErr; } - + if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { deviceType = ma_device_type_playback; } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { @@ -25001,14 +25004,14 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn } else { return noErr; /* Should never hit this. */ } - + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { ma_uint32 iDevice; for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { ma_result reinitResult; ma_device* pDevice; - + pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { if (deviceType == ma_device_type_playback) { @@ -25020,10 +25023,10 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; } - + if (reinitResult == MA_SUCCESS) { ma_device__post_init_setup(pDevice, deviceType); - + /* Restart the device if required. If this fails we need to stop the device entirely. */ if (ma_device_get_state(pDevice) == MA_STATE_STARTED) { OSStatus status; @@ -25050,7 +25053,7 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn } } ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - + /* Unused parameters. */ (void)objectID; (void)pUserData; @@ -25061,7 +25064,7 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); - + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { /* Don't do anything if we've already initializd device tracking. */ @@ -25069,15 +25072,15 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = kAudioObjectPropertyElementMaster; - + ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - + g_DeviceTrackingInitCounter_CoreAudio += 1; } } @@ -25089,19 +25092,19 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); - + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { g_DeviceTrackingInitCounter_CoreAudio -= 1; - + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = kAudioObjectPropertyElementMaster; - + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); @@ -25109,19 +25112,19 @@ static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pCont if (g_ppTrackedDevices_CoreAudio != NULL) { ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.", MA_INVALID_OPERATION); } - + ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); } } ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - + return MA_SUCCESS; } static ma_result ma_device__track__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { /* Allocate memory if required. */ @@ -25129,35 +25132,35 @@ static ma_result ma_device__track__coreaudio(ma_device* pDevice) ma_uint32 oldCap; ma_uint32 newCap; ma_device** ppNewDevices; - + oldCap = g_TrackedDeviceCap_CoreAudio; newCap = g_TrackedDeviceCap_CoreAudio * 2; if (newCap == 0) { newCap = 1; } - + ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks); if (ppNewDevices == NULL) { ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); return MA_OUT_OF_MEMORY; } - + g_ppTrackedDevices_CoreAudio = ppNewDevices; g_TrackedDeviceCap_CoreAudio = newCap; } - + g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; g_TrackedDeviceCount_CoreAudio += 1; } ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - + return MA_SUCCESS; } static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { ma_uint32 iDevice; @@ -25168,16 +25171,16 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; } - + g_TrackedDeviceCount_CoreAudio -= 1; - + /* If there's nothing else in the list we need to free memory. */ if (g_TrackedDeviceCount_CoreAudio == 0) { ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); g_ppTrackedDevices_CoreAudio = NULL; g_TrackedDeviceCap_CoreAudio = 0; } - + break; } } @@ -25291,7 +25294,7 @@ static void ma_device_uninit__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED); - + #if defined(MA_APPLE_DESKTOP) /* Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll @@ -25305,14 +25308,14 @@ static void ma_device_uninit__coreaudio(ma_device* pDevice) [pRouteChangeHandler remove_handler]; } #endif - + if (pDevice->coreaudio.audioUnitCapture != NULL) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } if (pDevice->coreaudio.audioUnitPlayback != NULL) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); } - + if (pDevice->coreaudio.pAudioBufferList) { ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } @@ -25386,16 +25389,16 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev pData->component = NULL; pData->audioUnit = NULL; pData->pAudioBufferList = NULL; - + #if defined(MA_APPLE_DESKTOP) result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MA_SUCCESS) { return result; } - + pData->deviceObjectID = deviceObjectID; #endif - + /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ pData->periodsOut = pData->periodsIn; if (pData->periodsOut == 0) { @@ -25404,35 +25407,35 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev if (pData->periodsOut > 16) { pData->periodsOut = 16; } - - + + /* Audio unit. */ status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } - - + + /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ enableIOFlag = 1; if (deviceType == ma_device_type_capture) { enableIOFlag = 0; } - + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } - + enableIOFlag = (enableIOFlag == 0) ? 1 : 0; status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } - - + + /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ #if defined(MA_APPLE_DESKTOP) status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); @@ -25456,24 +25459,24 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev break; } } - + if (found == MA_FALSE) { return MA_DOES_NOT_EXIST; } } } #endif - + /* Format. This is the hardest part of initialization because there's a few variables to take into account. 1) The format must be supported by the device. 2) The format must be supported miniaudio. 3) There's a priority that miniaudio prefers. - + Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - + On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. */ { @@ -25498,13 +25501,13 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } - + /* Technical Note TN2091: Device input using the HAL Output Audio Unit https://developer.apple.com/library/archive/technotes/tn2091/_index.html - + This documentation says the following: - + The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with @@ -25513,11 +25516,11 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it safe and apply the same rule to output as well. - + I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - + Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with this, however, is that it actually changes the sample rate at the operating system level and not just the application. This could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a @@ -25529,14 +25532,14 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev if (pData->allowNominalSampleRateChange) { AudioValueRange sampleRateRange; AudioObjectPropertyAddress propAddress; - + sampleRateRange.mMinimum = bestFormat.mSampleRate; sampleRateRange.mMaximum = bestFormat.mSampleRate; - + propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = kAudioObjectPropertyElementMaster; - + status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); if (status != noErr) { bestFormat.mSampleRate = origFormat.mSampleRate; @@ -25544,7 +25547,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } else { bestFormat.mSampleRate = origFormat.mSampleRate; } - + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { /* We failed to set the format, so fall back to the current format of the audio unit. */ @@ -25552,7 +25555,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } #else bestFormat = origFormat; - + /* Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since @@ -25562,7 +25565,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; MA_ASSERT(pAudioSession != NULL); - + [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; bestFormat.mSampleRate = pAudioSession.sampleRate; @@ -25577,25 +25580,25 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; } } - + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } #endif - + result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); if (result != MA_SUCCESS) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } - + if (pData->formatOut == ma_format_unknown) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MA_FORMAT_NOT_SUPPORTED; } - + pData->channelsOut = bestFormat.mChannelsPerFrame; pData->sampleRateOut = bestFormat.mSampleRate; } @@ -25604,7 +25607,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev if (pData->channelsOut > MA_MAX_CHANNELS) { pData->channelsOut = MA_MAX_CHANNELS; } - + /* Internal channel map. This is weird in my testing. If I use the AudioObject to get the channel map, the channel descriptions are set to "Unknown" for some reason. To work around @@ -25630,16 +25633,16 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* TODO: Figure out how to get the channel map using AVAudioSession. */ ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); #endif - + /* Buffer size. Not allowing this to be configurable on iOS. */ actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - + #if defined(MA_APPLE_DESKTOP) if (actualPeriodSizeInFrames == 0) { actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); } - + result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); if (result != MA_SUCCESS) { return result; @@ -25659,9 +25662,9 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* During testing I discovered that the buffer size can be too big. You'll get an error like this: - + kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - + Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. */ @@ -25670,9 +25673,9 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } - + pData->periodSizeInFramesOut = actualPeriodSizeInFrames; - + /* We need a buffer list if this is an input device. We render into this in the input callback. */ if (deviceType == ma_device_type_capture) { ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; @@ -25689,13 +25692,13 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev allocationSize += sizeof(AudioBuffer) * pData->channelsOut; allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut; } - + pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, &pContext->allocationCallbacks); if (pBufferList == NULL) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MA_OUT_OF_MEMORY; } - + if (isInterleaved) { pData->audioBufferSizeInBytes = actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut); pBufferList->mNumberBuffers = 1; @@ -25712,10 +25715,10 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * iBuffer); } } - + pData->pAudioBufferList = pBufferList; } - + /* Callbacks. */ callbackInfo.inputProcRefCon = pDevice_DoNotReference; if (deviceType == ma_device_type_playback) { @@ -25733,7 +25736,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev return ma_result_from_OSStatus(status); } } - + /* We need to listen for stop events. */ if (pData->registerStopEvent) { status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); @@ -25742,7 +25745,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev return ma_result_from_OSStatus(status); } } - + /* Initialize the audio unit. */ status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); if (status != noErr) { @@ -25751,7 +25754,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } - + /* Grab the name. */ #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); @@ -25762,7 +25765,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); } #endif - + return result; } @@ -25776,7 +25779,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } - + data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ if (deviceType == ma_device_type_capture) { @@ -25790,7 +25793,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap; data.shareMode = pDevice->capture.shareMode; data.registerStopEvent = MA_TRUE; - + if (disposePreviousAudioUnit) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); @@ -25809,7 +25812,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap; data.shareMode = pDevice->playback.shareMode; data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - + if (disposePreviousAudioUnit) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); @@ -25828,7 +25831,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev if (result != MA_SUCCESS) { return result; } - + if (deviceType == ma_device_type_capture) { #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; @@ -25836,7 +25839,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; pDevice->coreaudio.audioBufferSizeInBytes = data.audioBufferSizeInBytes; - + pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; pDevice->capture.internalSampleRate = data.sampleRateOut; @@ -25848,7 +25851,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; #endif pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - + pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; pDevice->playback.internalSampleRate = data.sampleRateOut; @@ -25856,7 +25859,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->playback.internalPeriods = data.periodsOut; } - + return MA_SUCCESS; } #endif /* MA_APPLE_DESKTOP */ @@ -25878,7 +25881,7 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } - + /* Capture needs to be initialized first. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; @@ -25901,12 +25904,12 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { data.periodsIn = 3; } - + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { return result; } - + pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; @@ -25914,14 +25917,14 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; pDevice->coreaudio.audioBufferSizeInBytes = data.audioBufferSizeInBytes; - + pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; pDevice->capture.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->capture.internalPeriods = data.periodsOut; - + #if defined(MA_APPLE_DESKTOP) /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly @@ -25932,7 +25935,7 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device } #endif } - + /* Playback. */ if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; @@ -25946,7 +25949,7 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap; data.shareMode = pConfig->playback.shareMode; - + /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ if (pConfig->deviceType == ma_device_type_duplex) { data.periodSizeInFramesIn = pDevice->capture.internalPeriodSizeInFrames; @@ -25958,7 +25961,7 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device data.periodsIn = pConfig->periods; data.registerStopEvent = MA_TRUE; } - + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { @@ -25969,20 +25972,20 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device } return result; } - + pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; #endif pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - + pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; pDevice->playback.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->playback.internalPeriods = data.periodsOut; - + #if defined(MA_APPLE_DESKTOP) /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly @@ -25993,11 +25996,11 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device } #endif } - + pDevice->coreaudio.originalPeriodSizeInFrames = pConfig->periodSizeInFrames; pDevice->coreaudio.originalPeriodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; pDevice->coreaudio.originalPeriods = pConfig->periods; - + /* When stopping the device, a callback is called on another thread. We need to wait for this callback before returning from ma_device_stop(). This event is used for this. @@ -26039,14 +26042,14 @@ static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device static ma_result ma_device_start__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (status != noErr) { return ma_result_from_OSStatus(status); } } - + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); if (status != noErr) { @@ -26056,7 +26059,7 @@ static ma_result ma_device_start__coreaudio(ma_device* pDevice) return ma_result_from_OSStatus(status); } } - + return MA_SUCCESS; } @@ -26072,14 +26075,14 @@ static ma_result ma_device_stop__coreaudio(ma_device* pDevice) return ma_result_from_OSStatus(status); } } - + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); if (status != noErr) { return ma_result_from_OSStatus(status); } } - + /* We need to wait for the callback to finish before returning. */ ma_event_wait(&pDevice->coreaudio.stopEvent); return MA_SUCCESS; @@ -26090,7 +26093,7 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_coreaudio); - + #if defined(MA_APPLE_MOBILE) if (!pContext->coreaudio.noAudioSessionDeactivate) { if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { @@ -26098,7 +26101,7 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) } } #endif - + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); @@ -26175,7 +26178,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma } } } - + if (!pConfig->coreaudio.noAudioSessionActivate) { if (![pAudioSession setActive:true error:nil]) { return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND); @@ -26183,23 +26186,23 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma } } #endif - + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } - + pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); - - + + pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); @@ -26218,7 +26221,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - + if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); @@ -26229,7 +26232,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma return MA_API_NOT_FOUND; } } - + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); @@ -26244,7 +26247,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - + #if defined(MA_APPLE_DESKTOP) pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; @@ -26252,7 +26255,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; #endif - + pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; @@ -26267,7 +26270,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma #endif pContext->isBackendAsynchronous = MA_TRUE; - + pContext->onUninit = ma_context_uninit__coreaudio; pContext->onEnumDevices = ma_context_enumerate_devices__coreaudio; pContext->onGetDeviceInfo = ma_context_get_device_info__coreaudio; @@ -26275,7 +26278,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma pContext->onDeviceUninit = ma_device_uninit__coreaudio; pContext->onDeviceStart = ma_device_start__coreaudio; pContext->onDeviceStop = ma_device_stop__coreaudio; - + /* Audio component. */ { AudioComponentDescription desc; @@ -26288,7 +26291,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; - + pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) @@ -26299,7 +26302,7 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma return MA_FAILED_TO_INIT_BACKEND; } } - + #if !defined(MA_APPLE_MOBILE) result = ma_context__init_device_tracking__coreaudio(pContext); if (result != MA_SUCCESS) { @@ -26428,7 +26431,7 @@ static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int b if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { return ma_format_unknown; } - + if (bits == 8 && bps == 1 && sig == 0) { return ma_format_u8; } @@ -26444,7 +26447,7 @@ static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int b if (bits == 32 && bps == 4 && sig == 1) { return ma_format_s32; } - + return ma_format_unknown; } @@ -26454,7 +26457,7 @@ static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps unsigned int iConfig; MA_ASSERT(caps != NULL); - + bestFormat = ma_format_unknown; for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { unsigned int iEncoding; @@ -26469,7 +26472,7 @@ static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } - + bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; @@ -26479,7 +26482,7 @@ static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps if (format == ma_format_unknown) { continue; /* Format not supported. */ } - + if (bestFormat == ma_format_unknown) { bestFormat = format; } else { @@ -26489,7 +26492,7 @@ static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps } } } - + return bestFormat; } @@ -26500,7 +26503,7 @@ static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* ca MA_ASSERT(caps != NULL); MA_ASSERT(requiredFormat != ma_format_unknown); - + /* Just pick whatever configuration has the most channels. */ maxChannels = 0; for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { @@ -26518,7 +26521,7 @@ static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* ca if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } - + bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; @@ -26528,7 +26531,7 @@ static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* ca if (format != requiredFormat) { continue; } - + /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; @@ -26539,24 +26542,24 @@ static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* ca } else { chan = caps->confs[iConfig].rchan; } - + if ((chan & (1UL << iChannel)) == 0) { continue; } - + if (deviceType == ma_device_type_playback) { channels = caps->pchan[iChannel]; } else { channels = caps->rchan[iChannel]; } - + if (maxChannels < channels) { maxChannels = channels; } } } } - + return maxChannels; } @@ -26570,7 +26573,7 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* MA_ASSERT(requiredFormat != ma_format_unknown); MA_ASSERT(requiredChannels > 0); MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - + firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ bestSampleRate = 0; @@ -26589,7 +26592,7 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } - + bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; @@ -26599,7 +26602,7 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* if (format != requiredFormat) { continue; } - + /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; @@ -26611,36 +26614,36 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* } else { chan = caps->confs[iConfig].rchan; } - + if ((chan & (1UL << iChannel)) == 0) { continue; } - + if (deviceType == ma_device_type_playback) { channels = caps->pchan[iChannel]; } else { channels = caps->rchan[iChannel]; } - + if (channels != requiredChannels) { continue; } - + /* Getting here means we have found a compatible encoding/channel pair. */ for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { ma_uint32 rate = (ma_uint32)caps->rate[iRate]; ma_uint32 ratePriority; - + if (firstSampleRate == 0) { firstSampleRate = rate; } - + /* Disregard this rate if it's not a standard one. */ ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); if (ratePriority == (ma_uint32)-1) { continue; } - + if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ bestSampleRate = rate; } @@ -26648,12 +26651,12 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* } } } - + /* If a standard sample rate was not found just fall back to the first one that was iterated. */ if (bestSampleRate == 0) { bestSampleRate = firstSampleRate; } - + return bestSampleRate; } @@ -26665,9 +26668,9 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); - + /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - + /* Playback. */ if (!isTerminating) { handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); @@ -26677,13 +26680,13 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en MA_ZERO_OBJECT(&deviceInfo); ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); } } - + /* Capture. */ if (!isTerminating) { handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); @@ -26695,11 +26698,11 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); } } - + return MA_SUCCESS; } @@ -26712,7 +26715,7 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi MA_ASSERT(pContext != NULL); (void)shareMode; - + /* We need to open the device before we can get information about it. */ if (pDeviceID == NULL) { ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); @@ -26721,16 +26724,16 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); } - + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); if (handle == NULL) { return MA_NO_DEVICE; } - + if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { return MA_ERROR; } - + for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { /* The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give @@ -26753,7 +26756,7 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } - + bits = caps.enc[iEncoding].bits; bps = caps.enc[iEncoding].bps; sig = caps.enc[iEncoding].sig; @@ -26763,7 +26766,7 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi if (format == ma_format_unknown) { continue; /* Format not supported. */ } - + /* Add this format if it doesn't already exist. */ for (iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) { if (pDeviceInfo->formats[iExistingFormat] == format) { @@ -26771,12 +26774,12 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi break; } } - + if (!formatExists) { pDeviceInfo->formats[pDeviceInfo->formatCount++] = format; } } - + /* Channels. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; @@ -26787,17 +26790,17 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi } else { chan = caps.confs[iConfig].rchan; } - + if ((chan & (1UL << iChannel)) == 0) { continue; } - + if (deviceType == ma_device_type_playback) { channels = caps.pchan[iChannel]; } else { channels = caps.rchan[iChannel]; } - + if (pDeviceInfo->minChannels > channels) { pDeviceInfo->minChannels = channels; } @@ -26805,7 +26808,7 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi pDeviceInfo->maxChannels = channels; } } - + /* Sample rates. */ for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { @@ -26893,7 +26896,7 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this to the requested channels, regardless of whether or not the default channel count is requested. - + For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the value returned by ma_find_best_channels_from_sio_cap__sndio(). */ @@ -26916,7 +26919,7 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev } } } - + if (pDevice->usingDefaultSampleRate) { sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); } @@ -26925,7 +26928,7 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); par.msb = 0; par.le = ma_is_little_endian(); - + switch (format) { case ma_format_u8: { @@ -26933,21 +26936,21 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev par.bps = 1; par.sig = 0; } break; - + case ma_format_s24: { par.bits = 24; par.bps = 3; par.sig = 1; } break; - + case ma_format_s32: { par.bits = 32; par.bps = 4; par.sig = 1; } break; - + case ma_format_s16: case ma_format_f32: default: @@ -26957,7 +26960,7 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev par.sig = 1; } break; } - + if (deviceType == ma_device_type_capture) { par.rchan = channels; } else { @@ -26973,7 +26976,7 @@ static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_dev par.round = internalPeriodSizeInFrames; par.appbufsz = par.round * pConfig->periods; - + if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED); @@ -27089,7 +27092,7 @@ static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFram if (pFramesWritten != NULL) { *pFramesWritten = frameCount; } - + return MA_SUCCESS; } @@ -27109,7 +27112,7 @@ static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_ if (pFramesRead != NULL) { *pFramesRead = frameCount; } - + return MA_SUCCESS; } @@ -27134,7 +27137,7 @@ static ma_result ma_device_main_loop__sndio(ma_device* pDevice) /* The process is: device_read -> convert -> callback -> convert -> device_write */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; @@ -27311,7 +27314,7 @@ static ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_con if (pContext->sndio.sndioSO == NULL) { return MA_NO_BACKEND; } - + pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); @@ -27379,10 +27382,10 @@ static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* MA_ASSERT(id != NULL); MA_ASSERT(idSize > 0); MA_ASSERT(deviceIndex >= 0); - + baseLen = strlen(base); MA_ASSERT(idSize > baseLen); - + ma_strcpy_s(id, idSize, base); ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); } @@ -27396,26 +27399,26 @@ static ma_result ma_extract_device_index_from_id__audio4(const char* id, const c MA_ASSERT(id != NULL); MA_ASSERT(base != NULL); MA_ASSERT(pIndexOut != NULL); - + idLen = strlen(id); baseLen = strlen(base); if (idLen <= baseLen) { return MA_ERROR; /* Doesn't look like the id starts with the base. */ } - + if (strncmp(id, base, baseLen) != 0) { return MA_ERROR; /* ID does not begin with base. */ } - + deviceIndexStr = id + baseLen; if (deviceIndexStr[0] == '\0') { return MA_ERROR; /* No index specified in the ID. */ } - + if (pIndexOut) { *pIndexOut = atoi(deviceIndexStr); } - + return MA_SUCCESS; } @@ -27523,7 +27526,7 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext MA_ASSERT(pContext != NULL); MA_ASSERT(fd >= 0); MA_ASSERT(pInfoOut != NULL); - + (void)pContext; (void)deviceType; @@ -27559,7 +27562,7 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext } if (deviceType == ma_device_type_playback) { - pInfoOut->minChannels = fdInfo.play.channels; + pInfoOut->minChannels = fdInfo.play.channels; pInfoOut->maxChannels = fdInfo.play.channels; pInfoOut->minSampleRate = fdInfo.play.sample_rate; pInfoOut->maxSampleRate = fdInfo.play.sample_rate; @@ -27573,13 +27576,13 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { return MA_ERROR; } - + format = ma_format_from_swpar__audio4(&fdPar); if (format == ma_format_unknown) { return MA_FORMAT_NOT_SUPPORTED; } pInfoOut->formats[pInfoOut->formatCount++] = format; - + if (deviceType == ma_device_type_playback) { pInfoOut->minChannels = fdPar.pchan; pInfoOut->maxChannels = fdPar.pchan; @@ -27587,11 +27590,11 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext pInfoOut->minChannels = fdPar.rchan; pInfoOut->maxChannels = fdPar.rchan; } - + pInfoOut->minSampleRate = fdPar.rate; pInfoOut->maxSampleRate = fdPar.rate; #endif - + return MA_SUCCESS; } @@ -27603,7 +27606,7 @@ static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_e MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); - + /* Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" version here since we can open it even when another process has control of the "/dev/audioN" device. @@ -27615,13 +27618,13 @@ static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_e ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - + if (stat(devpath, &st) < 0) { break; } /* The device exists, but we need to check if it's usable as playback and/or capture. */ - + /* Playback. */ if (!isTerminating) { fd = open(devpath, O_RDONLY, 0); @@ -27633,11 +27636,11 @@ static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_e if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } - + close(fd); } } - + /* Capture. */ if (!isTerminating) { fd = open(devpath, O_WRONLY, 0); @@ -27649,16 +27652,16 @@ static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_e if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } - + close(fd); } } - + if (isTerminating) { break; } } - + return MA_SUCCESS; } @@ -27671,7 +27674,7 @@ static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_dev MA_ASSERT(pContext != NULL); (void)shareMode; - + /* We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number from the device ID which will be in "/dev/audioN" format. @@ -27685,23 +27688,23 @@ static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_dev if (result != MA_SUCCESS) { return result; } - + ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); } - + fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); if (fd == -1) { return MA_NO_DEVICE; } - + if (deviceIndex == -1) { ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); } else { ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); } - + result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - + close(fd); return result; } @@ -27791,7 +27794,7 @@ static ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device close(fd); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED); } - + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { close(fd); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED); @@ -27874,10 +27877,10 @@ static ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device if (internalPeriodSizeInBytes < 16) { internalPeriodSizeInBytes = 16; } - + fdPar.nblks = pConfig->periods; fdPar.round = internalPeriodSizeInBytes; - + if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { close(fd); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED); @@ -27931,7 +27934,7 @@ static ma_result ma_device_init__audio4(ma_context* pContext, const ma_device_co if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - + pDevice->audio4.fdCapture = -1; pDevice->audio4.fdPlayback = -1; @@ -28096,7 +28099,7 @@ static ma_result ma_device_main_loop__audio4(ma_device* pDevice) /* The process is: device_read -> convert -> callback -> convert -> device_write */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; @@ -28510,7 +28513,7 @@ static void ma_device_uninit__oss(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { close(pDevice->oss.fdCapture); } - + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { close(pDevice->oss.fdPlayback); } @@ -28625,7 +28628,7 @@ static ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_co The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if it should be done before or after format/channels/rate. - + OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual value. */ @@ -28633,7 +28636,7 @@ static ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_co ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInBytes; ma_uint32 ossFragmentSizePower; - + periodSizeInFrames = pConfig->periodSizeInFrames; if (periodSizeInFrames == 0) { periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, (ma_uint32)ossSampleRate); @@ -28723,13 +28726,13 @@ static ma_result ma_device_stop__oss(ma_device* pDevice) /* We want to use SNDCTL_DSP_HALT. From the documentation: - + In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread that actually reads/writes the audio device. It must not be called by some master thread to kill the audio thread. The audio thread will not stop or get any kind of notification that the device was stopped by the master thread. The device gets stopped but the next read or write call will silently restart the device. - + This is actually safe in our case, because this function is only ever called from within our worker thread anyway. Just keep this in mind, though... */ @@ -28767,7 +28770,7 @@ static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames if (pFramesWritten != NULL) { *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); } - + return MA_SUCCESS; } @@ -28783,7 +28786,7 @@ static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_ui if (resultOSS < 0) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", ma_result_from_errno(errno)); } - + if (pFramesRead != NULL) { *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); } @@ -28806,7 +28809,7 @@ static ma_result ma_device_main_loop__oss(ma_device* pDevice) /* The process is: device_read -> convert -> callback -> convert -> device_write */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; @@ -29023,47 +29026,63 @@ typedef int32_t ma_aaudio_sharing_mode_t; typedef int32_t ma_aaudio_format_t; typedef int32_t ma_aaudio_stream_state_t; typedef int32_t ma_aaudio_performance_mode_t; +typedef int32_t ma_aaudio_content_type_t; +typedef int32_t ma_aaudio_input_preset_t; typedef int32_t ma_aaudio_data_callback_result_t; /* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 +#define MA_AAUDIO_OK 0 /* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 +#define MA_AAUDIO_DIRECTION_OUTPUT 0 +#define MA_AAUDIO_DIRECTION_INPUT 1 /* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 +#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 +#define MA_AAUDIO_SHARING_MODE_SHARED 1 /* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 +#define MA_AAUDIO_FORMAT_PCM_I16 1 +#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 /* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 +#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 +#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 +#define MA_AAUDIO_STREAM_STATE_OPEN 2 +#define MA_AAUDIO_STREAM_STATE_STARTING 3 +#define MA_AAUDIO_STREAM_STATE_STARTED 4 +#define MA_AAUDIO_STREAM_STATE_PAUSING 5 +#define MA_AAUDIO_STREAM_STATE_PAUSED 6 +#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 +#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 +#define MA_AAUDIO_STREAM_STATE_STOPPING 9 +#define MA_AAUDIO_STREAM_STATE_STOPPED 10 +#define MA_AAUDIO_STREAM_STATE_CLOSING 11 +#define MA_AAUDIO_STREAM_STATE_CLOSED 12 +#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 /* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 +#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 +#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 +#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 + +/* Content types. */ +#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 +#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 +#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 +#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 + +/* Input presets. */ +#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 +#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 +#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 +#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 +#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 +#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 /* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 +#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 +#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 /* Objects. */ typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; @@ -29085,6 +29104,8 @@ typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataC typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); +typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); @@ -29109,6 +29130,34 @@ static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) return MA_ERROR; } +static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) +{ + switch (contentType) { + case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; + case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; + case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; + case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; + default: break; + } + + return MA_AAUDIO_CONTENT_TYPE_SPEECH; +} + +static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) +{ + switch (inputPreset) { + case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; + case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; + case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; + case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; + case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; + default: break; + } + + return MA_AAUDIO_INPUT_PRESET_GENERIC; +} + static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) { ma_device* pDevice = (ma_device*)pUserData; @@ -29214,8 +29263,16 @@ static ma_result ma_open_stream__aaudio(ma_context* pContext, ma_device_type dev ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods); if (deviceType == ma_device_type_capture) { + if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default) { + ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); + } + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); } else { + if (pConfig->aaudio.contentType != ma_aaudio_content_type_default) { + ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); + } + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } @@ -29324,7 +29381,7 @@ static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_dev } else { pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; } - + /* Name */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); @@ -29600,7 +29657,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_aaudio); - + ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; @@ -29638,6 +29695,8 @@ static ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_co pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); @@ -29910,7 +29969,7 @@ static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_e /* TODO: Test Me. - + This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) @@ -30021,7 +30080,7 @@ static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_dev /* TODO: Test Me. - + This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) @@ -30479,7 +30538,7 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl; MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); } - + source.pLocator = &queue; source.pFormat = (SLDataFormat_PCM*)&pcm;