このページの本文へ

Audio Data APIでブラウザーをシーケンサーに! (5/6)

2010年11月02日 10時00分更新

文●古籏一浩

  • この記事をはてなブックマークに追加
本文印刷

マルチトラックに対応する

 ここまでで、MMLを使って演奏するプログラムができました。単音だと少し物足りないので、サンプル3を改造して二重和音に対応します(二重以上でもFirefoxとマシンパワーが許す限り多重演奏できます)。

 サンプル3との違いは、トラックごとにオブジェクトを生成し、オブジェクト内で処理する点です。playTrackというオブジェクトを用意し、ここまでに作成したプログラムを格納します。ptrやtempoなどの変数はオブジェクトのプロパティとして定義します。たとえば、

this.ptr
this.tempo

のように変更します。また、setTimeout()の仕様上、2回目の呼び出しはthisがWindowオブジェクトを指し示してしまうので、ローカル変数objにthisのデータを入れておきます。

var obj = this;

 実際のプログラムはサンプル4です。2つのテキストエリアに入力されたMMLが同時に処理され演奏されます。ちなみに、サンプル4を以下のように変更すると正弦波ではなく矩形波が出力されます。8ビットパソコンの雰囲気を味わいたい方はお試しください。

data[i] = Math.sin(k * i);

data[i] = Math.sin(k * i);
if (data[i] > 0) data[i] = 1; else data[i] = -1;

【図】fig04.png

テキストエリアに入力されたMMLが同時に解析、処理され演奏される

サンプル4

<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8" /> 
<title>MMLを使って演奏する(二重和音)</title> 
</head> 
<body>
<h1>MMLを使って演奏する(二重和音)</h1>
<form>
<input type="button" value="演奏" onclick="initMML()" /><br />
<textarea cols="20" rows="5" id="track1">O4 C4 D4 E2 F2</textarea>
<textarea cols="20" rows="5" id="track2">O2 C4 D4 E2</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;
for(i in freq) { freq[i] = freq[i] * 4;    }
// トラック1、2を再生する
function initMML(){
    track1 = new playTrack("track1");
    track1.playMML();
    track2 = new playTrack("track2");
    track2.playMML();
}
// 指定されたトラックIDを再生するためのクラス
function playTrack(trackID){
    var obj = this;
    this.octave = 4;    // オクターブ設定
    this.tempo = 60;    // テンポ設定
    this.ptr = 0; // 読み出し位置を初期化
    this.track = trackID; // トラックID
    this.playMML = function(){
        // console.log(obj.track+" = "+obj.ptr);    // 再生位置を調べる場合はこの注釈を消去
        var data = document.getElementById(obj.track).value;
        var MML = data.split(" ");  // 空白
        while(true){
            if (obj.ptr >= MML.length) return;  // 最後まで演奏が終わっていたら以後の処理はしない
            var sdata = MML[obj.ptr];
            var onkai = sdata.match(/[a-z]+/i)[0].toUpperCase();
            if (sdata.charAt(0) == "#") { onkai = "#"+onkai; }
            var num = sdata.match(/\d+/);
            if (onkai == "O") {   // オクターブ指定
                obj.octave = num;
                obj.ptr++;
                continue;
            }
            if (onkai == "T") {   // テンポ指定
                obj.tempo = num;
                obj.ptr++;
                continue;
            }
            break;
        }
        var sec = (60 / obj.tempo) / num; // 音長を計算
        var fr = freq[onkai]/obj.octave;
        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 * fr / sampleRate;
        for(var i=0; i<data.length; i++){
            data[i] = Math.sin(k * i);
            // if (data[i] > 0) data[i] = 1; else data[i] = -1;   // PSG(矩形波)にしたい場合はこの注釈を消去
        }
        audio.mozWriteAudio(data);
        obj.ptr++;
        setTimeout(obj.playMML, sec*1000);
        audio.play();
    }
}
</script> 
</body> 
</html> 

 HTML5のFile APIと組み合わせるとテキストエリアに入力されたMMLではなく、ローカルにあるMMLファイルも演奏できます。実際のプログラムはサンプル5です。File APIについては以下の記事を参考にしてください。

・File APIでブラウザーからローカルファイルを操作
http://ascii.jp/elem/000/000/559/559105/

File APIと組み合わせるとローカルにあるMMLファイルを演奏できる

サンプル5

<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8" /> 
<title>ローカルファイルのMMLを演奏</title> 
</head> 
<body>
<h1>ローカルファイルのMMLを演奏</h1>
<form>
<input type="file" id="myFile">
<input type="button" value="演奏" onclick="initMML()" /><br />
</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;
for(i in freq) { freq[i] = freq[i] * 4;    }
// トラック1、2を再生する
function initMML(){
    var filename = document.getElementById("myFile").files[0];
    var reader = new FileReader();
    reader.onload = function(evt){
        track1 = new playTrack(evt.target.result);
        track1.playMML();
    }
    reader.readAsText(filename, "utf-8"); // 文字コードをUTF-8として読み込む
}
// 指定されたトラックIDを再生するためのクラス
function playTrack(MMLdata){
    var obj = this;
    this.octave = 4;    // オクターブ設定
    this.tempo = 60;    // テンポ設定
    this.ptr = 0; // 読み出し位置を初期化
    this.data = MMLdata;    // MMLデータ
    this.playMML = function(){
        // console.log(obj.track+" = "+obj.ptr);    // 再生位置を調べる場合はこの注釈を消去
        var MML = obj.data.split(" ");  // 空白
        while(true){
            if (obj.ptr >= MML.length) return;  // 最後まで演奏が終わっていたら以後の処理はしない
            var sdata = MML[obj.ptr];
            var onkai = sdata.match(/[a-z]+/i)[0].toUpperCase();
            if (sdata.charAt(0) == "#") { onkai = "#"+onkai; }
            var num = sdata.match(/\d+/);
            if (onkai == "O") {   // オクターブ指定
                obj.octave = num;
                obj.ptr++;
                continue;
            }
            if (onkai == "T") {   // テンポ指定
                obj.tempo = num;
                obj.ptr++;
                continue;
            }
            break;
        }
        var sec = (60 / obj.tempo) / num; // 音長を計算
        var fr = freq[onkai]/obj.octave;
        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 * fr / sampleRate;
        for(var i=0; i<data.length; i++){
            data[i] = Math.sin(k * i);
            // if (data[i] > 0) data[i] = 1; else data[i] = -1;   // PSG(矩形波)にしたい場合はこの注釈を消去
        }
        audio.mozWriteAudio(data);
        obj.ptr++;
        setTimeout(obj.playMML, sec*1000);
        audio.play();
    }
}
</script> 
</body> 
</html> 

この連載の記事

一覧へ

この記事の編集者は以下の記事をオススメしています