超音波通信やろうとして音の可視化(可聴域の特定周波数の判別など)をやった話【前編】

  • 43
    Like
  • 0
    Comment
More than 1 year has passed since last update.

イントロ

こちらは、HTML5 Advent Calendar 2014の11日目のエントリーです。
まずは、タイトルに書いてる流れになった経緯を、軽く書いておきます。

  • 経緯
    1. エンタメな勉強会のネタの1つで、Javascript+超音波通信やりたいなぁ。
    2. ライブラリとかあるけど(※ 後述)、まずは自前でやってみよう!
    3. 可聴域以外の周波数の音って、入出力がデバイス(スピーカー・マイクとか)の性能に依存しそう・・・。
      出力側の確認が耳でできないし(自宅にアナライザ的なものはないし)、デバッグ面倒かも・・・。
    4. 可聴域だと、出力が楽器とか自分の声とか使えるし、デバッグもしやすい!?
    5. 子ども向けのエンタメコンテンツとかで、声のビジュアライゼーションできたら楽しいかも♪

という思考の流れから、
まずは可聴域の音の周波数に応じて、何かをブラウザに表示するってのを作ろう!!
となりました。

完成したもの

(※ 後ほど、Youtubeにデモ動画をアップ予定)
⇒ 特定の周波数の音をマイクへ入力すると、ブラウザのCanvas上にいくつかの矩形がランダムな座標に描画される。
  その矩形の色が、入力された音の周波数によって変わる(複数の周波数の音が混じってると、出力される矩形の色も混在した感じに)。

作成の過程など

概要

おおまかな仕組みは、こんな感じです。

1.WebRTCのgetUserMediaを使い、マイク入力の音声を拾う
2.取得した音声の周波数を Web Audio API の Analyser で解析
3.特定周波数(ひとまず、ドレミの音の周波数にしました)に対応したイベントをブラウザ上で発生させる
 ※ 今回は Canvas上に図形描画を行う

特定の周波数(ひとまず、ドレミ等の音階で)を判別する処理をする

Javascript実装

上記★1のサイトの内容を参照しつつ、特定の周波数の音を判別する処理を作ってみます。

★1のサイでトは時間ドメインと周波数ドメインの両方を表示するようにしてあるので、今回は周波数ドメインの部分のみを取り出して利用します(時間ドメインの処理部分は削除)。
ドレミの音階に対応した周波数をググって調べ(今回はこちらを参照 ⇒ あすら日記 音の周波数 - ドレミファソラシド の周波数の一覧)、★1のサイトのソースを書き換えます。
Javascriptについては、時間ドメイン関連の部分を削除する以外には、animation関数の中身を下記のように書き換えてみます。

コメントがつけてる辺りが、書き換えた部分です。

var animation = function(){

  analyser.getByteFrequencyData(frequencyData);

  // 特定周波数の値を設定
  var fsDivN = audioContext.sampleRate / analyser.fftSize;
  var n523Hz = Math.floor(523 / fsDivN); // ド
  var n587Hz = Math.floor(587 / fsDivN); // レ
  var n659Hz = Math.floor(659 / fsDivN); // ミ

  // 小さい音を拾わないための閾値設定
  var threshold = 160;

  // 上記で設定された周波数で、かつ、上記の閾値以上の大きさの音が
  // 検出された場合に描画を行う
  frequencyData[0] = 0;
  for (var i = 1, l = frequencyData.length; i < l; i++) {
    if((i != n523Hz)&&(i != n587Hz)&&(i != n659Hz)) {
      frequencyData[i] = 0;
    } else if(frequencyData[i] < threshold) {
      frequencyData[i] = 0;
    }
  }

  frequencyContext.clearRect(0, 0, width, height);

  frequencyContext.beginPath();
  frequencyContext.moveTo(0, height - frequencyData[0]);
  for (var i = 1, l = frequencyData.length; i < l; i++) {
    frequencyContext.lineTo(i, height - frequencyData[i]);
  }
  frequencyContext.stroke();

  requestAnimationFrame(animation);
};

音をだして確認してみる

PC上のブラウザで上記の内容を実行するのに、特定周波数の音を特定の音量でだす、という部分はアプリを使いました。
ちなみに、いくつかググって選定したアプリはこちら ⇒ 「Androidアプリ: 音叉」、「iOSアプリ: onsA440

もちろん、Web Audio API を使えば、ブラウザからこれらのアプリと同様に音を出力することも可能です。
(昨年の HTML5 Advent Calendar 2013 では、イベント で実演された内容に影響を受け、「Web Audio API を使ってみたい!」という思いから、ブラウザ上で音を鳴らすネタを書いてました。⇒ ブラウザで音を扱う: Web Audio API

これらの何れかの方法を使って、周波数が「523Hz、587Hz、659Hz」の音に反応して、ブラウザ上に★1のサイトで見られるようなグラフ描画が行われることを確認します。
周波数によって、グラフ描画の山ができる位置が変わります(グラフ描画領域を★1のソースから変更せず実行すると、グラフ描画の領域の左のほうのみが使われるので、周波数が異なる場合の描画の差が分かりにくいのですが・・・)。

Canvasに図形描画を行う

Javascript実装

★1のサイトのグラフ描画をしていた部分を、図形描画する処理に置き換えます。
音階については、周波数間の幅を大きくとるために、ドレミでなくドミソとしました。

var animation = function(){

  analyser.getByteFrequencyData(frequencyData);

  // 特定周波数の値を設定
  var fsDivN = audioContext.sampleRate / analyser.fftSize;
  var n523Hz = Math.floor(523 / fsDivN); // ド
  var n659Hz = Math.floor(659 / fsDivN); // ミ
  var n783Hz = Math.floor(783 / fsDivN); // ソ

  // Canvasに図形描画するときの座標を格納する変数    
  var random_x = 0;
  var random_y = 0;
  var random_width  = 0;
  var random_height = 0;

  // 小さい音を拾わないための閾値設定
  var threshold = 160;

  frequencyData[0] = 0;
  for (var i = 1, l = frequencyData.length; i < l; i++) {
    if((i != n523Hz)&&(i != n659Hz)&&(i != n783Hz)) {
      frequencyData[i] = 0;
    } else if(frequencyData[i] < threshold) {
      frequencyData[i] = 0;
    }
  }

  frequencyContext.clearRect(0, 0, width, height);

  // 描画する図形の、描画位置の座標と、描画する大きさの
  // 幅・高さをランダムに生成
  for (var i = 0, l = frequencyData.length; i < l; i++) {
    random_x = Math.floor( Math.random() * width );
    random_y = Math.floor( Math.random() * height );
    random_width  = Math.floor( Math.random() * 3 + 1 )* 30;
    random_height = random_width;

    // 図形の描画処理
    if(frequencyData[i] != 0) {
    if(i==n523Hz) {
      frequencyContext.fillStyle = 'rgb(255, 0, 0)';
    } else if (i==n659Hz) {
      frequencyContext.fillStyle = 'rgb(0, 255, 0)';
    } else if (i==n783Hz){
      frequencyContext.fillStyle = 'rgb(0, 0, 0)';
    }
      frequencyContext.fillRect(random_x, random_y, random_width, random_height);
    }
  }

  requestAnimationFrame(animation);

};

音をだして確認してみる

音の出力方法は前述の通りです。
下記のような結果が得られると思います。

  • 出力の違い
    • ドの音 ⇒ 赤い矩形が描画される
    • ミの音 ⇒ 緑色の矩形が描画される
    • ソの音 ⇒ 黒い矩形が描画される

続き

次回へ続きます。

音波通信関連のJavascriptライブラリ