AVFoundation连续系列之五为音乐文件添加音效
咱们再次回顾下咱们的AVAudioNode,它是咱们AVAudioEngine工作的时候最小的一个元素类,咱们上季讲的是它里面的一个音频单位(AVAudioUnit)中的AVAudioUnitEffect,继续看下这张图:
咱们上季讲的AVAudioUnitEffect既可以用于实时音频,也可以用于给音频文件添加音效,这季咱们看下,怎么给音频文件添加音效。
要给咱们音频文件添加音效,就需要使用咱们另外一个音频节点了-AVAudioPlayerNode(音频播放器节点)。
一、AVAudioPlayerNode
1.介绍
音频播放器节点可以播放音频的buffer、也可以播放音频文件的一段。
播放音频文件的某段,需要传入咱们的AVAudioFile
当咱们指定播放的内容后,就是咱们一系列播放的控制了,如预播放、播放、停止、暂停、播放指定位置。
咱们看下音频播放器节点的常用方法属性
1.1设置播放内容和完成的操作
播放音频流
public funcscheduleBuffer(buffer:AVAudioPCMBuffer, completionHandler:AVAudioNodeCompletionHandler?)
public funcscheduleBuffer(buffer:AVAudioPCMBuffer, atTime when:AVAudioTime?, options:AVAudioPlayerNodeBufferOptions, completionHandler:AVAudioNodeCompletionHandler?)
播放音频文件
public funcscheduleFile(file:AVAudioFile, atTime when:AVAudioTime?, completionHandler:AVAudioNodeCompletionHandler?)
public funcscheduleSegment(file:AVAudioFile, startingFrame startFrame:AVAudioFramePosition, frameCount numberFrames:AVAudioFrameCount, atTime when:AVAudioTime?, completionHandler:AVAudioNodeCompletionHandler?)
1.2播放器的操作
public funcstop()
public funcprepareWithFrameCount(frameCount:AVAudioFrameCount)
public funcplay()
public func playAtTime(when:AVAudioTime?)
public func pause()
public func nodeTimeForPlayerTime(playerTime:AVAudioTime) ->AVAudioTime?
public func playerTimeForNodeTime(nodeTime:AVAudioTime) ->AVAudioTime?
public var playing:Bool{ get }
2.实现
2.1咱们先看看它的简单实现吧!
lazyvarengine =AVAudioEngine()
lazyvarplayer =AVAudioPlayerNode()
overridefuncviewDidLoad() {
super.viewDidLoad()
//音频文件的路径
letpath =NSBundle.mainBundle().pathForResource("short", ofType:"mp3")
leturl =NSURL.init(string: path!)
//创建音频文件对象
letaudioFile =try!AVAudioFile.init(forReading: url!)
//将音频播放器节点附着到音频引擎
engine.attachNode(player)
//设置音频播放器节点
player.scheduleFile(audioFile, atTime:nil, completionHandler:nil)
player.volume=1.0
//音频引擎连接节点
engine.connect(player, to:engine.outputNode, format: audioFile.processingFormat)
}
@IBActionfuncplayOrStop(sender:AnyObject) {
try!engine.start()
player.play()
}
2.2为音频文件添加音效
上面是没有给音频文件添加音效的,大家猜测下,怎么给音频文件添加音效呢?
这就是我让大家看上面那张图的原因,音频播放器节点,他也是一个普通的节点,咱们要想给音频文件添加音效,可以直接把音效附着到咱们的音频引擎上,连接的时候,把音频播放器、音效等节点连接到一起,这样音效就添加好了。
但是连接的时候,大家一定要注意连接的顺序,这也是咱们在讲音频引擎的时候就重点提出要注意的。还是看一下这前面的另一张图:
看到图咱们连接的顺序也就出来了,音频输入->效果器->输出
好!咱们看下添加音效的案例:
lazyvarengine =AVAudioEngine()
lazyvarplayer =AVAudioPlayerNode()
overridefuncviewDidLoad() {
super.viewDidLoad()
//音频文件的路径
letpath =NSBundle.mainBundle().pathForResource("short", ofType:"mp3")
leturl =NSURL.init(string: path!)
//创建音频文件对象
letaudioFile =try!AVAudioFile.init(forReading: url!)
//将音频播放器节点附着到音频引擎
engine.attachNode(player)
//设置音频播放器节点
player.scheduleFile(audioFile, atTime:nil, completionHandler:nil)
player.volume=1.0
//初始化并设置混响效果器节点
letreverb =AVAudioUnitReverb()
reverb.loadFactoryPreset(.MediumHall)
reverb.wetDryMix=80
engine.attachNode(reverb)
//音频引擎连接节点
engine.connect(player, to: reverb, format: audioFile.processingFormat)
engine.connect(reverb, to:engine.outputNode, format: audioFile.processingFormat)
}
@IBActionfuncplayOrStop(sender:AnyObject) {
try!engine.start()
player.play()
}
这个就是咱们音频播放器的基本操作了,咱们看下还没有具体去看的音频文件(AVAudioFile)这个类。
二:AVAudioFile
1.介绍
还是老习惯,列举下他常用的方法属性
1.1初始化方式:
读取文件的创建
public init(forReading fileURL:NSURL)throws
public init(forReading fileURL:NSURL, commonFormat format:AVAudioCommonFormat, interleaved:Bool)throws
写入文件的创建
public init(forWriting fileURL:NSURL, settings: [String:AnyObject])throws
public init(forWriting fileURL:NSURL, settings: [String:AnyObject], commonFormat format:AVAudioCommonFormat, interleaved:Bool)throws
1.2参数获取、设置
public func readIntoBuffer(buffer:AVAudioPCMBuffer)throws 读取buffer实体
public func readIntoBuffer(buffer:AVAudioPCMBuffer, frameCount frames:AVAudioFrameCount)throws 读取buffer中的一部分
public func writeFromBuffer(buffer:AVAudioPCMBuffer)throws 写入buffer
public var url:NSURL{ get } 获得文件的url
public var fileFormat:AVAudioFormat{ get } 获得文件的格式
public var processingFormat:AVAudioFormat{ get } 获得处理的格式
public var length:AVAudioFramePosition{ get } 获得音频帧的长度
public var framePosition:AVAudioFramePosition 获得音频帧的位置
咱们看到这个音频文件的类中,包含了关于文件全面的操作。它里面包含写入buffer的方法!是不是可以录音呢?当然可以!
2.使用AVAudioFile写入buffer
直接上代码:
lazyvarengine =AVAudioEngine()
varaudioFile:AVAudioFile?
overridefuncviewDidLoad() {
super.viewDidLoad()
//音频文件的路径
letpath =NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask,true).first!asNSString
leturl =NSURL.init(string: path .stringByAppendingPathComponent("audio.caf"))
//创建音频文件对象
audioFile=try!AVAudioFile.init(forWriting: url!, settings: [:])
letinput =engine.inputNode!
input.installTapOnBus(0, bufferSize:4096, format: input.inputFormatForBus(0), block: { (buffer, audioTime)in
//注意获得到的是一个为添加音效的原声
try!self.audioFile?.writeFromBuffer(buffer)
})
//初始化并设置混响效果器节点
letreverb =AVAudioUnitReverb()
reverb.loadFactoryPreset(.MediumHall)
reverb.wetDryMix=80
engine.attachNode(reverb)
//音频引擎连接节点
engine.connect(input, to: reverb, format:audioFile!.processingFormat)
engine.connect(reverb, to:engine.outputNode, format:audioFile!.processingFormat)
}
@IBActionfuncplayOrStop(sender:AnyObject) {
letbutton = senderas!UIButton
button.selected= button.selected!=true?true:false
button.setTitle("stop", forState: .Selected)
ifbutton.selected==true{
print(NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask,true).first)
try!engine.start()
}else{
engine.inputNode?.removeTapOnBus(0)
engine.stop()
}
}
这里需要注意!这里就像是全民K歌的案例一样,咱们是录制的时候添加的实时音效!但是录制好的是原声。就像咱们去KTV一样。如果你想得到一个添加完音效的音频文件发你哥们去炫耀,那请等下季分享。
好!这季咱们也就先玩到这!
下次见!
所有代码在这里:
播放器节点示例
写入buffer到音频文件