2. 播放音頻
最快的時(shí)候导梆,這個(gè)音頻每分鐘播放 208 次贩绕,我們要準(zhǔn)備一個(gè) 0.3 秒不到短音頻艳汽。然后把音頻文件直接拖進(jìn)工程文件里,要記得在 Add to targets 勾選上項(xiàng)目屿笼。
為了讓?xiě)?yīng)用支持音頻功能牺荠,需要一個(gè)音頻框架翁巍,可以用現(xiàn)成的AVFoundation
驴一,在代碼開(kāi)頭加入:
import AVFoundation
并且定義一個(gè) audioPlayer 以及播放狀態(tài)一開(kāi)始處于暫停。
var audioPlayer = AVAudioPlayer()
var isPlaying = false
override func viewDidLoad()
這一段灶壶,會(huì)在應(yīng)用運(yùn)行的時(shí)候就開(kāi)始工作肝断,我需要它定位好的音頻文件。我扔進(jìn)去的音頻文件名叫 demo.m4a驰凛,其中參考:
override func viewDidLoad() {
super.viewDidLoad()
let url:NSURL = Bundle.main.url(forResource: "demo", withExtension: "m4a")! as NSURL
do{ audioPlayer = try AVAudioPlayer(contentsOf: url as URL, fileTypeHint: nil) }
catch let error as NSError { print(error.description)}
}
(參考別人代碼的過(guò)程中胸懈,遇到不少語(yǔ)法錯(cuò)誤,似乎是 Swift 3 的語(yǔ)法變更恰响。這些可以直接讓 Xcode 來(lái)自動(dòng)修正趣钱,上面這段很大程度上就是,點(diǎn)擊錯(cuò)誤代碼行前面的黃色嘆號(hào)圖標(biāo)胚宦。)
到這里首有,音頻應(yīng)該就指定好了,可以試試播起來(lái)枢劝。拖一個(gè)按鈕到 storyboard 里井联,同樣是創(chuàng)建為Action
功能,比如叫它playOrPauseMusic
您旁。但可以暫時(shí)先不管暫停的事情烙常,測(cè)測(cè)點(diǎn)它一下讓音頻播一下,看能不能成功:
@IBAction func playOrPauseMusic(_ sender: Any) {
audioPlayer.play()
}
3. 按頻率反復(fù)播放
最后也是最難的一步鹤盒,因?yàn)樾枰约簩?shí)現(xiàn)的思路蚕脏,最終請(qǐng)教了工程師 @EXC_BAD_ACCESS(對(duì)這不是那個(gè)程序錯(cuò)誤侦副,這是他簡(jiǎn)書(shū)昵稱(chēng))我才搞明白。
一開(kāi)始我陷入了困局驼鞭,想著讓音頻循環(huán)播放跃洛,然后控制循環(huán)的速度。這樣想的誤區(qū)在于终议,循環(huán)雖然很簡(jiǎn)單(audioPlayer.numbersOfLoop = -1
)汇竭,但循環(huán)的「速度」其實(shí)沒(méi)法控制,音頻播放一次就會(huì)接著繼續(xù)播下一次穴张。如果讓循環(huán)播放一個(gè)音頻列表细燎,播完一次指定音頻再播一次空白音頻,那么要控制空白音頻的時(shí)長(zhǎng)或者次數(shù)皂甘,也都非常麻煩(我不知道可以不可以)玻驻。@EXC_BAD_ACCESS 提醒我,沒(méi)有思路也跟我基礎(chǔ)不扎實(shí)偿枕、沒(méi)有系統(tǒng)地看過(guò)語(yǔ)法有關(guān)璧瞬。(但我主要是為了隨手做著玩兒…)
如果是讓音頻反復(fù)播放,然后控制這個(gè)命令多久執(zhí)行一次渐夸,這就可以讓執(zhí)行頻率跟音頻播放這件事分開(kāi)嗤锉。
先定義一個(gè) timer 和一個(gè)反復(fù)播放的命令:
var timer:Timer!
func repeatPlay() {
audioPlayer.play()
}
當(dāng)點(diǎn)擊按鈕的時(shí)候,判斷音頻的播放狀態(tài)墓塌,來(lái)決定 timer 是停用還是執(zhí)行瘟忱。把之前playOrPauseMusic
功能的代碼替換為:
@IBAction func playOrPauseMusic(_ sender: Any) {
if isPlaying{
isPlaying = false
timer.invalidate()
}else{
isPlaying = true
timer = Timer.scheduledTimer(timeInterval: 60/Double(selectedSpeed), target:self, selector:#selector(ViewController.repeatPlay), userInfo: nil, repeats: true)
}
}
timeInterval
后面的數(shù)字是每 x 秒執(zhí)行一次,而節(jié)拍器需要每分鐘響Double(selectedSpeed)
次苫幢,即每 60 秒響Double(selectedSpeed)
次访诱,即每 60/Double(selectedSpeed)
執(zhí)行一次。
最后把 timer 跟 slider 關(guān)聯(lián)起來(lái)韩肝。在sliderChanged
里加入:
timer.invalidate()
timer = Timer.scheduledTimer(timeInterval: 60/Double(selectedSpeed), target:self, selector:#selector(ViewController.repeatPlay), userInfo: nil, repeats: true)
(要讓 timer 每次先停用是因?yàn)榇ゲ耍詈貌灰寯?shù)據(jù)每變化一次就不停地在覆蓋發(fā)送。)
大功告成哀峻!
需要真機(jī)調(diào)試涡相,在工程文件 General 里面,把 Team 和 Signing Certificate 登一下自己的帳號(hào)谜诫,就可以把應(yīng)用 build 到自己手機(jī)里啦漾峡。
下一篇,設(shè)計(jì)的想法喻旷。