为playAudio提供更强大的功能,并增加tryAudio来播放音频地址列表

This commit is contained in:
kuangshen04 2024-05-23 01:57:12 +08:00
parent 46de6f4958
commit 0e0fefc811
4 changed files with 176 additions and 116 deletions

View File

@ -1356,61 +1356,148 @@ export class Game {
} }
} }
/** /**
* @overload
* @param { object } options
* @param { string } options.path
* //param { boolean } [options.broadcast = false]
* @param { boolean } [options.addVideo = true]
* @param { boolean } [options.video = false]
* @param { (evt: Event) => void } [options.onCanPlay = (evt => void 0)]
* @param { (evt: Event) => void } [options.onPlay = (evt => void 0)]
* @param { (evt: Event) => void } [options.onEnded = (evt => void 0)]
* @param { (evt: Event) => void } [options.onError = (evt => void 0)]
* @returns { HTMLAudioElement } * @returns { HTMLAudioElement }
*/ */
playAudio() { /**
let path = "", * @overload
emptyPath = true, * @param { ...string | number | ((evt: Event) => void) } args
notCheckDBPath = true, * @returns { HTMLAudioElement }
onError = null; */
if (_status.video) { playAudio(...args) {
// 为了能更美观的写代码默认返回audio而不额外加一个void类型 const options = (args.length === 1 && get.objtype(args[0]) === "object")
// @ts-ignore ? args[0]
if (arguments[1] != "video") return; : {
path = arguments[0]; path: args.filter(arg => typeof arg === 'string' || typeof arg === 'number').join("/"),
} else { onError: args.find(arg => typeof arg === "function"),
for (const argument of arguments) { };
if (typeof argument === "string" || typeof argument == "number") {
if (emptyPath) emptyPath = false; const {
else if (notCheckDBPath) { path = "",
notCheckDBPath = false; // broadcast = false,
if (/^db:extension-[^:]*$/.test(path)) path += ":"; addVideo = true,
else path += "/"; video = false,
} else path += "/"; onCanPlay = (evt => void 0),
path += argument; onPlay = (evt => void 0),
} else if (typeof argument == "function") onError = argument; onEnded = (evt => void 0),
if (_status.video) break; onError = (evt => void 0),
} } = options;
if (path.startsWith("ext:")) path = path.replace(/^ext:/, "extension/");
else if (!["db:", "blob:", "data:"].some(prefix => path.startsWith(prefix))) path = `audio/${path}`; // 为了能更美观的写代码默认返回audio而不额外加一个void类型
if (!lib.config.repeat_audio && _status.skillaudio.includes(path)) return; // @ts-ignore
} if (_status.video && !video) return;
const audio = document.createElement("audio");
audio.autoplay = true; let parsedPath = "";
if (["blob:", "data:"].some(prefix => path.startsWith(prefix))) parsedPath = path;
else if (path.startsWith('ext:')) parsedPath = path.replace(/^ext:/, 'extension/');
else if (path.startsWith('db:')) parsedPath = path.replace(/^(db:[^:]*)\//, (_, p) => p + ":");
else parsedPath = `audio/${path}`;
// @ts-ignore
if (!lib.config.repeat_audio && _status.skillaudio.includes(parsedPath)) return;
const audio = document.createElement('audio');
audio.volume = lib.config.volumn_audio / 8; audio.volume = lib.config.volumn_audio / 8;
//Some browsers do not support "autoplay", so "oncanplay" listening has been added audio.autoplay = true;
audio.oncanplay = () => Promise.resolve(audio.play()).catch(() => void 0);
audio.onplay = () => { audio.oncanplay = ev => {
_status.skillaudio.add(path); //Some browsers do not support "autoplay", so "oncanplay" listening has been added
setTimeout(() => _status.skillaudio.remove(path), 1000); Promise.resolve(audio.play()).catch(e => console.error(e));
game.addVideo("playAudio", null, path); onCanPlay(ev);
}
audio.onplay = ev => {
_status.skillaudio.add(parsedPath);
setTimeout(() => _status.skillaudio.remove(parsedPath), 1000);
// if (broadcast) game.broadcast(game.playAudio, options);
if (addVideo) game.addVideo("playAudio", null, path);
if (_status.video || game.online) return;
onPlay(ev);
}; };
audio.onended = event => audio.remove(); audio.onended = ev => {
audio.onerror = event => {
audio.remove(); audio.remove();
if (onError) onError(event); if (_status.video || game.online) return;
onEnded(ev);
}; };
new Promise((resolve, reject) => { audio.onerror = ev => {
if (path.startsWith("db:")) game.getDB("image", path.slice(3)).then(octetStream => resolve(get.objectURL(octetStream)), reject); audio.remove();
else if (lib.path.extname(path)) resolve(`${lib.assetURL}${path}`); if (_status.video || game.online) return;
else if (URL.canParse(path)) resolve(path); onError(ev);
else resolve(`${lib.assetURL}${path}.mp3`); };
}).then(resolvedPath => {
Promise.resolve().then(async () => {
let resolvedPath;
if (parsedPath.startsWith('db:')) resolvedPath = get.objectURL(await game.getDB('image', parsedPath.slice(3)));
else if (lib.path.extname(parsedPath)) resolvedPath = `${lib.assetURL}${parsedPath}`;
else if (URL.canParse(path)) resolvedPath = path;
else resolvedPath = `${lib.assetURL}${parsedPath}.mp3`;
audio.src = resolvedPath; audio.src = resolvedPath;
ui.window.appendChild(audio); ui.window.appendChild(audio);
}); });
return audio; return audio;
} }
/**
* @param { object } options
* @param { string[] } options.audioList
* @param { boolean } [options.autoplay = true]
* @param { boolean } [options.random = true]
* @param { boolean } [options.addVideo = true]
* @returns
*/
tryAudio({ audioList, autoplay = true, random = true, addVideo=true}) {
/**
* @type {string}
*/
let audio,
list = get.Audio.copy(audioList),
refresh = false; // 当前audioList是否有可播放的音频
const check = () => {
if (list.length) return true;
if (refresh) {
list = get.Audio.copy(audioList);
return true;
}
//@ts-ignore
if (!list.alternate) return false;
//@ts-ignore
audioList = list.alternate;
list = get.Audio.copy(audioList);
return check();
};
/**
* @returns {HTMLAudioElement}
*/
const play = () => {
//@ts-ignore
if (!check()) return;
//@ts-ignore
audio = random ? list.randomRemove() : list.shift();
return game.playAudio({
path: audio,
addVideo,
onCanPlay: () => refresh = true,
onError: play,
});
};
if (autoplay) return play();
return () => {
if (random) list = get.Audio.copy(audioList);
return play();
};
}
/** /**
* @deprecated 请使用get.Audio.skill + get.Audio.toFile * @deprecated 请使用get.Audio.skill + get.Audio.toFile
* *
@ -1465,7 +1552,7 @@ export class Game {
* @param { Player | string } player * @param { Player | string } player
* @param { boolean } [directaudio] * @param { boolean } [directaudio]
* @param { boolean } [nobroadcast] * @param { boolean } [nobroadcast]
* @param { ['lib']['skill'] } [skillInfo] * @param { any } [skillInfo]
* @returns * @returns
*/ */
trySkillAudio(skill, player, directaudio, nobroadcast, skillInfo) { trySkillAudio(skill, player, directaudio, nobroadcast, skillInfo) {
@ -1476,21 +1563,9 @@ export class Game {
if (!info) return; if (!info) return;
if (info.direct && !directaudio) return; if (info.direct && !directaudio) return;
if (lib.skill.global.includes(skill) && !info.forceaudio) return; if (lib.skill.global.includes(skill) && !info.forceaudio) return;
let audio, list = get.Audio.skill({ skill, player, info: skillInfo }).randomSort(); const audioList = get.Audio.toFile(get.Audio.skill({ skill, player, info: skillInfo }));
const check = () => { return game.tryAudio({ audioList });
if (list.length) return true;
//@ts-ignore
if (!list.alternate) return false;
//@ts-ignore
list = list.alternate;
return check();
};
return (function play() {
if (!check()) return;
audio = list.shift();
return game.playAudio(audio.file, play);
})();
} }
/** /**
* @param { Player | string } player * @param { Player | string } player
@ -1500,22 +1575,11 @@ export class Game {
game.broadcast(game.tryDieAudio, player); game.broadcast(game.tryDieAudio, player);
if (!lib.config.background_speak) return; if (!lib.config.background_speak) return;
let audio, list = get.Audio.die({player}).randomSort(); const audioList = get.Audio.toFile(get.Audio.die({ player }));
const check = () => { return game.tryAudio({ audioList });
if (list.length) return true;
//@ts-ignore
if (!list.alternate) return false;
//@ts-ignore
list = list.alternate;
return check();
};
return (function play() {
if (!check()) return;
audio = list.shift();
return game.playAudio(audio.file, play);
})();
} }
/** /**
* @deprecated
* @param { string } name * @param { string } name
* @param { number } [index] * @param { number } [index]
* @returns * @returns
@ -2576,7 +2640,7 @@ export class Game {
} }
}, },
playAudio: function (str) { playAudio: function (str) {
game.playAudio(str, "video"); game.playAudio({ path: str, video: true });
}, },
playSkillAudio: function (name) { playSkillAudio: function (name) {
game.playSkillAudio(name, "video"); game.playSkillAudio(name, "video");

View File

@ -155,7 +155,7 @@ export class Audio {
const parseAudioWithCache = (name, audioInfo, data, isDefault = false) => { const parseAudioWithCache = (name, audioInfo, data, isDefault = false) => {
const key = this.#getCacheKey(options, name, audioInfo, data); const key = this.#getCacheKey(options, name, audioInfo, data);
const result = this.#Cache[key]; const result = this.#Cache[key];
if (result !== void 0) return this.#copy(result); if (result !== void 0) return this.copy(result);
else { else {
const result = parseAudio(name, audioInfo, data); const result = parseAudio(name, audioInfo, data);
if (isDefault && name.includes("_")) { if (isDefault && name.includes("_")) {
@ -163,7 +163,7 @@ export class Audio {
result.alternate = getInfoAudio(name, originData); result.alternate = getInfoAudio(name, originData);
} }
this.#Cache[key] = result; this.#Cache[key] = result;
return this.#copy(result); return this.copy(result);
} }
} }
@ -258,14 +258,15 @@ export class Audio {
} }
/** /**
* @param {textMap[]} list * @template {textMap | string} T
* @param {T[]} list
* @this {typeof get.Audio} * @this {typeof get.Audio}
* @returns {textMap[]} * @returns {T[]}
*/ */
#copy = function (list) { copy(list) {
const result = JSON.parse(JSON.stringify(list)); const result = JSON.parse(JSON.stringify(list));
//@ts-ignore //@ts-ignore
if (list.alternate) result.alternate = this.#copy(list.alternate); if (list.alternate) result.alternate = this.copy(list.alternate);
return result; return result;
} }

