extract delay to define, 10ms

This commit is contained in:
Joseph Mattiello 2025-06-10 12:21:13 -04:00
parent 663f318d5f
commit 3344af01d7
No known key found for this signature in database
1 changed files with 58 additions and 56 deletions

View File

@ -33,6 +33,8 @@
#include <math.h>
#include <pthread.h> /* For mutexes */
#define COREAUDIO_MIC_BUFFER_DURATION_S 0.01f /* Buffer duration in seconds (e.g., 0.01f for 10ms) */
typedef struct coreaudio_macos_microphone
{
AudioUnit audio_unit;
@ -85,12 +87,12 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
coreaudio_macos_microphone_t *mic = (coreaudio_macos_microphone_t*)inRefCon;
if (!mic || !atomic_load_explicit(&mic->is_running, memory_order_relaxed))
return noErr;
/* Calculate buffer size needed for this callback */
size_t bytes_needed = inNumberFrames * mic->format.mBytesPerFrame;
if (bytes_needed == 0)
return noErr;
/* Use a temporary buffer for AudioUnitRender - zero-initialized to avoid random data */
void *temp_buffer = calloc(1, bytes_needed);
if (!temp_buffer)
@ -98,14 +100,14 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
RARCH_ERR("[CoreAudio macOS Mic]: Failed to allocate temporary buffer\n");
return kAudio_MemFullError;
}
/* Set up buffer list for rendering */
AudioBufferList buffer_list;
buffer_list.mNumberBuffers = 1;
buffer_list.mBuffers[0].mDataByteSize = (UInt32)bytes_needed;
buffer_list.mBuffers[0].mData = temp_buffer;
buffer_list.mBuffers[0].mNumberChannels = mic->format.mChannelsPerFrame;
/* Render audio from INPUT BUS (bus 1) */
OSStatus status = AudioUnitRender(mic->audio_unit,
ioActionFlags,
@ -113,7 +115,7 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
1, /* Input bus is always 1 for HAL AudioUnits */
inNumberFrames,
&buffer_list);
/* Handle both complete success and partial success cases */
if (status == noErr || status == kAudioUnitErr_NoConnection)
{
@ -122,7 +124,7 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
{
/* Ensure we don't write more than what was actually rendered */
size_t actual_bytes = MIN(bytes_needed, buffer_list.mBuffers[0].mDataByteSize);
/* Write all audio data to FIFO - no silence detection to reduce CPU overhead */
{
/* Check if there's enough space in the FIFO */
@ -145,7 +147,7 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
{
RARCH_ERR("[CoreAudio macOS Mic]: Failed to render audio: %d\n", (int)status);
}
/* Clean up temporary buffer */
free(temp_buffer);
return status;
@ -183,16 +185,16 @@ static void coreaudio_macos_microphone_set_format(coreaudio_macos_microphone_t *
{
if (!mic)
return;
/* Store the format choice */
mic->use_float = use_float;
/* Setup the format for the AudioUnit based on the parameters */
AudioStreamBasicDescription *format = &mic->format;
/* Clear the format struct */
memset(format, 0, sizeof(AudioStreamBasicDescription));
/* Set basic properties */
format->mSampleRate = mic->sample_rate;
format->mFormatID = kAudioFormatLinearPCM;
@ -204,7 +206,7 @@ static void coreaudio_macos_microphone_set_format(coreaudio_macos_microphone_t *
format->mBitsPerChannel = use_float ? 32 : 16;
format->mBytesPerFrame = format->mChannelsPerFrame * format->mBitsPerChannel / 8;
format->mBytesPerPacket = format->mBytesPerFrame * format->mFramesPerPacket;
RARCH_LOG("[CoreAudio macOS Mic]: Format setup: sample_rate=%.0f Hz, bits=%u, bytes_per_frame=%u, %s\n",
format->mSampleRate,
(unsigned)format->mBitsPerChannel,
@ -422,7 +424,7 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
{
RARCH_ERR("[CoreAudio macOS Mic]: Failed to find HALOutput AudioComponent.\n");
if (mic->device_name) free(mic->device_name);
free(mic);
return NULL;
}
@ -455,7 +457,7 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
return NULL;
}
RARCH_LOG("[CoreAudio macOS Mic]: AudioUnit instance created: %p\n", mic->audio_unit);
/* Set the specific audio device if one was requested (not default) */
if (mic->selected_device_id != kAudioObjectUnknown)
{
@ -482,7 +484,7 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
/* 2. Enable input on the AudioUnit - CRITICAL STEP */
UInt32 enable_io = 1;
/* Enable input on input bus */
status = AudioUnitSetProperty(mic->audio_unit,
kAudioOutputUnitProperty_EnableIO,
@ -495,7 +497,7 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
RARCH_ERR("[CoreAudio macOS Mic]: Failed to enable input on AudioUnit: %d\n", (int)status);
AudioComponentInstanceDispose(mic->audio_unit);
if (mic->device_name) free(mic->device_name);
free(mic);
return NULL;
}
@ -564,7 +566,7 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
(unsigned int)actual_device_id_check, (unsigned int)mic->selected_device_id);
}
/* Update mic->selected_device_id to reflect the actual one, especially if it was default or fallback */
mic->selected_device_id = actual_device_id_check;
mic->selected_device_id = actual_device_id_check;
}
else
{
@ -574,9 +576,9 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
/* 4. Set stream format */
coreaudio_macos_microphone_set_format(mic, false /* use int16 for better compatibility */);
RARCH_LOG("[CoreAudio macOS Mic]: After format setup - mic->format.mSampleRate = %.0f Hz\n", mic->format.mSampleRate);
/* First get the device's native format from the INPUT scope, INPUT bus (bus 1) */
AudioStreamBasicDescription device_format = {0};
UInt32 prop_size = sizeof(device_format);
@ -589,30 +591,30 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
if (format_status != noErr) {
RARCH_WARN("[CoreAudio macOS Mic]: Could not get device native format from input bus: %d. Using default format.\n", (int)format_status);
} else {
RARCH_LOG("[CoreAudio macOS Mic]: Device native format (input bus) - Sample rate: %.0f, Channels: %u, Bits: %u\n",
device_format.mSampleRate,
RARCH_LOG("[CoreAudio macOS Mic]: Device native format (input bus) - Sample rate: %.0f, Channels: %u, Bits: %u\n",
device_format.mSampleRate,
(unsigned)device_format.mChannelsPerFrame,
(unsigned)device_format.mBitsPerChannel);
/* Use the device's native sample rate but ALWAYS use mono for better compatibility */
mic->format.mSampleRate = device_format.mSampleRate;
mic->format.mChannelsPerFrame = 1; /* Always force mono regardless of device capabilities */
/* Update mic->channels to match what we're actually using */
mic->channels = device_format.mChannelsPerFrame;
/* Re-calculate format bytes per frame to match the channel count */
mic->format.mBytesPerFrame = mic->format.mChannelsPerFrame * (mic->format.mBitsPerChannel / 8);
mic->format.mFramesPerPacket = 1;
mic->format.mBytesPerPacket = mic->format.mBytesPerFrame * mic->format.mFramesPerPacket;
RARCH_LOG("[CoreAudio macOS Mic]: Updated format - SR: %.0f, CH: %u, BitsPerCh: %u, BytesPerFrame: %u\n",
mic->format.mSampleRate,
mic->format.mSampleRate,
(unsigned)mic->format.mChannelsPerFrame,
(unsigned)mic->format.mBitsPerChannel,
(unsigned)mic->format.mBytesPerFrame);
}
RARCH_LOG("[CoreAudio macOS Mic]: Setting client format on OUTPUT scope of INPUT bus\n");
OSStatus set_format_status = AudioUnitSetProperty(mic->audio_unit,
kAudioUnitProperty_StreamFormat,
@ -625,20 +627,20 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
RARCH_ERR("[CoreAudio macOS Mic]: Failed to set client stream format: %d\n", (int)set_format_status);
AudioComponentInstanceDispose(mic->audio_unit);
if (mic->device_name) free(mic->device_name);
free(mic);
return NULL;
}
/* Set up input callback */
AURenderCallbackStruct callback_struct;
callback_struct.inputProc = coreaudio_macos_input_callback;
callback_struct.inputProcRefCon = mic;
status = AudioUnitSetProperty(mic->audio_unit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callback_struct,
sizeof(callback_struct));
if (status != noErr)
@ -646,13 +648,13 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
RARCH_ERR("[CoreAudio macOS Mic]: Failed to set INPUT callback: %d\n", (int)status);
AudioComponentInstanceDispose(mic->audio_unit);
if (mic->device_name) free(mic->device_name);
free(mic);
return NULL;
}
RARCH_LOG("[CoreAudio macOS Mic]: Initializing AudioUnit %p\n", mic->audio_unit);
/* Set a smaller buffer frame size for lower latency */
UInt32 buffer_frame_size = 256; /* Small buffer for lower latency */
status = AudioUnitSetProperty(mic->audio_unit,
@ -663,16 +665,16 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
sizeof(buffer_frame_size));
if (status != noErr)
{
RARCH_WARN("[CoreAudio macOS Mic]: Failed to set buffer frame size to %u: %d\n",
RARCH_WARN("[CoreAudio macOS Mic]: Failed to set buffer frame size to %u: %d\n",
(unsigned)buffer_frame_size, (int)status);
/* Non-fatal, continue with default buffer size */
}
else
{
RARCH_LOG("[CoreAudio macOS Mic]: Set buffer frame size to %u frames for lower latency\n",
RARCH_LOG("[CoreAudio macOS Mic]: Set buffer frame size to %u frames for lower latency\n",
(unsigned)buffer_frame_size);
}
status = AudioUnitInitialize(mic->audio_unit);
if (status != noErr)
{
@ -685,13 +687,13 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
atomic_store(&mic->is_initialized, true);
RARCH_LOG("[CoreAudio macOS Mic]: AudioUnit successfully initialized.\n");
/* Initialize FIFO buffer - 50ms buffer size */
size_t fifo_size = mic->format.mSampleRate * mic->format.mBytesPerFrame * 0.05f;
RARCH_LOG("[CoreAudio macOS Mic]: Creating FIFO buffer of size %u bytes (%.1f ms at %.0f Hz)\n",
(unsigned)fifo_size,
/* Initialize FIFO buffer */
size_t fifo_size = mic->format.mSampleRate * mic->format.mBytesPerFrame * COREAUDIO_MIC_BUFFER_DURATION_S;
RARCH_LOG("[CoreAudio macOS Mic]: Creating FIFO buffer of size %u bytes (%.1f ms at %.0f Hz)\n",
(unsigned)fifo_size,
(float)fifo_size * 1000.0f / (mic->format.mSampleRate * mic->format.mBytesPerFrame),
mic->format.mSampleRate);
/* Create and initialize FIFO buffer */
mic->fifo = fifo_new(fifo_size);
if (!mic->fifo)
@ -700,23 +702,23 @@ static void *coreaudio_macos_microphone_open_mic(void *data, const char *device,
AudioUnitUninitialize(mic->audio_unit);
AudioComponentInstanceDispose(mic->audio_unit);
if (mic->device_name) free(mic->device_name);
free(mic);
return NULL;
}
/* Explicitly clear the FIFO buffer to ensure no random data */
fifo_clear(mic->fifo);
RARCH_LOG("[CoreAudio macOS Mic]: FIFO buffer initialized and cleared\n");
/* Allocate AudioBufferList for AudioUnitRender in the callback */
/* We don't need to pre-allocate buffer list or calculate max frames per slice
* since we're using temporary buffers in the callback like the iOS version */
RARCH_LOG("[CoreAudio macOS Mic]: COMPLETE CONFIG - Sample rate: %.0f Hz, Format: %s, Channels: %u\n",
mic->format.mSampleRate,
mic->use_float ? "Float" : "Int16",
RARCH_LOG("[CoreAudio macOS Mic]: COMPLETE CONFIG - Sample rate: %.0f Hz, Format: %s, Channels: %u\n",
mic->format.mSampleRate,
mic->use_float ? "Float" : "Int16",
(unsigned)mic->format.mChannelsPerFrame);
if (new_rate)
@ -749,7 +751,7 @@ static void coreaudio_macos_microphone_close_mic(void *data, void *mic_data)
/* No buffer list cleanup needed since we're using temporary buffers */
if (mic->fifo)
{
@ -790,21 +792,21 @@ static bool coreaudio_macos_microphone_start_mic(void *data, void *mic_data)
/* Check microphone permission on macOS */
RARCH_LOG("[CoreAudio macOS Mic]: Checking microphone permission...\n");
/* Check if we have input devices available */
AudioObjectPropertyAddress prop_addr = {
kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
AudioDeviceID default_input_device = kAudioObjectUnknown;
UInt32 prop_size = sizeof(AudioDeviceID);
OSStatus perm_status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop_addr, 0, NULL, &prop_size, &default_input_device);
if (perm_status != noErr || default_input_device == kAudioObjectUnknown)
{
RARCH_ERR("[CoreAudio macOS Mic]: No default input device available or permission denied. Status: %d, Device ID: %u\n",
RARCH_ERR("[CoreAudio macOS Mic]: No default input device available or permission denied. Status: %d, Device ID: %u\n",
(int)perm_status, (unsigned)default_input_device);
}
else
@ -823,7 +825,7 @@ static bool coreaudio_macos_microphone_start_mic(void *data, void *mic_data)
RARCH_ERR("[CoreAudio macOS Mic]: No FIFO buffer available\n");
return false;
}
OSStatus status = AudioOutputUnitStart(mic->audio_unit);
if (status == noErr)
{