5_1_3:鼻歌音程モニター

AndroidやiOSアプリの「ボーカル音程モニター」にヒントを得て、「鼻歌音程モニター」なるものを試作しました。

これはピッチやMIDI番号、音程の取得までは前の「5_1_2:ピッチ抽出ピアノ」と同じで、ピッチの数値を折れ線グラフで描いたものです。

グラフの描画はp5.jsのライブラリの「grafica.js」で行っています。

次のリンクをクリックすると「鼻歌音程モニター」サンプルが開きます。

HTMLではgrafica.jsを読み込んでおきます。

<script src="lib/grafica.min.js"></script>
...
<h1>鼻歌音程モニター</h1>
<p id="status">画面をクリック!</p>
<p>ピッチ:<span id=freq>...</span></p>
<p>MIDI:<span id=midi>...</span></p>
<p>音程:<span id=cde>...</span></p>
<canvas id="sketch_canvas"></canvas>
<script src="sketch.js"></script>

sketch.js

const pitchGraph = (p) => {
    let point;
    const points = [];
    let plot;
    let count = 0;

    let pitch;
    let audioContext;
    let mic;
    const scale = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

    p.setup = function() {
        const cnv = p.createCanvas(800, 500);
        cnv.position(20, 240);
        p.background(255);

        // grafica.jsのGPoltインスタンス
        plot = new GPlot(p);
        // グラフの位置とサイズ、線の色と丸の色を設定
        plot.setPos(5, 5);
        plot.setDim(750, 300);
        plot.setLineColor(p.color(255, 0, 0));
        plot.setPointColor(p.color(100, 100, 255, 10));
        // 軸ラベルとタイトルの設定
        plot.getXAxis().setAxisLabelText("時間");
        plot.getYAxis().setAxisLabelText("周波数");
        plot.setTitleText("鼻歌音程モニター");

        mic = new p5.AudioIn();
        mic.start();
    }

    // 毎フレーム自動的に呼び出される
    p.draw = function() {
        // グラフを描画
        plot.defaultDraw();
    }

    p.touchStarted = function() {
        if (p.getAudioContext().state !== 'running') {
            audioContext = p.getAudioContext();
            audioContext.resume();
            startPitch();
        }
    }

    function startPitch() {
        pitch = ml5.pitchDetection('./model/', audioContext, mic.stream, p.modelLoaded);
    }

    p.modelLoaded = function() {
        console.log('モデルが読み込まれた')
        p.select('#status').html('モニタリング中...');
        p.getPitching();
    }


    p.getPitching = function() {
        pitch.getPitch(function(err, frequency) {
            if (frequency) {
                // MIDI番号と今の音を表示
                const midiNum = p.freqToMidi(frequency);
                const currentNote = scale[midiNum % 12];
                p.select('#freq').html(frequency);
                p.select('#midi').html(midiNum);
                p.select('#cde').html(currentNote);

                // グラフに描画する点をGPointオブジェクトで作成
                // x値がcount、y値がピッチ値
                point = new GPoint(count, frequency);
            }
            else {
                point = new GPoint(count, 0);
            }
            // GPointオブジェクトを配列に追加。
            points.push(point)
                // グラフの点に設定。
            plot.setPoints(points);
            count++;
            p.getPitching();
        })
    }
}


const sketch = new p5(pitchGraph, "sketch__canvas");

/*
var p5 = function(sketch, node, sync) {
...
}
*/

最後のnew p5()はp5クラスのコンストラクタです。p5.jsには次のように書かれています。

p5インスタンスは、p5スケッチに関連するすべてのプロパティとメソッドを保持する。コンストラクタは、入ってくるsketchクロージャを期待する。また生成されたp5キャンバスを割り当てる、オプションのnodeパラメータを取ることもできる。sketchクロージャは、新しく作成されたp5インスタンスを唯一の引数として取り、そこではスケッチを実行するための、preload()やsetup()、draw()などが設定できる。

p5スケッチはグローバルモードとインスタンスモードで実行できる。
グローバル – すべてのプロパティとメソッドはwindowに割り当てられる。
インスタンス – すべてのプロパティとメソッドはp5オブジェクトに関連付けられる。

@class p5
@constructor
@param {function} sketch オプションでp5インスタンス上にpreload()やsetup()、draw()プロパティなどが設定できるクロージャ,
@param {HTMLElement|Boolean} [node] キャンバスを割り当てるHTML要素。ブール値が渡された場合にはsyncとして使用する。
@param {Boolean} [sync] 同期処理で開始する(オプション)
@return {p5} p5インスタンス

var p5 = function(sketch, node, sync) {
  if (typeof node === 'boolean' && typeof sync === 'undefined') {
    sync = node;
    node = undefined;
  }

つまり、new p5(pitchGraph, “sketch__canvas”)によって、pitchGraph()関数に記述した機能を持ち、キャンバスとして<canvas id="sketch_canvas"></canvas>を使用するp5インスタンスを作成している訳です。pitchGraph()関数内では、引数として渡される変数pで、(アドオンも含む)p5.jsのプロパティやメソッドにアクセスできます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA