<!DOCTYPE html>
<!--

  Brickworks

  Copyright (C) 2022, 2023 Orastron Srl unipersonale

  Brickworks is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, version 3 of the License.

  Brickworks is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Brickworks.  If not, see <http://www.gnu.org/licenses/>.

  File author: Stefano D'Angelo

-->
<html>
  <head>
    <title>Brickworks web effect example</title>
    <script src="config.js"></script>
    <script>
var audioCtx;

var Effect = {
	node: null,
	moduleAdded: false,
	wasmBytes: null,

	init: async function () {
		if (!this.moduleAdded) {
			await audioCtx.audioWorklet.addModule("processor.js");
			this.moduleAdded = true;
		}

		if (!this.wasmBytes)
			this.wasmBytes = await fetch("module.wasm")
				.then((response) => response.arrayBuffer())
				.then((bytes) => bytes);
		if (!this.wasmBytes.byteLength) {
			this.wasmBytes = null;
			throw "could not fetch WebAssembly module";
		}

		if (!this.node)
			this.node = new AudioWorkletNode(audioCtx, "BWExample",
				{
					numberOfInputs: buses.filter(function (b) { return !b.output; }).length,
					numberOfOutputs: buses.filter(function (b) { return b.output; }).length,
					outputChannelCount: buses.filter(function (b) { return b.output}).map(function (b) { return b.stereo ? 2 : 1 }),
					processorOptions: {
						wasmBytes: this.wasmBytes
					}
				});
		this.node.connect(audioCtx.destination);
	}
};

var Player = {
	sourceBuffer: null,
	playing: false,
	started: false,

	load: function (buffer, successCb, errorCb) {
		let t = this;
		audioCtx.decodeAudioData(buffer,
			function (data) {
				if (t.started)
					t.sourceBuffer.stop();
				if (t.playing)
					t.sourceBuffer.disconnect();
				t.sourceBuffer = audioCtx.createBufferSource();
				t.sourceBuffer.buffer = data;
				t.sourceBuffer.loop = true;
				if (t.started)
					t.sourceBuffer.start();
				if (t.playing) {
					t.started = true;
					t.sourceBuffer.connect(Effect.node);
				}
				successCb();
			},
			function () { errorCb(); });
	},
	
	togglePlayPause: function () {
		if (this.playing) {
			this.sourceBuffer.disconnect();
			this.playing = false;
		} else {
			if (!this.started) {
				this.sourceBuffer.start();
				this.started = true;
			}
			this.sourceBuffer.connect(Effect.node);
			this.playing = true;
		}
	}
};

var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited

window.addEventListener("load", function (e) {
	var start = document.getElementById("start");
	var starting = document.getElementById("starting");
	var main = document.getElementById("main");
	var file = document.getElementById("file");
	var playPause = document.getElementById("playPause");
	var controls = document.getElementById("controls");

	// reset on refresh
	file.value = "";
	playPause.disabled = true; 

	for (var i = 0; i < parameters.length; i++) {
		var div = document.createElement("div");
		controls.appendChild(div);

		var label = document.createElement("label");
		label.setAttribute("for", "p" + i);
		label.innerText = parameters[i].name;
		div.appendChild(label);

		var range = document.createElement("input");
		range.setAttribute("type", "range");
		range.setAttribute("id", "p" + i);
		range.setAttribute("name", "p" + i);
		range.setAttribute("min", "0");
		range.setAttribute("max", "1");
		if (!parameters[i].step)
			range.setAttribute("step", "any");
		else
			range.setAttribute("step", 1 / parameters[i].step);
		range.value = parameters[i].defaultValue;
		if (parameters[i].output)
			range.setAttribute("readonly", "true");
		else {
			let index = i;
			range.addEventListener("input", function (e) {
				var p = Effect.node.parameters.get(parameters[index].name);
				p.setValueAtTime(e.target.value, 0);
			});
		}
		div.appendChild(range);
	}

	start.addEventListener("click", async function () {
		initState = 1;
		start.disabled = true;
		starting.hidden = false;

		try {
			if (!audioCtx)
				audioCtx = new AudioContext();
			await Effect.init();

			Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
			
			initState = 2;
			start.hidden = true;
			starting.hidden = true;
			main.hidden = false;
		} catch (err) {
			alert("Colud not initialize: " + err);

			initState = 0;
			start.disabled = false;
			starting.hidden = true;
		}
	});

	file.addEventListener("change", function () {
		var fileReader = new FileReader();
		fileReader.readAsArrayBuffer(this.files[0]);
		fileReader.onload = function (e) {
			Player.load(e.target.result,
				function () { playPause.disabled = false; },
				function () { alert("Could not decode the chosen file"); });
		};
		fileReader.onerror = function (e) { alert("Could not read file"); };
	});

	playPause.addEventListener("click", function () {
		Player.togglePlayPause();
		playPause.innerText = Player.playing ? "Pause" : "Play";
	});
});
    </script>
  </head>
  <body>
    <h1>Brickworks web effect example</h1>
    <input id="start" type="button" value="Start">
    <p id="starting" hidden>Starting...</p>
    <div id="main" hidden>
      <h2>Player</h2>
      <label for="file">Choose a file:</label>
      <input type="file" id="file" name="file" accept="audio/*">
      <br>
      <button id="playPause" disabled>Play</button>
      <h2>Effect</h2>
      <div id="controls">
      </div>
    </div>
  </body>
</html>