3333#include < math.h>
3434#include < pthread.h> /* For mutexes */
3535
36+ #define COREAUDIO_MIC_BUFFER_DURATION_S 0 .01f /* Buffer duration in seconds (e.g., 0.01f for 10ms) */
37+
3638typedef struct coreaudio_macos_microphone
3739{
3840 AudioUnit audio_unit;
@@ -85,35 +87,35 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
8587 coreaudio_macos_microphone_t *mic = (coreaudio_macos_microphone_t *)inRefCon;
8688 if (!mic || !atomic_load_explicit (&mic->is_running , memory_order_relaxed))
8789 return noErr;
88-
90+
8991 /* Calculate buffer size needed for this callback */
9092 size_t bytes_needed = inNumberFrames * mic->format .mBytesPerFrame ;
9193 if (bytes_needed == 0 )
9294 return noErr;
93-
95+
9496 /* Use a temporary buffer for AudioUnitRender - zero-initialized to avoid random data */
9597 void *temp_buffer = calloc (1 , bytes_needed);
9698 if (!temp_buffer)
9799 {
98100 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to allocate temporary buffer\n " );
99101 return kAudio_MemFullError ;
100102 }
101-
103+
102104 /* Set up buffer list for rendering */
103105 AudioBufferList buffer_list;
104106 buffer_list.mNumberBuffers = 1 ;
105107 buffer_list.mBuffers [0 ].mDataByteSize = (UInt32)bytes_needed;
106108 buffer_list.mBuffers [0 ].mData = temp_buffer;
107109 buffer_list.mBuffers [0 ].mNumberChannels = mic->format .mChannelsPerFrame ;
108-
110+
109111 /* Render audio from INPUT BUS (bus 1) */
110112 OSStatus status = AudioUnitRender (mic->audio_unit ,
111113 ioActionFlags,
112114 inTimeStamp,
113115 1 , /* Input bus is always 1 for HAL AudioUnits */
114116 inNumberFrames,
115117 &buffer_list);
116-
118+
117119 /* Handle both complete success and partial success cases */
118120 if (status == noErr || status == kAudioUnitErr_NoConnection )
119121 {
@@ -122,7 +124,7 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
122124 {
123125 /* Ensure we don't write more than what was actually rendered */
124126 size_t actual_bytes = MIN (bytes_needed, buffer_list.mBuffers [0 ].mDataByteSize );
125-
127+
126128 /* Write all audio data to FIFO - no silence detection to reduce CPU overhead */
127129 {
128130 /* Check if there's enough space in the FIFO */
@@ -145,7 +147,7 @@ static OSStatus coreaudio_macos_input_callback(void *inRefCon,
145147 {
146148 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to render audio: %d \n " , (int )status);
147149 }
148-
150+
149151 /* Clean up temporary buffer */
150152 free (temp_buffer);
151153 return status;
@@ -183,16 +185,16 @@ static void coreaudio_macos_microphone_set_format(coreaudio_macos_microphone_t *
183185{
184186 if (!mic)
185187 return ;
186-
188+
187189 /* Store the format choice */
188190 mic->use_float = use_float;
189-
191+
190192 /* Setup the format for the AudioUnit based on the parameters */
191193 AudioStreamBasicDescription *format = &mic->format ;
192-
194+
193195 /* Clear the format struct */
194196 memset (format, 0 , sizeof (AudioStreamBasicDescription));
195-
197+
196198 /* Set basic properties */
197199 format->mSampleRate = mic->sample_rate ;
198200 format->mFormatID = kAudioFormatLinearPCM ;
@@ -204,7 +206,7 @@ static void coreaudio_macos_microphone_set_format(coreaudio_macos_microphone_t *
204206 format->mBitsPerChannel = use_float ? 32 : 16 ;
205207 format->mBytesPerFrame = format->mChannelsPerFrame * format->mBitsPerChannel / 8 ;
206208 format->mBytesPerPacket = format->mBytesPerFrame * format->mFramesPerPacket ;
207-
209+
208210 RARCH_LOG (" [CoreAudio macOS Mic]: Format setup: sample_rate=%.0f Hz, bits=%u , bytes_per_frame=%u , %s \n " ,
209211 format->mSampleRate ,
210212 (unsigned )format->mBitsPerChannel ,
@@ -422,7 +424,7 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
422424 {
423425 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to find HALOutput AudioComponent.\n " );
424426 if (mic->device_name ) free (mic->device_name );
425-
427+
426428 free (mic);
427429 return NULL ;
428430 }
@@ -455,7 +457,7 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
455457 return NULL ;
456458 }
457459 RARCH_LOG (" [CoreAudio macOS Mic]: AudioUnit instance created: %p \n " , mic->audio_unit );
458-
460+
459461 /* Set the specific audio device if one was requested (not default) */
460462 if (mic->selected_device_id != kAudioObjectUnknown )
461463 {
@@ -482,7 +484,7 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
482484
483485 /* 2. Enable input on the AudioUnit - CRITICAL STEP */
484486 UInt32 enable_io = 1 ;
485-
487+
486488 /* Enable input on input bus */
487489 status = AudioUnitSetProperty (mic->audio_unit ,
488490 kAudioOutputUnitProperty_EnableIO ,
@@ -495,7 +497,7 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
495497 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to enable input on AudioUnit: %d \n " , (int )status);
496498 AudioComponentInstanceDispose (mic->audio_unit );
497499 if (mic->device_name ) free (mic->device_name );
498-
500+
499501 free (mic);
500502 return NULL ;
501503 }
@@ -564,7 +566,7 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
564566 (unsigned int )actual_device_id_check, (unsigned int )mic->selected_device_id );
565567 }
566568 /* Update mic->selected_device_id to reflect the actual one, especially if it was default or fallback */
567- mic->selected_device_id = actual_device_id_check;
569+ mic->selected_device_id = actual_device_id_check;
568570 }
569571 else
570572 {
@@ -574,9 +576,9 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
574576
575577 /* 4. Set stream format */
576578 coreaudio_macos_microphone_set_format (mic, false /* use int16 for better compatibility */ );
577-
579+
578580 RARCH_LOG (" [CoreAudio macOS Mic]: After format setup - mic->format.mSampleRate = %.0f Hz\n " , mic->format .mSampleRate );
579-
581+
580582 /* First get the device's native format from the INPUT scope, INPUT bus (bus 1) */
581583 AudioStreamBasicDescription device_format = {0 };
582584 UInt32 prop_size = sizeof (device_format);
@@ -589,30 +591,30 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
589591 if (format_status != noErr) {
590592 RARCH_WARN (" [CoreAudio macOS Mic]: Could not get device native format from input bus: %d . Using default format.\n " , (int )format_status);
591593 } else {
592- RARCH_LOG (" [CoreAudio macOS Mic]: Device native format (input bus) - Sample rate: %.0f , Channels: %u , Bits: %u \n " ,
593- device_format.mSampleRate ,
594+ RARCH_LOG (" [CoreAudio macOS Mic]: Device native format (input bus) - Sample rate: %.0f , Channels: %u , Bits: %u \n " ,
595+ device_format.mSampleRate ,
594596 (unsigned )device_format.mChannelsPerFrame ,
595597 (unsigned )device_format.mBitsPerChannel );
596-
598+
597599 /* Use the device's native sample rate but ALWAYS use mono for better compatibility */
598600 mic->format .mSampleRate = device_format.mSampleRate ;
599601 mic->format .mChannelsPerFrame = 1 ; /* Always force mono regardless of device capabilities */
600-
602+
601603 /* Update mic->channels to match what we're actually using */
602604 mic->channels = device_format.mChannelsPerFrame ;
603-
605+
604606 /* Re-calculate format bytes per frame to match the channel count */
605607 mic->format .mBytesPerFrame = mic->format .mChannelsPerFrame * (mic->format .mBitsPerChannel / 8 );
606608 mic->format .mFramesPerPacket = 1 ;
607609 mic->format .mBytesPerPacket = mic->format .mBytesPerFrame * mic->format .mFramesPerPacket ;
608-
610+
609611 RARCH_LOG (" [CoreAudio macOS Mic]: Updated format - SR: %.0f , CH: %u , BitsPerCh: %u , BytesPerFrame: %u \n " ,
610- mic->format .mSampleRate ,
612+ mic->format .mSampleRate ,
611613 (unsigned )mic->format .mChannelsPerFrame ,
612614 (unsigned )mic->format .mBitsPerChannel ,
613615 (unsigned )mic->format .mBytesPerFrame );
614616 }
615-
617+
616618 RARCH_LOG (" [CoreAudio macOS Mic]: Setting client format on OUTPUT scope of INPUT bus\n " );
617619 OSStatus set_format_status = AudioUnitSetProperty (mic->audio_unit ,
618620 kAudioUnitProperty_StreamFormat ,
@@ -625,34 +627,34 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
625627 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to set client stream format: %d \n " , (int )set_format_status);
626628 AudioComponentInstanceDispose (mic->audio_unit );
627629 if (mic->device_name ) free (mic->device_name );
628-
630+
629631 free (mic);
630632 return NULL ;
631633 }
632-
634+
633635 /* Set up input callback */
634636 AURenderCallbackStruct callback_struct;
635637 callback_struct.inputProc = coreaudio_macos_input_callback;
636638 callback_struct.inputProcRefCon = mic;
637-
639+
638640 status = AudioUnitSetProperty (mic->audio_unit ,
639- kAudioOutputUnitProperty_SetInputCallback ,
640- kAudioUnitScope_Global ,
641- 0 ,
641+ kAudioOutputUnitProperty_SetInputCallback ,
642+ kAudioUnitScope_Global ,
643+ 0 ,
642644 &callback_struct,
643645 sizeof (callback_struct));
644646 if (status != noErr)
645647 {
646648 RARCH_ERR (" [CoreAudio macOS Mic]: Failed to set INPUT callback: %d \n " , (int )status);
647649 AudioComponentInstanceDispose (mic->audio_unit );
648650 if (mic->device_name ) free (mic->device_name );
649-
651+
650652 free (mic);
651653 return NULL ;
652654 }
653-
655+
654656 RARCH_LOG (" [CoreAudio macOS Mic]: Initializing AudioUnit %p \n " , mic->audio_unit );
655-
657+
656658 /* Set a smaller buffer frame size for lower latency */
657659 UInt32 buffer_frame_size = 256 ; /* Small buffer for lower latency */
658660 status = AudioUnitSetProperty (mic->audio_unit ,
@@ -663,16 +665,16 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
663665 sizeof (buffer_frame_size));
664666 if (status != noErr)
665667 {
666- RARCH_WARN (" [CoreAudio macOS Mic]: Failed to set buffer frame size to %u : %d \n " ,
668+ RARCH_WARN (" [CoreAudio macOS Mic]: Failed to set buffer frame size to %u : %d \n " ,
667669 (unsigned )buffer_frame_size, (int )status);
668670 /* Non-fatal, continue with default buffer size */
669671 }
670672 else
671673 {
672- RARCH_LOG (" [CoreAudio macOS Mic]: Set buffer frame size to %u frames for lower latency\n " ,
674+ RARCH_LOG (" [CoreAudio macOS Mic]: Set buffer frame size to %u frames for lower latency\n " ,
673675 (unsigned )buffer_frame_size);
674676 }
675-
677+
676678 status = AudioUnitInitialize (mic->audio_unit );
677679 if (status != noErr)
678680 {
@@ -685,13 +687,13 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
685687 atomic_store (&mic->is_initialized , true );
686688 RARCH_LOG (" [CoreAudio macOS Mic]: AudioUnit successfully initialized.\n " );
687689
688- /* Initialize FIFO buffer - 50ms buffer size */
689- size_t fifo_size = mic->format .mSampleRate * mic->format .mBytesPerFrame * 0 . 05f ;
690- RARCH_LOG (" [CoreAudio macOS Mic]: Creating FIFO buffer of size %u bytes (%.1f ms at %.0f Hz)\n " ,
691- (unsigned )fifo_size,
690+ /* Initialize FIFO buffer */
691+ size_t fifo_size = mic->format .mSampleRate * mic->format .mBytesPerFrame * COREAUDIO_MIC_BUFFER_DURATION_S ;
692+ RARCH_LOG (" [CoreAudio macOS Mic]: Creating FIFO buffer of size %u bytes (%.1f ms at %.0f Hz)\n " ,
693+ (unsigned )fifo_size,
692694 (float )fifo_size * 1000 .0f / (mic->format .mSampleRate * mic->format .mBytesPerFrame ),
693695 mic->format .mSampleRate );
694-
696+
695697 /* Create and initialize FIFO buffer */
696698 mic->fifo = fifo_new (fifo_size);
697699 if (!mic->fifo )
@@ -700,23 +702,23 @@ static void coreaudio_macos_microphone_device_list_free(const void *data, struct
700702 AudioUnitUninitialize (mic->audio_unit );
701703 AudioComponentInstanceDispose (mic->audio_unit );
702704 if (mic->device_name ) free (mic->device_name );
703-
705+
704706 free (mic);
705707 return NULL ;
706708 }
707-
709+
708710 /* Explicitly clear the FIFO buffer to ensure no random data */
709711 fifo_clear (mic->fifo );
710712 RARCH_LOG (" [CoreAudio macOS Mic]: FIFO buffer initialized and cleared\n " );
711713
712714 /* Allocate AudioBufferList for AudioUnitRender in the callback */
713-
715+
714716 /* We don't need to pre-allocate buffer list or calculate max frames per slice
715717 * since we're using temporary buffers in the callback like the iOS version */
716718
717- RARCH_LOG (" [CoreAudio macOS Mic]: COMPLETE CONFIG - Sample rate: %.0f Hz, Format: %s , Channels: %u \n " ,
718- mic->format .mSampleRate ,
719- mic->use_float ? " Float" : " Int16" ,
719+ RARCH_LOG (" [CoreAudio macOS Mic]: COMPLETE CONFIG - Sample rate: %.0f Hz, Format: %s , Channels: %u \n " ,
720+ mic->format .mSampleRate ,
721+ mic->use_float ? " Float" : " Int16" ,
720722 (unsigned )mic->format .mChannelsPerFrame );
721723
722724 if (new_rate)
@@ -749,7 +751,7 @@ static void coreaudio_macos_microphone_close_mic(void *data, void *mic_data)
749751
750752 /* No buffer list cleanup needed since we're using temporary buffers */
751753
752-
754+
753755
754756 if (mic->fifo )
755757 {
@@ -790,21 +792,21 @@ static bool coreaudio_macos_microphone_start_mic(void *data, void *mic_data)
790792
791793 /* Check microphone permission on macOS */
792794 RARCH_LOG (" [CoreAudio macOS Mic]: Checking microphone permission...\n " );
793-
795+
794796 /* Check if we have input devices available */
795797 AudioObjectPropertyAddress prop_addr = {
796798 kAudioHardwarePropertyDefaultInputDevice ,
797799 kAudioObjectPropertyScopeGlobal ,
798800 kAudioObjectPropertyElementMaster
799801 };
800-
802+
801803 AudioDeviceID default_input_device = kAudioObjectUnknown ;
802804 UInt32 prop_size = sizeof (AudioDeviceID);
803805 OSStatus perm_status = AudioObjectGetPropertyData (kAudioObjectSystemObject , &prop_addr, 0 , NULL , &prop_size, &default_input_device);
804-
806+
805807 if (perm_status != noErr || default_input_device == kAudioObjectUnknown )
806808 {
807- RARCH_ERR (" [CoreAudio macOS Mic]: No default input device available or permission denied. Status: %d , Device ID: %u \n " ,
809+ RARCH_ERR (" [CoreAudio macOS Mic]: No default input device available or permission denied. Status: %d , Device ID: %u \n " ,
808810 (int )perm_status, (unsigned )default_input_device);
809811 }
810812 else
@@ -823,7 +825,7 @@ static bool coreaudio_macos_microphone_start_mic(void *data, void *mic_data)
823825 RARCH_ERR (" [CoreAudio macOS Mic]: No FIFO buffer available\n " );
824826 return false ;
825827 }
826-
828+
827829 OSStatus status = AudioOutputUnitStart (mic->audio_unit );
828830 if (status == noErr)
829831 {
0 commit comments