钱 立
(四川职业技术学院 计算机科学系,四川 遂宁 629000)
虚拟化电子乐器是很有趣的音乐软件,大家可在PC 或手机上使用,虚拟钢琴就是其中一种。以前设计的虚拟钢琴主要有这样几种:Flash 版,网页版,手机端Android 或iOS 原生代码开发版。因技术趋势,Flash 不再推荐使用。网页版钢琴设计思路主要是按琴键时加载相应的音频文件来发音。在这种方式下,若音频文件较大,加载和播放较耗时,有时能明显感觉到延时,不够灵敏;若音频文件过小,音效又较差。在手机安卓系统中可使用AudioTrack 来合成音调播放,但编程过程复杂。同时在安卓中设计钢琴键盘也比HTML5 网页界面复杂,控制起来也更麻烦。
Web Audio 是HTML5 中一个重要技术,使用该技术后可使得网页版应用或复杂的网页游戏具备华丽的音效,包括一些空间声响效果。本文使用该技术结合JS 设计出的虚拟简易钢琴有很好灵敏度和音效。
Web Audio 采用了模块化设计,这种模块化设计提供了灵活创建动态效果的复合音频的方法。一个简单而典型的Web Audio 流程如下[1]:
(1)创建音频上下文;
(2) 在音频上下文里创建源( 输入):例如〈audio〉、振荡器、流;
(3) 创建效果节点:例如混响、双二阶滤波器、平移、压缩;
(4)为音频选择一个目的地:例如你的系统扬声器;
(5)连接源到效果器,对目的地进行效果输出。
图1 Web Audio 典型流程
本文设计的虚拟钢琴使用振荡器运算得到不同音调发声。使用到Web Audio 相关API 如下:
AudioContext(音频上下文)代表由音频模块构成的音频处理图。它控制其所包含节点的创建和音频处理、解码。使用其它接口前必需创建一个音频上下文,一切操作都在这个环境里进行。
AudioNode(音频节点)是一个音频处理模块,可以是音频源、音频输出或中间处理模块(比如音量控制器GainNode)。
AudioParam 代表音频相关的参数,比如一个AudioNode 的参数。它可以设置为特定值或值的变化,并且可以在指定的时间之后以指定模式变更。
OscillatorNode(振荡器节点)代表一种随时间变化的波形,比如正弦波形。类型是AudioNode,功能是音频处理模块,可以产生指定频率的波形。
GainNode 用于音量变化,是一个AudioNode类型的音频处理模块,输入后应用增益效果,然后输出。
图2 24 键钢琴界面
设计的24 键钢琴界面如图2,这个界面使用HTML 和CSS 完 成[2]。 琴 键 样 式key,白 键 样 式whiteKey,黑键样式blackKey,使用样式叠加呈现出黑白琴键。
〈div class="main"〉
〈div id="key45" class="key whiteKey"〉〈/div〉
〈div id="key46" class="key blackKey"〉〈/div〉
…
〈/div〉
为了把琴键布置好,设置key 样式为position:absolute;float:left;。然后使用JS 对每一个琴键设置样式left 为不同偏移量,白键的宽度是手机屏幕横屏时宽度的1/14,黑键宽度是白键的2/3,黑键高度是白键的一半。一定要给琴键赋id 值,便于定位键来确定发音频率。
设计一个函数playSound(hz)来播放不同的音调。 hz 代表不同的音调,国际标准A 调是440.0hz。 这24 键琴音hz 用一个数组来保存,arrHz=[349.23,370.0,392.00,…][3]。
Web Audio 产生的音调波形主要有正弦波,三角波,矩形波,锯齿波四种,但开发者可以自定义波型。这里设计了一种音效来模拟钢琴发音后声音逐渐消失的过程[4]:首先创建音频上下文,指定音调为正弦波和频率hz 后,再设置当前时间音量为0,0.01 秒后音量变到1,然后从当前时间开始播放音调,最后声音在1 秒内慢慢减低直到停止。参看代码如下:
function playSound(hz){
//创建OscillatorNode,表示一个周期性波形(振荡),指定频率后表示一个音调
var oscillator = audioCtx.createOscillator();
//创建GainNode,控制音频总音量
var gainNode = audioCtx.createGain();
//把音量、音调和目的地节点关联。audioCtx.destination 通常表示音频渲染设备
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);
}
在手机端上为了实现触摸屏幕滑动琴键发音,则需使用JS 实现TouchEvent 事件的监听。主要 监 听 三 个 事 件 touchstart,touchmove,touchend。touchstart 事件也可看着是click 事件,可实现单个或多个琴键按下时发音。为了判断触摸从一个琴键移动到另一个琴键,需要保存上一个产生touchmove 事件的元素oldEle,同时要使用document.elementFromPoint(x,y)[5]来取得当前产生touchmove 事件的元素ele,后做进一
步判断。关键代码如下:
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";
}
}//这里函数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]);
}
为了使这个网页应用运行时有效果,可使用Chrome 浏览器打开该HTML 页面,再按F12 键切换到开发者状态,在右侧开发者工具视图中切换到手机视图效果,这时鼠标放在页面上会出现一个灰色的大圆点,这就代表触屏。可点击左键表示触屏tap 或按住左键移动表示touchmove。这样就可以发出类似钢琴的琴音了,按琴键或在琴键上快速移动都有敏捷的响应,发出的声音效果也不错。
如果想要直接运行在真实手机上,比如Android 系统,主要有两种方法。 一种是使用HBuilder 开发工具,连接上手机,自动安装好HBuilder 基座后,编写好网页应用立刻就能在手机上运行出效果。另一种是在可开发Android 程序的Eclipse 或Android Studio 中创建Android应用,在主Activity 中加入WebView 组件(全屏效果),然后通过WebView 组件加载本地HTML 网页,并允许JS 执行。这两种方法对于Android 系统最终均可生成apk 文件用于正常安装运行。
以往设计的虚拟钢琴通常是加载音频文件发音,随着Web Audio 技术的完善提高,我们可采用其技术通过波形和频率来产生特定的音调和音效。 使用HTML5 和CSS 设计钢琴界面,使用Web Audio 与JS 结合实现发音效果,这能较容易地设计出响应灵敏和漂亮音色的虚拟钢琴。本文案例应用在教学过程中,一方面提高了学生开发兴趣,另一方面也拓展了知识,这个综合性案例有很好的教学效果。