287 lines
7.9 KiB
JavaScript
287 lines
7.9 KiB
JavaScript
#include utility.js
|
|
|
|
var AmiSound = function() {
|
|
var playing = [];
|
|
var isMuted = false,
|
|
volume = .8;
|
|
|
|
var packName = undefined,
|
|
packs = new Map,
|
|
library = new Map;
|
|
|
|
var supported = [], fallback = [];
|
|
var formats = {
|
|
opus: 'audio/ogg;codecs=opus',
|
|
ogg: 'audio/ogg;codecs=vorbis',
|
|
mp3: 'audio/mpeg;codecs=mp3',
|
|
caf: 'audio/x-caf;codecs=opus',
|
|
wav: 'audio/wav',
|
|
};
|
|
|
|
var extractKeys = function(map) {
|
|
if('Array' in window && 'from' in Array)
|
|
return Array.from(map.keys());
|
|
|
|
var names = [];
|
|
map.forEach(function(value, key) {
|
|
names.push(key);
|
|
});
|
|
return names;
|
|
};
|
|
|
|
var createPack = function(name, title, events) {
|
|
name = (name || '').toString();
|
|
title = (title || '').toString();
|
|
events = events || new Map;
|
|
|
|
return {
|
|
getName: function() {
|
|
return name;
|
|
},
|
|
getTitle: function() {
|
|
return title;
|
|
},
|
|
getEventNames: function() {
|
|
return extractKeys(events);
|
|
},
|
|
hasEventSound: function(eventName) {
|
|
return events.has(eventName);
|
|
},
|
|
getEventSound: function(eventName) {
|
|
if(!events.has(eventName))
|
|
throw 'event not registered';
|
|
return events.get(eventName);
|
|
},
|
|
};
|
|
};
|
|
|
|
var createSound = function(name, title, sources) {
|
|
name = (name || '').toString();
|
|
title = (title || ('Nameless Sound (' + name + ')')).toString();
|
|
sources = sources || {};
|
|
|
|
return {
|
|
getName: function() {
|
|
return name;
|
|
},
|
|
getTitle: function() {
|
|
return title;
|
|
},
|
|
getSources: function() {
|
|
return sources;
|
|
},
|
|
};
|
|
};
|
|
|
|
(function() {
|
|
var elem = $e('audio');
|
|
for(var name in formats) {
|
|
var format = formats[name],
|
|
support = elem.canPlayType(format);
|
|
if(support === 'probably')
|
|
supported.push(name);
|
|
else if(support === 'maybe')
|
|
fallback.push(name);
|
|
}
|
|
})();
|
|
|
|
var findSupportedUrl = function(urls) {
|
|
if(typeof urls === 'string')
|
|
return urls;
|
|
|
|
var url = null;
|
|
for(var i in supported) {
|
|
var type = supported[i];
|
|
if(type in urls) {
|
|
url = urls[type];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(url === null)
|
|
for(var i in fallback) {
|
|
var type = fallback[i];
|
|
if(type in urls) {
|
|
url = urls[type];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return url;
|
|
};
|
|
|
|
var playSound = function(urls, localVolume, rate) {
|
|
if(isMuted)
|
|
return;
|
|
|
|
var url = findSupportedUrl(urls);
|
|
if(url === undefined || url === null)
|
|
return;
|
|
|
|
// there's a caveat here where if the user changes the volume while a sound is playing
|
|
// this adjustment is essentially undone and reset back to 1.0
|
|
// it's generally only used to increase volume, but it could cause a Jump Scare for the few instances
|
|
// where it is used to lower the volume
|
|
if(localVolume === undefined)
|
|
localVolume = 1.0;
|
|
|
|
if(rate === undefined) rate = 1.0;
|
|
else rate = Math.min(4.0, Math.max(0.25, rate));
|
|
|
|
var audio = $e({
|
|
tag: 'audio',
|
|
attrs: { src: url },
|
|
});
|
|
audio.volume = Math.max(0.0, Math.min(1.0, volume * localVolume));
|
|
audio.muted = isMuted;
|
|
audio.preservesPitch = false;
|
|
audio.playbackRate = rate;
|
|
playing.push(audio);
|
|
|
|
audio.addEventListener('ended', function() {
|
|
$ari(playing, audio);
|
|
});
|
|
|
|
audio.addEventListener('loadeddata', function() {
|
|
audio.play();
|
|
});
|
|
};
|
|
|
|
var playLibrarySound = function(soundName, localVolume, rate) {
|
|
var soundInfo = library.get(soundName);
|
|
if(soundInfo === undefined)
|
|
return;
|
|
|
|
playSound(soundInfo.getSources(), localVolume, rate);
|
|
};
|
|
|
|
var pub = {};
|
|
|
|
pub.findSupportedUrl = findSupportedUrl;
|
|
pub.playSound = playSound;
|
|
pub.playLibrarySound = playLibrarySound;
|
|
|
|
pub.playEventSound = function(eventName, localVolume, rate) {
|
|
if(packName === undefined || packName === null || isMuted)
|
|
return;
|
|
|
|
var pack = packs.get(packName);
|
|
if(pack === undefined)
|
|
return;
|
|
|
|
if(typeof eventName === 'string') {
|
|
if(!pack.hasEventSound(eventName))
|
|
return;
|
|
} else {
|
|
var names = eventName;
|
|
eventName = null;
|
|
|
|
for(var i in names) {
|
|
var name = names[i];
|
|
if(pack.hasEventSound(name)) {
|
|
eventName = name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(eventName === null)
|
|
return;
|
|
}
|
|
|
|
playLibrarySound(pack.getEventSound(eventName), localVolume, rate);
|
|
};
|
|
|
|
pub.getPackName = function() { return packName; };
|
|
pub.setPackName = function(name) { packName = name; };
|
|
|
|
pub.forEachPack = function(body) {
|
|
packs.forEach(function(value) {
|
|
body(value);
|
|
});
|
|
};
|
|
pub.hasPacks = function() {
|
|
return packs.size > 0;
|
|
};
|
|
pub.getPacks = function() {
|
|
var arr = [];
|
|
packs.forEach(function(value) {
|
|
arr.push(value);
|
|
});
|
|
return arr;
|
|
};
|
|
pub.clearPacks = function() {
|
|
packs.clear();
|
|
};
|
|
|
|
var loadSoundFormats = ['opus', 'caf', 'ogg', 'mp3', 'wav'];
|
|
pub.registerLibrarySound = function(soundInfo) {
|
|
if(typeof soundInfo !== 'object')
|
|
throw 'soundInfo must be an object';
|
|
if(typeof soundInfo.name !== 'string')
|
|
throw 'soundInfo must contain a name field';
|
|
if(typeof soundInfo.sources !== 'object')
|
|
throw 'soundInfo must contain a sources field';
|
|
|
|
var sources = {},
|
|
sCount = 0;
|
|
for(var i in loadSoundFormats) {
|
|
var fmt = loadSoundFormats[i];
|
|
if(typeof soundInfo.sources[fmt] !== 'string')
|
|
continue;
|
|
sources[fmt] = soundInfo.sources[fmt];
|
|
++sCount;
|
|
}
|
|
|
|
if(sCount < 1)
|
|
throw 'soundInfo does not contain any valid sources';
|
|
|
|
library.set(soundInfo.name, createSound(soundInfo.name, soundInfo.title, sources));
|
|
};
|
|
|
|
pub.registerSoundPack = function(packInfo) {
|
|
if(typeof packInfo !== 'object')
|
|
throw 'packInfo must be an object';
|
|
if(typeof packInfo.name !== 'string')
|
|
throw 'packInfo must contain a name field';
|
|
if(typeof packInfo.events !== 'object')
|
|
throw 'packInfo must contain a events field';
|
|
|
|
var events = new Map;
|
|
for(var eventName in packInfo.events) {
|
|
if(typeof packInfo.events[eventName] !== 'string')
|
|
continue;
|
|
events.set(eventName, packInfo.events[eventName]);
|
|
}
|
|
|
|
if(events.size < 1)
|
|
throw 'packInfo does not contain any valid events';
|
|
|
|
packs.set(packInfo.name, createPack(packInfo.name, packInfo.title, events));
|
|
};
|
|
|
|
pub.hasSoundLibrary = function() {
|
|
return library.size > 0;
|
|
};
|
|
pub.clearSoundLibrary = function() {
|
|
library.clear();
|
|
};
|
|
|
|
pub.getVolume = function() { return volume; };
|
|
pub.setVolume = function(value) {
|
|
volume = Math.max(0.0, Math.min(1.0, value));
|
|
for(var i in playing)
|
|
playing[i].volume = volume;
|
|
};
|
|
|
|
pub.isMuted = function() {
|
|
return isMuted;
|
|
};
|
|
pub.setMuted = function(mute) {
|
|
isMuted = !!mute;
|
|
for(var i in playing)
|
|
playing[i].muted = isMuted;
|
|
};
|
|
|
|
return pub;
|
|
};
|