mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Completed the audio panel
This commit is contained in:
@@ -209,6 +209,10 @@ export class AudioManager {
|
||||
if (sink instanceof RadioSink) sink.setName(`Radio ${idx++}`);
|
||||
});
|
||||
document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
|
||||
this.#sources.forEach((source) => {
|
||||
if (source.getConnectedTo().includes(sink))
|
||||
source.disconnect(sink)
|
||||
})
|
||||
}
|
||||
|
||||
getGuid() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getApp } from "../olympusapp";
|
||||
|
||||
/* Base audio sink class */
|
||||
export class AudioSink {
|
||||
export abstract class AudioSink {
|
||||
#name: string;
|
||||
#gainNode: GainNode;
|
||||
|
||||
@@ -25,4 +25,7 @@ export class AudioSink {
|
||||
getInputNode() {
|
||||
return this.#gainNode;
|
||||
}
|
||||
|
||||
abstract setPtt(ptt: boolean): void;
|
||||
abstract getPtt(): boolean;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ export abstract class AudioSource {
|
||||
}
|
||||
|
||||
connect(sink: AudioSink) {
|
||||
this.getOutputNode().connect(sink.getInputNode());
|
||||
this.#connectedTo.push(sink);
|
||||
document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
|
||||
if (!this.#connectedTo.includes(sink)) {
|
||||
this.getOutputNode().connect(sink.getInputNode());
|
||||
this.#connectedTo.push(sink);
|
||||
document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(sinkToDisconnect?: AudioSink) {
|
||||
|
||||
@@ -3,8 +3,6 @@ import { Unit } from "../unit/unit";
|
||||
import { Filter, Noise } from "./audiolibrary";
|
||||
import { AudioPacket } from "./audiopacket";
|
||||
|
||||
const MAX_DISTANCE = 1852; // Ignore clients that are further away than 1NM, to save performance.
|
||||
|
||||
export class AudioUnitPipeline {
|
||||
#inputNode: GainNode;
|
||||
#sourceUnit: Unit;
|
||||
@@ -14,11 +12,15 @@ export class AudioUnitPipeline {
|
||||
#audioTrackProcessor: any;
|
||||
#encoder: AudioEncoder;
|
||||
|
||||
#wetGainNode: GainNode;
|
||||
#delayNode: DelayNode;
|
||||
#convolverNode: ConvolverNode;
|
||||
#tailOsc: Noise;
|
||||
#tailOscillator: Noise;
|
||||
|
||||
#distance: number = 0;
|
||||
#packetID = 0;
|
||||
#ptt: boolean = false;
|
||||
#maxDistance: number = 1852;
|
||||
|
||||
constructor(sourceUnit: Unit, unitID: number, inputNode: GainNode) {
|
||||
this.#sourceUnit = sourceUnit;
|
||||
@@ -64,6 +66,27 @@ export class AudioUnitPipeline {
|
||||
/* Create the pipeline */
|
||||
this.#inputNode = inputNode;
|
||||
this.#setupEffects();
|
||||
|
||||
/* Create the interval task to update the data */
|
||||
setInterval(() => {
|
||||
/* Get the destination unit and compute the distance to it */
|
||||
let destinationUnit = getApp().getUnitsManager().getUnitByID(this.#unitID);
|
||||
if (destinationUnit) {
|
||||
let distance = destinationUnit?.getPosition().distanceTo(this.#sourceUnit.getPosition());
|
||||
|
||||
/* The units positions are updated at a low frequency. Filter the distance to avoid sudden volume jumps */
|
||||
this.#distance = 0.9 * this.#distance + 0.1 * distance;
|
||||
|
||||
/* Don't bother updating parameters if the client is too far away */
|
||||
if (this.#distance < this.#maxDistance) {
|
||||
/* Compute a new gain decreasing with distance. */
|
||||
let newGain = 1.0 - Math.pow(this.#distance / this.#maxDistance, 2); // Arbitrary
|
||||
|
||||
/* Set the values of the main gain node and the multitap gain node, used for reverb effect */
|
||||
this.#gainNode.gain.setValueAtTime(newGain, getApp().getAudioManager().getAudioContext().currentTime);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
handleEncodedData(encodedAudioChunk, unitID) {
|
||||
@@ -92,7 +115,7 @@ export class AudioUnitPipeline {
|
||||
|
||||
handleRawData(audioData) {
|
||||
/* Ignore players that are too far away */
|
||||
if (this.#distance < MAX_DISTANCE) {
|
||||
if (this.#distance < this.#maxDistance && this.#ptt) {
|
||||
this.#encoder.encode(audioData);
|
||||
audioData.close();
|
||||
}
|
||||
@@ -102,18 +125,18 @@ export class AudioUnitPipeline {
|
||||
/* Create the nodes necessary for the pipeline */
|
||||
this.#convolverNode = getApp().getAudioManager().getAudioContext().createConvolver();
|
||||
|
||||
let wetGainNode = getApp().getAudioManager().getAudioContext().createGain();
|
||||
wetGainNode.gain.setValueAtTime(2.0, getApp().getAudioManager().getAudioContext().currentTime)
|
||||
let delayNode = getApp().getAudioManager().getAudioContext().createDelay(1);
|
||||
delayNode.delayTime.setValueAtTime(0.09, getApp().getAudioManager().getAudioContext().currentTime)
|
||||
this.#wetGainNode = getApp().getAudioManager().getAudioContext().createGain();
|
||||
this.#wetGainNode.gain.setValueAtTime(2.0, getApp().getAudioManager().getAudioContext().currentTime)
|
||||
this.#delayNode = getApp().getAudioManager().getAudioContext().createDelay(1);
|
||||
this.#delayNode.delayTime.setValueAtTime(0.09, getApp().getAudioManager().getAudioContext().currentTime)
|
||||
|
||||
this.#inputNode.connect(this.#gainNode);
|
||||
|
||||
this.#gainNode.connect(this.#destinationNode);
|
||||
|
||||
this.#gainNode.connect(wetGainNode);
|
||||
wetGainNode.connect(delayNode);
|
||||
delayNode.connect(this.#convolverNode);
|
||||
this.#gainNode.connect(this.#wetGainNode);
|
||||
this.#wetGainNode.connect(this.#delayNode);
|
||||
this.#delayNode.connect(this.#convolverNode);
|
||||
this.#convolverNode.connect(this.#destinationNode);
|
||||
|
||||
/* Render the random noise needed for the convolver node to simulate reverb */
|
||||
@@ -132,17 +155,17 @@ export class AudioUnitPipeline {
|
||||
);
|
||||
|
||||
/* A noise oscillator and a two filters are added to smooth the reverb */
|
||||
this.#tailOsc = new Noise(tailContext, 1);
|
||||
this.#tailOscillator = new Noise(tailContext, 1);
|
||||
const tailLPFilter = new Filter(tailContext, "lowpass", 5000, 1);
|
||||
const tailHPFilter = new Filter(tailContext, "highpass", 500, 1);
|
||||
|
||||
/* Initialize and connect the oscillator with the filters */
|
||||
this.#tailOsc.init();
|
||||
this.#tailOsc.connect(tailHPFilter.input);
|
||||
this.#tailOscillator.init();
|
||||
this.#tailOscillator.connect(tailHPFilter.input);
|
||||
tailHPFilter.connect(tailLPFilter.input);
|
||||
tailLPFilter.connect(tailContext.destination);
|
||||
this.#tailOsc.attack = attack;
|
||||
this.#tailOsc.decay = decay;
|
||||
this.#tailOscillator.attack = attack;
|
||||
this.#tailOscillator.decay = decay;
|
||||
|
||||
setTimeout(() => {
|
||||
/* Set the buffer of the convolver node */
|
||||
@@ -150,8 +173,16 @@ export class AudioUnitPipeline {
|
||||
this.#convolverNode.buffer = buffer;
|
||||
});
|
||||
|
||||
this.#tailOsc.on({ frequency: 500, velocity: 127 });
|
||||
this.#tailOscillator.on({ frequency: 500, velocity: 127 });
|
||||
//tailOsc.off(); // TODO In the original example I copied, this was turned off. No idea why but it seems to work correctly if left on. To investigate.
|
||||
}, 20);
|
||||
}
|
||||
|
||||
setPtt(ptt) {
|
||||
this.#ptt = ptt;
|
||||
}
|
||||
|
||||
setMaxDistance(maxDistance) {
|
||||
this.#maxDistance = maxDistance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ scramble calls and so on. Ideally, one may want to move this code to the backend
|
||||
export class UnitSink extends AudioSink {
|
||||
#unit: Unit;
|
||||
#unitPipelines: { [key: string]: AudioUnitPipeline } = {};
|
||||
#ptt: boolean = false;
|
||||
#maxDistance: number = 1852;
|
||||
|
||||
constructor(unit: Unit) {
|
||||
super();
|
||||
@@ -33,6 +35,8 @@ export class UnitSink extends AudioSink {
|
||||
.forEach((unitID) => {
|
||||
if (unitID !== 0 && !(unitID in this.#unitPipelines)) {
|
||||
this.#unitPipelines[unitID] = new AudioUnitPipeline(this.#unit, unitID, this.getInputNode());
|
||||
this.#unitPipelines[unitID].setPtt(false);
|
||||
this.#unitPipelines[unitID].setMaxDistance(this.#maxDistance);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -42,4 +46,28 @@ export class UnitSink extends AudioSink {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setPtt(ptt) {
|
||||
this.#ptt = ptt;
|
||||
Object.values(this.#unitPipelines).forEach((pipeline) => {
|
||||
pipeline.setPtt(ptt);
|
||||
})
|
||||
document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
|
||||
}
|
||||
|
||||
getPtt() {
|
||||
return this.#ptt;
|
||||
}
|
||||
|
||||
setMaxDistance(maxDistance) {
|
||||
this.#maxDistance = maxDistance;
|
||||
Object.values(this.#unitPipelines).forEach((pipeline) => {
|
||||
pipeline.setMaxDistance(maxDistance);
|
||||
})
|
||||
document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
|
||||
}
|
||||
|
||||
getMaxDistance() {
|
||||
return this.#maxDistance;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user