ホーム > カテゴリ > HTML5・JavaScript >

AudioWorkletの使い方 [Web Audio API]

AudioWorkletとAudioWorkletProcessorの使い方です。

AudioWorkletProcessorでは波形の生データをリアルタイムで編集可能です。Web Workerのようになっていますのでメインスレッドとは別のスレッドで実行されます。メッセージ(変数)の受け渡しも可能です。

デモ

リアルタイムに波形データを編集して音量を変更します。



再生ボタンを押した後に次を操作してださい。
5

ソースコード

ローカル環境でこのコードを実行するとエラーになります。ソースコードはサーバにアップロードして下さい。また、コードを変更した場合は「Ctrl+F5」で完全にキャッシュを削除してからテストしてください。

[demo.html]

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body> 
<audio id="myAudio" src="voice.mp3"></audio>
<button onclick="play();">再 生</button> <button onclick="stop();">停 止</button><br>
<br>再生ボタンを押した後に次を操作してださい。<br>
<input type="range" id="range_myParam" min="1" max="10" value="5"  onchange="onChange();"> <span id="msg_myParam">5</span><br>
<br>
<button onclick="add();">AudioWorkletProcessorで3 +5をしてみる</button>
<script>
var audioCtx;
var audioSourceNode;
var workletNode; 
var tracks;

var audioEle;
var wm = new WeakMap();

var firstFlag = true;

window.onload = function(){
  audioEle = document.getElementById('myAudio');
}    

function play(){

  if(firstFlag){
    // AudioContextの生成
    audioCtx =  new AudioContext(); 
    firstFlag = false;    
    
    if ('audioWorklet' in audioCtx){
    }else{
      alert('お使いのブラウザはAudioWorkletに対応していません。'); 
    }       
  }    
  
  modann();
}      

function stop(){
  audioEle.pause();
  
  if(workletNode){
    workletNode.port.postMessage({"state":"exit"});    
  }
}

async function modann(stream){

  // MediaElementAudioSourceNodeの生成       
  if(audioSourceNode){
    audioSourceNode.disconnect();
  }    
  if (wm.has(audioEle)) { 
    audioSourceNode = wm.get(audioEle); 
  } else { 
    console.log(1);
    audioSourceNode = audioCtx.createMediaElementSource(audioEle); 
    wm.set(audioEle, audioSourceNode); 
  }     

  // AudioWorkletの読み込み
  await audioCtx.audioWorklet.addModule("modan.js");

  // AudioWorkletNodeを取得する
  if(workletNode){
    workletNode.disconnect();
  }      
  workletNode = new AudioWorkletNode(audioCtx,"MyWorklet");
  
  // メッセージの受け取り
  workletNode.port.onmessage = function(e){
    
     alert(e.data.result);
     console.log(e.data);

  };
  workletNode.port.start();
  workletNode.port.postMessage({"state":"init","sampleRate":audioCtx.sampleRate,
                                "myParam":document.getElementById('range_myParam').value});

  audioSourceNode.connect(workletNode);
  workletNode.connect(audioCtx.destination);
 
  audioEle.play();
}

function onChange(){
  document.getElementById('msg_myParam').innerHTML = document.getElementById('range_myParam').value;
  if(workletNode){
    workletNode.port.postMessage({"myParam":document.getElementById('range_myParam').value});  
  }
}
function add(){
  if(workletNode){
    workletNode.port.postMessage({"state":"add","a":3,"b":5});  
  }
}
</script>
</body>
</html> 

[modan.js]

class MyWorklet extends AudioWorkletProcessor { 
  
  // コントラスタ
  constructor (options) {
    super(options);
    
    this.port.onmessage = event => {
      if (event.data.state){
        this.state = event.data.state;
      }
      if (event.data.sampleRate){
        this.sampleRate = event.data.sampleRate;
      }         
      if (event.data.a){
        this.a = event.data.a;
      }      
      if (event.data.state){
        this.b = event.data.b;
      }       
      if (event.data.myParam){
        this.myParam = event.data.myParam;
      }                   
    }
    this.port.start();
  }
  
  // ココに波形の生データが出力される
  process (inputs, outputs, parameters) {
    
    console.log("実行中");  
    
    // 波形データの編集
    let input = inputs[0];
    let output = outputs[0];
    for (let channel = 0; channel < output.length; ++channel) {
      for (let i = 0; i < output[channel].length; ++i) {
        output[channel][i] = input[channel][i] * this.myParam/10;
      }
    }
    
    // メソッドのテスト
    if (this.state == "add"){
      this.port.postMessage({'result': this.add(this.a,this.b)});
      this.state = "";
    }    
    
    // 終了判定 
    if (this.state == "exit"){
      console.log("終了"); 
      
      // 終了
      return false;   
    }else{
      // 次の生データを受け取る
      return true;             
    }      
  }    
  
  // オリジナルのメソッド
  add(a,b) {
    return a + b;   
  }        
}

// プロセッサーの登録
registerProcessor("MyWorklet", MyWorklet);

注意事項

AudioWorkletは2019年2月現在ではChromeのみ対応しています。

パソコン内の音声を録音する音声録音くんを製作中にAudioWorklet/AudioWorkletProcessorをテストしていたのですが、稀にわかずかな音が途切れるバグがあるようです。現状ではcreateScriptProcessor(scriptProcessorNode)の方が安定しています。

プログラム上ではcreateScriptProcessor()を優先して、廃止された場合は自動的にAudioWorklet/AudioWorkletProcessorを使用するようにした方が良いです。

参考サイト

Web Audio API (日本語訳)





関連記事



公開日:2019年02月15日
記事NO:02730