製作更複雜的音效

在前面的幾個單元中,我們較側重於觀察聲音的「頻率」,但並未去實時的改變每個單一振盪器的振幅,它們一但開始震動後,我們就不會去變動它們的振幅,因此它們發出的音量總是恆定的。

還記得我們試圖用rampTime週期性的改變振盪器的頻率時,它的波型圖嗎?

我們也可以用rampTime來改變振盪器的振幅,只是在威力強大的AKOutputWaveformPlot上,並不能明顯地看出效果,因為這個元件主要用途是觀測波形,其運行速度太快,導致我們隨時間改變它的振幅時,看起來卻像把整段波同時壓扁或拉長了。在這個單元中,我們將改用AKNodeOutputPlot元件來觀察一個振盪器的變化:

let plot = AKNodeOutputPlot(oscillator, frame: CGRect(x: 0, y: 50, width: 440, height: 200))
// 此處的oscillator為欲追蹤的AKNode物件

plot.plotType = .rolling
addSubview(plot)

相較於AKOutputWaveformPlot來說,AKNodeOutputPlot專注於顯示振幅上的變化,當然頻率的變化還是稍微有影響的,大家可以自行嘗試,此處不贅述這個元件的功能。

首先仿照之前我們用rampTime來控制頻率的方式,改為控制振幅,使其變換於0.01.0之間,會輸出這樣非常淺顯易懂的圖形:

在常見的樂器中,音量與時間的關係顯然不可能是這麼恆定的,由於空氣摩擦力會讓震動的能量損失,因此在正常環境中,無論我們敲打或彈奏什麼樂器,之後不去觸碰或影響它,它的震動也很快會停止,振幅的圖形會比較像這樣:

以鋼琴為例,在敲擊琴鍵的瞬間,琴槌擊中琴弦,造成震動而發出聲音,發出的聲波很快到達最大振幅,接著迅速降低到一個水平,此時我們的手指仍然在琴鍵上,隨後我們的手指離開琴鍵,此時琴弦以較緩慢的速度持續損失振幅,最後歸於平靜,這個模式稱為A-D-S-R模式,廣泛存在傳統的樂器中。

  • Attack:從震動開始到發出最大音量的過程。
  • Decay:從最大音量下降到穩定音量的過程。
  • Sustain:維持在穩定音量,或極緩慢的損失振幅,直到停止了彈奏樂器的動作為止。
  • Release:樂器由自然振動,緩慢地歸於平靜的過程。

每個樂器的A-D-S-R模型各自佔有不同的比例,例如小提琴可能因為樂師持續的拉動琴弓,喇叭等管樂器可能因吹奏者吐出空氣的節奏拉得很長,而擁有較長的Attack階段,有踏板(Petal)的鋼琴可能有很長的Sustain過程等等。而單純用電信原理,以振盪器來發出的聲音很難有這種特色,因此我們必須自己控制參數來模擬A-D-S-R模型,這也是許多人可能會常在合成器上看到的一個控制模組:「包絡」(Envelope)。

包絡的Wikipedia解釋是:一個振盪器的上下兩端的極限端點,串連成二維函數定義出來的線段,如圖所示:

AudioKit中,這個所謂的二維函數則是由AKAmplitudeEnvelope定義,一個最簡單的Envelope可以由定義每個階段的時間長度來模擬,例如在前文藍色線段的波形圖,其實就是以下的程式碼模擬出來的,這是一個Attack極短的範例,即使仍然只由一個振盪器發聲,但製造出的音效已經相對更接近於彈奏鋼琴。

// 此處的oscillator為欲套用Envelope的AKNode物件
let envelope = AKAmplitudeEnvelope(oscillator) 

// 設定各個階段的時長,單位為秒
envelope.attackDuration = 0.01
envelope.decayDuration = 0.09
envelope.sustainLevel = 0.15
envelope.releaseDuration = 0.3

// 在以下的程式碼,每0.7秒切換一次envelope的開關,否則運行一次後音量就永遠是0囉!
AKPlaygroundLoop(every: 0.7) {
  if (envelope.isStarted) {
    envelope.stop()
  } else {
    envelope.start()
  }
}

AudioKit.output = envelope
AudioKit.start()

results matching ""

    No results matching ""