錢(qián) 立
(四川職業(yè)技術(shù)學(xué)院 計(jì)算機(jī)科學(xué)系,四川 遂寧 629000)
虛擬化電子樂(lè)器是很有趣的音樂(lè)軟件,大家可在PC 或手機(jī)上使用,虛擬鋼琴就是其中一種。以前設(shè)計(jì)的虛擬鋼琴主要有這樣幾種:Flash 版,網(wǎng)頁(yè)版,手機(jī)端Android 或iOS 原生代碼開(kāi)發(fā)版。因技術(shù)趨勢(shì),F(xiàn)lash 不再推薦使用。網(wǎng)頁(yè)版鋼琴設(shè)計(jì)思路主要是按琴鍵時(shí)加載相應(yīng)的音頻文件來(lái)發(fā)音。在這種方式下,若音頻文件較大,加載和播放較耗時(shí),有時(shí)能明顯感覺(jué)到延時(shí),不夠靈敏;若音頻文件過(guò)小,音效又較差。在手機(jī)安卓系統(tǒng)中可使用AudioTrack 來(lái)合成音調(diào)播放,但編程過(guò)程復(fù)雜。同時(shí)在安卓中設(shè)計(jì)鋼琴鍵盤(pán)也比HTML5 網(wǎng)頁(yè)界面復(fù)雜,控制起來(lái)也更麻煩。
Web Audio 是HTML5 中一個(gè)重要技術(shù),使用該技術(shù)后可使得網(wǎng)頁(yè)版應(yīng)用或復(fù)雜的網(wǎng)頁(yè)游戲具備華麗的音效,包括一些空間聲響效果。本文使用該技術(shù)結(jié)合JS 設(shè)計(jì)出的虛擬簡(jiǎn)易鋼琴有很好靈敏度和音效。
Web Audio 采用了模塊化設(shè)計(jì),這種模塊化設(shè)計(jì)提供了靈活創(chuàng)建動(dòng)態(tài)效果的復(fù)合音頻的方法。一個(gè)簡(jiǎn)單而典型的Web Audio 流程如下[1]:
(1)創(chuàng)建音頻上下文;
(2) 在音頻上下文里創(chuàng)建源( 輸入):例如〈audio〉、振蕩器、流;
(3) 創(chuàng)建效果節(jié)點(diǎn):例如混響、雙二階濾波器、平移、壓縮;
(4)為音頻選擇一個(gè)目的地:例如你的系統(tǒng)揚(yáng)聲器;
(5)連接源到效果器,對(duì)目的地進(jìn)行效果輸出。
圖1 Web Audio 典型流程
本文設(shè)計(jì)的虛擬鋼琴使用振蕩器運(yùn)算得到不同音調(diào)發(fā)聲。使用到Web Audio 相關(guān)API 如下:
AudioContext(音頻上下文)代表由音頻模塊構(gòu)成的音頻處理圖。它控制其所包含節(jié)點(diǎn)的創(chuàng)建和音頻處理、解碼。使用其它接口前必需創(chuàng)建一個(gè)音頻上下文,一切操作都在這個(gè)環(huán)境里進(jìn)行。
AudioNode(音頻節(jié)點(diǎn))是一個(gè)音頻處理模塊,可以是音頻源、音頻輸出或中間處理模塊(比如音量控制器GainNode)。
AudioParam 代表音頻相關(guān)的參數(shù),比如一個(gè)AudioNode 的參數(shù)。它可以設(shè)置為特定值或值的變化,并且可以在指定的時(shí)間之后以指定模式變更。
OscillatorNode(振蕩器節(jié)點(diǎn))代表一種隨時(shí)間變化的波形,比如正弦波形。類型是AudioNode,功能是音頻處理模塊,可以產(chǎn)生指定頻率的波形。
GainNode 用于音量變化,是一個(gè)AudioNode類型的音頻處理模塊,輸入后應(yīng)用增益效果,然后輸出。
圖2 24 鍵鋼琴界面
設(shè)計(jì)的24 鍵鋼琴界面如圖2,這個(gè)界面使用HTML 和CSS 完 成[2]。 琴 鍵 樣 式key,白 鍵 樣 式whiteKey,黑鍵樣式blackKey,使用樣式疊加呈現(xiàn)出黑白琴鍵。
〈div class="main"〉
〈div id="key45" class="key whiteKey"〉〈/div〉
〈div id="key46" class="key blackKey"〉〈/div〉
…
〈/div〉
為了把琴鍵布置好,設(shè)置key 樣式為position:absolute;float:left;。然后使用JS 對(duì)每一個(gè)琴鍵設(shè)置樣式left 為不同偏移量,白鍵的寬度是手機(jī)屏幕橫屏?xí)r寬度的1/14,黑鍵寬度是白鍵的2/3,黑鍵高度是白鍵的一半。一定要給琴鍵賦id 值,便于定位鍵來(lái)確定發(fā)音頻率。
設(shè)計(jì)一個(gè)函數(shù)playSound(hz)來(lái)播放不同的音調(diào)。 hz 代表不同的音調(diào),國(guó)際標(biāo)準(zhǔn)A 調(diào)是440.0hz。 這24 鍵琴音hz 用一個(gè)數(shù)組來(lái)保存,arrHz=[349.23,370.0,392.00,…][3]。
Web Audio 產(chǎn)生的音調(diào)波形主要有正弦波,三角波,矩形波,鋸齒波四種,但開(kāi)發(fā)者可以自定義波型。這里設(shè)計(jì)了一種音效來(lái)模擬鋼琴發(fā)音后聲音逐漸消失的過(guò)程[4]:首先創(chuàng)建音頻上下文,指定音調(diào)為正弦波和頻率hz 后,再設(shè)置當(dāng)前時(shí)間音量為0,0.01 秒后音量變到1,然后從當(dāng)前時(shí)間開(kāi)始播放音調(diào),最后聲音在1 秒內(nèi)慢慢減低直到停止。參看代碼如下:
function playSound(hz){
//創(chuàng)建OscillatorNode,表示一個(gè)周期性波形(振蕩),指定頻率后表示一個(gè)音調(diào)
var oscillator = audioCtx.createOscillator();
//創(chuàng)建GainNode,控制音頻總音量
var gainNode = audioCtx.createGain();
//把音量、音調(diào)和目的地節(jié)點(diǎn)關(guān)聯(lián)。audioCtx.destination 通常表示音頻渲染設(shè)備
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.type=’sine’;
oscillator.frequency.value=hz;
gainNode. gain. setValueAtTime(0, audioCtx.currentTime);
gainNode. gain. linearRampToValueAtTime(1,audioCtx.currentTime+0.01);
oscillator.start(audioCtx.currentTime);
gainNode. gain. exponentialRampToValueAt-Time(0.001,audioCtx.currentTime+1);
oscillator.stop(audioCtx.currentTime+1);
}
在手機(jī)端上為了實(shí)現(xiàn)觸摸屏幕滑動(dòng)琴鍵發(fā)音,則需使用JS 實(shí)現(xiàn)TouchEvent 事件的監(jiān)聽(tīng)。主要 監(jiān) 聽(tīng) 三 個(gè) 事 件 touchstart,touchmove,touchend。touchstart 事件也可看著是click 事件,可實(shí)現(xiàn)單個(gè)或多個(gè)琴鍵按下時(shí)發(fā)音。為了判斷觸摸從一個(gè)琴鍵移動(dòng)到另一個(gè)琴鍵,需要保存上一個(gè)產(chǎn)生touchmove 事件的元素oldEle,同時(shí)要使用document.elementFromPoint(x,y)[5]來(lái)取得當(dāng)前產(chǎn)生touchmove 事件的元素ele,后做進(jìn)一
步判斷。關(guān)鍵代碼如下:
var oldEle = null;
keys[i].addEventListener(’touchstart’,han
dlStart,false);
keys[i]. addEventListener(’touchmove’, han
dlMove,false);
function handleStart(e){
e.preventDefault();
var touches=e.changedTouches;
var idx=e.target.id.substr(3,2);
play(arrHz[idx-45]);
e.target.style.backgroundColor="#9cf";
tmp=function(){
var obj=e.target;
if(obj.className=="key whiteKey"){
obj.style.backgroundColor="#ffe";
}else{
obj.style.backgroundColor="#444";
}
}//這里函數(shù)tmp 功能是用于還原琴鍵背景色
setTimeout(tmp,100);//此處琴鍵按下后背景
變色0.1 秒又還原
}
function handleMove(e){
var ele=document. elementFromPoint(e.
touches[0].clientX,e.touches[0].clientY);
if(oldEle==null){
oldEle=ele;
}else{
if(ele!=oldEle){
oldEle=ele;
}else{
return;
}
}
var idx=ele.id.substr(3,2);
playSound(arrHz[idx-45]);
}
為了使這個(gè)網(wǎng)頁(yè)應(yīng)用運(yùn)行時(shí)有效果,可使用Chrome 瀏覽器打開(kāi)該HTML 頁(yè)面,再按F12 鍵切換到開(kāi)發(fā)者狀態(tài),在右側(cè)開(kāi)發(fā)者工具視圖中切換到手機(jī)視圖效果,這時(shí)鼠標(biāo)放在頁(yè)面上會(huì)出現(xiàn)一個(gè)灰色的大圓點(diǎn),這就代表觸屏。可點(diǎn)擊左鍵表示觸屏tap 或按住左鍵移動(dòng)表示touchmove。這樣就可以發(fā)出類似鋼琴的琴音了,按琴鍵或在琴鍵上快速移動(dòng)都有敏捷的響應(yīng),發(fā)出的聲音效果也不錯(cuò)。
如果想要直接運(yùn)行在真實(shí)手機(jī)上,比如Android 系統(tǒng),主要有兩種方法。 一種是使用HBuilder 開(kāi)發(fā)工具,連接上手機(jī),自動(dòng)安裝好HBuilder 基座后,編寫(xiě)好網(wǎng)頁(yè)應(yīng)用立刻就能在手機(jī)上運(yùn)行出效果。另一種是在可開(kāi)發(fā)Android 程序的Eclipse 或Android Studio 中創(chuàng)建Android應(yīng)用,在主Activity 中加入WebView 組件(全屏效果),然后通過(guò)WebView 組件加載本地HTML 網(wǎng)頁(yè),并允許JS 執(zhí)行。這兩種方法對(duì)于Android 系統(tǒng)最終均可生成apk 文件用于正常安裝運(yùn)行。
以往設(shè)計(jì)的虛擬鋼琴通常是加載音頻文件發(fā)音,隨著Web Audio 技術(shù)的完善提高,我們可采用其技術(shù)通過(guò)波形和頻率來(lái)產(chǎn)生特定的音調(diào)和音效。 使用HTML5 和CSS 設(shè)計(jì)鋼琴界面,使用Web Audio 與JS 結(jié)合實(shí)現(xiàn)發(fā)音效果,這能較容易地設(shè)計(jì)出響應(yīng)靈敏和漂亮音色的虛擬鋼琴。本文案例應(yīng)用在教學(xué)過(guò)程中,一方面提高了學(xué)生開(kāi)發(fā)興趣,另一方面也拓展了知識(shí),這個(gè)綜合性案例有很好的教學(xué)效果。