首页 文章

AVAudioEngine播放多声道音频

提问于
浏览
1

简单的问题 . 如何使用AVAudioEngine播放多声道音频文件(> 2声道),以便我可以在默认的2声道输出(耳机/扬声器)上听到所有声道 . 下面的代码(删除错误检查以显示)播放文件前两个通道,但我只能在插入耳机时听到它 .

AVAudioFile *file = [[AVAudioFile alloc] initForReading:[[NSBundle mainBundle] URLForResource:@"nums6ch" withExtension:@"wav"] error:nil];
AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AVAudioPlayerNode *player = [[AVAudioPlayerNode alloc] init];
AVAudioMixerNode *mixer = [[AVAudioMixerNode alloc] init];
[engine attachNode:player];
[engine attachNode:mixer];
AVAudioFormat *processingFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:file.processingFormat.streamDescription->mSampleRate channels:2 interleaved:false];
[engine connect:player to:mixer format:processingFormat];
[engine connect:mixer to:engine.outputNode format:processingFormat];
[engine startAndReturnError:nil];
[player scheduleFile:file atTime:nil completionHandler:nil];
[player play];

我尝试了很多与播放器 - >混音器和混音器 - >输出连接格式的组合,但它们要么与上面的代码相同,要么更可能是崩溃:

ERROR:     [0x19c392310] AVAudioNode.mm:495: AUGetFormat: required condition is false: nil != channelLayout && channelLayout.channelCount == asbd.mChannelsPerFrame
*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: nil != channelLayout && channelLayout.channelCount == asbd.mChannelsPerFrame'

或者AUSetFormat OSStatus代码-10868(如果我没有记错的话,这种格式不受支持) . 对任何连接使用file.processingFormat会崩溃应用程序与上面的第一个错误 . 此外,崩溃发生在 [player play];

必须有一些像我想要的那样播放它,因为我能够毫无问题地使用AUGraph,但是,因为AVAudioEngine提供了我必须包含的一个功能,所以我坚持使用它 .

我用来检查我的代码的多声道音频文件可以找到here

UPDATE 好的,所以只听耳机听音频可能是因为我忘了在我的测试应用程序中将音频会话设置为活动状态 . 但我仍然只听到前两个 Channels ......

