4

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

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

为了降低复杂性,我只是试图让一个 AVAudioPlayerNode 的立体声从默认输出通道 0 和 1 以外的其他东西中发出。

USB音频接口正面

USB音频接口背面

我希望通过 AVAudioEngine + 一些低级核心音频来实现这一点。

到目前为止,通过配置 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 音频接口的通道 4+5,正如 theanalogkid 在 Apple 开发论坛中所描述的那样

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

因此,我没有在 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 应用于混音器到 mainMixer 的连接,USB 音频接口根本不会收到任何声音。

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

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

4

1 回答 1

5

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

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

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

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

于 2018-04-09T11:03:44.730 回答