Аудиоустройство Core Audio HALOutput не может вызвать обратный вызов ввода - PullRequest
0 голосов
/ 06 июля 2018

Я пытаюсь записать звук с помощью Core Audio на Mac, но мой входной обратный вызов не вызывается. Я уверен, что упускаю что-то очевидное, но я искал и пробовал вещи весь день. Мой код написан на 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 обратный вызов просто возвращает 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
}

Следовательно, я ожидал бы, что «привет ввод» будет неоднократно печататься на консоли. На самом деле ничего не печатается вообще.

1 Ответ

0 голосов
/ 07 июля 2018

На Mac вы должны указать AudioUnit, какое аудиоустройство использовать. В моем случае мне пришлось отключить выход динамика на устройстве - вероятно, потому что устройство ввода по умолчанию на моем Mac делает только вход.

В C (я полагаю, версии Rust понадобится несколько as *const _ as *const _ с и as u32 с):

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