View File

@ -14,7 +14,10 @@ export class status {
event = null; event = null;
ai = {}; ai = {};
lastdragchange = []; lastdragchange = [];
skillaudio = []; /**
* @type { string[] }
*/
skillaudio = []
dieClose = []; dieClose = [];
dragline = []; dragline = [];
dying = []; dying = [];
@ -56,6 +59,10 @@ export class status {
* @type { boolean | undefined } * @type { boolean | undefined }
*/ */
connectMode = undefined; connectMode = undefined;
/**
* @type { boolean | undefined }
*/
video = undefined
/** /**
* @type { boolean | undefined } * @type { boolean | undefined }
*/ */

View File

@ -3555,23 +3555,17 @@ export class Click {
}); });
} }
if (lib.config.background_speak && e !== "init") { if (lib.config.background_speak && e !== 'init') {
let audio, if (!this.playAudio) {
skillnode = this; const audioList = get.Audio.toFile(get.Audio.skill({ skill: this.link, player: playername }));
const playedAudios = []; this.playAudio = game.tryAudio({
(function play() { audioList,
if (!skillnode.audioList || !skillnode.audioList.length) { addVideo: false,
skillnode.audioList = game.parseSkillAudio(skillnode.link, playername); random: false,
if ( autoplay: false
!skillnode.audioList.length || });
skillnode.audioList.length == playedAudios.length }
) this.playAudio();
return;
}
audio = skillnode.audioList.shift();
playedAudios.push(audio);
game.playAudio(audio, play);
})();
} }
}; };
} else { } else {
@ -3914,23 +3908,17 @@ export class Click {
}); });
} }
if (lib.config.background_speak && e !== "init") { if (lib.config.background_speak && e !== 'init') {
let audio, if (!this.playAudio) {
skillnode = this; const audioList = get.Audio.toFile(get.Audio.skill({ skill: this.link, player: playername }));
const playedAudios = []; this.playAudio = game.tryAudio({
(function play() { audioList,
if (!skillnode.audioList || !skillnode.audioList.length) { addVideo: false,
skillnode.audioList = game.parseSkillAudio(skillnode.link, playername); random: false,
if ( autoplay: false
!skillnode.audioList.length || });
skillnode.audioList.length == playedAudios.length }
) this.playAudio();
return;
}
audio = skillnode.audioList.shift();
playedAudios.push(audio);
game.playAudio(audio, play);
})();
} }
}; };
} }