Web Audio APIで音源ファイルを使わずにブラウザから音を出す

プログラミング

Web Audio APIを呼び出すJavaScript … 3(webaudio-1.0.0.js)

initメソッドは4.のJavaScriptを呼び出し、AudioWorkletのプログラムを起動します。

3.のJavaScriptでは、Web Audio APIの実体ともいえる、AudioContextのオブジェクトを生成し、AudioContextのaddModuleメソッドで、4.のJavaScriptに記述しているAudioWorkletProcessorクラスのオブジェクトを呼び出します。

3.と4.のJavaScriptのファイルが別々のファイルになっているのは、「AudioWorkletProcessorクラスのオブジェクトは、addModuleメソッドでファイル名(URL)を指定して呼び出さなければならない」という制約によるものです。

今回は、4.のJavaScriptを呼び出して生成したAudioWorkletNodeオブジェクトに、setFrequencyというメソッド(function)を追加しています。

AudioWorkletNodeオブジェクトのport.postMessageメソッドを使うと、4.のJavaScriptに記述されているプログラムにメッセージを送ることができます。今回のプログラムでは、setFrequencyメソッドの中で、発声したい周波数の値を送るために使用しています。

逆に、AudioWorkletNodeオブジェクトのport.onmessage変数の値(ハンドラ)として例のようなコードを記述すると、4.のJavaScriptに記述されているプログラムからのメッセージを受け取ることができます。今回のプログラムでは、4.のJavaScriptの中のprocessメソッドの動作が始まったことを「enabled」メッセージで教えてもらい、2.のJavaScriptでinitメソッドの引数として渡された関数を呼び出す(コールバックする)ために使用しています。

このメッセージのやりとりは双方向で行えます。

"use strict";

var WEBAUDIO = function () {
	var wapAudioWorklet;
	this.enable = false;

	var rate = 44100;

	// ChromeではAudioContext、Safari 14.1より前のバージョンではwebkitAudioContext
	var AudioContext = window.AudioContext || window.webkitAudioContext;
	var ctx, gain;

	var onenabledcallback;

	// AudioWorklet有効化時のコールバック関数
	var onenabled = function () {
		this.enable = true;
		if (onenabledcallback) onenabledcallback();
	};

	// 初期化
	this.init = function (callback) {
		this.enable = false;
		onenabledcallback = callback;
		ctx = new AudioContext();
		gain = ctx.createGain();
		gain.gain.value = 1;
		gain.connect(ctx.destination);
		rate = ctx.sampleRate;

		try {
			ctx.audioWorklet.addModule('webaudioawp-1.0.1.js').then(() => {
				wapAudioWorklet = new AudioWorkletNode(ctx, "WEBAUDIO", {
					processorOptions: {
						rate: rate
					}
				});

				wapAudioWorklet.setFrequency = (function (value) {
					this.port.postMessage({
						message: 'frequency',
						freq: value
					})
				}).bind(wapAudioWorklet);

				wapAudioWorklet.port.onmessage = function (event) {
					const message = event.data;
					switch (message.message) {
						case 'enabled':
							wapAudioWorklet.connect(gain).connect(ctx.destination);
							onenabled.bind(this)();
							break;
					}
				}.bind(this);
			});
		} catch (e) {
			// AudioWorkletに対応していない
			console.info('This browser not support AudioWorklet.');
		}
	}

	this.setFrequency = function (freq) {
		if (wapAudioWorklet) wapAudioWorklet.setFrequency(freq);
	};

	this.getAudioContext = function () {
		return ctx;
	}
};