|
- let playing = [];
- let looping = {};
- let loopGains = {};
-
- let waiting = {};
-
- let audioContext;
- let gainControl;
-
- let audioBaseUrl;
- let storyName;
-
- let audioDict = {};
-
- function setVolume(vol) {
- gainControl.gain.value = vol;
- }
-
- // play some sound
-
- function playSfx(name) {
- if (audioDict[name] == undefined) {
-
- if (waiting[name]) {
- waiting[name].push({
- type: "sfx",
- name: name
- });
- console.warn(name + " isn't ready yet");
- return;
- }
-
- console.error(name + " is not loaded yet, dingus");
- return;
- }
-
- let src = audioContext.createBufferSource();
- src.buffer = audioDict[name];
- src.connect(gainControl);
-
- playing.push(src);
-
- src.name = name;
- src.onended = (event) => src.done = true;
-
- src.start(0);
- }
-
- function playLoop(name, volume=1) {
- if (audioDict[name] === undefined) {
-
- if (waiting[name]) {
- waiting[name].push({
- type: "loop",
- name: name
- });
- console.warn(name + " isn't ready yet");
- return;
- }
- console.error(name + " is not loaded yet, dingus");
- return;
- }
-
- loopGains[name].gain.value = volume;
-
- // if already playing, just keep going
- if (looping[name] && !looping[name].done) {
- return;
- }
-
- let src = audioContext.createBufferSource();
- src.buffer = audioDict[name];
- src.connect(loopGains[name]);
- loopGains[name].connect(gainControl);
-
- looping[name] = src;
-
- src.name = name;
- src.onended = (event) => src.done = true;
-
- src.loop = true;
-
- src.start(0);
- }
-
- function stopSfx(name) {
- playing.map(item => {
- if (item.name == name)
- item.stop();
- } );
- cleanPlaying();
- }
-
- function stopAllSfx() {
- playing.map(item => item.stop());
- cleanPlaying();
- }
-
- function stopLoop(name) {
- if (looping[name]) {
- looping[name].stop();
- delete looping[name];
- }
- }
-
- function stopAllLoops() {
- Object.entries(looping).forEach(([key, val]) => {
- val.stop();
- delete looping[key];
- });
- }
-
- function stopAllSound() {
- stopAllSfx();
- stopAllLoops();
- }
-
- function cleanPlaying() {
- playing = playing.filter(item => !item.done);
- }
-
- // asynchronously load an audio file
-
- function loadAudio(name, flush=false) {
-
- // are we already trying to get the audio?
-
- if (waiting[name]) {
- return;
- }
-
- // do we already have the audio?
-
- if (audioDict[name] && !flush) {
- return;
- }
-
- loopGains[name] = audioContext.createGain();
-
- waiting[name] = [];
-
- // is the audio already stored locally?
-
- if (!flush) {
- checkCache(
- "audio",
- name,
- (data) => parseAudioData(name, data),
- () => loadRemoteAudio(name)
- );
- } else {
- loadRemoteAudio(name);
- }
- }
-
- function cacheAndParse(name, data) {
- storeCache("audio", name, data.slice(0));
- parseAudioData(name, data);
- }
-
- function parseAudioData(name, data) {
-
- audioContext.decodeAudioData(data, function(buffer) {
- audioDict[name] = buffer;
-
- waiting[name].forEach(queued => {
- if (queued.type == "sfx") {
- playSfx(name);
- }
-
- if (queued.type == "loop") {
- playLoop(name);
- }
-
- });
-
- delete waiting[name];
- }, function(e){
- console.error("Error with decoding audio data" + e.err);
- delete waiting[name];
- });
-
- }
-
- function loadRemoteAudio(name) {
- let xhr = new XMLHttpRequest();
-
- xhr.open("GET", audioBaseUrl + name, true);
- xhr.responseType = "arraybuffer";
- xhr.onload = (res) => {
- if (xhr.status == 200)
- cacheAndParse(name, xhr.response);
- else {
- console.error("Couldn't load " + name);
- delete waiting[name];
- }
- }
- xhr.onerror = (xhr) => {
- console.error("Couldn't load " + name);
- }
-
- xhr.send();
- }
-
- // check if the content is cached
- function checkCache(type, name, hit, miss) {
- const req = window.indexedDB.open("cache", 3);
- req.onsuccess = () => {
- const db = req.result;
- const tx = db.transaction([type], "readonly");
-
- const audio = tx.objectStore(type);
-
- const read = audio.get([storyName, name]);
-
- read.onsuccess = (event) => {
- const res = event.target.result;
-
- if (res) {
- console.log("cache hit on " + name);
- hit(res.content);
- } else {
- console.log("cache miss on " + name);
- miss();
- }
- }
-
- tx.oncomplete = () => {
- db.close();
- }
- }
- }
-
- function initAudio(story) {
- if (!audioContext)
- audioContext = new (window.AudioContext || window.webkitAudioContext)();
-
- if (!gainControl) {
- gainControl = audioContext.createGain();
- gainControl.gain.value = 1;
- gainControl.connect(audioContext.destination);
- }
-
- createCache();
-
- audioBaseUrl = "./media/" + story.id + "/audio/";
- storyName = story.id;
-
- story.sounds.forEach(sound => {
- loadAudio(sound);
- });
- }
-
- // caching stuff here
-
- function storeCache(type, name, blob) {
- const req = window.indexedDB.open("cache", 3);
- req.onsuccess = () => {
- const db = req.result;
- const tx = db.transaction([type], "readwrite");
-
- const audio = tx.objectStore(type);
-
- const update = audio.put({
- story: storyName,
- name: name,
- content: blob
- });
-
- tx.oncomplete = () => {
- db.close();
- }
- }
- }
-
- // if the indexedDB table doesn't exist at all, make it
- function createCache() {
- let idb = window.indexedDB;
-
- let req = idb.open("cache", 3);
-
- req.onupgradeneeded = event => {
- const db = event.target.result;
-
- if (event.oldVersion > 0 && event.oldVersion < 3) {
- db.deleteObjectStore("audio");
- }
-
- const audio = db.createObjectStore("audio", { keyPath: ["story", "name"] });
- }
-
- req.onerror = event => {
- alert("Couldn't open the database?");
- }
- }
|