fix: refactor AudioMixer logic
This commit is contained in:
+24
-68
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
AudioPlayer,
|
||||
AudioPlayerStatus,
|
||||
createAudioPlayer,
|
||||
createAudioResource,
|
||||
StreamType,
|
||||
@@ -11,7 +12,7 @@ import { PassThrough, Readable } from 'stream';
|
||||
|
||||
import prism from 'prism-media';
|
||||
|
||||
const DURATION_EXTRA_MS = 500;
|
||||
const DURATION_EXTRA_MS = 1000;
|
||||
|
||||
export class StreamQueue {
|
||||
private queue: Readable[] = [];
|
||||
@@ -65,8 +66,7 @@ export class StreamQueue {
|
||||
export class MixedStream {
|
||||
public readonly player: AudioPlayer;
|
||||
private mixer: AudioMixer;
|
||||
private output: PassThrough;
|
||||
private silenceInterval: NodeJS.Timeout;
|
||||
private output: PassThrough | undefined;
|
||||
|
||||
private queues: Map<string, StreamQueue> = new Map();
|
||||
|
||||
@@ -78,34 +78,10 @@ export class MixedStream {
|
||||
bitDepth: 16,
|
||||
sampleRate: 48000,
|
||||
autoClose: false,
|
||||
generateSilence: false // does not work :<
|
||||
generateSilence: true
|
||||
});
|
||||
|
||||
const silenceInput = this.mixer.createAudioInput({
|
||||
channels: 2,
|
||||
sampleRate: 48000,
|
||||
bitDepth: 16,
|
||||
volume: 100
|
||||
});
|
||||
|
||||
const chunk = Buffer.alloc(3840);
|
||||
this.silenceInterval = setInterval(() => {
|
||||
if (silenceInput.writable && silenceInput.writableLength < 3840 * 10) {
|
||||
silenceInput.write(chunk);
|
||||
}
|
||||
}, 20);
|
||||
|
||||
this.output = new PassThrough({ highWaterMark: 1024 * 16 });
|
||||
this.mixer.pipe(this.output);
|
||||
|
||||
const resource = createAudioResource(this.output, {
|
||||
inputType: StreamType.Raw
|
||||
});
|
||||
|
||||
this.player.play(resource);
|
||||
this.player.on('error', (error) => {
|
||||
console.error('Error: ', error.message);
|
||||
});
|
||||
this.setupPipeline();
|
||||
}
|
||||
|
||||
public getQueue(name: string): StreamQueue {
|
||||
@@ -121,6 +97,10 @@ export class MixedStream {
|
||||
completion: Promise<void>;
|
||||
stop: () => void;
|
||||
} {
|
||||
if (this.player.state.status === AudioPlayerStatus.Idle) {
|
||||
this.setupPipeline();
|
||||
}
|
||||
|
||||
let stopCallback: () => void = () => {};
|
||||
const completion = new Promise<void>((resolve) => {
|
||||
const mixerInput = this.mixer.createAudioInput({
|
||||
@@ -154,41 +134,15 @@ export class MixedStream {
|
||||
const cleanup = () => {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
try {
|
||||
source.unpipe(transcoder);
|
||||
source.destroy();
|
||||
} catch (e) {
|
||||
console.error('Error destroying source:', e);
|
||||
}
|
||||
|
||||
try {
|
||||
transcoder.unpipe(mixerInput);
|
||||
transcoder.destroy();
|
||||
} catch (e) {
|
||||
console.error('Error destroying transcoder:', e);
|
||||
}
|
||||
source.unpipe(transcoder);
|
||||
source.destroy();
|
||||
|
||||
try {
|
||||
this.mixer.removeAudioinput(mixerInput);
|
||||
} catch (e) {
|
||||
console.error('Error removing audio input:', e);
|
||||
}
|
||||
transcoder.unpipe(mixerInput);
|
||||
transcoder.destroy();
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if (typeof (this.mixer as any).removeAudioInput === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.mixer as any).removeAudioInput(mixerInput);
|
||||
}
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
try {
|
||||
mixerInput.destroy();
|
||||
} catch (e) {
|
||||
console.error('Error destroying mixer input:', e);
|
||||
}
|
||||
this.mixer.removeAudioinput(mixerInput);
|
||||
mixerInput.destroy();
|
||||
|
||||
resolve();
|
||||
};
|
||||
@@ -216,24 +170,26 @@ export class MixedStream {
|
||||
|
||||
public destroy(): void {
|
||||
this.player.stop();
|
||||
this.output.destroy();
|
||||
if (this.output) this.output.destroy();
|
||||
this.mixer.destroy();
|
||||
clearInterval(this.silenceInterval);
|
||||
}
|
||||
|
||||
public flush(): void {
|
||||
this.player.stop();
|
||||
this.setupPipeline();
|
||||
}
|
||||
|
||||
this.mixer.unpipe(this.output);
|
||||
this.output.destroy();
|
||||
|
||||
this.output = new PassThrough({ highWaterMark: 1024 * 16 });
|
||||
private setupPipeline(): void {
|
||||
if (this.output) {
|
||||
this.mixer.unpipe(this.output);
|
||||
this.output.destroy();
|
||||
}
|
||||
this.output = new PassThrough({ highWaterMark: 1024 * 256 });
|
||||
this.mixer.pipe(this.output);
|
||||
|
||||
const resource = createAudioResource(this.output, {
|
||||
inputType: StreamType.Raw
|
||||
});
|
||||
|
||||
this.player.play(resource);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user