interactive-player
Version:
A front-end interactive music audio player using AudioWorklet for javascript and typescript
93 lines • 2.77 kB
JavaScript
// src/interactive-player.ts
var InteractivePlayer = class {
constructor() {
this.ctx = new AudioContext();
// 遅延初期化
this.graph = {};
const moduleUrl = (
// ESM (ブラウザ / Node ESM)
typeof import.meta !== "undefined" && import.meta.url || `file://${__dirname}/`
);
const workletUrl = new URL(
"interactive-player-processor.js",
moduleUrl
);
this.workletReady = this.ctx.audioWorklet.addModule(workletUrl.toString()).then(() => {
this.node = new AudioWorkletNode(
this.ctx,
"interactive-player-processor"
);
this.node.connect(this.ctx.destination);
});
}
/** 複数トラックをまとめてロード */
async loadAll(tracks) {
this.graph = tracks.reduce(
(g, t, i) => (g[i] = t.next ?? null, g),
{}
);
const buffers = await Promise.all(
tracks.map(async (t) => {
const arr = typeof t.src === "string" ? await (await fetch(t.src)).arrayBuffer() : t.src;
return this.ctx.decodeAudioData(arr);
})
);
await this.workletReady;
const transferables = [];
const payload = buffers.map((buf) => {
const chans = [];
for (let ch = 0; ch < buf.numberOfChannels; ch++) {
const copy = new Float32Array(buf.getChannelData(ch));
chans.push(copy.buffer);
transferables.push(copy.buffer);
}
return chans;
});
this.node.port.postMessage(
{ type: "loadMulti", tracks: payload, transitions: this.graph },
transferables
);
}
/** 再生 */
async play(startIdx = 0) {
if (this.ctx.state !== "running") await this.ctx.resume();
await this.workletReady;
this.node.port.postMessage({ type: "play", start: startIdx });
}
/** 停止(頭出し) */
async stop() {
await this.workletReady;
this.node.port.postMessage({ type: "stop" });
}
/** ポーズ/レジューム */
async pause(isPause) {
await this.workletReady;
this.node.port.postMessage({ type: isPause ? "pause" : "resume" });
}
/** 次トラックをキューイング */
async next(idx, currentIs = null) {
await this.workletReady;
this.node.port.postMessage({ type: "next", next: idx, currentIs });
}
async setFadeInSec(sec) {
await this.workletReady;
this.node.port.postMessage({
type: "setFadeIn",
fadeInSamples: this.secToSamples(sec)
});
}
async setFadeOutSec(sec) {
await this.workletReady;
this.node.port.postMessage({
type: "setFadeOut",
fadeOutSamples: this.secToSamples(sec)
});
}
secToSamples(sec) {
return Math.floor(sec * this.ctx.sampleRate);
}
};
export {
InteractivePlayer as default
};
//# sourceMappingURL=interactive-player.js.map