【问题标题】:Core Audio HALOutput audio unit fails to call input callbackCore Audio HALOutput 音频单元调用输入回调失败
【发布时间】:2018-07-06 03:32:23
【问题描述】:

我正在尝试在 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”后只返回 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”重复打印到控制台。实际上,根本没有打印任何内容。

【问题讨论】:

    标签: rust core-audio


    【解决方案1】:

    在 Mac 上,您需要告诉AudioUnit 使用哪个音频设备。在我的情况下,我不得不禁用设备上的扬声器输出 - 可能是因为我的 Mac 上的默认输入设备只输入。

    在 C 中(我想 Rust 版本需要几个 as *const _ as *const _s 和 as u32s):

    // 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;
    }
    

    【讨论】:

    • 好电话,我从来没有想过。不幸的是,这似乎并没有解决我的问题。我尝试通过objc crate:[[NSRunLoop mainRunLoop] run]; 在 Rust 中添加与以下 ObjC 代码等效的代码,我得到了相同的结果(没有输出)。你是这么想的吗?我也试过CFRunLoopRun,也没用。
    • 我已更新原始问题以包含我的代码以供您提出建议。
    • 更新了答案,毕竟无限循环似乎很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 2012-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多