音の鳴るブログ

鳴らないこともある

FunctionからWebWorkerを作るやつ作った

今まで Worker を作るときは

  • worker用のファイルを作る
    • 普通のやりかた
  • 1つのファイルで main と worker 用のコードを切り分ける
    • ファイルたくさん作りたくないとき
  • 文字列で worker の中身を記述して Blob 経由で URL を生成する
    • Workerの規模が小さいとき

みたいにしていたけど、3番目のやり方をもうちょっと便利にした。

使い方

バックグラウンドで動くタイマー (タブを切り替えても精度が落ちないやつ)

var timer = new WorkerBuilder(function(onmessage, postMessage) {
  // ここが worker の中身
  // 引数は無視されるのでなくても良い (lint対策)

  var t = 0;

  onmessage = function(e) {
    if (t) {
      clearInterval(t);
    }

    t = 0;

    if (typeof e.data === "number" && e.data > 0) {
      t = setInterval(function() {
        postMessage(null);
      }, e.data);
    }
  };

}).build(); // build() で Worker を生成する

// wb.createURL() で URL だけ取得することもできる


// あとは普通の Worker として使える
timer.onmessage = function() {
  console.log("!");
};

timer.postMessage(1000);

仕組み

単純に関数を文字列化して中身を取り出す -> Blob -> URL.createURL しているだけです。

用途

Web Audio APIに将来的に導入される AudioWorkerNode はノード毎にファイルを用意させるみたいな仕様っぽいので、そんな面倒なこと絶対にしたくない気持ちで作った。動かせる環境がないので確認できないけど以下のように使えると思う。

var bitcrusher_worker = new WorkerBuilder(function(onaudioprocess) {
  var phaser = 0;
  var lastDataValue = 0;

  onaudioprocess = function (e) {
    for (var channel = 0; channel < e.inputBuffers.length; channel++) {
      var inputBuffer = e.inputBuffers[channel];
      var outputBuffer = e.outputBuffers[channel];
      var bufferLength = inputBuffer.length;
      var bitsArray = e.parameters.bits;
      var frequencyReductionArray = e.parameters.frequencyReduction;

      for (var i = 0; i < bufferLength; i++) {
        var bits = bitsArray ? bitsArray[i] : 8;
        var frequencyReduction = frequencyReductionArray ? frequencyReductionArray[i] : 0.5;

        var step = Math.pow(1 / 2, bits);
        phaser += frequencyReduction;
        if (phaser >= 1.0) {
          phaser -= 1.0;
          lastDataValue = step * Math.floor(inputBuffer[i] / step + 0.5);
        }
        outputBuffer[i] = lastDataValue;
      }
    }
  };
});

var bitcrusherNode = audioContext.createAudioWorker(bitcrusher_worker.createURL(), 1, 1);

// Custom parameter - number of bits to crush down to - default 8
bitcrusherNode.addParameter( "bits", 8 );

// Custom parameter - frequency reduction, 0-1, default 0.5
bitcrusherNode.addParameter( "frequencyReduction", 0.5 );