"use strict";
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audio_context;
let sounds = new Map();
let playbacks = [];
let sound_key_next = 1;
let playback_key_next = 1;
function audio_init() {
if (audio_context == null) {
audio_context = new AudioContext();
let audio_listener = audio_context.listener;
{
let AudioContext = window.AudioContext || window.webkitAudioContext;
let ctx = new AudioContext();
var fixAudioContext = function (e) {
console.log("fix");
// On newer Safari AudioContext starts in a suspended state per
// spec but is only resumable by a call running in an event
// handler triggered by the user. Do it here. Reference:
// https://stackoverflow.com/questions/56768576/safari-audiocontext-suspended-even-with-onclick-creation
audio_context.resume();
// On older Safari, audio context should be explicitly unpaused
// in a mouse/touch input event even if it was created after
// first input event on the page thanks to:
// https://gist.github.com/kus/3f01d60569eeadefe3a1
// Create empty buffer
var buffer = ctx.createBuffer(1, 1, 22050);
var source = ctx.createBufferSource();
source.buffer = buffer;
// Connect to output (speakers)
source.connect(ctx.destination);
// Play sound
if (source.start) {
source.start(0);
} else if (source.play) {
source.play(0);
} else if (source.noteOn) {
source.noteOn(0);
}
// Remove event handlers
document.removeEventListener('touchstart', fixAudioContext);
document.removeEventListener('touchend', fixAudioContext);
document.removeEventListener('mousedown', fixAudioContext);
document.removeEventListener('keydown', fixAudioContext);
};
// iOS 6-8
document.addEventListener('touchstart', fixAudioContext);
// iOS 9
document.addEventListener('touchend', fixAudioContext);
// Mac
document.addEventListener('mousedown', fixAudioContext);
document.addEventListener('keydown', fixAudioContext);
}
}
}
function audio_add_buffer(content, content_len) {
let content_array = wasm_memory.buffer.slice(content, content + content_len);
let sound_key = sound_key_next;
sound_key_next += 1;
audio_context.decodeAudioData(content_array, function(buffer) {
sounds.set(sound_key, buffer);
}, function(e) {
// fail
console.error("Failed to decode audio buffer", e);
});
return sound_key;
}
function audio_source_is_loaded(sound_key) {
return sounds.has(sound_key) && sounds.get(sound_key) != undefined;
}
function recycle_playback() {
let playback = playbacks.find(playback => playback.sound_key === 0);
if (playback != null) {
playback.source = audio_context.createBufferSource();
} else {
playback = {
sound_key: 0,
playback_key: 0,
source: audio_context.createBufferSource(),
gain_node: audio_context.createGain(),
ended: null,
};
playbacks.push(playback);
}
return playback;
}
function stop(playback) {
try {
playback.source.removeEventListener('ended', playback.ended);
playback.source.disconnect();
playback.gain_node.disconnect();
playback.sound_key = 0;
playback.playback_key = 0;
} catch (e) {
console.error("Error stopping sound", e);
}
}
function audio_play_buffer(sound_key, volume, repeat) {
let playback_key = playback_key_next++;
let pb = recycle_playback();
pb.sound_key = sound_key;
pb.playback_key = playback_key;
pb.source.connect(pb.gain_node);
pb.gain_node.connect(audio_context.destination);
pb.gain_node.gain.value = volume;
pb.source.loop = repeat;
pb.ended = function() {
stop(pb);
};
pb.source.addEventListener('ended', pb.ended);
try {
pb.source.buffer = sounds.get(sound_key);
pb.source.start(0);
} catch (e) {
console.error("Error starting sound", e);
}
return playback_key;
}
function audio_source_set_volume(sound_key, volume) {
playbacks.forEach(playback => {
if (playback.sound_key === sound_key) {
playback.gain_node.gain.value = volume;
}
});
}
function audio_source_stop(sound_key) {
playbacks.forEach(playback => {
playback.sound_key === sound_key && stop(playback);
});
}
function audio_source_delete(sound_key) {
audio_source_stop(sound_key);
sounds.delete(sound_key);
}
function audio_playback_stop(playback_key) {
let playback = playbacks.find(playback => playback.playback_key === playback_key);
playback != null && stop(playback);
}
function audio_playback_set_volume(playback_key, volume) {
let playback = playbacks.find(playback => playback.playback_key === playback_key);
if (playback != null) {
playback.gain_node.gain.value = volume;
}
}
function register_plugin(importObject) {
importObject.env.audio_init = audio_init;
importObject.env.audio_add_buffer = audio_add_buffer;
importObject.env.audio_play_buffer = audio_play_buffer;
importObject.env.audio_source_is_loaded = audio_source_is_loaded;
importObject.env.audio_source_set_volume = audio_source_set_volume;
importObject.env.audio_source_stop = audio_source_stop;
importObject.env.audio_source_delete = audio_source_delete;
importObject.env.audio_playback_stop = audio_playback_stop;
importObject.env.audio_playback_set_volume = audio_playback_set_volume;
}
miniquad_add_plugin({ register_plugin, version: 1, name: "macroquad_audio" });