mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
143 lines
3.7 KiB
TypeScript
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();
|
|
}
|
|
}
|