Web Audio API でビープ音を作る・モールス信号を流してみる

ふと思い立って、モールス信号を流すツールが作れないか調べてみた。Web Audio API というのを使うと、簡単なビープ音程度であれば JavaScript で生成・再生できるようだったので、試してみた。

デモ

早速だが作ってみたモノ。↓

「Dit」ボタンを押すと短点 (トン)、「Dah」ボタンを押すと長点 (ツー) が再生されるはず。それぞれアクセスキーに th を設定してある。

「SOS」ボタンを押すと、Chrome ブラウザではちゃんと SOS のモールス信号が鳴るはず。iOS Safari だと上手く鳴らなかった…。この辺、制御方法がまだイマイチなんだろうな。

ビープ音の作り方

ビープ音の鳴らすためのコードは次のとおり。

const beep = options => new Promise(resolve => {
  // Options
  options         = options           || {};
  const type      = options.type      == null ? 'sine' : options.type;
  const frequency = options.frequency == null ? 440    : options.frequency;
  const time      = options.time      == null ? 0.1    : options.time;
  const volume    = options.volume    == null ? 0.1    : options.volume;
  // Create
  const audioContext = new window.AudioContext();
  const oscillator = audioContext.createOscillator();
  const gain = audioContext.createGain();
  oscillator.connect(gain);
  gain.connect(audioContext.destination);
  // Set Options
  oscillator.type = type;
  oscillator.frequency.value = frequency;
  gain.gain.value = volume;
  // Start
  oscillator.onended = () => {
    gain.disconnect(audioContext.destination);
    oscillator.disconnect(gain);
    resolve();
  };
  oscillator.start(audioContext.currentTime);
  oscillator.stop(audioContext.currentTime + time);  // Stop Timing
});

作成した beep() 関数は連想配列でオプションを取れるようにしている。音声のタイプ、周波数、音量、音の長さを指定できる。

Web Audio API を扱うには、window.AudioContext() でコンテキストというモノを作り、そこに波形を作る OscillatorNode をくっつけたり、音量を操作する GainNode をくっつけたりして音声を作っていく。

createOscillator() で波形を調整するための OscillatorNode を作る。波形のタイプは sinesquaresawtoothtriangle などが選べる。frequency で周波数をヘルツ単位で指定できる。デフォルトは 440 になっているので、A の音が鳴ることになる。

続いて、createGain()GainNode を作り、これを使ってボリュームを調整する。大音量が鳴るとうるさいので、自分が作った beep() 関数ではデフォルトの音量を 10% (0.1) にしている。

音声の再生は、OscillatorNodestart() 関数と stop() 関数を使う。引数で再生する時刻を指定するのだが、window.AudioContext() で生成したコンテキストが currentTime という時刻情報を持っている。コレを利用すると、setTimeout()setInterval() などを使うよりも正確に、再生開始のタイミング、再生停止のタイミングを設定できる。

oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + time);

stop() 関数の方は、currentTime にいくらか値をプラスすることで、「0.1秒だけ再生する」とか「2秒間再生する」みたいな設定ができる。

自作の beep() 関数は、onended でコンテキストと Node の切断処理を一応入れて、Promise を解決するようにしてみた。Promise 形式にしたら、色んなビープ音を連続して実装しやすいかなと思ったためだ。

自作の sos() 関数は、この beep() 関数を使って「SOS」のモールス信号を組み立ててみている。iOS Safari では最初の短点しか再生されないので、どうも Promise での扱いだと微妙らしい。この辺よく分からないが、まぁいいかと思ってココまでとしている。w

参考文献