# - Web Audio API
- MediaStream Processing API
- Web Speechi Api
HTML5の音声関連 API の利用方法について調べてみた
@cyokodog shift + ↑:サムネイル表示
## Web Audio API - W3C 策定 - audio 要素よりも複雑な音声操作が可能(ミキシング/プロセッシング/フィルタリングなど)
Web Audio API の構成
### モジュラールーティング context と node の2つの要素で構成される - context - node を生成するもの - node - 音の生成、加工、出力を行うもの - それぞれの node 同士で接続し音を鳴らす →
モジュラールーティング
- ギター(out) → エフェクター(in-out) → アンプ(in) のイメージ
### 「音の生成」の種類
名前
内容
OscillatorNode
自前で音を生成する
シンセサイザーの音(サイン波,ノコギリ波)
AudioBufferSourceNode
mp3,wav 等の音声ファイルから音を生成する
MediaElementAudioSourceNode
audio、video要素から音を生成する
MediaStreamAudioSourceNode
MediaStream(マイク、カメラ)のストリームデータから音を生成する
### 「音の加工」の種類
名前
内容
BiquadFilterNode
低次フィルタ(ローパス、ハイパス、バンドパス、ローシェルフ、ハイシェルフ、ピーキング、ノッチ、オールパス)をかける
ConvolverNode
コンサートホールのような残響効果の加工をする
IRデータが別途必要(後述)
DelayNode
ディレイをかける
DynamicsCompressorNode
コンプレッサー。音の粒をそろえる
GainNode
音量調整を行う
WaveShaperNode
ディストーションをかける
### 「音の出力」の種類
名前
内容
AudioDestinationNode
スピーカーから音を鳴らす
MediaStreamAudioDestinationNode
AudioMediaStreamTrackを持つMediaStreamを表すオーディオの出力地点。
### その他
名前
内容
AnalyserNode
ミュージックビジュアライザー等のサウンドの視覚化に使用
ChannelSplitterNode
ルーティンググラフ内のオーディオストリームの個別チャンネルへのアクセスに使用
ChannelMergerNode
複数のオーディオストリームから1つのオーディオストリームへのチャンネル結合に使用
AudioListener
空間音響効果(音源を聞いてる位置)に使用。
PannerNode
空間音響効果(音源の位置)に使用。
ScriptProcessorNode
音の合成や加工をjavaScriptで直接行う
音を鳴らしてみる
「音の生成」→「音の出力」
### 音を鳴らす //ベースとなるオブジェクト window.AudioContext = window.AudioContext || window.webkitAudioContext; //safari対応
var audioContext = new AudioContext();
//「音の生成」
var osciillatorNode = audioContext.createOscillator();
//「音源」→「音の出力」
osciillatorNode.connect( audioContext.destination );
//音を鳴らす osciillatorNode.start = osciillatorNode.start || osciillatorNode.noteOn; //safari対応 osciillatorNode.start(0); //音を止める osciillatorNode.stop(1); [DEMO](demo/#!Demo1)
### 音のスケジューリング var audioContext = new AudioContext(); message.fadeIn('slow',function(){ //音源ノード生成(使い捨て) var osciillatorNode = audioContext.createOscillator(); osciillatorNode.connect(audioContext.destination); //audioContextを生成してからの経過時間
var currentTime = audioContext.currentTime;
//スケジューリング
osciillatorNode.start(currentTime + 1); osciillatorNode.stop(currentTime + 3);
//音声終了 osciillatorNode.onended = function(){ message.fadeOut(); } }) [DEMO](demo/#!Demo2) - 正確な再生・停止タイミング、リズムマシンやシーケンサーのようにワンショットの音源をつなぎ合わせるアプリには必須の機能 - audioContext.currentTimeで経過時間を取得 - 音源ノードは使い捨て
### 電話のツーツー音 デフォルト周波数は電話のツーツー音と同じ 440Hz でラ(A)の音にあたる for(var i = 0; i < 6; i++){ var osciillatorNode = audioContext.createOscillator(); osciillatorNode.connect(audioContext.destination);
//440Hz console.log(osciillatorNode.frequency.value);
osciillatorNode.start(i); osciillatorNode.stop(i+.5); } [DEMO](demo/#!Demo3)
### 音階の調整 現在の周波数*2で1オクターブアップ var score = [1,0,1,0,1,1,0,1,0,1,0,1,1], len = 0; score.forEach(function(v, i){ if(v){ var osciillatorNode = audioContext.createOscillator(); //周波数設定
var scale = Math.pow(Math.pow(2,1/12),i); osciillatorNode.frequency.value = 440 * scale;
osciillatorNode.connect(audioContext.destination); len ++; osciillatorNode.start(len/2); osciillatorNode.stop(len/2+.5); } }); [DEMO](demo/#!Demo4)
### 音声ファイルの取得・再生 var xhr = new XMLHttpRequest(); xhr.open('GET', 'sound/music.mp3', true); xhr.responseType = "arraybuffer"; xhr.onload = function(){ //変換 audioContext.
decodeAudioData
( xhr.response, function(
audioBuffer
){ //AudioBufferSourceNode 生成
var bufferSource = audioContext.createBufferSource();
//取得したaudioBufferをセット
bufferSource.buffer = audioBuffer;
//接続・再生 bufferSource.connect(audioContext.destination); bufferSource.start(); }, function(error) { alert('decodeAudioData error', error); } ); }; xhr.send(); [DEMO](demo/#!Demo5)
### 音声ファイルで音階調整 var score = [1,0,1,0,1,1,0,1,0,1,0,1,1], len = 0; score.forEach(function(v, i){ if(v){ var bufferSource = audioContext.createBufferSource(); bufferSource.buffer = audioBuffer; //再生速度を変え音程調整
var scale = Math.pow(Math.pow(2,1/12),i); bufferSource.playbackRate.value = 1 * scale;
bufferSource.connect(audioContext.destination); len ++; bufferSource.start(audioContext.currentTime + len/2); } }); [DEMO](demo/#!Demo6) playbackRate で再生速度を変え音程調整
### スケジュールの変更
var buffers = [];
・・・ var scale = Math.pow(Math.pow(2,1/12),i); bufferSource.playbackRate.value = 1 * scale; bufferSource.start(audioContext.currentTime + len/2); //退避
buffers.push(bufferSource);
//使用したら削除
bufferSource.oneded = function(){ buffers.shift(); }
・・・ //途中から1オクターブ上げる setTimeout(function(){ //スケジュールの変更
buffers.forEach(function(bufferSource){ bufferSource.playbackRate.value = bufferSource.playbackRate.value * 2; });
},2000); [DEMO](demo/#!Demo7) 使い捨ての音源ノードをバッファリングしておき、必要に応じて修正できるようにする
音を加工してみる
「音の生成」→「音の加工」→「音の出力」
### 音量の調整 var audioContext = new AudioContext(); var gainNode = audioContext.createGain(); //gainNode → destination gainNode.connect(audioContext.destination); ・・・ //xhrのコールバック function(audioBuffer){ var bufferSource = audioContext.createBufferSource(); bufferSource.buffer = audioBuffer; //bufferSource → gainNode bufferSource.connect(gainNode); bufferSource.start(); el_volume.addEventListener('change', function(){ //音量変更 gainNode.gain.value = this.value; }); } [DEMO](demo/#!Demo8) gainNode を挟み音量調整する
### 音色の調整 var audioContext = new AudioContext(); ・・・ //gainNode → destination var gainNode = audioContext.createGain(); gainNode.connect(audioContext.destination); //osciillatorNode → gainNode var osciillatorNode = audioContext.createOscillator(); osciillatorNode.connect(gainNode); //少しつづ音量を下げる len ++; var time = len/2; gainNode.gain.setValueAtTime(0, time); gainNode.gain.linearRampToValueAtTime(1.0, time + 0.01); gainNode.gain.linearRampToValueAtTime(0.7, time + 0.20); gainNode.gain.linearRampToValueAtTime(0.4, time + 0.40); gainNode.gain.linearRampToValueAtTime(0.0, time + 0.80); osciillatorNode.start(time); [DEMO](demo/#!Demo9) 少しつづ音量を下げ osciillatorNode の音色をソフトにする
### ルーム・エフェクト - 音の知覚・・・その音を聴く部屋によって大きく変わる - インパルスレスポンス(IR)・・・元の音と実際に耳に聴こえる音の差分を指すオーディオ用語 - IRデータ・・・インパルスレスポンスを録音したオーディオファイル。http://www.openairlib.net/ で Creative Commons で配布されてる
loadSound('sound/music.mp3', function(musicBuffer){ loadSound('sound/ir/underground_car_park.wav', function(effectBuffer){ //インパルス応答の適用 var convolver = audioContext.createConvolver(); convolver.connect(audioContext.destination) convolver.buffer = effectBuffer; var bufferSource = audioContext.createBufferSource(); bufferSource.buffer = musicBuffer; bufferSource.connect(convolver); bufferSource.start(); }); }); [DEMO](demo/#!Demo10)
## MediaStream Processing API 音声や動画をキャプチャする。WebRTC のメディアストリームで使用。
### ベンダープレフィックス navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
### 動画のキャプチャと再生 //キャプチャ開始 navigator.getUserMedia( {video: true, audio: false}, function(stream){ var video = document.querySelector('.video'); video.src = URL.createObjectURL(stream); video.play(); setTimeout(function(){ //キャプチャ終了 stream.stop(); }, 5000) }, function(err){ console.log(err.name ? err.name : err); } ); [DEMO](demo/#!Demo11)
### 動画のスクリーンショット canvas.getContext("2d").drawImage(video, 0, 0, 320, 240); [DEMO](demo/#!Demo12)
### 音声の録音・再生・保存(Firefox限定) - 音声はリアルタイム再生するとハウリングする。録音した後に再生。 - MediaStream Recording → 2015年4月現在 Firefox にのみ実装 Firefox Only navigator.getUserMedia( {video: false, audio: true}, function(stream){ //録音開始 var recorder = new MediaRecorder(stream); recorder.start(); // 録音が停止されると呼び出される recorder.ondataavailable = function(event) { var blob = event.data; var url = URL.createObjectURL(blob); //再生 audio.src = url; audio.play(); //保存 link.href = url; link.download = 'output.wav'; } // 5秒後に録音を停止 setTimeout(function(){ recorder.stop(); }, 5000); }, ); [DEMO](demo/#!Demo13)
### ライブラリで録音 - [MediaStreamRecorder.js](https://github.com/streamproc/MediaStreamRecorder) - Chrome の場合、一度ストリームを停止すると再キャプチャができない・・バグ? - [http://www.cyokodog.net/blog/media-capture-and-streams-web-audio-api/](http://www.cyokodog.net/blog/media-capture-and-streams-web-audio-api/)
### Web Audio API で録音 - キャプチャした音声を配列に記録 - stream →
mediaStreamSource
→
scriptProcessor
→ audioContext.destination
navigator.getUserMedia( {video: false, audio: true}, function(stream){ var audioBufferArray = []; //音声データの記録用配列 var bufferSize = 4096; var mediaStreamSource = audioContext.createMediaStreamSource(stream); var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1); mediaStreamSource.connect(scriptProcessor); scriptProcessor.connect(audioContext.destination);
scriptProcessor.onaudioprocess コールバック関数にて音声データを配列(audioBufferArray)に記録 //配列に保存 scriptProcessor.onaudioprocess = function(event){ var channel = event.inputBuffer.getChannelData(0); var buffer = new Float32Array(bufferSize); for (var i = 0; i < bufferSize; i++) { buffer[i] = channel[i]; } audioBufferArray.push(buffer); }
### 録音した音声を再生 録音した音声データを、再生用に生成した audioBuffer に割り当て再生する var getAudioBuffer = function(audioBufferArray){ var buffer = audioContext.createBuffer( 1, audioBufferArray.length * bufferSize, audioContext.sampleRate ); var channel = buffer.getChannelData(0); for (var i = 0; i < audioBufferArray.length; i++) { for (var j = 0; j < bufferSize; j++) { channel[i * bufferSize + j] = audioBufferArray[i][j]; } } return buffer; } var src = audioContext.createBufferSource(); src.buffer = getAudioBuffer(audioBufferArray); src.connect(audioContext.destination); src.start()
### 録音した音声を wav ファイルで保存 録音した音声データを、wav ファイル形式の blob データに変換し保存する var blob = exportWAV(audioBufferArray, audioContext.sampleRate) var url = URL.createObjectURL(blob); link.href = url; link.download = 'output.wav'; link.textContent = 'download'; [DEMO](demo/#!Demo14)
音声キャプチャ機能付きドラムマシンを作ってみた
実装上のポイント - 再生スケジュールの変更 - スケジューリングのタイミング - ルームエフェクトの適用タイミング [DEMO](http://cyokodog.github.io/web-audio-rythem/)
## Web Speech API
### 喋らせてみる speechSynthesis.speak( new SpeechSynthesisUtterance("No Programming, No Life") ); 日本語 var synthes = new SpeechSynthesisUtterance( '日本語のテキストを認識させるには、lang 属性に ja-JP を指定します。' ); synthes.lang = "ja-JP" speechSynthesis.speak( synthes );
パラメータ var synthes = new SpeechSynthesisUtterance(); synthes.voiceURI = 'native'; synthes.volume = 1; synthes.rate = 1; synthes.pitch = 2; synthes.text = 'No Programming, No Life'; synthes.lang = 'en-US'; synthes.onend = function(e) { alert('Finished in ' + event.elapsedTime + ' seconds.'); }; speechSynthesis.speak(synthes);
### いろいろな音声で喋らせてみる イタリア人風 var synthes = new SpeechSynthesisUtterance('No Programming, No Life'); var voices = speechSynthesis.getVoices(); voices.forEach(function(v, i){ //イタリア人風 if(v.name == 'Google Italiano') synthes.voice = v; }); speechSynthesis.speak(synthes); その他 demo
## Speech Recognition API
### 音声認識させてみる 一定時間の経過でキャプチャ状態は自動終了する window.SpeechRecognition = window.SpeechRecognition || webkitSpeechRecognition; var recognition = new webkitSpeechRecognition(); recognition.lang = 'ja'; // キャプチャ終了時トリガー recognition.addEventListener('result', function(event){ alert(event.results.item(0).item(0).transcript) }, false); // キャプチャ開始 recognition.start();
alert(event.results.item(0).item(0).transcript) .item(0).item(0)..? //音節単位の配列 var results = event.results; for(var i = 0; i < results.length; i++){ //変換候補の配列 var result = results.item(i); for(var j = 0; j < result.length; j++){ var alternative = result.item(j); alert(alternative.transcript) } }
### 連続音節認識 連続音節認識させるには continuous パラメータに true を指定する。音声入力待ち状態を長くすることができる。
### 変換候補リスト 複数の変換候補リストを求める場合は、maxAlternatives パラメータに候補数を指定する