DCSOlympus/frontend/react/src/audio/playbackpipeline.ts

87 lines
2.7 KiB
TypeScript

import { getApp } from "../olympusapp";
import { Filter, Noise } from "./audiolibrary";
export class PlaybackPipeline {
#decoder = new AudioDecoder({
output: (chunk) => this.#handleDecodedData(chunk),
error: (e) => console.log(e),
});
#trackGenerator: any; // TODO can we have typings?
#writer: any;
#gainNode: GainNode;
#pannerNode: StereoPannerNode;
#enabled: boolean = false;
constructor() {
this.#decoder.configure({
codec: "opus",
numberOfChannels: 1,
sampleRate: 16000,
//@ts-ignore // TODO why is this giving an error?
opus: {
frameDuration: 40000,
},
bitrateMode: "constant",
});
//@ts-ignore
this.#trackGenerator = new MediaStreamTrackGenerator({ kind: "audio" });
this.#writer = this.#trackGenerator.writable.getWriter();
const stream = new MediaStream([this.#trackGenerator]);
const mediaStreamSource = getApp().getAudioManager().getAudioContext().createMediaStreamSource(stream);
/* Connect to the device audio output */
this.#gainNode = getApp().getAudioManager().getAudioContext().createGain();
this.#pannerNode = getApp().getAudioManager().getAudioContext().createStereoPanner();
let splitter = getApp().getAudioManager().getAudioContext().createChannelSplitter();
let bandpass = new Filter(getApp().getAudioManager().getAudioContext(), "banpass", 600, 10);
bandpass.setup();
mediaStreamSource.connect(this.#gainNode);
this.#gainNode.connect(bandpass.input);
bandpass.output.connect(splitter);
splitter.connect(this.#pannerNode);
this.#pannerNode.pan.setValueAtTime(0, getApp().getAudioManager().getAudioContext().currentTime);
let noise = new Noise(getApp().getAudioManager().getAudioContext(), 0.01);
noise.init();
noise.connect(this.#gainNode);
}
playBuffer(arrayBuffer) {
const init = {
type: "key",
data: arrayBuffer,
timestamp: 0,
duration: 2000000,
transfer: [arrayBuffer],
};
//@ts-ignore //TODO Typings?
let encodedAudioChunk = new EncodedAudioChunk(init);
this.#decoder.decode(encodedAudioChunk);
}
setEnabled(enabled) {
if (enabled && !this.#enabled) {
this.#enabled = true;
this.#pannerNode.connect(getApp().getAudioManager().getAudioContext().destination);
} else if (!enabled && this.#enabled) {
this.#enabled = false;
this.#pannerNode.disconnect(getApp().getAudioManager().getAudioContext().destination);
}
}
setPan(pan) {
this.#pannerNode.pan.setValueAtTime(pan, getApp().getAudioManager().getAudioContext().currentTime);
}
#handleDecodedData(audioData) {
this.#writer.ready.then(() => {
this.#writer.write(audioData);
});
}
}