Я пытаюсь преобразовать 2 CAF-файла локально в один файл. Эти два файла CAF являются монопотоками, и в идеале я бы хотел, чтобы они были стереофоническим файлом, поэтому я мог бы иметь микрофон от одного канала и динамика от другого.Как конвертировать 2 монофонических файла в один стереофайл в iOS?
Первоначально я начал с использования AVAssetTrack и AVMutableCompositionTracks, однако я не смог разрешить смешение. Мой объединенный файл был единственным монопотоком, который перемежал два файла. Поэтому я выбрал маршрут AVAudioEngine.
С моей точки зрения, я могу передавать в своих двух файлах входные узлы, присоединять их к микшеру и иметь выходной узел, который может получить стереомикс. Выходной файл имеет стерео-макет, но аудиоданные не записываются в него, так как я могу открыть его в Audacity и посмотреть стерео-макет. Размещение сигнала просачивания в канавке вокруг вызова installTapOnBus тоже не помогло. Любое понимание было бы оценено по мере того, как CoreAudio было бы сложно понять.
// obtain path of microphone and speaker files
NSString *micPath = [[NSBundle mainBundle] pathForResource:@"microphone" ofType:@"caf"];
NSString *spkPath = [[NSBundle mainBundle] pathForResource:@"speaker" ofType:@"caf"];
NSURL *micURL = [NSURL fileURLWithPath:micPath];
NSURL *spkURL = [NSURL fileURLWithPath:spkPath];
// create engine
AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AVAudioFormat *stereoFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:16000 channels:2];
AVAudioMixerNode *mainMixer = engine.mainMixerNode;
// create audio files
AVAudioFile *audioFile1 = [[AVAudioFile alloc] initForReading:micURL error:nil];
AVAudioFile *audioFile2 = [[AVAudioFile alloc] initForReading:spkURL error:nil];
// create player input nodes
AVAudioPlayerNode *apNode1 = [[AVAudioPlayerNode alloc] init];
AVAudioPlayerNode *apNode2 = [[AVAudioPlayerNode alloc] init];
// attach nodes to the engine
[engine attachNode:apNode1];
[engine attachNode:apNode2];
// connect player nodes to engine's main mixer
stereoFormat = [mainMixer outputFormatForBus:0];
[engine connect:apNode1 to:mainMixer fromBus:0 toBus:0 format:audioFile1.processingFormat];
[engine connect:apNode2 to:mainMixer fromBus:0 toBus:1 format:audioFile2.processingFormat];
[engine connect:mainMixer to:engine.outputNode format:stereoFormat];
// start the engine
NSError *error = nil;
if(![engine startAndReturnError:&error]){
NSLog(@"Engine failed to start.");
}
// create output file
NSString *mergedAudioFile = [[micPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"merged.caf"];
[[NSFileManager defaultManager] removeItemAtPath:mergedAudioFile error:&error];
NSURL *mergedURL = [NSURL fileURLWithPath:mergedAudioFile];
AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:mergedURL settings:[engine.inputNode inputFormatForBus:0].settings error:&error];
// write from buffer to output file
[mainMixer installTapOnBus:0 bufferSize:4096 format:[mainMixer outputFormatForBus:0] block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when){
NSError *error;
BOOL success;
NSLog(@"Writing");
if((outputFile.length < audioFile1.length) || (outputFile.length < audioFile2.length)){
success = [outputFile writeFromBuffer:buffer error:&error];
NSCAssert(success, @"error writing buffer data to file, %@", [error localizedDescription]);
if(error){
NSLog(@"Error: %@", error);
}
}
else{
[mainMixer removeTapOnBus:0];
NSLog(@"Done writing");
}
}];
}
Вы держите сильную ссылку на AVAudioFile вы пишете? – dave234
@ Dave, outputFile не существует до написания. С точки зрения сильной ссылки, я устанавливаю, что audioFile записывает в объединенныйURL, который является файломURLWithPath of mergedAudioFile. Других объектов/переменных, ссылающихся на outputFile, нет, и я не уничтожаю их после вызова installTapOnBus. – A21
Одной из слабых сторон этого подхода является то, что вам придется ждать, пока продолжительность файлов будет отображаться в одном. При этом, если вы придерживаетесь AVAudioEngine, вы можете попробовать сначала перенести оба файла. Затем, как только этот шаг будет завершен, установите кран и напишите в файл. Но если бы я сам это сделал, я бы использовал C API. – dave234