Skip to content

Instantly share code, notes, and snippets.

@jamiebullock
Last active September 24, 2022 10:27
Show Gist options
  • Save jamiebullock/10829962 to your computer and use it in GitHub Desktop.
Save jamiebullock/10829962 to your computer and use it in GitHub Desktop.
Set up RemoteIO or HALOutput Audio Unit for recording
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#define INPUT_BYTES_PER_SAMPLE sizeof(SInt16)
#define AU_IO_COMPONENT kAudioUnitSubType_RemoteIO
#define AU_DEFAULT_COMPONENT AU_IO_COMPONENT
#elif TARGET_OS_MAC
#define INPUT_BYTES_PER_SAMPLE sizeof(Float32)
#define AU_IO_COMPONENT kAudioUnitSubType_HALOutput
#define AU_DEFAULT_COMPONENT kAudioUnitSubType_DefaultOutput
#endif
AS_ErrorCode as_InputAudioUnit_Create (AS_AudioRecorder *audioRecorder)
{
AudioComponentDescription inputcd = {0};
inputcd.componentType = kAudioUnitType_Output;
inputcd.componentSubType = AU_IO_COMPONENT;
inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent audioComponent = AudioComponentFindNext(NULL, &inputcd);
if (audioComponent == NULL)
{
AS_LOG(AS_LogType_Error, "Error: can't get audio component");
assert(false);
return AS_ErrorCode_Error;
}
assert_noErr(AudioComponentInstanceNew(audioComponent, &audioRecorder->inputUnit));
UInt32 disableFlag = 0;
UInt32 enableFlag = 1;
OSStatus status = noErr;
status = AudioUnitSetProperty(
audioRecorder->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
INPUT_BUS_ID,
&enableFlag,
sizeof(enableFlag)
);
assert_noErr(status);
status = AudioUnitSetProperty(
audioRecorder->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
OUTPUT_BUS_ID,
&disableFlag,
sizeof(enableFlag)
);
assert_noErr(status);
UInt32 propertySize = 0;
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
AudioDeviceID defaultDevice = kAudioObjectUnknown;
propertySize = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;
status = AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&defaultDeviceProperty,
0,
NULL,
&propertySize,
&defaultDevice
);
assert_noErr(status);
status = AudioUnitSetProperty(
audioRecorder->inputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
OUTPUT_BUS_ID,
&defaultDevice,
sizeof(defaultDevice)
);
assert_noErr(status);
#endif
propertySize = sizeof(AudioStreamBasicDescription);
status = AudioUnitGetProperty(
audioRecorder->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
INPUT_BUS_ID,
&audioRecorder->streamFormat,
&propertySize
),
assert_noErr(status);
AudioStreamBasicDescription deviceFormat;
status = AudioUnitGetProperty(
audioRecorder->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
INPUT_BUS_ID,
&deviceFormat,
&propertySize
);
assert_noErr(status);
audioRecorder->streamFormat.mSampleRate = deviceFormat.mSampleRate;
propertySize = sizeof (AudioStreamBasicDescription);
status = AudioUnitSetProperty(
audioRecorder->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
INPUT_BUS_ID,
&audioRecorder->streamFormat,
propertySize
);
assert_noErr(status);
Float64 sampleRate = 0.f;
propertySize = sizeof(sampleRate);
#if TARGET_IPHONE_SIMULATOR
sampleRate = AS_SAMPLERATE;
#elif TARGET_OS_IPHONE
status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
&propertySize,
&sampleRate);
assert_noErr(status);
#elif TARGET_OS_MAC
AudioObjectPropertyAddress propertyAddressSampleRate = {
kAudioDevicePropertyNominalSampleRate,
kAudioObjectPropertyScopeInput,
0
};
propertySize = sizeof(sampleRate);
status = AudioObjectGetPropertyData(defaultDevice,
&propertyAddressSampleRate,
0,
NULL,
&propertySize,
&sampleRate);
assert_noErr(status);
#endif
if (sampleRate != AS_SAMPLERATE)
{
AS_LOG(AS_LogType_Error, "Samplerate mismatch. Device samplerate was %f but audio unit samplerate was set to %d",
sampleRate, AS_SAMPLERATE);
sampleRate = AS_SAMPLERATE;
AS_LOG(AS_LogType_Warning, "Attempting to force samplerate to: %f", sampleRate);
#if TARGET_OS_IPHONE
status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate,
propertySize,
&sampleRate);
assert_noErr(status);
AudioSessionSetActive(true);
status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
&propertySize,
&sampleRate);
#elif TARGET_OS_MAC
status = AudioObjectSetPropertyData(defaultDevice,
&propertyAddressSampleRate,
0,
NULL,
propertySize,
&sampleRate);
#endif
assert_noErr(status);
if (sampleRate != AS_SAMPLERATE)
{
AS_LOG(AS_LogType_Error, "failed to set samplerate\n");
assert(false);
}
}
UInt32 bufferSizeFrames = 0;
#if TARGET_IPHONE_SIMULATOR
bufferSizeFrames = 512;
#elif TARGET_OS_IPHONE
Float32 preferredBufferSize = 512.f / sampleRate; // in seconds
status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
sizeof(preferredBufferSize),
&preferredBufferSize);
assert_noErr(status);
Float32 bufferDuration = 0;
propertySize = sizeof(Float32);
status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
&propertySize,
&bufferDuration);
assert_noErr(status);
bufferSizeFrames = bufferDuration * sampleRate;
bufferSizeFrames = next_power_of_two(bufferSizeFrames);
#elif TARGET_OS_MAC
propertySize = sizeof(UInt32);
status = AudioUnitGetProperty(
audioRecorder->inputUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
0,
&bufferSizeFrames,
&propertySize
);
assert_noErr(status);
#endif
size_t bufferListTotalBytes = 0;
audioRecorder->bufferSizeFrames = bufferSizeFrames;
audioRecorder->inputBufferList = as_AudioBufferList_New(audioRecorder->streamFormat, bufferSizeFrames, &bufferListTotalBytes);
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = inputCallback;
callbackStruct.inputProcRefCon = audioRecorder;
status = AudioUnitSetProperty(
audioRecorder->inputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callbackStruct,
sizeof(callbackStruct)
);
assert_noErr(status);
status = AudioUnitInitialize(audioRecorder->inputUnit);
assert_noErr(status);
return AS_ErrorCode_NoError;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment