为playAudio提供更强大的功能,并增加tryAudio来播放音频地址列表
This commit is contained in:
parent
46de6f4958
commit
0e0fefc811
|
@ -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) {
|
||||
/**
|
||||
* @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 (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;
|
||||
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;
|
||||
audio.autoplay = true;
|
||||
|
||||
audio.oncanplay = ev => {
|
||||
//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);
|
||||
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) {
|
||||
|
@ -1477,20 +1564,8 @@ export class Game {
|
|||
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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
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
|
||||
});
|
||||
}
|
||||
audio = skillnode.audioList.shift();
|
||||
playedAudios.push(audio);
|
||||
game.playAudio(audio, play);
|
||||
})();
|
||||
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;
|
||||
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
|
||||
});
|
||||
}
|
||||
audio = skillnode.audioList.shift();
|
||||
playedAudios.push(audio);
|
||||
game.playAudio(audio, play);
|
||||
})();
|
||||
this.playAudio();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue