sono
Version:
A simple yet powerful JavaScript library for working with Web Audio
314 lines (271 loc) • 6.27 kB
JavaScript
import 'core-js/fn/object/assign';
import context from './context';
import Effects from './effects';
import file from './utils/file';
import Group from './group';
import Loader from './utils/loader';
import log from './utils/log';
import pageVisibility from './utils/pageVisibility';
import Sound from './sound';
import SoundGroup from './utils/sound-group';
import touchLock from './utils/touchLock';
import utils from './utils/utils';
const VERSION = '2.1.6';
const bus = new Group(context, context.destination);
/*
* Get Sound by id
*/
function get(id) {
return bus.find(id);
}
/*
* Create group
*/
function group(sounds) {
const soundGroup = new SoundGroup(context, bus.gain);
if (sounds) {
sounds.forEach((sound) => soundGroup.add(sound));
}
return soundGroup;
}
/*
* Loading
*/
function add(config) {
const src = file.getSupportedFile(config.src || config.url || config.data || config);
const sound = new Sound(Object.assign({}, config || {}, {
src,
context,
destination: bus.gain
}));
sound.isTouchLocked = isTouchLocked;
if (config) {
sound.id = config.id || config.name || '';
sound.loop = !!config.loop;
sound.volume = config.volume;
sound.effects = config.effects || [];
}
bus.add(sound);
return sound;
}
function queue(config, loaderGroup) {
const sound = add(config).prepare();
if (loaderGroup) {
loaderGroup.add(sound.loader);
}
return sound;
}
function load(config) {
const src = config.src || config.url || config.data || config;
let sound, loader;
if (file.containsURL(src)) {
sound = queue(config);
loader = sound.loader;
} else if (Array.isArray(src) && file.containsURL(src[0].src || src[0].url)) {
sound = [];
loader = new Loader.Group();
src.forEach((url) => sound.push(queue(url, loader)));
} else {
const errorMessage = 'sono.load: No audio file URLs found in config.';
if (config.onError) {
config.onError('[ERROR] ' + errorMessage);
} else {
throw new Error(errorMessage);
}
return null;
}
if (config.onProgress) {
loader.on('progress', (progress) => config.onProgress(progress));
}
if (config.onComplete) {
loader.once('complete', () => {
loader.off('progress');
config.onComplete(sound);
});
}
loader.once('error', err => {
loader.off('error');
if (config.onError) {
config.onError(err);
} else {
console.error(err);
}
});
loader.start();
return sound;
}
/*
* Create Sound
*
* Accepted values for param config:
* Object config e.g. { id:'foo', url:['foo.ogg', 'foo.mp3'] }
* Array (of files e.g. ['foo.ogg', 'foo.mp3'])
* ArrayBuffer
* HTMLMediaElement
* Filename string (e.g. 'foo.ogg')
* Oscillator type string (i.e. 'sine', 'square', 'sawtooth', 'triangle')
*/
function create(config) {
// try to load if config contains URLs
if (file.containsURL(config)) {
return load(config);
}
const sound = add(config);
sound.data = config.data || config;
return sound;
}
/*
* Destroy
*/
function destroy(soundOrId) {
bus.find(soundOrId, (sound) => sound.destroy());
return sono;
}
function destroyAll() {
bus.destroy();
return sono;
}
/*
* Controls
*/
function mute() {
bus.mute();
return sono;
}
function unMute() {
bus.unMute();
return sono;
}
function fade(volume, duration) {
bus.fade(volume, duration);
return sono;
}
function pauseAll() {
bus.pause();
return sono;
}
function resumeAll() {
bus.resume();
return sono;
}
function stopAll() {
bus.stop();
return sono;
}
function play(id, delay, offset) {
bus.find(id, (sound) => sound.play(delay, offset));
return sono;
}
function pause(id) {
bus.find(id, (sound) => sound.pause());
return sono;
}
function stop(id) {
bus.find(id, (sound) => sound.stop());
return sono;
}
/*
* Mobile touch lock
*/
let isTouchLocked = touchLock(context, () => {
isTouchLocked = false;
bus.sounds.forEach(sound => (sound.isTouchLocked = false));
});
/*
* Page visibility events
*/
const pageHiddenPaused = [];
// pause currently playing sounds and store refs
function onHidden() {
bus.sounds.forEach(sound => {
if (sound.playing) {
sound.pause();
pageHiddenPaused.push(sound);
}
});
}
// play sounds that got paused when page was hidden
function onShown() {
while (pageHiddenPaused.length) {
pageHiddenPaused.pop().play();
}
}
const pageVis = pageVisibility(onHidden, onShown);
function register(name, fn, attachTo = Effects.prototype) {
attachTo[name] = fn;
sono[name] = fn;
return fn;
}
const sono = {
canPlay: file.canPlay,
context,
create,
createGroup: group,
createSound: create,
destroyAll,
destroy,
effects: bus.effects,
extensions: file.extensions,
fade,
file,
gain: bus.gain,
getOfflineContext: utils.getOfflineContext,
get,
getSound: get,
group,
hasWebAudio: !context.isFake,
isSupported: file.extensions.length > 0,
load,
log: () => log(sono),
mute,
pause,
pauseAll,
play,
register,
resumeAll,
stop,
stopAll,
unMute,
utils,
VERSION,
get effects() {
return bus.effects;
},
set effects(value) {
bus.effects.removeAll().add(value);
},
get fx() {
return this.effects;
},
set fx(value) {
this.effects = value;
},
get isTouchLocked() {
return isTouchLocked;
},
get playInBackground() {
return !pageVis.enabled;
},
set playInBackground(value) {
pageVis.enabled = !value;
if (!value) {
onShown();
}
},
get sounds() {
return bus.sounds.slice(0);
},
get volume() {
return bus.volume;
},
set volume(value) {
bus.volume = value;
},
// expose for unit testing
__test: {
Effects,
Group,
Sound
}
};
export default sono;