MMLを解析して楽曲を演奏する
次に、MMLを解析して楽曲を演奏します。MMLデータを順番に解析していくために、再生(解析)中の位置を示す変数ptrを用意します。MMLデータは1音ずつ配列に入っているので、音が鳴り止んだら変数ptrの値を1つ増やしていきます。最後まで解析して音を鳴らせばMMLデータを元にした演奏ができます。
演奏が終了するとオーディオオブジェクトにendedイベントが発生します。本来はこのendedイベントを捕捉して変数ptrの値を増やせればよいのですが、Firefox 4ベータ版の仕様なのか、自前で音を生成した場合はendedイベントが発生しません。同様に、MozAudioAvailableイベントも発生しません。
そこでやむを得ずsetTimeout()を使い、演奏した秒数後にMMLを処理する関数を呼び出します(この方法では正確な音長は処理できませんが、今回は簡易的に採用しています)。
setTimeout("ptr++;playMML()", sec*1000);
ここまでをまとめたのがサンプル2です。演奏ボタンをクリックするとテキストエリアに入力されたMMLデータが演奏されます。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>MMLを使って演奏する2</title> </head> <body> <h1>MMLを使って演奏する2</h1> <form> <input type="button" value="演奏" onclick="initMML()" /><br /> <textarea cols="20" rows="5" id="track1">A4</textarea> </form> <script type="text/javascript"> var freq = []; // 音階に応じた周波数を入れる配列 freq["R"] = 1; freq["C"] = 261; freq["D"] = 293; freq["E"] = 329; freq["F"] = 349; freq["G"] = 392; freq["A"] = 440; freq["B"] = 493; freq["#C"] = 277; freq["#D"] = 311; freq["#F"] = 370; freq["#G"] = 415; freq["#A"] = 466; // 演奏前の初期化を行う function initMML(){ tempo = 60; // テンポ設定(グローバル変数) ptr = 0; // 読み出し位置を初期化(グローバル変数) playMML(); } // 再生処理 function playMML(){ var data = document.getElementById("track1").value; var MML = data.split(" "); // 空白 if (ptr >= MML.length) return; // 最後まで演奏が終わっていたら以後の処理はしない var sdata = MML[ptr]; var onkai = sdata.match(/[a-z]+/i)[0].toUpperCase(); var sec = sdata.match(/\d+/); if (sdata.charAt(0) == "#") { onkai = "#"+onkai; } var sec = (60 / tempo) / sec; // 音長を計算 startAudio(freq[onkai], sec); } // 指定された周波数の音を指定時間出力する function startAudio(freq, sec){ var sampleRate = 44100; // 44.1kHz var audio = new Audio(); audio.mozSetup(1, sampleRate); // 1ch, 44kHz var bufferSize = Math.ceil(sampleRate * sec); // 再生秒数 var data = new Float32Array(bufferSize); var k = 2* Math.PI * freq / sampleRate; for(var i=0; i<data.length; i++){ data[i] = Math.sin(k * i); } audio.mozWriteAudio(data); setTimeout("ptr++;playMML()", sec*1000); audio.play(); } </script> </body> </html>