2024-11-22 14:56:26 +01:00

143 lines
3.7 KiB
TypeScript

import { AudioSink } from "./audiosink";
import { AudioPacket } from "./audiopacket";
import { getApp } from "../olympusapp";
import { AudioSinksChangedEvent } from "../events";
import { timeStamp } from "console";
import { makeID } from "../other/utils";
/* Radio sink, basically implements a simple SRS Client in Olympus. Does not support encryption at this moment */
export class RadioSink extends AudioSink {
#encoder: AudioEncoder;
#desinationNode: MediaStreamAudioDestinationNode;
#audioTrackProcessor: any; // TODO can we have typings?
#frequency = 251000000;
#modulation = 0;
#ptt = false;
#tuned = true;
#volume = 0.5;
#receiving = false;
#clearReceivingTimeout: number;
#packetID = 0;
#guid = makeID(22);
constructor() {
super();
this.#encoder = new AudioEncoder({
output: (data) => this.handleEncodedData(data),
error: (e) => {
console.log(e);
},
});
this.#encoder.configure({
codec: "opus",
numberOfChannels: 1,
sampleRate: 16000,
//@ts-ignore // TODO why is it giving error?
opus: {
frameDuration: 40000,
},
bitrateMode: "constant",
});
this.#desinationNode = getApp().getAudioManager().getAudioContext().createMediaStreamDestination();
this.#desinationNode.channelCount = 1;
//@ts-ignore
this.#audioTrackProcessor = new MediaStreamTrackProcessor({
track: this.#desinationNode.stream.getAudioTracks()[0],
});
this.#audioTrackProcessor.readable.pipeTo(
new WritableStream({
write: (arrayBuffer) => this.handleRawData(arrayBuffer),
})
);
this.getInputNode().connect(this.#desinationNode);
}
setFrequency(frequency) {
this.#frequency = frequency;
AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getFrequency() {
return this.#frequency;
}
setModulation(modulation) {
this.#modulation = modulation;
AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getModulation() {
return this.#modulation;
}
setPtt(ptt) {
this.#ptt = ptt;
AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getPtt() {
return this.#ptt;
}
setTuned(tuned) {
this.#tuned = tuned;
AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getTuned() {
return this.#tuned;
}
setVolume(volume) {
this.#volume = volume;
AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getVolume() {
return this.#volume;
}
setReceiving(receiving) {
// Only do it if actually changed
if (receiving !== this.#receiving) AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
if (receiving) {
window.clearTimeout(this.#clearReceivingTimeout);
this.#clearReceivingTimeout = window.setTimeout(() => this.setReceiving(false), 500);
}
this.#receiving = receiving;
}
getReceiving() {
return this.#receiving;
}
handleEncodedData(encodedAudioChunk: EncodedAudioChunk) {
let arrayBuffer = new ArrayBuffer(encodedAudioChunk.byteLength);
encodedAudioChunk.copyTo(arrayBuffer);
if (this.#ptt) {
let audioPacket = new AudioPacket();
audioPacket.setAudioData(new Uint8Array(arrayBuffer));
audioPacket.setPacketID(this.#packetID++);
audioPacket.setFrequencies([{
frequency: this.#frequency,
modulation: this.#modulation,
encryption: 0
}])
audioPacket.setClientGUID(getApp().getAudioManager().getGuid());
audioPacket.setTransmissionGUID(this.#guid);
getApp().getAudioManager().send(audioPacket.toByteArray());
}
}
handleRawData(audioData) {
this.#encoder.encode(audioData);
audioData.close();
}
}