AVFoundation連續(xù)系列之五為音樂文件添加音效
咱們再次回顧下咱們的AVAudioNode迫悠,它是咱們AVAudioEngine工作的時候最小的一個元素類,咱們上季講的是它里面的一個音頻單位(AVAudioUnit)中的AVAudioUnitEffect闻蛀,繼續(xù)看下這張圖:
咱們上季講的AVAudioUnitEffect既可以用于實時音頻,也可以用于給音頻文件添加音效,這季咱們看下崩掘,怎么給音頻文件添加音效吼驶。
要給咱們音頻文件添加音效惩激,就需要使用咱們另外一個音頻節(jié)點了-AVAudioPlayerNode(音頻播放器節(jié)點)。
一蟹演、AVAudioPlayerNode
1.介紹
音頻播放器節(jié)點可以播放音頻的buffer风钻、也可以播放音頻文件的一段。
播放音頻文件的某段酒请,需要傳入咱們的AVAudioFile
當咱們指定播放的內(nèi)容后骡技,就是咱們一系列播放的控制了,如預播放羞反、播放布朦、停止、暫停昼窗、播放指定位置是趴。
咱們看下音頻播放器節(jié)點的常用方法屬性
1.1設(shè)置播放內(nèi)容和完成的操作
播放音頻流
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.實現(xiàn)
2.1咱們先看看它的簡單實現(xiàn)吧!
lazyvarengine =AVAudioEngine()
lazyvarplayer =AVAudioPlayerNode()
overridefuncviewDidLoad() {
super.viewDidLoad()
//音頻文件的路徑
letpath =NSBundle.mainBundle().pathForResource("short", ofType:"mp3")
leturl =NSURL.init(string: path!)
//創(chuàng)建音頻文件對象
letaudioFile =try!AVAudioFile.init(forReading: url!)
//將音頻播放器節(jié)點附著到音頻引擎
engine.attachNode(player)
//設(shè)置音頻播放器節(jié)點
player.scheduleFile(audioFile, atTime:nil, completionHandler:nil)
player.volume=1.0
//音頻引擎連接節(jié)點
engine.connect(player, to:engine.outputNode, format: audioFile.processingFormat)
}
@IBActionfuncplayOrStop(sender:AnyObject) {
try!engine.start()
player.play()
}
2.2為音頻文件添加音效
上面是沒有給音頻文件添加音效的澄惊,大家猜測下唆途,怎么給音頻文件添加音效呢?
這就是我讓大家看上面那張圖的原因掸驱,音頻播放器節(jié)點肛搬,他也是一個普通的節(jié)點,咱們要想給音頻文件添加音效毕贼,可以直接把音效附著到咱們的音頻引擎上温赔,連接的時候,把音頻播放器鬼癣、音效等節(jié)點連接到一起陶贼,這樣音效就添加好了。
但是連接的時候待秃,大家一定要注意連接的順序骇窍,這也是咱們在講音頻引擎的時候就重點提出要注意的。還是看一下這前面的另一張圖:
看到圖咱們連接的順序也就出來了锥余,音頻輸入->效果器->輸出
好腹纳!咱們看下添加音效的案例:
lazyvarengine =AVAudioEngine()
lazyvarplayer =AVAudioPlayerNode()
overridefuncviewDidLoad() {
super.viewDidLoad()
//音頻文件的路徑
letpath =NSBundle.mainBundle().pathForResource("short", ofType:"mp3")
leturl =NSURL.init(string: path!)
//創(chuàng)建音頻文件對象
letaudioFile =try!AVAudioFile.init(forReading: url!)
//將音頻播放器節(jié)點附著到音頻引擎
engine.attachNode(player)
//設(shè)置音頻播放器節(jié)點
player.scheduleFile(audioFile, atTime:nil, completionHandler:nil)
player.volume=1.0
//初始化并設(shè)置混響效果器節(jié)點
letreverb =AVAudioUnitReverb()
reverb.loadFactoryPreset(.MediumHall)
reverb.wetDryMix=80
engine.attachNode(reverb)
//音頻引擎連接節(jié)點
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初始化方式:
讀取文件的創(chuàng)建
public init(forReading fileURL:NSURL)throws
public init(forReading fileURL:NSURL, commonFormat format:AVAudioCommonFormat, interleaved:Bool)throws
寫入文件的創(chuàng)建
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參數(shù)獲取足画、設(shè)置
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 獲得音頻幀的位置
咱們看到這個音頻文件的類中,包含了關(guān)于文件全面的操作佃牛。它里面包含寫入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"))
//創(chuàng)建音頻文件對象
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)
})
//初始化并設(shè)置混響效果器節(jié)點
letreverb =AVAudioUnitReverb()
reverb.loadFactoryPreset(.MediumHall)
reverb.wetDryMix=80
engine.attachNode(reverb)
//音頻引擎連接節(jié)點
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歌的案例一樣讨便,咱們是錄制的時候添加的實時音效!但是錄制好的是原聲畏铆。就像咱們?nèi)TV一樣默伍。如果你想得到一個添加完音效的音頻文件發(fā)你哥們?nèi)レ乓酰钦埖认录痉窒怼?/p>
好惫东!這季咱們也就先玩到這莉给!
下次見!
所有代碼在這里: