AVAudioEngine和AVAudioPlayerNode的使用
AVAudioPlayer能通过rate属性控制播放的速率,但是变速后会变调,而AVAudioEngine能做到控制播放变速不变调。
//create Node
self.mbEngine = [AVAudioEngine new];
self.mbPlayerNode = [AVAudioPlayerNode new];
self.mbAudioUnitTimePitch = [AVAudioUnitTimePitch new];
self.mbAudioUnitVarispeed = [AVAudioUnitVarispeed new];
[self.mbEngine attachNode:self.mbPlayerNode];
[self.mbEngine attachNode:self.mbAudioUnitTimePitch];
[self.mbEngine attachNode:self.mbAudioUnitVarispeed];
//connectNode
[self.mbEngine connect:self.mbPlayerNode to:self.mbAudioUnitTimePitch format:self.mbAudioFile.processingFormat];
[self.mbEngine connect:self.mbAudioUnitTimePitch to:self.mbAudioUnitVarispeed format:self.mbAudioFile.processingFormat];
[self.mbEngine connect:self.mbAudioUnitVarispeed to:self.mbEngine.mainMixerNode format:self.mbAudioFile.processingFormat];
//start Engine
[self.mbEngine prepare];
[self.mbEngine startAndReturnError:&error];
//通过设置mbAudioUnitVarispeed和AVAudioUnitTimePitch来控制速度和音调。两者间存在个关系。
float speed = 0.5;
float pitch = 0;
pitch = fabsf(log2f(speed)) *1200;
self.mbAudioUnitVarispeed.rate = speed;
self.mbAudioUnitTimePitch.pitch = pitch;
//然后是播放,控制一下两边极值
seekTime = seekTime > self.playerNodeDuration ? self.playerNodeDuration : seekTime;
seekTime = seekTime > 0 ? seekTime : 0;
AVAudioFramePosition seekFrame = seekTime * tempFile.processingFormat.sampleRate;
//记录一下播放前的位置,用来计算当前的播放时间
self.lastStartFramePosition = seekFrame;
AVAudioFrameCount frameCount = (AVAudioFrameCount)(tempFile.length - seekFrame);
BOOL isPlaying = self.mbPlayerNode.isPlaying;
[self.mbPlayerNode stop];
if (seekFrame < (AVAudioFramePosition)tempFile.length) {
[self.mbPlayerNode scheduleSegment:tempFile startingFrame:seekFrame frameCount:frameCount atTime:nil completionHandler:^{
}];
if (isPlaying) {
[self.mbPlayerNode play];
}
}
关于获取总时间和当前的播放时间
AVAudioTime *playerTime = [self.mbPlayerNode playerTimeForNodeTime:self.mbPlayerNode.lastRenderTime];
self.playerNodeCurrenttime = (self.lastStartFramePosition+playerTime.sampleTime)/playerTime.sampleRate;
NSLog(@"当前时间%f",self.playerNodeCurrenttime);
可能会出现的一些问题
//1,engine被置空为nil了
required condition is false: _engine != nil
//2,engine在node播放前未启动
required condition is false: _engine->IsRunning()