mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 12:49:25 +08:00
播放音效
This commit is contained in:
parent
069ee0613b
commit
bdaf37cb0c
26
package.json
26
package.json
@ -27,22 +27,22 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.21.5",
|
"@babel/cli": "^7.22.5",
|
||||||
"@babel/core": "^7.21.8",
|
"@babel/core": "^7.22.5",
|
||||||
"@babel/preset-env": "^7.21.5",
|
"@babel/preset-env": "^7.22.5",
|
||||||
"@rollup/plugin-babel": "^6.0.3",
|
"@rollup/plugin-babel": "^6.0.3",
|
||||||
"@rollup/plugin-commonjs": "^25.0.0",
|
"@rollup/plugin-commonjs": "^25.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^15.0.2",
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||||
"@rollup/plugin-replace": "^5.0.2",
|
"@rollup/plugin-replace": "^5.0.2",
|
||||||
"@rollup/plugin-terser": "^0.4.3",
|
"@rollup/plugin-terser": "^0.4.3",
|
||||||
"@rollup/plugin-typescript": "^11.1.1",
|
"@rollup/plugin-typescript": "^11.1.1",
|
||||||
"@types/babel__core": "^7.20.0",
|
"@types/babel__core": "^7.20.1",
|
||||||
"@types/fontmin": "^0.9.0",
|
"@types/fontmin": "^0.9.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/lodash-es": "^4.17.7",
|
"@types/lodash-es": "^4.17.7",
|
||||||
"@types/node": "^18.16.14",
|
"@types/node": "^18.16.18",
|
||||||
"@types/ws": "^8.5.4",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-legacy": "^4.0.3",
|
"@vitejs/plugin-legacy": "^4.0.4",
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
@ -51,12 +51,12 @@
|
|||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"rollup": "^3.23.0",
|
"rollup": "^3.25.1",
|
||||||
"terser": "^5.17.6",
|
"terser": "^5.18.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^5.1.3",
|
||||||
"unplugin-vue-components": "^0.22.12",
|
"unplugin-vue-components": "^0.22.12",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.9",
|
||||||
"vue-tsc": "^1.6.5",
|
"vue-tsc": "^1.6.5",
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
}
|
}
|
||||||
|
1992
pnpm-lock.yaml
1992
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -245,7 +245,7 @@ main.prototype.init = async function (mode, callback) {
|
|||||||
const b = {};
|
const b = {};
|
||||||
new Proxy(a, b);
|
new Proxy(a, b);
|
||||||
new Promise(res => res());
|
new Promise(res => res());
|
||||||
eval('`${123}`');
|
eval('`${0}`');
|
||||||
} catch {
|
} catch {
|
||||||
alert('浏览器版本过低,无法游玩本塔!');
|
alert('浏览器版本过低,无法游玩本塔!');
|
||||||
alert('建议使用Edge浏览器或Chrome浏览器游玩!');
|
alert('建议使用Edge浏览器或Chrome浏览器游玩!');
|
||||||
|
89
src/core/audio/audio.ts
Normal file
89
src/core/audio/audio.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
|
||||||
|
|
||||||
|
const ac = new AudioContext();
|
||||||
|
|
||||||
|
interface BaseNode {
|
||||||
|
node: AudioNode;
|
||||||
|
channel?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AudioPlayerEvent extends EmitableEvent {
|
||||||
|
play: (node: AudioBufferSourceNode) => void;
|
||||||
|
update: (audio: AudioBuffer) => void;
|
||||||
|
end: (node: AudioBufferSourceNode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
||||||
|
static ac: AudioContext = ac;
|
||||||
|
static index: number = 0;
|
||||||
|
|
||||||
|
/** 音频的索引,这样的话可以复用来提高性能表现 */
|
||||||
|
index: number = AudioPlayer.index++;
|
||||||
|
|
||||||
|
data: ArrayBuffer;
|
||||||
|
buffer: AudioBuffer | null = null;
|
||||||
|
source?: AudioBufferSourceNode;
|
||||||
|
|
||||||
|
baseNode: BaseNode[] = [];
|
||||||
|
|
||||||
|
constructor(data: ArrayBuffer) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
this.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新音频数据
|
||||||
|
* @param data 音频的ArrayBuffer数据
|
||||||
|
*/
|
||||||
|
async update(data: ArrayBuffer) {
|
||||||
|
this.data = data;
|
||||||
|
this.buffer = await ac.decodeAudioData(data);
|
||||||
|
this.emit('update', this.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取音频源数据节点
|
||||||
|
*/
|
||||||
|
getSource() {
|
||||||
|
this.source ??= ac.createBufferSource();
|
||||||
|
this.source.buffer = this.buffer;
|
||||||
|
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放音频
|
||||||
|
*/
|
||||||
|
play(when?: number, offset?: number, duration?: number) {
|
||||||
|
if (!this.source) return;
|
||||||
|
this.ready();
|
||||||
|
this.source?.start(when, offset, duration);
|
||||||
|
|
||||||
|
const source = this.source;
|
||||||
|
this.source?.addEventListener('ended', ev => {
|
||||||
|
this.emit('end', source);
|
||||||
|
});
|
||||||
|
this.emit('play', source);
|
||||||
|
|
||||||
|
delete this.source;
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 准备音频资源连接
|
||||||
|
*/
|
||||||
|
ready() {
|
||||||
|
const source = this.getSource();
|
||||||
|
this.baseNode.forEach(v => {
|
||||||
|
source.connect(v.node, 0, v.channel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取音频输出destination
|
||||||
|
*/
|
||||||
|
getDestination() {
|
||||||
|
return ac.destination;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,137 @@
|
|||||||
export class SoundEffect {}
|
import { has } from '../../plugin/utils';
|
||||||
|
import { AudioPlayer } from './audio';
|
||||||
|
|
||||||
|
export class SoundEffect extends AudioPlayer {
|
||||||
|
static playIndex = 0;
|
||||||
|
|
||||||
|
private playing: Record<string, AudioBufferSourceNode> = {};
|
||||||
|
private _stopingAll: boolean = false;
|
||||||
|
|
||||||
|
constructor(data: ArrayBuffer, stereo: boolean = false) {
|
||||||
|
super(data);
|
||||||
|
|
||||||
|
this.on('end', node => {
|
||||||
|
if (this._stopingAll) return;
|
||||||
|
const entry = Object.entries(this.playing);
|
||||||
|
const index = entry.find(v => node === v[1])?.[0];
|
||||||
|
if (!index) return;
|
||||||
|
delete this.playing[index];
|
||||||
|
});
|
||||||
|
this.initAudio(stereo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置音频路由线路
|
||||||
|
* @param stereo 是否启用立体声
|
||||||
|
*/
|
||||||
|
protected initAudio(stereo: boolean = false) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放音频
|
||||||
|
* @returns 音频的唯一id
|
||||||
|
*/
|
||||||
|
playSE() {
|
||||||
|
const node = this.play();
|
||||||
|
if (!node) return;
|
||||||
|
const index = SoundEffect.playIndex++;
|
||||||
|
this.playing[index] = node;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止所有音频
|
||||||
|
*/
|
||||||
|
stopAll() {
|
||||||
|
this._stopingAll = true;
|
||||||
|
Object.values(this.playing).forEach(v => {
|
||||||
|
v.stop();
|
||||||
|
});
|
||||||
|
this.playing = {};
|
||||||
|
this._stopingAll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据唯一id停止音频
|
||||||
|
* @param index 音频唯一id
|
||||||
|
*/
|
||||||
|
stopByIndex(index: number) {
|
||||||
|
this.playing[index]?.stop();
|
||||||
|
delete this.playing[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SoundController {
|
class SoundController {
|
||||||
add(uri: string, data: ArrayBuffer) {}
|
list: Record<string, SoundEffect> = {};
|
||||||
|
|
||||||
|
private seIndex: Record<number, SoundEffect> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个新的音频
|
||||||
|
* @param uri 音频的uri
|
||||||
|
* @param data 音频的ArrayBuffer信息,会被解析为AudioBuffer
|
||||||
|
*/
|
||||||
|
add(uri: string, data: ArrayBuffer) {
|
||||||
|
const se = new SoundEffect(data);
|
||||||
|
if (this.list[uri]) {
|
||||||
|
console.warn(`Repeated sound effect: ${uri}.`);
|
||||||
|
}
|
||||||
|
return (this.list[uri] = se);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个音频
|
||||||
|
* @param uri 要移除的音频的uri
|
||||||
|
*/
|
||||||
|
remove(uri: string) {
|
||||||
|
delete this.list[uri];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放音频
|
||||||
|
* @param sound 音效的名称
|
||||||
|
* @returns 本次播放的音效的唯一标识符,如果音效不存在返回-1
|
||||||
|
*/
|
||||||
|
play(sound: SoundIds): number {
|
||||||
|
const se = this.get(sound);
|
||||||
|
const index = se.playSE();
|
||||||
|
if (!has(index)) return -1;
|
||||||
|
this.seIndex[index] = se;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止一个音效的播放
|
||||||
|
* @param id 音效的唯一标识符
|
||||||
|
*/
|
||||||
|
stop(id: number) {
|
||||||
|
const se = this.seIndex[id];
|
||||||
|
se.stopByIndex(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止一个名称的所有音效的播放
|
||||||
|
* @param id 音效名称
|
||||||
|
*/
|
||||||
|
stopById(id: SoundIds) {
|
||||||
|
const se = this.get(id);
|
||||||
|
se.stopAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止所有音效的播放
|
||||||
|
*/
|
||||||
|
stopAll() {
|
||||||
|
Object.values(this.list).forEach(v => v.stopAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个音效实例
|
||||||
|
* @param sound 音效名称
|
||||||
|
*/
|
||||||
|
get(sound: SoundIds) {
|
||||||
|
return this.list[`sounds.${sound}`];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -1,27 +1,15 @@
|
|||||||
import resource from '../../data/resource.json';
|
import resource from '../../data/resource.json';
|
||||||
import { NonZipResource, Resource, getTypeByResource } from './resource';
|
import { Resource, getTypeByResource } from './resource';
|
||||||
|
|
||||||
const info = resource as ResourceInfo[];
|
const info = resource;
|
||||||
|
|
||||||
export interface ResourceInfo {
|
|
||||||
includes: string[];
|
|
||||||
zip: boolean;
|
|
||||||
zippedName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readyAllResource() {
|
export function readyAllResource() {
|
||||||
info.forEach(v => {
|
info.forEach(v => {
|
||||||
if (v.zip) {
|
|
||||||
const id = `zip.${v.zippedName}`;
|
|
||||||
ancTe.zipResource.push([[id, new Resource(id, 'zip')]]);
|
|
||||||
} else {
|
|
||||||
const res: [string, Resource<NonZipResource>][] = v.includes.map(
|
|
||||||
v => {
|
|
||||||
const type = getTypeByResource(v);
|
const type = getTypeByResource(v);
|
||||||
return [v, new Resource(v, type as NonZipResource)];
|
if (type === 'zip') {
|
||||||
}
|
ancTe.zipResource.set(v, new Resource(v, 'zip'));
|
||||||
);
|
} else {
|
||||||
ancTe.resource.push(res);
|
ancTe.resource.set(v, new Resource(v, type));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
[
|
[
|
||||||
{
|
""
|
||||||
"includes": [],
|
|
||||||
"zip": false
|
|
||||||
}
|
|
||||||
]
|
]
|
Loading…
Reference in New Issue
Block a user