2 回答

  • 1

    我在Swift中遇到过类似的问题 . 错误是'com.apple.coreaudio.avfaudio',原因是:'required condition is false:!nodeimpl-> SslEngineImpl()' .

    任务是一个接一个地播放两个音频文件 . 如果我在播放第一个音频文件然后播放第二个音频文件后点击停止,则系统崩溃 .

    我发现在我创建的函数中我有 audioEngine.attachNode(audioPlayerNode) 这意味着 audioPlayerNode 被附加到 audioEngine 一次然后退出 . 所以我将此附件移动到 viewDidLoad() 函数,以便每次都传递它 .

  • 2

    所以这就是我到目前为止所做的事情 . 它远非完美,但它有点奏效 .

    要获得所有通道,您需要使用AVAudioPCMBuffer并在每个通道中存储两个通道 . 此外,对于每个通道对,您需要单独的AVAudioPlayerNode,然后将每个播放器连接到AVAudioMixerNode,我们就完成了 . 一些简单的6声道音频代码:

    AVAudioFormat *outputFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:file.processingFormat.sampleRate channels:2 interleaved:false];
    AVAudioFile *file = [[AVAudioFile alloc] initForReading:[[NSBundle mainBundle] URLForResource:@"nums6ch" withExtension:@"wav"] error:nil];
    AVAudioPCMBuffer *wholeBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:file.processingFormat frameCapacity:(AVAudioFrameCount)file.length];
    AVAudioPCMBuffer *buffer1 = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:(AVAudioFrameCount)file.length];
    AVAudioPCMBuffer *buffer2 = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:(AVAudioFrameCount)file.length];
    AVAudioPCMBuffer *buffer3 = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:(AVAudioFrameCount)file.length];
    memcpy(buffer1.audioBufferList->mBuffers[0].mData, wholeBuffer.audioBufferList->mBuffers[0].mData, wholeBuffer.audioBufferList->mBuffers[0].mDataByteSize);
    memcpy(buffer1.audioBufferList->mBuffers[1].mData, wholeBuffer.audioBufferList->mBuffers[1].mData, wholeBuffer.audioBufferList->mBuffers[1].mDataByteSize);
    buffer1.frameLength = wholeBuffer.audioBufferList->mBuffers[0].mDataByteSize/sizeof(UInt32);
    memcpy(buffer2.audioBufferList->mBuffers[0].mData, wholeBuffer.audioBufferList->mBuffers[2].mData, wholeBuffer.audioBufferList->mBuffers[2].mDataByteSize);
    memcpy(buffer2.audioBufferList->mBuffers[1].mData, wholeBuffer.audioBufferList->mBuffers[3].mData, wholeBuffer.audioBufferList->mBuffers[3].mDataByteSize);
    buffer2.frameLength = wholeBuffer.audioBufferList->mBuffers[0].mDataByteSize/sizeof(UInt32);
    memcpy(buffer3.audioBufferList->mBuffers[0].mData, wholeBuffer.audioBufferList->mBuffers[4].mData, wholeBuffer.audioBufferList->mBuffers[4].mDataByteSize);
    memcpy(buffer3.audioBufferList->mBuffers[1].mData, wholeBuffer.audioBufferList->mBuffers[5].mData, wholeBuffer.audioBufferList->mBuffers[5].mDataByteSize);
    buffer3.frameLength = wholeBuffer.audioBufferList->mBuffers[0].mDataByteSize/sizeof(UInt32);
    
    AVAudioEngine *engine = [[AVAudioEngine alloc] init];
    AVAudioPlayerNode *player1 = [[AVAudioPlayerNode alloc] init];
    AVAudioPlayerNode *player2 = [[AVAudioPlayerNode alloc] init];
    AVAudioPlayerNode *player3 = [[AVAudioPlayerNode alloc] init];
    AVAudioMixerNode *mixer = [[AVAudioMixerNode alloc] init];
    [engine attachNode:player1];
    [engine attachNode:player2];
    [engine attachNode:player3];
    [engine attachNode:mixer];
    [engine connect:player1 to:mixer format:outputFormat];
    [engine connect:player2 to:mixer format:outputFormat];
    [engine connect:player3 to:mixer format:outputFormat];
    [engine connect:mixer to:engine.outputNode format:outputFormat];
    [engine startAndReturnError:nil];
    
    [player1 scheduleBuffer:buffer1 completionHandler:nil];
    [player2 scheduleBuffer:buffer2 completionHandler:nil];
    [player3 scheduleBuffer:buffer3 completionHandler:nil];
    [player1 play];
    [player2 play];
    [player3 play];
    

    现在这个解决方案远非完美,因为在不同时间为每个玩家调用 play 会在两个 Channels 之间产生延迟 . 我还是无法从我的测试文件中播放8声道音频(参见OP中的链接) . AVAudioFile处理格式的通道数为0,即使我使用正确的通道数和布局创建自己的格式,我也会收到缓冲区读取错误 . 请注意,我可以使用AUGraph完美地播放此文件 .

    所以我会在接受这个答案之前等待,如果你有更好的解决方案请分享 .

    EDIT

    因此,似乎无法同步节点问题而无法播放此特定8声道音频都是错误(Apple开发人员支持确认) .

    对于在iOS上干扰音频的人来说,这么少的建议 . 虽然AVAudioEngine对于简单的东西来说很好,但你应该选择AUGraph来处理更复杂的东西,甚至可以选择与AVAudioEngine一起使用的东西 . 如果你不知道如何从AUGraph中的AVAudioEngine复制某些东西(比如我自己),那么,运气不好 .

相关问题