Core Audio HALOutput 音频单元调用输入回调失败
Core Audio HALOutput audio unit fails to call input callback
我正尝试在 Mac 上使用 Core Audio 录制音频,但我的输入回调未被调用。我确定我遗漏了一些明显的东西,但我一整天都在搜索和尝试。我的代码是用 Rust 编写的,但希望 C/C++ 的人不会太难理解。这是:
extern crate coreaudio_sys;
#[macro_use]
extern crate objc;
use std::mem::size_of_val;
use std::os::raw::c_void;
use std::ptr;
use coreaudio_sys::*;
use objc::runtime::{Object, Class};
#[link(name = "Foundation", kind = "framework")]
extern {}
fn main() {
unsafe {
// First, create an audio unit.
let desc = AudioComponentDescription {
componentType: kAudioUnitType_Output,
componentSubType: kAudioUnitSubType_HALOutput,
componentManufacturer: kAudioUnitManufacturer_Apple,
..AudioComponentDescription::default()
};
let mut au_hal: AudioComponentInstance = ptr::null_mut();
let comp = AudioComponentFindNext(ptr::null_mut(), &desc);
assert_ne!(comp, ptr::null_mut());
let mut code: OSStatus;
code = AudioComponentInstanceNew(comp, &mut au_hal);
assert_eq!(code, 0);
// Next, enable IO for input.
let enable_io: UInt32 = 1;
code = AudioUnitSetProperty(
au_hal,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&enable_io as *const _ as *const _,
size_of_val(&enable_io) as u32,
);
assert_eq!(code, 0);
// Set the input callback.
let input = AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: ptr::null_mut(),
};
code = AudioUnitSetProperty(
au_hal,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
1,
&input as *const _ as *const _,
size_of_val(&input) as u32,
);
assert_eq!(code, 0);
// Finally, initialize and start the unit.
code = AudioUnitInitialize(au_hal);
assert_eq!(code, 0);
code = AudioOutputUnitStart(au_hal);
assert_eq!(code, 0);
// Edit: As per Rhythmic Fistman's answer, use an
// NSRunLoop instead of a "loop {}".
// This code translates to [[NSRunLoop mainRunLoop] run]; in ObjC.
//
// This did not solve my problem.
let NSRunLoop = Class::get("NSRunLoop").unwrap();
let run_loop: *mut Object = msg_send![NSRunLoop, mainRunLoop];
msg_send![run_loop, run];
}
}
出于调试目的,我的 input_proc
回调在打印 "hi input":
后只是 returns 0
unsafe extern "C" fn input_proc(
in_ref_con: *mut c_void,
io_action_flags: *mut AudioUnitRenderActionFlags,
in_time_stamp: *const AudioTimeStamp,
in_bus_number: UInt32,
in_number_frames: UInt32,
io_data: *mut AudioBufferList,
) -> OSStatus {
println!("hi input");
0
}
因此,我希望看到 "hi input" 重复打印到控制台。实际上,根本不会打印任何内容。
在 Mac 上,您需要告诉 AudioUnit
使用哪个音频设备。在我的例子中,我不得不禁用设备上的扬声器输出——可能是因为我的 Mac 上的默认输入设备只进行输入。
在 C 中(我想 Rust 版本将需要一些 as *const _ as *const _
s 和 as u32
s):
// disable output (needed, otherwise I can't set device)
flag = 0;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &flag, sizeof(flag));
assert(noErr == err);
// get default input device
AudioObjectPropertyAddress propAddr = {
.mSelector = kAudioHardwarePropertyDefaultInputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
AudioDeviceID deviceID;
UInt32 propertySize = sizeof(deviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddr, 0, NULL, &propertySize, &deviceID);
assert(noErr == err);
// set audio unit current device
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, sizeof(deviceID));
assert(noErr == err);
完整:
static OSStatus
AUInputCallback(
void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
printf("poot\n");
return noErr;
}
int
main(int argc, char **argv) {
AudioComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_HALOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
.componentFlags = 0,
.componentFlagsMask = 0
};
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
assert(comp);
AudioUnit au;
OSStatus err = AudioComponentInstanceNew(comp, &au);
assert(noErr == err);
// enable input
UInt32 flag = 1;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));
assert(noErr == err);
// disable output (needed, otherwise I can't set device)
flag = 0;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &flag, sizeof(flag));
assert(noErr == err);
AURenderCallbackStruct cb;
cb.inputProc = AUInputCallback;
cb.inputProcRefCon = NULL;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));
assert(noErr == err);
// set audio unit device to default input device
AudioObjectPropertyAddress propAddr = {
.mSelector = kAudioHardwarePropertyDefaultInputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
AudioDeviceID deviceID;
UInt32 propertySize = sizeof(deviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddr, 0, NULL, &propertySize, &deviceID);
assert(noErr == err);
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, sizeof(deviceID));
assert(noErr == err);
err = AudioUnitInitialize(au);
assert(noErr == err);
err = AudioOutputUnitStart(au);
assert(noErr == err);
while (1) {}
return 0;
}
我正尝试在 Mac 上使用 Core Audio 录制音频,但我的输入回调未被调用。我确定我遗漏了一些明显的东西,但我一整天都在搜索和尝试。我的代码是用 Rust 编写的,但希望 C/C++ 的人不会太难理解。这是:
extern crate coreaudio_sys;
#[macro_use]
extern crate objc;
use std::mem::size_of_val;
use std::os::raw::c_void;
use std::ptr;
use coreaudio_sys::*;
use objc::runtime::{Object, Class};
#[link(name = "Foundation", kind = "framework")]
extern {}
fn main() {
unsafe {
// First, create an audio unit.
let desc = AudioComponentDescription {
componentType: kAudioUnitType_Output,
componentSubType: kAudioUnitSubType_HALOutput,
componentManufacturer: kAudioUnitManufacturer_Apple,
..AudioComponentDescription::default()
};
let mut au_hal: AudioComponentInstance = ptr::null_mut();
let comp = AudioComponentFindNext(ptr::null_mut(), &desc);
assert_ne!(comp, ptr::null_mut());
let mut code: OSStatus;
code = AudioComponentInstanceNew(comp, &mut au_hal);
assert_eq!(code, 0);
// Next, enable IO for input.
let enable_io: UInt32 = 1;
code = AudioUnitSetProperty(
au_hal,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&enable_io as *const _ as *const _,
size_of_val(&enable_io) as u32,
);
assert_eq!(code, 0);
// Set the input callback.
let input = AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: ptr::null_mut(),
};
code = AudioUnitSetProperty(
au_hal,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
1,
&input as *const _ as *const _,
size_of_val(&input) as u32,
);
assert_eq!(code, 0);
// Finally, initialize and start the unit.
code = AudioUnitInitialize(au_hal);
assert_eq!(code, 0);
code = AudioOutputUnitStart(au_hal);
assert_eq!(code, 0);
// Edit: As per Rhythmic Fistman's answer, use an
// NSRunLoop instead of a "loop {}".
// This code translates to [[NSRunLoop mainRunLoop] run]; in ObjC.
//
// This did not solve my problem.
let NSRunLoop = Class::get("NSRunLoop").unwrap();
let run_loop: *mut Object = msg_send![NSRunLoop, mainRunLoop];
msg_send![run_loop, run];
}
}
出于调试目的,我的 input_proc
回调在打印 "hi input":
unsafe extern "C" fn input_proc(
in_ref_con: *mut c_void,
io_action_flags: *mut AudioUnitRenderActionFlags,
in_time_stamp: *const AudioTimeStamp,
in_bus_number: UInt32,
in_number_frames: UInt32,
io_data: *mut AudioBufferList,
) -> OSStatus {
println!("hi input");
0
}
因此,我希望看到 "hi input" 重复打印到控制台。实际上,根本不会打印任何内容。
在 Mac 上,您需要告诉 AudioUnit
使用哪个音频设备。在我的例子中,我不得不禁用设备上的扬声器输出——可能是因为我的 Mac 上的默认输入设备只进行输入。
在 C 中(我想 Rust 版本将需要一些 as *const _ as *const _
s 和 as u32
s):
// disable output (needed, otherwise I can't set device)
flag = 0;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &flag, sizeof(flag));
assert(noErr == err);
// get default input device
AudioObjectPropertyAddress propAddr = {
.mSelector = kAudioHardwarePropertyDefaultInputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
AudioDeviceID deviceID;
UInt32 propertySize = sizeof(deviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddr, 0, NULL, &propertySize, &deviceID);
assert(noErr == err);
// set audio unit current device
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, sizeof(deviceID));
assert(noErr == err);
完整:
static OSStatus
AUInputCallback(
void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
printf("poot\n");
return noErr;
}
int
main(int argc, char **argv) {
AudioComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_HALOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
.componentFlags = 0,
.componentFlagsMask = 0
};
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
assert(comp);
AudioUnit au;
OSStatus err = AudioComponentInstanceNew(comp, &au);
assert(noErr == err);
// enable input
UInt32 flag = 1;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));
assert(noErr == err);
// disable output (needed, otherwise I can't set device)
flag = 0;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &flag, sizeof(flag));
assert(noErr == err);
AURenderCallbackStruct cb;
cb.inputProc = AUInputCallback;
cb.inputProcRefCon = NULL;
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));
assert(noErr == err);
// set audio unit device to default input device
AudioObjectPropertyAddress propAddr = {
.mSelector = kAudioHardwarePropertyDefaultInputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
AudioDeviceID deviceID;
UInt32 propertySize = sizeof(deviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddr, 0, NULL, &propertySize, &deviceID);
assert(noErr == err);
err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, sizeof(deviceID));
assert(noErr == err);
err = AudioUnitInitialize(au);
assert(noErr == err);
err = AudioOutputUnitStart(au);
assert(noErr == err);
while (1) {}
return 0;
}