2014-12-02 2 views
1

간단한 질문입니다. AVAudioEngine을 사용하여 멀티 채널 오디오 파일 (> 2 채널)을 재생하여 기본 2 채널 출력 (헤드폰/스피커)에서 모든 채널을들을 수 있도록하는 방법 (제시 검사 오류 제거) 다음 코드는 파일의 처음 두 채널을한다하지만 헤드폰을 연결하면 나는 단지 그것을들을 수 있습니다.AVAudioEngine 멀티 채널 오디오 재생

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]; 

나는 두 플레이어 -> 믹서의 형식과 함께 조합을 많이 시도 mixer-> 출력 연결하지만, 하나는 동일한 하나에 위의 코드 또는 가능성 충돌로 일에 결과 :

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' 

나 (I 틀리지 않는 경우 형식은 지원되지 않음) AUSetFormat OSStatus 코드 -10868. 모든 연결에 file.processingFormat을 사용하면 위의 첫 번째 오류로 인해 응용 프로그램이 충돌합니다. 또한 충돌이 발생합니다 [player play];

AVAudioEngine이 포함시켜야하는 기능을 제공하므로 AUGraph를 사용하여 문제없이 할 수 있기 때문에 원하는 방식으로 재생해야합니다. 그걸로 붙어. 내 코드를 확인하는 데 사용

멀티 채널 오디오 파일은 아마 내 테스트 응용 프로그램에 적극적으로 오디오 세션을 설정 잊고 때문 헤드폰 here

UPDATE에게 좋아, 그래서 듣는 오디오를 찾을 수 있습니다. 하지만 여전히 처음 두 개의 채널 만 듣고 있습니다 ...

답변

1

그래서 지금까지 내가 관리 한 내용이 있습니다. 그것은 완벽하지는 않지만 다소 효과적입니다.

모든 채널을 얻으려면 AVAudioPCMBuffer를 사용하고 각각에 파일의 두 채널을 저장해야합니다. 또한 각 채널 쌍마다 별도의 AVAudioPlayerNode가 필요합니다. 그런 다음 각 플레이어를 AVAudioMixerNode에 연결하면 완료됩니다. 6 채널 오디오에 대한 몇 가지 간단한 코드 : 때문에 서로 다른 시간에 각 선수에 대한 play를 호출 채널 쌍 사이에 지연이있을 것이기 때문에

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]; 

지금이 솔루션은 지금까지 완벽한에서입니다. 또한 테스트 파일에서 8 채널 오디오를 재생할 수 없습니다 (OP의 링크 참조). AVAudioFile 처리 형식은 채널 수에 대해 0을 가지며 올바른 수의 채널 및 레이아웃으로 자체 형식을 생성하더라도 버퍼 읽기 오류가 발생합니다. AUGraph를 사용하여이 파일을 완벽하게 재생할 수 있습니다.

더 나은 솔루션을 공유하려면이 대답을 수락하기 전에 기다려야합니다.

편집은 그래서 모두 내 노드 문제를 동기화 할 수없는하지가 (애플 개발자 지원에 의해 확인) 버그를이 특정 8 채널 오디오를 재생할 수 있습니다있는 것으로 보인다.

iOS에서 오디오를 간섭하는 사람들을위한 조언이 거의 없습니다. AVAudioEngine은 간단한 작업을하는 동안 문제가되지 않지만, AVAudioEngine을 사용한다고 가정 할 때보 다 복잡한 작업으로 AUGraph를 사용해야합니다. AUGraph에서 AVAudioEngine의 특정 항목을 복제하는 방법을 알지 못하는 경우 (제 자신처럼), 힘든 행운입니다.

+0

언급 한 버그를 자세히 설명해주십시오. 'AVAudioPlayerNode'의'scheduleBuffer : atTime : options : completionHandler'를 사용하여 세 노드 모두에 대해 동일한'AVAudioTIme'을 지정할 수 있습니까? – Mark

+1

이 질문과 답변을 작성할 때 세 노드 모두에 대해 동일한 AVAudioTime을 넣더라도 사운드간에 약간의 지연이 여전히 남아 있습니다. 그들이 그것을 고칠 지 모르겠다 던노. – Kegluneq

2

나는 Swift에서 비슷한 문제가있었습니다. 오류는 'com.apple.coreaudio.avfaudio'였습니다. 이유 : 'required condition is false :! nodeimpl-> SslEngineImpl()'.

작업은 차례로 두 개의 오디오 파일을 재생하는 것이 었습니다. 첫 번째 오디오 파일을 재생 한 후 중지 버튼을 누른 다음 두 번째 오디오 파일을 재생하면 시스템이 다운됩니다.

나는 내가 만든 함수에서 audioEngine.attachNode(audioPlayerNode)이라는 것을 발견했다. 즉, audioPlayerNodeaudioEngine에 연결되었다가 나가는 것을 의미한다. 그래서이 첨부 파일을 viewDidLoad() 함수로 옮겨 매번 전달되도록했습니다.