第一個合成器

至此,我們已經擁有撰寫最簡單的合成器的能力,下面的程式碼中僅使用了一個新元件AKPropertySlider,此元件會創建一個滑條,供使用者操控,並在滑條的值被修改時,將之指定到其他元件上,此處我們要設定的是各個音高的振盪器的音量。

這個專案在啟動時會比較慢,這裡是因為顧及方便性,以及在課程的前半段出現邏輯比較複雜的程式碼會影響學習等原因,在一開始就啟動了所有16個振盪器,只是其音量皆為0而已,比較正確的做法是只啟動需要用到的振盪器,並且隨時關掉不再需要的振盪器,有興趣的同學可以自己試著改進(振盪器停止的指令為stop())。

在這個專案中,你會發現僅僅是這麼基礎的合成器,已經具有模擬一些真實生活中會出現的聲音的能力:例如僅啟動相距兩格的鍵盤,將會發出近似於撥電話時的等待聲(實際上此聲音由一個約350赫茲和一個約440赫茲的正弦波構成)。

這段程式碼中使用了較多未介紹的語法,因此不必太費心於看懂程式碼,自己把玩一下這個合成器,並且觀察AKOutputWaveformPlot隨著不同的振盪器組合變化的情形,享受成為聲音工程師的樂趣!

import AudioKit
import PlaygroundSupport

var oscillators:Array<AKOscillator> = []
let mixer = AKMixer()
let baseFreq:Array<Float> = [16.35, 18.35, 20.6, 21.83, 24.5, 27.5, 30.87, 32.7]
// C, D, E, F, G, A, B, C1 這八個音的頻率

class PlaygroundView: AKPlaygroundView {
    override func setup() {
        addTitle("First Pad")

        // 1. 設置最底層的視圖,此視圖是一個容器,裝載所有其他視圖
        let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 600))

        // 2. 創建一個視圖來裝載鍵盤,背景設為黑色,並在鍵盤彼此之間留下空隙來視覺化表格
        let padContainerView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
        padContainerView.layer.backgroundColor = UIColor.black.cgColor

        // 3. 以for-in迴圈創建振盪器、以及控制振盪器音量的滑條
        for i in 1...4 {
            for j in 1...4 {
                let mOsc = AKOscillator()
                let noteOffset:Int = (j-1) + (i-1)*4
                mOsc.frequency = baseFreq[noteOffset%8] * (noteOffset/8+1) * 16
                let mSlider = AKPropertySlider(property: "", 
                                                format: "", 
                                                value: 1, 
                                                minimum: 0, 
                                                maximum: 1, 
                                                color: UIColor(colorLiteralRed: (Float)(0.22*i), green: (Float)(0.22*j), blue: (Float)(0.11*(i+j)), alpha: 1), 
                                                frame: CGRect(x: 100*(i-1)+1, y: 100*(j-1)+1, width: 98, height: 98), 
                                                callback: { amp in mOsc.amplitude = amp })
                oscillators.append(mOsc)
                mixer.connect(mOsc)
                mOsc.start()
                padContainerView.addSubview(mSlider)         
            }
        }

        // 4. 創建輸出波形圖
        let plot = AKOutputWaveformPlot(frame: CGRect(x: 0, y: 0, width: 400, height: 200))
        plot.plotType = .buffer
        plot.backgroundColor = AKColor.white
        plot.originalColor = AKColor.black
        plot.shouldCenterYAxis = true

        // 5. 創建一個視圖來裝載波形圖,並將其Y方向壓縮10倍,這屬於偷懶的做法
        let plotContainerView = UIView(frame: CGRect(x: 0, y: 400, width: 400, height: 200))
        plotContainerView.addSubview(plot)
        plotContainerView.layer.transform = CATransform3DMakeScale(1, 0.1, 1)

        containerView.addSubview(padContainerView)
        containerView.addSubview(plotContainerView)
        addSubview(containerView)
    }
}
AudioKit.output = mixer
AudioKit.start()

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = PlaygroundView()

results matching ""

    No results matching ""