getUserMedia使ってJSで動くチューナー作った。

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

少し前に作ったものですが折角の機会なので公開しようと思います。
実験のための実装なのでUIがひどいことになっていますが見逃してください。
ChromeかFirefoxなら動くと思います。

デモ:http://ymmtmdk.github.io/jstuner/
ソース:https://github.com/ymmtmdk/jstuner

少しだけですが、WebRTCに絡む部分で何をしているか説明したいと思います。
getUserMediaを使っている周辺を抜粋します。CoffeeScriptです。
まず初期化の部分から。

Meteor.startup ->
  navigator.getUserMedia = navigator.getUserMedia or navigator.webkitGetUserMedia or navigator.mozGetUserMedia or navigator.msGetUserMedia unless navigator.getUserMedia
  window.AudioContext = window.webkitAudioContext unless window.AudioContext

  if navigator.getUserMedia and window.AudioContext
    navigator.getUserMedia
      audio: true
    ,connectRecorder, -> alert "error capturing audio."
  else
    alert "not supported in this browser."
    return

  canvas = document.getElementById("wave")
  canvasContext = canvas.getContext("2d")

Meteor.startupとなっているのは気にしないでください。onloadのようなものと思っていただければ。
このstartupでやっているのはブラウザ間の差異を吸収した上でconnectRecorder関数をコールバックにgetUserMediaを呼ぶだけです。この辺りは先日までのWebRTCカレンダーで書かれていますので是非読んでください。

次のconnectRecorderがこの記事におけるメインです。

connectRecorder = (stream) ->
  audioContext = new AudioContext()

  hzElement = document.getElementById("hz")
  noteElement = document.getElementById("note")

  bufferSize = 2048
  recorder = audioContext.createScriptProcessor(bufferSize, 2, 2)
  recorder.onaudioprocess = (e) ->
    left = e.inputBuffer.getChannelData(0)
    drawWave(left)
    hz = Pitcher.pitch(left, audioContext.sampleRate)
    return unless hz >= 30
    note = new Note(hz)
    hzElement.innerHTML = 'hz = ' + hz
    noteElement.innerHTML = 'note = ' + note.name()

  # connect the recorder
  input = audioContext.createMediaStreamSource(stream)
  input.connect recorder
  recorder.connect audioContext.destination

まずWeb Audio APIの司令塔たるAudioContextを用意します。
次にチューナーの処理を担当するノードをaudioContext.createScriptProcessorで作り出します。
ここではrecorderと名づけているこのノードのonaudioprocessハンドラーにチューナー処理を登録します。
このハンドラーがバッファーが貯まるたびに呼ばれ、音程を算出するわけです。

次にaudioContext.createMediaStreamSource(stream)として入力ノードを取り出します。
この入力ノードにさきほどのrecorderノードを接続します。
さらにrecorderノードにaudioContext.destinationノードを接続して完成です。

入力ノード > チューナーノード > 出力ノード

この様にWeb Audio APIでは各役割をこなすノードを作って、それを接続する、という手順でプログラムが構成されます。ここに出た以外に色々なノードが用意されてますの詳しくはリファレンスを御覧ください。

以上、なんとも雑な説明ではありますが、割と簡単に音が扱えるのねと思われたなら幸いです。

Web Audio API (日本語訳):http://g200kg.github.io/web-audio-api-ja/#ModularRouting
Web Audio API:http://www.w3.org/TR/webaudio/