鋼琴從古到今一直是樂曲常用的樂器,但是鋼琴聲因為同時包含了打擊與絃樂器的特色,因此不易使用FM等方式進行實作。Csound支援Phase Vocoder,且在QuteCsound中也有工具能夠分析傳入的WAV檔案。使用Phase Vocoder就可以輕易模擬出鋼琴的聲音。



Phase Vocoder

Phase Vocoder技術建立於傅利葉轉換的基礎上,需要一些專門的數學來推導,在本文章中就不去探討。

Csound 鋼琴聲實作

在Csound中,提供多種Phase Vocoder相關的opcode,例如本文使用的「pvoc」就是其中之一。將鋼琴聲的WAV檔使用QuteCsound的PVANAL工具,進行分析,轉成pvx檔案,再由pvoc運算子讀取進來。使用pvoc,可以在維持聲音音色的情況下,任意調整聲音的長度與音調。

pvx檔案下載

			idur init p3*1.5
			
			ifC4 init cpspch(8.00)
			ifC4u init cpspch(8.01)
			ifD4 init cpspch(8.02)
			ifD4u init cpspch(8.03)
			ifE4 init cpspch(8.04)
			ifF4 init cpspch(8.05)
			ifF4u init cpspch(8.06)
			ifG4 init cpspch(8.07)
			ifG4u init cpspch(8.08)
			ifA4 init cpspch(8.09)
			ifA4u init cpspch(8.10)
			ifB4 init cpspch(8.11)
			ifreq init cpspch(p4)
			ipvx init 3.00
			ipitchInt = floor(p4)
			ipitchDecimal = p4 - ipitchInt
			krr = 1
			if(ipitchDecimal>=0&&ipitchDecimal<0.005) then
				ktime line 0, idur, ipvx
				krr = ifreq/ifC4
				assig pvoc ktime, krr, "piano_C4_2.pvx"
			elseif(ipitchDecimal>=0.005&&ipitchDecimal<0.015) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifC4u
				assig pvoc ktime, krr, "piano_C4u_2.pvx"
			elseif(ipitchDecimal>=0.015&&ipitchDecimal<0.025) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifD4
				assig pvoc ktime, krr, "piano_D4_2.pvx"
			elseif(ipitchDecimal>=0.025&&ipitchDecimal<0.035) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifD4u
				assig pvoc ktime, krr, "piano_D4u_2.pvx"
			elseif(ipitchDecimal>=0.035&&ipitchDecimal<0.045) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifE4
				assig pvoc ktime, krr, "piano_E4_2.pvx"
			elseif(ipitchDecimal>=0.045&&ipitchDecimal<0.055) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifF4
				assig pvoc ktime, krr, "piano_F4_2.pvx"
			elseif(ipitchDecimal>=0.055&&ipitchDecimal<0.065) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifF4u
				assig pvoc ktime, krr, "piano_F4u_2.pvx"
			elseif(ipitchDecimal>=0.065&&ipitchDecimal<0.075) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifG4
				assig pvoc ktime, krr, "piano_G4_2.pvx"
			elseif(ipitchDecimal>=0.075&&ipitchDecimal<0.085) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifG4u
				assig pvoc ktime, krr, "piano_G4u_2.pvx"
			elseif(ipitchDecimal>=0.085&&ipitchDecimal<0.095) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifA4
				assig pvoc ktime, krr, "piano_A4_2.pvx"
			elseif(ipitchDecimal>=0.095&&ipitchDecimal<0.105) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifA4u
				assig pvoc ktime, krr, "piano_A4u_2.pvx"
			else
				ktime line 0, idur, ipvx
				krr =  ifreq/ifB4
				assig pvoc ktime, krr, "piano_B4_2.pvx"
			endif
			av linen 1, 0.05, p3, 0.05 ;ring the amplitude
			assig = assig * av / krr
			out assig, assig

如果要讓鋼琴聲音聽起來更渾厚,可以加入echo,讓前一個聲音可以稍微延續到下一個聲音,實作方式如下:

		gasig init 0
		instr 1
			;piano
			;p4=pitch
			;p3=duration
			
			idur init p3*1.5
			
			ifC4 init cpspch(8.00)
			ifC4u init cpspch(8.01)
			ifD4 init cpspch(8.02)
			ifD4u init cpspch(8.03)
			ifE4 init cpspch(8.04)
			ifF4 init cpspch(8.05)
			ifF4u init cpspch(8.06)
			ifG4 init cpspch(8.07)
			ifG4u init cpspch(8.08)
			ifA4 init cpspch(8.09)
			ifA4u init cpspch(8.10)
			ifB4 init cpspch(8.11)
			ifreq init cpspch(p4)
			ipvx init 3.00
			ipitchInt = floor(p4)
			ipitchDecimal = p4 - ipitchInt
			krr = 1
			if(ipitchDecimal>=0&&ipitchDecimal<0.005) then
				ktime line 0, idur, ipvx
				krr = ifreq/ifC4
				assig pvoc ktime, krr, "piano_C4_2.pvx"
			elseif(ipitchDecimal>=0.005&&ipitchDecimal<0.015) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifC4u
				assig pvoc ktime, krr, "piano_C4u_2.pvx"
			elseif(ipitchDecimal>=0.015&&ipitchDecimal<0.025) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifD4
				assig pvoc ktime, krr, "piano_D4_2.pvx"
			elseif(ipitchDecimal>=0.025&&ipitchDecimal<0.035) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifD4u
				assig pvoc ktime, krr, "piano_D4u_2.pvx"
			elseif(ipitchDecimal>=0.035&&ipitchDecimal<0.045) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifE4
				assig pvoc ktime, krr, "piano_E4_2.pvx"
			elseif(ipitchDecimal>=0.045&&ipitchDecimal<0.055) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifF4
				assig pvoc ktime, krr, "piano_F4_2.pvx"
			elseif(ipitchDecimal>=0.055&&ipitchDecimal<0.065) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifF4u
				assig pvoc ktime, krr, "piano_F4u_2.pvx"
			elseif(ipitchDecimal>=0.065&&ipitchDecimal<0.075) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifG4
				assig pvoc ktime, krr, "piano_G4_2.pvx"
			elseif(ipitchDecimal>=0.075&&ipitchDecimal<0.085) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifD4
				assig pvoc ktime, ifreq/ifG4u, "piano_G4u_2.pvx"
			elseif(ipitchDecimal>=0.085&&ipitchDecimal<0.095) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifA4
				assig pvoc ktime, krr, "piano_A4_2.pvx"
			elseif(ipitchDecimal>=0.095&&ipitchDecimal<0.105) then
				ktime line 0, idur, ipvx
				krr =  ifreq/ifA4u
				assig pvoc ktime, krr, "piano_A4u_2.pvx"
			else
				ktime line 0, idur, ipvx
				krr =  ifreq/ifB4
				assig pvoc ktime, krr, "piano_B4_2.pvx"
			endif
			av linen 1, 0.05, p3, 0.05 ;ring the amplitude
			assig = assig * av / krr
			gasig = gasig + assig
			;out assig, assig
		endin
		
		instr 99
			gaecho delayr 0.1          ;delay line length, echo delay 0.1 seconds
			gaout = gaecho*0.4 + gasig ;echo amplitude reduce to 0.4 ratio
			delayw gaout               ;write gaout into delay line
			out gaout,gaout            ;out delay line
			gasig = 0 ;
		endin

鋼琴聲音試聽