如何在USB音频接口上播放特定输出通道的声音?

时间:2018-04-06 22:28:14

标签: audio avfoundation core-audio avaudioengine avaudioplayernode

我有一个运行MacOS 10.13.3的Mac连接的UltraLite mk4 USB音频接口。

我试图播放三个立体声声音文件,每个声音文件都有自己的立体声扬声器。

从较低的复杂性开始,我只是试图让一个AVAudioPlayerNode的立体声声音从默认输出通道0和1以外的其他地方传出。

USB audio interface front

USB audio interface back

我希望用AVAudioEngine +一些低级别的Core Audio来实现这一目标。

到目前为止,通过配置AVAudioEngine的输出节点,我已成功设法将音频播放到USB音频接口:

AudioUnit audioUnit = [[self.avAudioEngine outputNode] audioUnit];
OSStatus error = AudioUnitSetProperty(audioUnit,
    kAudioOutputUnitProperty_CurrentDevice,
    kAudioUnitScope_Global,
    0,
    &deviceID,
    sizeof(deviceID));
if (error) {
    NSLog(@"Failed to set desired output audio device: %d", (int)
}

我还成功地设置了一个频道地图,让一个播放器播放一个立体声文件到我的USB音频接口{+ 3}}的4 + 5频道。

频道映射的问题在于它全局应用于AVAudioEngine的输出,它会影响所有AVAudioPlayerNodes。所以这不是解决方案。

因此,我没有在AVAudioEngine的outputNode的AudioUnit上使用该频道映射,而是尝试使用自定义频道布局创建一个AVAudioFormat,指定离散频道索引4和5:

// Create audio channel layout struct with two channels
int numChannels = 2;
AudioChannelLayout *audioChannelLayout = calloc(1, sizeof(AudioChannelLayout) + (numChannels - 1) * sizeof(AudioChannelDescription));
audioChannelLayout->mNumberChannelDescriptions = numChannels;
audioChannelLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
audioChannelLayout->mChannelBitmap = 0;

// Configure channel descriptions
audioChannelLayout->mChannelDescriptions[0].mChannelFlags = kAudioChannelFlags_AllOff;
audioChannelLayout->mChannelDescriptions[0].mChannelLabel = kAudioChannelLabel_Discrete_4;
audioChannelLayout->mChannelDescriptions[0].mCoordinates[0] = 0;
audioChannelLayout->mChannelDescriptions[0].mCoordinates[1] = 0;
audioChannelLayout->mChannelDescriptions[0].mCoordinates[2] = 0;

audioChannelLayout->mChannelDescriptions[1].mChannelFlags = kAudioChannelFlags_AllOff;
audioChannelLayout->mChannelDescriptions[1].mChannelLabel = kAudioChannelLabel_Discrete_5;
audioChannelLayout->mChannelDescriptions[1].mCoordinates[0] = 0;
audioChannelLayout->mChannelDescriptions[1].mCoordinates[1] = 0;
audioChannelLayout->mChannelDescriptions[1].mCoordinates[2] = 0;

// Create AVAudioChannelLayout
AVAudioChannelLayout *avAudioChannelLayout = [[AVAudioChannelLayout alloc] initWithLayout:audioChannelLayout];

// Create AVAudioFormat
AVAudioOutputNode *outputNode = engine.avAudioEngine.outputNode;
AVAudioFormat *outputHWFormat = [outputNode outputFormatForBus:0];
AVAudioFormat *targetFormat = [[AVAudioFormat alloc] initWithStreamDescription:player.avAudioFile.processingFormat.streamDescription
                                                                 channelLayout:avAudioChannelLayout];

然后我使用输出硬件格式将播放器的调音台连接到引擎的主调音台:

[engine connect:speakerMixer to:[engine mainMixerNode] format:outputHWFormat];

播放器到其调音台,使用自定义AVAudioFormat和调整后的频道布局:

[engine connect:player to:speakerMixer format:targetFormat];

结果?声音播放,但仍然只有USB音频接口的默认0和1声道。

如果我将targetFormat应用于mixer-to-mainMixer连接,则USB音频接口根本不会收到任何声音。

我还试图将频道映射应用到AVAudioMixerNode的底层AVAudioUnit,但我似乎无法从AVAudioUnit获取底层的裸机AudioUnit以应用频道映射。也许这对AVAudioEngine来说是不可能的。如果是这样,您是否知道有其他方法可以做到这一点?

可能我忽略了一些关键的东西。我花了很多天研究这个,并感到困惑。感谢我能得到的任何帮助。

1 个答案:

答案 0 :(得分:4)

多年前,AVAudioEngine的文档中发出警告,即应用程序应该只有一个实例。

显然,这个限制消失了,我已经成功创建了多个实例,每个实例都有自己的通道映射应用于引擎的outputNode的AudioUnit。

文档仍然声称引擎的outputNode是一个" singleton",虽然我在Mac上的测试显示每个AVAudioEngine实例都有自己的输出节点实例,并且可以设置一个实例上的通道映射,不会影响另一个实例的输出。

这并不是我一直在寻找的东西,尽管它似乎确实有效。我仍然更喜欢一种解决方案,可以将播放器或混音器节点的频道路由到音频硬件的特定输出通道,并使用一个AVAudioEngine实例完成所有操作。另一方面,现在我很难想出为什么运行多个AVAudioEngine实例会很糟糕。