import {Howl, Howler} from 'howler'; import {Resource} from '@avocado/resource'; export class Sound extends Resource { static load(uri) { if (!this.loadCache) { this.loadCache = {}; } if (!this.refCache) { this.refCache = {}; } if (!(uri in this.refCache)) { this.refCache[uri] = 0; } this.refCache[uri] += 1; if (this.loadCache[uri]) { return this.loadCache[uri]; } return this.loadCache[uri] = this.read(uri).then((json) => { const instance = new Sound(json); let lastTime = performance.now(); instance.tickHandle = setInterval(() => { const now = performance.now(); const elapsed = (now - lastTime) / 1000; lastTime = now; instance.tick(elapsed); }, 20); return new Promise((resolve) => { instance.sound.once('load', () => { resolve(instance); }); }); }); } constructor(json) { super(); this.interval = 0; this.lastPlayHandle = undefined; this.locked = false; this.originalVolume = 1; this.remaining = 0; this.sound = undefined; this.tickHandle = undefined; if ('undefined' !== typeof json) { this.fromJSON(json); } } destroy() { clearInterval(this.tickHandle); if (this.sound) { this.sound.unload(); } } fromJSON(json) { super.fromJSON(json); if (json.interval) { this.interval = json.interval; } if (json.volume) { this.originalVolume = json.volume; } this.sound = new Howl(json); return this; } play() { if (this.locked) { const volume = this.sound.volume(this.lastPlayHandle); this.sound.volume( Math.min(1, Math.min(volume, this.originalVolume * 20)), this.lastPlayHandle ); return; } this.lastPlayHandle = this.sound.play(); if (this.interval > 0) { this.locked = true; this.remaining = this.interval; } } release() { const ctor = this.constructor; const uri = this.uri; ctor.refCache[uri] -= 1; if (0 === ctor.refCache[uri]) { delete ctor.loadCache[uri]; this.destroy(); } } tick(elapsed) { if (this.locked && this.interval > 0) { this.remaining -= elapsed; if (this.remaining <= 0) { this.locked = false; } } } }