2014-09-22 15 views
4

我試圖在一次同步使用AVAudioEngine在iOS的8打數相當長的文件iOS8上AVAudioEngine:適當的方式來播放多個(長)的音頻文件

我可以用下面這樣做:

- (void)startAudio { 

    AVAudioMixerNode *mainMixer = [self.engine mainMixerNode]; 

    AVAudioPlayerNode *player = [[AVAudioPlayerNode alloc] init]; 
    AVAudioPlayerNode *player2 = [[AVAudioPlayerNode alloc] init]; 
    AVAudioPlayerNode *player3 = [[AVAudioPlayerNode alloc] init]; 

    [self.engine attachNode:player]; 
    [self.engine attachNode:player2]; 
    [self.engine attachNode:player3]; 

    NSString *fileName = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], @"file1.caf"]; 
    NSURL *fileUrl = [NSURL fileURLWithPath:fileName]; 
    AVAudioFile *file = [[AVAudioFile alloc] initForReading:fileUrl error:nil]; 

    NSString *fileName2 = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], @"file2.caf"]; 
    NSURL *fileUrl2 = [NSURL fileURLWithPath:fileName2]; 
    AVAudioFile *file2 = [[AVAudioFile alloc] initForReading:fileUrl2 error:nil]; 

    NSString *fileName3 = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], @"file3.caf"]; 
    NSURL *fileUrl3 = [NSURL fileURLWithPath:fileName3]; 
    AVAudioFile *file3 = [[AVAudioFile alloc] initForReading:fileUrl3 error:nil]; 

    [self.engine connect:player to:mainMixer format:file.processingFormat]; 
    [self.engine connect:player2 to:mainMixer format:file2.processingFormat]; 
    [self.engine connect:player3 to:mainMixer format:file3.processingFormat]; 

    [player scheduleFile:file atTime:nil completionHandler:nil]; 
    [player2 scheduleFile:file2 atTime:nil completionHandler:nil]; 
    [player3 scheduleFile:file3 atTime:nil completionHandler:nil]; 

    NSError *error; 
    [self.engine startAndReturnError:&error]; 
    [player play]; 
    [player2 play]; 
    [player3 play]; 
} 

但這似乎很繁瑣。我認爲有一種方法可以將節點鏈接在一起,但我找不到任何示例,並且不清楚如何通過觀看AVAudioEngine上的WWDC會話來執行此操作。

任何想法?

+0

您是否找到解決方案? – ucangetit 2015-06-27 18:28:10

回答

1

你不應該atTime:零,但選擇在不久的將來。像現在一樣+ 0.1秒,並安排所有玩家在那個時候玩。

看看我的問題的答案在:Scheduling an audio file for playback in the future with AVAudioTime 找出如何。 scheduleSegment或scheduleFile的工作方式大致相同,但使用scheduleSegment可以在文件中的任意位置開始播放。

+0

不介意開始***時間:無*** - 反正系統不會啓動你 ,直到未來的下一個渲染幀(1024個樣本) - 即使你開始*** atTime:播放器.lastRenderTime ***。沒有延遲值是必需的,除非您真的希望您的播放器在未來的晚些時候開始使用...... - 延遲不會改變任何同步行爲的準確性 - 只要它正確完成! – mramosch 2016-07-16 02:41:58

+0

問題是,如果你想一次啓動多個玩家,他們需要在同一時間開始。可能發生的情況是,其中一名球員在較晚的時間段內開始,然後是其他球員,從而產生可聽見的延遲。所以你應該在將來啓動球員。在撰寫我以前的回答時,我不知道player.lastRenderTime,但請記住,您需要使用來自第一個玩家的player.lastRenderTime來安排所有玩家。 – 2016-07-18 09:52:47

+0

肯定是真的 - 但由於Apple設計中的缺陷,AVAudioPlayerNode稍微微妙一些。我甚至會把它們稱爲bug。請注意,原帖正在同步*** PLAYING ***並且不同步***開始*** !!!由於AVAudioEngine中的這個錯誤,這會產生很大的差異。通過使用player.lastRenderTime,您已經在同步玩***的問題中死了,因爲lastRenderTime是過去的時間戳,而且您實際上未在未來播放* atTime:*,如您所建議的,而是過去。系統重新安排你,現在你被擰緊了... – mramosch 2016-07-18 15:51:08

1

注意 - 注意同步之間的差異STARTING和同步演奏

如果你只是想開始幾個球員ONCE,並在相同的開始時間你一定要使用playAtTime:方法與他們指的是同一現在時間,你可以自由選擇。您可以選擇作爲一個值,系統會將它們安排在下一個渲染幀1024個樣本之後。以防萬一你沒有playAtTime:連續呼叫(你在上播放:),而是被其他呼叫或NSLog-s中斷,分配一個開始時間通常更安全而不是僅僅把。但是請注意,這隻能讓你開始同步FIRST開始,但沒有進一步的保證讓新增的球員同步到最近開始的球員隊伍。

蘋果方面存在一些設計缺陷(我稱之爲bug),這使得與AVAudioEngine同步播放幾乎不可能。例如

  • 如果你的啓動時間是playAtTime:playerLastRender你已經搞砸了。您的播放器DO開始同步對方,但由於您的引擎節點時間已經不同步,因爲...

  • 在某些情況下,您提交的開始時間會被系統忽略和更改,所有後續的計算 - 你必須爲例如同步一個額外的播放器 - 這是基於你自己的要求(但在發動機罩下默默改變)開始時間將不同步...

  • 該系統甚至goof自己了!playerTimeForNodeTime:nodeTimeForPlayerTime:做報告錯誤價值 - 所以你不能基於這些值的任何決定或者等等

對於細微之處看看我的

答案

AVAudioEngine multiple AVAudioInputNodes do not play in perfect sync

我已經介紹了很多AVAudioEngine的東西,一個人應該知道!

玩得開心!