Skip to content

Instantly share code, notes, and snippets.

@takumifukasawa
Last active November 4, 2020 14:05
Show Gist options
  • Save takumifukasawa/2e8354b8b6dd26fe4ed9897b4080a24c to your computer and use it in GitHub Desktop.
Save takumifukasawa/2e8354b8b6dd26fe4ed9897b4080a24c to your computer and use it in GitHub Desktop.
howler: manage audio for play, load, cache...
import { Howl, HowlOptions } from "howler";
import { map } from "lodash";
const caches = new Map<string | string[], Howl>();
/**
* howlオブジェクトを生成してキャッシュする。。キャッシュの中にあったらキャッシュを返す
*
* @param {HowlOptions} options
* @returns {(Howl | null)}
*/
function getAudio(options: HowlOptions): Howl | null {
if (!options.src) {
console.error("[audioActions] howler option has not src");
return null;
}
const { src } = options;
console.log("[audioActions] current cache", caches);
if (caches.has(src)) {
return caches.get(src) || null;
}
const obj = new Howl(options);
caches.set(src, obj);
console.log("[audioActions] new cache", caches);
return obj;
}
/**
* 音声をロード
*
* @export
* @param {HowlOptions} options
* @returns {Promise<Howl>}
*/
export async function loadAudio(options: HowlOptions): Promise<Howl> {
return new Promise((resolve, reject) => {
if (!options.src) {
reject(new Error("[audioActions] howler option has not src"));
return;
}
// for manually loading
if (typeof options.preload === "undefined") {
options.preload = false;
}
const obj = getAudio(options);
if (!obj) {
reject(new Error("[audioActions] has not howler obj"));
return;
}
const loadingState = obj.state();
if (loadingState === "loaded" || loadingState === "loading") {
console.log(
`[audioActions] already ${loadingState} audio: ${options.src}`
);
resolve(obj);
return;
}
obj.once("load", () => {
console.log(`[audioActions] loaded audio: ${options.src}`);
resolve(obj);
});
obj.once("loaderror", () => {
reject(new Error(`[audioActions] howler load error: ${options.src}`));
});
obj.load();
});
}
/**
* 音声群をロード
*
* @export
* @param {HowlOptions[]} data
* @returns {Promise<Howl[]>}
*/
export async function loadAudios(data: HowlOptions[]): Promise<Howl[]> {
return Promise.all(
map(data, (options) => {
return loadAudio(options);
})
);
}
/**
* 音声を再生し、howlerオブジェクトがキャッシュされる
*
* @export
* @param {HowlOptions} options
* @returns {(Howl | null)}
*/
export function playAudio(options: HowlOptions): Howl | null {
if (!options.src) {
console.error("[audioActions] howler option has not src");
return null;
}
console.log(`[audioActions] play audio: ${options.src}`);
options.preload = true;
const obj = getAudio(options);
if (!obj) {
console.error("[audioActions] has not howler obj");
return null;
}
obj.seek(0);
obj.play();
return obj;
}
/**
* 音声をreleaseし、キャッシュも削除
*
* @export
* @param {HowlOptions} options
* @returns {void}
*/
export function unloadAudio(options: HowlOptions): void {
if (!options.src) {
console.error("[audioActions] howler option has not src");
return;
}
const { src } = options;
console.log(`[audioActions] unload audio: ${src}`);
const cachedObj = caches.get(src);
if (!cachedObj) {
return;
}
if (cachedObj.state() === "loaded") {
cachedObj.unload();
}
caches.delete(src);
console.log("[audioActions] current audio caches", caches);
}
/**
* 音を一回だけ再生する。キャッシュしないのでただ再生だけしたい時用途
*
* @export
* @param {HowlOptions} options
* @returns {(Howl | null)}
*/
export function playOnceAudio(options: HowlOptions): Howl | null {
if (!options.src) {
console.error("[audioActions] howler option has not src");
return null;
}
console.log(`[audioActions] play once audio: ${options.src}`);
options.preload = true;
const obj = new Howl(options);
obj.on("end", () => {
obj.unload();
});
obj.play();
return obj;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment