原文:AudioKit Tutorial: Getting Started
作者:Colin Eberhardt
同時(shí)感謝:kmyhy
近來手上有一些音頻相關(guān)的開發(fā)工作,搜搜基Hub,目前最為強(qiáng)大拉队,性能屌爆蔑水,編碼炫酷的開源庫也只有AudioKit了贰镣。
Raywenderlich
也能找到相關(guān)教程垦搬,介于作者是3.0+的教程皮钠,很多代碼都不能跑了,特此整理一趴火的。
本文不僅是一篇iOS開發(fā)教程壶愤,更是一篇精彩的科普文。關(guān)于編程與藝術(shù)的結(jié)合馏鹤,聲學(xué)物理與音樂的碰撞征椒,盡在此文。推薦所有程序員都好好讀一讀它湃累,讓我們的生活除了代碼勃救,還有藝術(shù),還有音樂治力。感謝作者Colin Eberhardt蒙秒。
iOS 設(shè)備提供了豐富的多媒體體驗(yàn),比如絢麗的視覺效果宵统、聲音和可觸摸的交互界面晕讲。盡管能夠使用各種各樣的特性,但作為開發(fā)者马澈,我們更多地關(guān)注了應(yīng)用的視覺設(shè)計(jì)瓢省,而忽略了用戶體驗(yàn)的聲學(xué)效果。
AudioKit
是一個(gè)高級(jí)音頻框架痊班,由聲學(xué)設(shè)計(jì)師净捅、程序員和音樂家為 iOS 專門打造。AudioKit底層混合了Swift
辩块、Objective-C
蛔六、C++
和C
,負(fù)責(zé)和蘋果音頻已硬件的Api打交道废亭。所有神奇(同時(shí)十分復(fù)雜的)技術(shù)都封裝成為極其友好的 Swift Api 国章,你甚至可以直接在Xcode
的Playground
中使用它。
本文無法全面覆蓋 AudioKit 的知識(shí)點(diǎn)豆村。相反液兽,我們會(huì)通過介紹聲音合成和計(jì)算機(jī)聲頻的歷史,來帶你進(jìn)行一次有趣和時(shí)尚的 AudioKit 之旅掌动。通過這種方式四啰,你會(huì)學(xué)到基本的聲學(xué)物理,了解早期的合成器比如電子琴是如何工作的粗恢。最終來到現(xiàn)代柑晒,一個(gè)大混音時(shí)代。
請(qǐng)給自己來一杯咖啡眷射,拖過一張椅子匙赞,開始我們的旅程佛掖!
開始
原教程的原始內(nèi)容使用的是 3.4.0 版本,用的是Playground做案例涌庭,本文使用最新的 4.3.0 芥被,用Xcode創(chuàng)建的Swift項(xiàng)目做案例。
港真坐榆,教程的第一步并不是特別雞凍的拴魄。為了在 Playground 中使用 AudioKit,我們必須提前進(jìn)行一些準(zhǔn)備工作席镀。
我們需要先到AudioKit下源碼AudioKit-4.3匹中,或者自行clone:
git clone https://github.com/AudioKit/AudioKit.git
解壓好后用Terminal
進(jìn)入AudioKit-4.3
文件夾執(zhí)行編譯命令:
$ cd Frameworks
$ ./build_frameworks.sh
編譯時(shí)間較長(zhǎng)(14的頂配編譯的近十分鐘吧),你闊以繼續(xù)閱讀愉昆,或者擼一把职员,或者雞一把麻蹋。
一定要編譯完成跛溉,編譯好后進(jìn)入Playgrounds
文件,打開下面的AudioKitPlaygrounds.xcodeproj
扮授,這里我們就闊以看官方為我們寫好的示例代碼芳室,提前體驗(yàn)一把 AudioKit 的強(qiáng)大。體驗(yàn)完了別忘了紙巾刹勃,進(jìn)入正題開始教程之旅堪侯。
新建一個(gè)名為Jornery
的Playground
。錄入一下代碼:
import AudioKitPlaygrounds
import AudioKit
let oscillator = AKOscillator()
AudioKit.output = oscillator
try? AudioKit.start()
oscillator.start()
sleep(10)
編譯時(shí)荔仁,你會(huì)聽到大約 10 秒鐘的蜂鳴聲伍宦。你可以點(diǎn)擊 Playground 調(diào)試窗口左下角的 Play/Stop 按鈕停止或運(yùn)行 Playground。
注意:建議打開工程的時(shí)候就直接
Command + B
編譯一遍乏梁,再跑想看的代碼次洼。如果 Playground 執(zhí)行出錯(cuò),并在 Debug 窗口中出現(xiàn)錯(cuò)誤遇骑,你可以重啟 Xcode卖毁。不幸的是,當(dāng) Playground 和框架一起使用時(shí)落萎,總是容易出現(xiàn)一些小問題亥啦,并且無法預(yù)知。
另外建議把 Run 方式改為Manually Run
练链,Automatically Run
太容易故障了翔脱。
振蕩器和聲學(xué)基礎(chǔ)
人類通過物體制造音樂——通過擊打、拖拉或者彈奏等形式——有數(shù)千年的歷史媒鼓。我們的許多民族樂器碍侦,比如鼓粱坤、吉他,已經(jīng)發(fā)明幾個(gè)世紀(jì)了瓷产。電子樂器的第一個(gè)次使用記錄站玄,或者是第一次通過電路發(fā)聲,是 1874 年 Elisha Gray 創(chuàng)下的濒旦,他從事電信行業(yè)株旷。Elisha 發(fā)明了振蕩器,最原始的聲音合成裝置尔邓,你的探索將從這個(gè)東西開始晾剖。
右鍵點(diǎn)擊 Playground,選擇 New Playground Page梯嗽,創(chuàng)建一個(gè)新的 Playground 文件 Oscillators
齿尽。
將 Xcode 產(chǎn)生的代碼替換為:
import AudioKitPlaygrounds
import AudioKit
// 1. Create an oscillator
let oscillator = AKOscillator()
// 2. Start the AudioKit 'engine'
AudioKit.output = oscillator
try? AudioKit.start()
// 3. Start the oscillator
oscillator.start()
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
這個(gè) Playground 將沒完沒了地發(fā)出嗶嗶聲——呃,有意思灯节。你可以按 Stop 來停止它循头。
這和前面創(chuàng)建的測(cè)試 Playground 差不多,但這次我們將深入討論細(xì)節(jié)炎疆。
代碼分成了幾個(gè)步驟:
- 創(chuàng)建一個(gè) AudioKit 振蕩器卡骂,它是一個(gè) AKNode 子類。節(jié)點(diǎn)是構(gòu)成你的音頻序列的主要元素形入。
- 將 AudioKit 引擎和你最終輸出的節(jié)點(diǎn)關(guān)聯(lián)起來全跨,在這里這個(gè)節(jié)點(diǎn)是你唯一的節(jié)點(diǎn)。音頻引擎就像物理引擎或游戲引擎:你必須啟動(dòng)它并讓它持續(xù)運(yùn)轉(zhuǎn)亿遂,這樣你的音頻序列才能被執(zhí)行浓若。
- 最后,打開振蕩器蛇数,它會(huì)發(fā)出一條聲波挪钓。
一個(gè)振蕩器會(huì)創(chuàng)建一個(gè)重復(fù)的、或者周期性的無限延續(xù)的信號(hào)苞慢。在這個(gè) Playground 中诵原,AKOscillator 發(fā)出了一個(gè)正弦波。這個(gè)數(shù)字化的正弦波經(jīng)過 AudioKit 處理挽放,直接輸出到你的揚(yáng)聲器或者耳麥绍赛,導(dǎo)致真正的振蕩器以同樣的正弦波進(jìn)行振蕩。聲音通過壓縮你耳朵周圍的空氣傳播到你耳中辑畦,最終你就聽到了這個(gè)煩人的嘯叫聲吗蚌!
有兩個(gè)參數(shù)決定了振蕩器發(fā)出的聲音是什么樣子:amplitude - 振幅,它是正弦波的高度并決定聲音的大小纯出,以及 frequency - 頻率蚯妇,它決定了音高敷燎。
在你的 Playground 中,在創(chuàng)建振蕩器之后加入這兩句:
oscillator.frequency = 300
oscillator.amplitude = 0.5
傾聽一會(huì)箩言,你會(huì)發(fā)現(xiàn)現(xiàn)在的音量只是剛才的一半硬贯,而且音高也比剛才低了。頻率單位為赫茲(即每秒周期數(shù))陨收,決定了音符的音高饭豹。而振幅,范圍從 0-1 务漩,決定了音量拄衰。
Elisha Gray 在專利官司中輸給了Alexander Graham Bell,失去了成為電話機(jī)發(fā)明者的機(jī)會(huì)饵骨。但是翘悉,他的偶然發(fā)明振蕩器卻導(dǎo)致第一個(gè)電子樂器專利的產(chǎn)生。
許多年以后居触,Léon Theremin 發(fā)明了一個(gè)怪異的電子樂器妖混,至今仍然被使用著。使用特雷門琴饼煞,你可以在這個(gè)樂器上方揮舞手臂來改變電子振蕩器的頻率源葫。如果你不知道怎么形容這個(gè)樂器發(fā)出的聲音诗越,我建議你聽一聽 The Beach Boys 演唱的 Good Vibrations, 這首歌曲中特雷門琴所發(fā)出的獨(dú)特聲音令人記憶深刻砖瞧。
你可以在 Playground 的最后加入以下代碼模擬這種效果:
oscillator.rampDuration = 0.2
oscillator.frequency = 500
import AudioKitUI
AKPlaygroundLoop(every: 0.5) {
oscillator.frequency = (oscillator.frequency == 500 ? 100 : 500)
}
rampDuration 屬性允許振蕩器在屬性值之間平滑過渡(比如頻率或振幅)。AKPlaygroundLoop 是一個(gè)很有用的實(shí)用函數(shù)嚷狞,允許周期性地執(zhí)行 Playground 中的代碼块促。在這里,你簡(jiǎn)單滴每 0.5 秒就切換一次振蕩器的頻率床未,從 500Hz 到 100 Hz竭翠。
你制造了自己的特雷門琴!
簡(jiǎn)單的振蕩器可以發(fā)出音符薇搁,但是并不能令耳朵愉悅斋扰。真正的樂器還受許多別的因素的影響,比如鋼琴啃洋,它的聲音很獨(dú)特传货。在后面幾節(jié)中,你會(huì)繼續(xù)探索它們是如何形成的宏娄。
完整代碼:
import AudioKitPlaygrounds
import AudioKit
let oscillator = AKOscillator()
oscillator.frequency = 300
oscillator.amplitude = 0.5
AudioKit.output = oscillator
try? AudioKit.start()
oscillator.start()
oscillator.rampDuration = 0.2
oscillator.frequency = 500
import AudioKitUI
AKPlaygroundLoop(every: 0.5) {
oscillator.frequency = (oscillator.frequency == 500 ? 100 : 500)
}
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
聲音封包
當(dāng)樂器演奏出一個(gè)音符時(shí)问裕,振幅(或音量)是會(huì)變化的,并且每個(gè)樂器都不相同孵坚。有一個(gè)能夠模擬這個(gè)效果的模型粮宛,叫做 Attack-Decay-Sustain-Release (ADSR) 封包:
這個(gè)封包由幾個(gè)部分構(gòu)成:
- Attack - 上行: 在這個(gè)階段聲音上行至最大音量窥淆。
- Decay - 下行: 這個(gè)時(shí)候聲音下滑到 Sustain 水平。
- Sustain - 維持: 這個(gè)階段聲音會(huì)維持在釋放終止時(shí)的音量巍杈,一直到開始 Release忧饭。
- Release - 釋放: 這個(gè)階段音量開始下滑到 0。
一臺(tái)鋼琴筷畦,當(dāng)琴弦被木錘敲擊眷昆,會(huì)發(fā)出一個(gè)非常短促的上行音然后迅速下降。一把小提琴則會(huì)發(fā)出比較長(zhǎng)的上行汁咏、下行和維持亚斋,因?yàn)檠葑鄷r(shí)琴弓不會(huì)離開琴弦。
電子琴是第一批電子樂器中使用 ADSR 封包的樂器之一攘滩。這種樂器發(fā)明于 1939 年帅刊,由 163 個(gè)電子管和 1000 多個(gè)特制的電容器構(gòu)成,重達(dá) 500 英磅(230 kg)漂问。但不幸的是赖瞒,只制造了 1000 臺(tái)電子琴,它沒有獲得商業(yè)上的成功蚤假。
圖片來自于 courtesy of Hollow Sun – 遵循 CC attribution 協(xié)議栏饮。
右鍵單擊 Playground 中的頂層元素,Journey磷仰,選擇 New Playground Page 袍嬉,創(chuàng)建一個(gè)新的 Playground 叫做ADSR
。編輯文件內(nèi)容為:
import AudioKitPlaygrounds
import AudioKit
let oscillator = AKOscillator()
創(chuàng)建了一個(gè)振蕩器灶平,這個(gè)你已經(jīng)很熟悉了伺通。然后繼續(xù)加入代碼:
let envelope = AKAmplitudeEnvelope(oscillator)
envelope.attackDuration = 0.01
envelope.decayDuration = 0.1
envelope.sustainLevel = 0.1
envelope.releaseDuration = 0.3
這次創(chuàng)建了一個(gè) AKAmplitudeEnvelope 并定義了一個(gè) ADSR 封包。durantion 參數(shù)用秒為單位指定逢享,level 參數(shù)指定的是音量罐监,取值訪問 0-1 之間。
AKAmplitudeEnvelope 是 AKNode 子類瞒爬,同 AKOscillator 一樣弓柱。在上面的代碼中,你可以看到侧但,振蕩器作為參數(shù)被傳遞給了封包的構(gòu)造函數(shù)矢空,兩個(gè)節(jié)點(diǎn)連在了一起。
接著:
AudioKit.output = envelope
try? AudioKit.start()
oscillator.start()
AudioKit 引擎啟動(dòng)俊犯,這次將輸出改成 ADSR 封包妇多,然后打開振蕩器。
為了聽到封包效果燕侠,你必須重復(fù)播放封包者祖,然后停止封包:
import AudioKitUI
AKPlaygroundLoop(every: 0.5) {
if (envelope.isStarted) {
envelope.stop()
} else {
envelope.start()
}
}
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
現(xiàn)在你會(huì)聽到同一個(gè)音符被反復(fù)播放立莉,但這次帶上了聲音封包效果,聽起來有點(diǎn)鋼琴的味道了七问。
每秒播放兩次蜓耻,每個(gè)循環(huán)都以 ADSR 開始和結(jié)束。當(dāng)循環(huán)開始后械巡,快速上行到最大音量刹淌,這個(gè)過程大約 0.01 秒,緊接著是 0.1 秒的下行讥耗,到達(dá)維持水平有勾。這個(gè)過程約 0.5 秒,然后釋放 0.3 秒古程。
修改 ADSR 值蔼卡,嘗試創(chuàng)建其他聲音的效果。試試如何模擬小提琴挣磨?
從振蕩器發(fā)出正弦波開始到現(xiàn)在雇逞,已經(jīng)過去很長(zhǎng)時(shí)間了。當(dāng)你用振蕩器演奏音符的同時(shí)茁裙,會(huì)使用 ADSR 去讓聲音更加柔和塘砸,但你仍然不能把它稱之為真正的音樂!
下一節(jié)晤锥,你會(huì)學(xué)習(xí)如何創(chuàng)建更加豐富的聲音掉蔬。
完整代碼:
import AudioKitPlaygrounds
import AudioKit
let oscillator = AKOscillator()
let envelope = AKAmplitudeEnvelope(oscillator)
envelope.attackDuration = 0.01
envelope.decayDuration = 0.1
envelope.sustainLevel = 0.1
envelope.releaseDuration = 0.3
AudioKit.output = envelope
try? AudioKit.start()
oscillator.start()
import AudioKitUI
AKPlaygroundLoop(every: 0.5) {
if (envelope.isStarted) {
envelope.stop()
} else {
envelope.start()
}
}
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
聲音疊加合成
每種樂器都有獨(dú)一無二的音質(zhì),并以其音色而得名查近。這就是為什么鋼琴的聲音和小提琴的聲音截然不同的原因眉踱,哪怕它們演奏同一個(gè)音符挤忙。音色的一個(gè)重要屬性是樂器所產(chǎn)生的聲譜霜威。聲譜表示樂器發(fā)出一個(gè)單音符時(shí)所組成的頻率范圍。你的 Playground 當(dāng)前所用的振蕩器只能發(fā)出單一的頻率册烈,所以聽起來非常假戈泼。
通過將一系列振蕩器合并在一起作為輸出并演奏同一個(gè)音符,你能夠真實(shí)地模擬出一個(gè)樂器赏僧。這就是“疊加合成”大猛。這是你的下一個(gè)課題。
右鍵單擊 Playground,選擇 New Playground Page 創(chuàng)建新的頁淀零,叫做Additive Synthesis
挽绩,編輯如下代碼:
import AudioKitPlaygrounds
import AudioKit
func createAndStartOscillator(frequency: Double) -> AKOscillator {
let oscillator = AKOscillator()
oscillator.frequency = frequency
return oscillator
}
對(duì)于疊加合成,你需要使用多個(gè)振蕩器驾中。createAndStartOscillator 方法用于創(chuàng)建它們唉堪。
然后寫入:
let frequencies = (1...5).map { $0 * 261.63 }
這里用了一個(gè) Range 操作來創(chuàng)建一個(gè)從 1 到 5 的序列模聋。然后對(duì)這個(gè)序列進(jìn)行 map 操作,將每個(gè)數(shù)字乘以 261.63唠亚。這個(gè)數(shù)字是標(biāo)注鍵盤上的中音 C 的音頻链方。將其他數(shù)字乘以這個(gè)值,這就是“和聲”灶搜。
然后繼續(xù)加入:
let oscillators = frequencies.map {
createAndStartOscillator(frequency: $0)
}
再次進(jìn)行一個(gè) map 操作祟蚀,以創(chuàng)建多個(gè)振蕩器。
然后將它們合成在一起割卖。加入:
let mixer = AKMixer()
oscillators.forEach { mixer.connect(input: $0) }
AKMixer 類也是 AudioKit 中的節(jié)點(diǎn)前酿;
它將 1 個(gè)或多個(gè)節(jié)點(diǎn)作為輸出并將它們合成在一起。
然后:
let envelope = AKAmplitudeEnvelope(mixer)
envelope.attackDuration = 0.01
envelope.decayDuration = 0.1
envelope.sustainLevel = 0.1
envelope.releaseDuration = 0.3
AudioKit.output = envelope
try? AudioKit.start()
oscillators.map { $0.start() }
import AudioKitUI
AKPlaygroundLoop(every: 0.5) {
if (envelope.isStarted) {
envelope.stop()
} else {
envelope.start()
}
}
上述代碼你已經(jīng)很熟悉了鹏溯;它用 mixer 創(chuàng)建了一個(gè) ADSR 封包薪者,將它提供給 AudioKit 引擎,然后不停地播放和停止它剿涮。
要真正能夠聽出合成的效果言津,你可以嘗試一下將這些頻率進(jìn)行不同的組合。當(dāng)你嘗試這樣做的時(shí)候取试,Playground 的 live-view 是一個(gè)不錯(cuò)的工具悬槽!
加入下列代碼:
class LiveView: AKLiveViewController {
override func viewDidLoad() {
addTitle("Mixer")
oscillators.forEach { oscillator in
let slider = AKSlider(property: "\(oscillator.frequency) Hz", value: oscillator.amplitude) { amplitude in
oscillator.amplitude = amplitude
}
addView(slider)
}
}
}
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = LiveView()
AudioKit 有許多類允許你輕松創(chuàng)建交互式的 Playground;我們?cè)谶@里也使用了其中幾個(gè)瞬浓。
LiveView 類是 AKLiveViewController 的子類初婆,它由一系列垂直排列的 subview 組成。在 viewDidLoad 方法中猿棉,你遍歷每個(gè)振蕩器磅叛,為每個(gè)振蕩器創(chuàng)建一個(gè) AKSlider。每個(gè) Slider 用每個(gè)振蕩器的頻率和振幅進(jìn)行初始化萨赁,當(dāng)遍歷到一個(gè) slider 時(shí)弊琴,都可以為它設(shè)置一個(gè)回調(diào)塊,這樣當(dāng)你拖動(dòng) slider 時(shí)回調(diào)塊被執(zhí)行杖爽。尾隨閉包就是這個(gè)回調(diào)塊敲董,允許你修改每個(gè)振蕩器的頻率。通過這種簡(jiǎn)單的方式慰安,你可以和 Playground 進(jìn)行交互腋寨。
為了測(cè)試上述代碼,你必須開啟 live view化焕。點(diǎn)擊右上角的雙環(huán)圖標(biāo)萄窜,打開助手窗口。同時(shí)將 live view 設(shè)置為正確的 playground 文件。
你可以通過修改每個(gè) Slider 的振幅來改變樂器的音色查刻。為了獲得更加自然的音質(zhì)番宁,我建議你參照上圖來進(jìn)行設(shè)置。
最早的一種采用疊加合成的合成器是 200 噸重的電傳簧風(fēng)琴赖阻。如此巨大的體量蝶押,立即宣告了這種樂器的消亡。結(jié)局更好的電子管風(fēng)琴使用了類似的轉(zhuǎn)速脈沖輪技術(shù)火欧,但體積更小棋电,用同樣的加法合成實(shí)現(xiàn)了獨(dú)特的聲音。電傳簧風(fēng)琴在 1935 年發(fā)明苇侵,在前衛(wèi)搖滾時(shí)代仍然是一種廣為人知的流行樂器赶盔。
C3 電傳簧風(fēng)琴 – 圖片來自 public domain image。
轉(zhuǎn)速脈沖輪上有旋轉(zhuǎn)的輪盤榆浓,輪沿上有許多光滑的隆起于未,旋轉(zhuǎn)輪盤附近有一個(gè)拾波器總成。電傳簧風(fēng)琴由許多這樣的轉(zhuǎn)速脈沖輪組成陡鹃,它們以不同的速度旋轉(zhuǎn)烘浦。音樂家通過拉桿來混合這些聲音并產(chǎn)生一個(gè)音符。這種發(fā)聲方式真的十分簡(jiǎn)陋萍鲸,嚴(yán)格地講闷叉,與其說是電子式的,不如說是機(jī)電式的脊阴。
要?jiǎng)?chuàng)建更真實(shí)的聲譜有許多別的技術(shù)握侧,比如 調(diào)頻技術(shù)(FM)和脈寬調(diào)制技術(shù)(PWM),這兩種在 AudioKit 中都可以通過AKFMOscillator
和AKPWMOscillator
類來實(shí)現(xiàn)嘿期。無疑品擎,我將鼓勵(lì)你去嘗試這兩者。為什么不在你的 Playground 中用這兩者將 AKOscillator 替換掉呢备徐?
復(fù)音
上個(gè)世紀(jì) 70 年代萄传,出現(xiàn)了一種偏離模塊化合成的理論,它使用單獨(dú)的振蕩器坦喘、封包和過濾器盲再,并使用了微處理器。替代模擬電路瓣铣,它使用了數(shù)字合成的方式發(fā)聲。它導(dǎo)致了價(jià)格極其低廉和便攜式合成器的出現(xiàn)贷揽,比如著名的雅馬哈電子合成器棠笑,被專業(yè)和業(yè)余音樂愛好者廣泛使用。
1983 年的 Yamaha DX7 – 圖片來自public domain image
你所有的 Playground 都被死死限制在只能一次演奏一個(gè)音符禽绪。如果使用多個(gè)樂器蓖救,音樂家可以同時(shí)演奏多個(gè)音符洪规。這種演奏方式就叫做“復(fù)音”,相反循捺,如果一次只能演奏一個(gè)音符斩例,就像你的 Playground 一樣,則叫做“單音”从橘。
為了制造復(fù)音念赶,你需要?jiǎng)?chuàng)建多個(gè)振蕩器,每個(gè)振蕩器演奏不同的音符恰力,并通過一個(gè) mixer 節(jié)點(diǎn)播放出來叉谜。但是,我們還有一種更簡(jiǎn)單的 方法:使用 AudioKit 的振蕩器 bank踩萎。
右鍵單擊 Playground停局,選擇 New Playground Page 創(chuàng)建一個(gè)新的 page 就叫做Polyphony
。寫入以下代碼:
import AudioKitPlaygrounds
import AudioKit
let bank = AKOscillatorBank()
AudioKit.output = bank
try? AudioKit.start()
這里創(chuàng)建了一個(gè)振蕩器 bank香府,并將它作為 AudioKit 的輸出董栽。如果你按下 Command 鍵點(diǎn)擊 AKOscilatorBank,你將看到它的類定義企孩,你會(huì)發(fā)現(xiàn)它其實(shí)繼承了 AKPolyphonicNode裆泳。如果你繼續(xù)深究下去,你會(huì)發(fā)現(xiàn)它又繼承了 AKNode 并采用了AKPolyphonic 協(xié)議柠硕。
因此工禾,振蕩器 bank 和其他 AudioKit 一樣,它的輸出也能夠被 mixer蝗柔、封包和其它濾鏡和效果所加工闻葵。AKPolyphonic 協(xié)議描述了你應(yīng)該如何在這個(gè)復(fù)音節(jié)點(diǎn)上演奏音符,等下你就知道了癣丧。
為了測(cè)試這個(gè)振蕩器槽畔,你需要設(shè)法和諧地播放多個(gè)音符。這聽起來好復(fù)雜胁编?
在 Playground 后面加入下列代碼厢钧,同時(shí)打開 live view:
import AudioKitUI
class LiveView: AKLiveViewController, AKKeyboardDelegate {
override func viewDidLoad() {
addTitle("Keyboard")
let keyboard = AKKeyboardView(width: 440, height: 100)
keyboard.delegate = self
keyboard.polyphonicMode = true
addView(keyboard)
}
func noteOn(note: MIDINoteNumber) {
bank.play(noteNumber: note, velocity: 80)
}
func noteOff(note: MIDINoteNumber) {
bank.stop(noteNumber: note)
}
}
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = LiveView()
當(dāng) Playground 編譯成功,你會(huì)看到這個(gè):
這么酷嬉橙?一個(gè) Playground 居然畫出了一個(gè)音樂鍵盤早直?
AKKeyboardView 另外一個(gè) AudioKit 提供的實(shí)用工具,它使這個(gè)框架真的容易使用和研究里面的功能市框。
當(dāng)你按下一個(gè)鍵霞扬,鍵盤會(huì)調(diào)用 noteON 委托方法。方法的實(shí)現(xiàn)很簡(jiǎn)單,簡(jiǎn)單地播放了振蕩器 bank喻圃。noteOff 方法則調(diào)用對(duì)應(yīng)的 stop 方法萤彩。
點(diǎn)擊并在鍵盤上滑動(dòng),你會(huì)發(fā)現(xiàn)它演奏出了優(yōu)美的音階斧拍。振蕩器 bank 內(nèi)置了 ADSR 支持雀扶。因此,一個(gè)音符的下行會(huì)和另一個(gè)音符的上升肆汹、松開和保持混在了一起愚墓,發(fā)出了令人愉悅的聲音。
你可能注意到了县踢,鍵盤提供的音符不再以頻率的方式提供转绷,而是以 MIDINoteNumber 類型提供。如果你按住 Command 鍵并點(diǎn)擊左鼠鍵硼啤,查看它的定義议经,你會(huì)看到它只是一個(gè)整型:
public typealias MIDINoteNumber = Int
MIDI 標(biāo)準(zhǔn)全稱是 Musical Instrument Digital Interface(樂器數(shù)字接口),它在樂器間進(jìn)行通訊時(shí)廣泛使用。 音符數(shù)字和標(biāo)準(zhǔn)鍵盤上的音符一一對(duì)應(yīng)谴返。play 方法的第二個(gè)參數(shù) velocity 是另一個(gè) MIDI 屬性煞肾,用于描述一個(gè)音符的敲擊力度。值越小表明敲擊得越輕嗓袱,會(huì)發(fā)出一個(gè)更小的聲音籍救。
最后一步是將鍵盤設(shè)置為復(fù)音模式。在 setup 方法代碼最后中加入:
keyboard.polyphonicMode = true
你會(huì)發(fā)現(xiàn)現(xiàn)在可以同時(shí)演奏多個(gè)音符了渠抹,只需要這樣:
……太不可思議了蝙昙,C-大調(diào)。這個(gè)項(xiàng)目使用了 Soundpipe梧却,代碼來自于 CSound奇颠,一個(gè)起始于 1985 年的 MIT 開源項(xiàng)目。令人不可思議的是它可以在 Playground 中運(yùn)行并添加到你的 App 中放航,而它竟然擁有超過 30 年的歷史了烈拒!
抽樣
你已經(jīng)學(xué)習(xí)了半天的聲音合成技術(shù)了,在這個(gè)過程中你嘗試用非常原始的方式制造擬真的聲音:振蕩器广鳍、過濾器和混合器荆几。早在上世紀(jì) 70 年代,隨著計(jì)算機(jī)處理能力和存儲(chǔ)的增長(zhǎng)赊时,一種完全不同的方法出現(xiàn)了——聲音取樣——目標(biāo)是制造聲音的數(shù)字復(fù)制品吨铸。
取樣是相對(duì)簡(jiǎn)單的概念,它和數(shù)字影像技術(shù)中的原理相同蛋叼。自然聲音是光滑的波形焊傅,取樣只是在固定的時(shí)間間隔內(nèi)簡(jiǎn)單地記錄聲波的震動(dòng):
在抽樣過程中剂陡,有兩個(gè)因素直接影響了記錄的擬真度:
- Bit depth - 位深: 表示一個(gè)取樣器能夠復(fù)制的離散振幅數(shù)狈涮。
- Sample rate - 速率: 表示多久進(jìn)行一次振幅測(cè)量狐胎,單位是 Hz。
你將用另一個(gè) Playground 來學(xué)習(xí)這些屬性歌馍。
在 Playground 上右鍵握巢,選擇 New Playground Page 并創(chuàng)建新的 page 名為Samples
。編輯如下代碼:
import AudioKitPlaygrounds
import AudioKit
let file = try AKAudioFile(readFileName: "climax-disco-part2.wav", baseDir: .resources)
let player = try AKAudioPlayer(file: file)
player.looping = true
這段代碼載入了一個(gè)示例音頻松却,創(chuàng)建了一個(gè)聲音播放器暴浦,并設(shè)置它的循環(huán)播放這個(gè)聲音。
這個(gè)波表文件放在這個(gè)zip文件中晓锻。解壓縮這個(gè) zip 文件歌焦,將 WAV 文件拖到 Playground 的 resources 文件夾中。
然后砚哆,在 Playground 文件最后繼續(xù)加入:
AudioKit.output = player
try? AudioKit.start()
player.play()
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
這會(huì)將你的聲頻播放器傳遞給 AudioKit 引擎并開始播放独撇。調(diào)大音量,注意聽躁锁。
這個(gè)簡(jiǎn)單的例子重復(fù)播放各種聲音纷铣,這些聲音很難用基本的振蕩器來模擬。
正在使用的音頻有一個(gè)比較高的位深和取樣率战转,能夠產(chǎn)生清脆和清晰的聲音搜立。為了試驗(yàn)這兩個(gè)參數(shù),在創(chuàng)建音頻播放器之后槐秧,加入如下代碼:
let bitcrusher = AKBitCrusher(player)
bitcrusher.bitDepth = 16
bitcrusher.sampleRate = 40000
AudioKit.output = bitcrusher
現(xiàn)在播放的聲音就截然不同了:仍然是同一個(gè)抽樣文件啄踊,但聲音變得非常尖銳。
AKBitCrusher 是一種 AudioKit 音效刁标,用于模擬低位深低取樣率的效果颠通。使用它,你可以制造出這種效果命雀,就像是早期用 ZX Spectrum 或 BBC Micro 進(jìn)行抽樣的聲音蒜哀,這些電腦僅有幾 Kb 的內(nèi)存和處理器,比起如今的電腦來說要慢上幾百萬倍吏砂!
最后的實(shí)驗(yàn)撵儿,是將許多節(jié)點(diǎn)組合在一起,制造出立體聲延遲效果狐血。刪除代碼中用于創(chuàng)建和配置 bitcrusher 的三行代碼淀歇。然后添加:
let delay = AKDelay(player)
delay.time = 0.1
delay.dryWetMix = 1
這會(huì)用你的抽樣文件創(chuàng)建出大約 0.1 秒的延遲效果。干/濕混合值讓你將延遲聲音和未延遲的聲音進(jìn)行混合匈织,設(shè)置為 1 表示只有經(jīng)過延遲的聲音被節(jié)點(diǎn)輸出浪默。
然后渔期,加入代碼:
let leftPan = AKPanner(player, pan: -1)
let rightPan = AKPanner(delay, pan: 1)
AKPanner 節(jié)點(diǎn)允許你將音頻進(jìn)行移動(dòng)牛曹,左移、右移或者之間的某個(gè)地方。上述代碼將延遲過的音頻左移产镐,為延遲的聲音右移猜敢。
最后一個(gè)步驟是將兩者混合在一起脑漫,并設(shè)置 AudioKit 的輸出秸仙,用下面的代碼替換掉原來設(shè)置 AudioKit 的輸出為 bitcrusher 的代碼:
let mix = AKMixer(leftPan, rightPan)
AudioKit.output = mix
這將播放同一個(gè)文件,但在左右揚(yáng)聲器之間有一個(gè)非常短的延遲胜榔。
完整代碼:
import AudioKitPlaygrounds
import AudioKit
let file = try AKAudioFile(readFileName: "climax-disco-part2.wav", baseDir: .resources)
let player = try AKAudioPlayer(file: file)
player.looping = true
//let bitcrusher = AKBitCrusher(player)
//bitcrusher.bitDepth = 16
//bitcrusher.sampleRate = 40000
//AudioKit.output = bitcrusher
let delay = AKDelay(player)
delay.time = 0.1
delay.dryWetMix = 1
let leftPan = AKPanner(player, pan: -1)
let rightPan = AKPanner(delay, pan: 1)
let mix = AKMixer(leftPan, rightPan)
AudioKit.output = mix
try? AudioKit.start()
player.play()
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
結(jié)語
在本教程中胳喷,你對(duì)“使用 AudioKit 能干什么”有了一個(gè)大致的理解了。開始探險(xiǎn)吧——嘗試一下穆格過濾夭织,升降調(diào)吭露、混響,或者圖像均衡器的效果怎么樣尊惰?
只需要一小點(diǎn)創(chuàng)意讲竿,你就可以制造出自己的聲音、電子樂器或者游戲音效择浊。
你可以下載最終項(xiàng)目戴卜。當(dāng)然,你仍然還需要像最開始那樣編譯動(dòng)態(tài)庫琢岩,并將最終代碼拖入進(jìn)去即可投剥。
最后,感謝 AudioKit 項(xiàng)目的Lead担孔,Aurelius Prochazka江锨,審閱了本文。