设置 AVAudioEngine 的输入和输出设备。

12

我一直在尝试使用苹果最新的 AVFoundation 库,但目前为止我还无法设置 AVAudioEngine 使用的输入或输出设备(例如USB声卡),而且我似乎无法在文档中找到任何关于是否可以实现这一点的信息。

有人有相关经验吗?


作为一个不太可能的尝试,我尝试将AVAudioEnginemainMixerNode强制转换为AVAudioIONode,这实际上起作用了(出于某种原因?)- 这是一个开始。 - Benjineer
2个回答

7

好的,在第十次阅读文档后,我注意到AVAudioEngine有成员inputNodeoutputNode(不确定我是怎么错过了这个!)。

以下代码似乎可以完成任务:

AudioDeviceID inputDeviceID = 53; // get this using AudioObjectGetPropertyData
AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AudioUnit audioUnit = [[engine inputNode] audioUnit];

OSStatus error = AudioUnitSetProperty(audioUnit,
                                      kAudioOutputUnitProperty_CurrentDevice,
                                      kAudioUnitScope_Global,
                                      0,
                                      &inputDeviceID,
                                      sizeof(inputDeviceID));

我从CAPlayThrough示例中借用了非AVFoundation C代码。


当我尝试对输出节点执行此操作时,会出现错误“所需条件为false:numChannelsAggDevice> = numChannelsSubDevice”。 - DanielGibbs
你是否使用 AudioObjectGetPropertyData 得到了正确的设备 ID,还是只是用了 53?你的输入设备有多少个声道? - Benjineer
我得到了正确的设备ID,返回状态为0,但是没有任何输出从设备中出来。 - DanielGibbs
抱歉如果我说了显而易见的事情,但是你是否将[engine inputNode]更改为[engine outputNode]了呢(假设你正在设置输出设备)? - Benjineer
4
说实话,苹果的文档和设备选择功能太糟糕了,我读了几天,简直无语。 - akuz
显示剩余6条评论

5

这是一个完整的,虽然有些粗糙的函数,用于测试目的播放一些音频(如果您没有安装GarageBand,请选择其他文件)。为避免硬编码设备ID,它会切换到您在系统偏好设置中设置的警报(“声音效果”)设备。

AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AudioUnit outputUnit = engine.outputNode.audioUnit;

OSStatus err = noErr;
AudioDeviceID outputDeviceID;
UInt32 propertySize;

AudioObjectPropertyAddress propertyAddress = {
    kAudioHardwarePropertyDefaultSystemOutputDevice,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster };
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &outputDeviceID);
if (err) { NSLog(@"AudioHardwareGetProperty: %d", (int)err); return; }

err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &outputDeviceID, sizeof(outputDeviceID));
if (err) { NSLog(@"AudioUnitSetProperty: %d", (int)err); return; }

NSURL *url = [NSURL URLWithString:@"/Applications/GarageBand.app/Contents/Frameworks/MAAlchemy.framework/Versions/A/Resources/Libraries/WaveNoise/Liquid.wav"];
NSError *error = nil;
AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:&error];
if (file == nil) { NSLog(@"AVAudioFile error: %@", error); return; }

AVAudioPlayerNode *player = [[AVAudioPlayerNode alloc] init];
[engine attachNode:player];
[engine connect:player to:engine.outputNode format:nil];

NSLog(@"engine: %@", engine);

if (![engine startAndReturnError:&error]) {
    NSLog(@"engine failed to start: %@", error);
    return;
}

[player scheduleFile:file atTime:[AVAudioTime timeWithHostTime:mach_absolute_time()] completionHandler:^{
    NSLog(@"complete");
}];
[player play];

1
太好了,之前在向 [engine connect] 传递格式时出现了错误 - 传递 nil 就解决了问题。 - Benjineer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接