UNPKG

avg-core-ts

Version:

Develop Adventure Game (or ADV, Visual Novel, Galgame, etc.) on your own!

359 lines (279 loc) 7.29 kB
/** * @file Audio Manager * @author Icemic Jia <bingfeng.web@gmail.com> * @copyright 2015-2017 Icemic Jia * @link https://www.avgjs.org * @license Apache License 2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Howl, Howler } from 'howler'; let Pool = {}; let ChannelVolume = {}; export function channelExist(channel) { return !!Pool[channel]; } export function channelVolume(channel, vol) { if (typeof vol === 'number') { const prevVol = ChannelVolume[channel]; const sound = Pool[channel]; ChannelVolume[channel] = vol; if (sound) { sound.volume(sound.volume() / prevVol * vol); } } return ChannelVolume[channel]; } export function globalVolume(vol) { return Howler.volume(vol); } export function globalMute(muted) { return Howler.muted(muted); } export function create(channel, src, options = {}) { const _options = { volume: 1, loop: false, preload: true, autoplay: true, mute: false, rate: 1, autounload: true, exclusive: true, ...options }; if (_options.exclusive && channelExist(channel)) { destroy(channel); } const channelVolume = ChannelVolume[channel] || 1.0; const sound = new Howl({ src, ..._options, volume: _options.volume * channelVolume }); if (_options.autounload) { const autoUnload = () => { if (!sound.loop()) { destroy(channel); } }; sound.on('end', autoUnload); sound.on('stop', autoUnload); } sound.autounload = _options.autounload; sound.exclusive = _options.exclusive; Pool[channel] = sound; return Pool[channel]; } export function destroy(channel) { if (channelExist(channel)) { Pool[channel].off(); Pool[channel].unload(); delete Pool[channel]; } } export function getByChannel(channel) { return Pool[channel]; } export function play(channel) { const sound = getByChannel(channel); if (sound) { sound.play(); return new Promise(resolve => { sound.once('play', resolve); }); } return Promise.resolve(); } export function pause(channel) { const sound = getByChannel(channel); if (sound) { sound.pause(); return new Promise(resolve => { sound.once('pause', resolve); }); } return Promise.resolve(); } export function stop(channel) { const sound = getByChannel(channel); if (sound) { sound.stop(); return new Promise(resolve => { sound.once('stop', resolve); }); } return Promise.resolve(); } export function mute(channel, muted) { const sound = getByChannel(channel); if (sound) { sound.mute(muted); return new Promise(resolve => { sound.once('mute', resolve); }); } return Promise.resolve(); } export function volume(channel, vol) { const sound = getByChannel(channel); const channelVolume = ChannelVolume[channel] || 1.0; if (sound && vol != null) { sound.volume(vol * channelVolume); return new Promise(resolve => { sound.once('volume', resolve); }); } else if (sound) { return sound.volume(); } return NaN; } export function fade(channel, from = 0, to = 1, duration) { const sound = getByChannel(channel); const channelVolume = ChannelVolume[channel] || 1.0; if (sound) { sound.fade(from * channelVolume, to * channelVolume, duration); return new Promise(resolve => { sound.once('fade', resolve); }); } return Promise.resolve(); } export function fadeIn(channel, to, duration) { return play(channel) .then(() => fade(channel, 0, to, duration)); } export function fadeOut(channel, duration) { const sound = getByChannel(channel); const from = (sound && sound.volume()) || 1.0; return fade(channel, from, 0, duration) .then(() => stop(channel)); } export function rate(channel, rate) { const sound = getByChannel(channel); if (sound && rate != null) { sound.rate(rate); return new Promise(resolve => { sound.once('rate', resolve); }); } else if (sound) { return sound.rate(); } return Promise.resolve(); } export function seek(channel, position) { const sound = getByChannel(channel); if (sound && position != null) { sound.seek(position); return new Promise(resolve => { sound.once('seek', resolve); }); } else if (sound) { return sound.seek(); } return Promise.resolve(); } export function loop(channel, loop) { const sound = getByChannel(channel); if (sound && loop != null) { sound.loop(loop); return new Promise(resolve => { sound.once('loop', resolve); }); } else if (sound) { return sound.loop(); } return Promise.resolve(); } export function stopAll() { const channels = Object.keys(Pool); const promises = []; for (const channel of channels) { const sound = getByChannel(channel); if (sound) { sound.stop(); promises.push(new Promise(resolve => { sound.once('stop', resolve); })); } } return Promise.all(promises); } /* for save and load */ export function getSaveData() { const globalMuted = Howler._muted; const globalVolume = Howler._volume; const channels = Object.keys(Pool); const channelData = []; for (const channel of channels) { const sound = Pool[channel]; const channelVolume = ChannelVolume[channel] || 1.0; let pos = sound.seek(); if (typeof pos !== 'number') { pos = 0; } channelData.push({ channel, src: sound._src, volume: sound.volume() / channelVolume, loop: sound.loop(), autoplay: sound._autoplay, mute: sound._muted, rate: sound.rate(), paused: !sound.playing(), ended: !sound.playing(), position: pos, autounload: sound.autounload, exclusive: sound.exclusive }); } return { globalMuted, globalVolume, channelVolume: ChannelVolume, channelData }; } export function setSaveData(data) { Howler.unload(); Pool = {}; Howler.mute(data.globalMuted); Howler.volume(data.globalVolume); ChannelVolume = data.channelVolume; const channelData = data.channelData; for (const item of channelData) { create(item.channel, item.src, { volume: item.volume, loop: item.loop, autoplay: false, mute: item.mute, rate: item.rate }); // Pool[item.channel].seek(item.seek); if (item.ended) { // do nothing } else if (item.paused) { // do nothing // } else if (!item.autoplay && item.seek === 0) { // do noting } else { /** * not ended && * not paused && * autoplay is false but seek is not zero * or autoplay is true */ play(item.channel); } } }