環境光センサーと近接センサーとWebAudioで楽器アプリを作る(1)
ここまでで一通りのハードウェアアクセスAPIを見てきましたが、ここからは、APIを実際の使用例に基いて解説していきます。今回は、環境光センサーAPIと近接センサーAPI、WebAudio APIと組み合わせて簡単な楽器アプリを作っていきます。
まず楽器アプリの概要についてです。今回の楽器アプリはWebAudio APIで単純な音を鳴らします。音程の制御は環境光センサーの値を使って行い、音量はタッチパネルのタッチ位置で制御するようにします。さらに近接センサーAPIを用いて一定以上手を近づけると音声が停止するようにします。つまりタッチパネルをドラッグしながらデバイスに手をかざして、手を近づけて影を強くしたり手を離して影を作らないようにして演奏するテルミンのようなアプリになります。
ソースコードは以下のリポジトリで公開しています。あわせて参考にしてください。
WebAudio APIで音を鳴らす
音程を変えるにも音量を変えるにもまずは音声を鳴らさなければ始まりません。WebAudio APIで音を鳴らしてみましょう。
WebAudio APIは、全体を管理するAudioContextオブジェクトにモジュールをつないでいく構造を持っています。現実世界で音源、アンプ、エフェクタ、スピーカをつないでいる状態をモデル化しているといえます。今回は複雑な波形もエフェクトも使用しないので、AudioContextに発振子とアンプを繋ぎます。
// Web Audio APIの状態管理オブジェクトを生成 audio.context = new AudioContext(); // オシレータとゲインを生成 audio.oscillator = audio.context.createOscillator(); audio.gainNode = audio.context.createGain(); // オシレータとゲインをスピーカにつなぐ audio.oscillator.connect(audio.gainNode); audio.gainNode.connect(audio.context.destination);
発振子の設定を変更して再生開始します。今回は発振子の波形、周波数、デチューン幅を設定しています。
// オシレータオプションを設定する
audio.oscillator.type = 'square'; // 矩形波
audio.oscillator.frequency.value = INITFREQ; // 発振周波数(Hz単位)
audio.oscillator.detune.value = 100; // デチューン設定(セント単位)
audio.oscillator.start();
audio.oscillator.onended = function() {
console.log('再生停止');
}
ゲインも初期化しておきましょう。
// ゲインを設定する audio.gainNode.gain.value = INITVOL;
これで矩形波が再生されます。audio.oscillator.frequency.valueを変更することで音程が、audio.gainNode.gain.valueを変更することで音量が操作できるようになりました。
環境光センサーAPIの値に合わせて音程を変える
音程と音量を制御できるようになったので、環境光センサーの値に応じて音程を変えてみましょう。
環境光センサーAPIは、前述したとおりwindowのdevicelightイベントにイベントハンドラを登録する形で使用します。
| イベント名 | devicelight |
|---|---|
| イベントハンドラのインターフェイス | function devicelightHandler(DeviceLightEvent event); |
| イベント引数の型 | DeviceLightEvent |
| イベント引数のプロパティ | value:取得したセンサー値(ルクス単位) |
| min:取得可能な最低値 | |
| max:取得可能な最大値 |
今回は音程を変えたいので、イベントハンドラの中でaudio.oscillator.frequency.valueを変更しています。また環境光センサーの値は揺れるので単純移動平均を取るようにしています。
window.addEventListener('devicelight', changeBrightness);
function changeBrightness(e) {
currentBrightness = e.value;
if (!maxBright) maxBright = currentBrightness; // フェイルセーフ
// 最大値は超えない
if (currentBrightness > maxBright) currentBrightness = maxBright;
// 直近n回の移動平均を取って周波数を作る
var ave = average(push(smaBrightness, currentBrightness, NUM_SMASAMPLE));
audio.oscillator.frequency.value = (ave / maxBright) * MAXFREQ;
console.log('light: ' + ave + ', maxBright: ' + maxBright);
}
タッチパネルのタッチ位置に合わせて音量を変える
次はタッチパネルのタッチ位置に応じて音量を制御しましょう。
タッチパネルのタッチ位置は、documentオブジェクトのtouchdownイベント、またはtouchmoveイベント、またはtouchupイベントで取得します。今回はドラッグに追従したいのでtouchmoveイベントを使用します。
| イベント名 | touchmove |
|---|---|
| イベントハンドラのインターフェイス | function touchmoveHandler(TouchEvent event); |
| イベント引数の型 | TouchEvent |
| イベント引数のプロパティ | touches:現在タッチしているタッチポイントのリスト |
| targetTouches:現在タッチしているターゲットからタッチ開始したタッチポイント | |
| changedTouches:前回のTouchEventから動いたタッチポイントのリスト |
タッチ位置はTouchListオブジェクトとして返ってくるので、特定の点を取りだすためにitemメソッドでインデックスを指定します。
var touchItem = touchList.item(index);
| メソッド名 | Touch TouchList.item(index) |
|---|---|
| 引数 | index:TouchListオブジェクトの値を読みだすインデックス |
| 返り値 | 読みだしたTouchオブジェクト |
これらを踏まえてタッチ位置で音量を変化させます。
document.addEventListener('touchmove', touchMove);
// マウス位置
var curX;
var curY;
// タッチ位置が動いたらタッチ座標からゲインを設定する
function touchMove(e) {
curX = e.targetTouches.item(0).screenX;
curY = e.targetTouches.item(0).screenY;
audio.gainNode.gain.value = (curY/HEIGHT) * MAXVOL;
//console.log('cur: ' + curY + ', h: ' + HEIGHT);
canvasDraw();
}

