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

View File

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

View File

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

View File

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