Add audio layer toggles and reduce item emit volume
This commit is contained in:
@@ -41,6 +41,7 @@ export class AudioEngine {
|
||||
private loopbackEnabled = false;
|
||||
private loopbackRuntime: EffectRuntime | null = null;
|
||||
private outputMode: OutputMode = 'stereo';
|
||||
private voiceLayerEnabled = true;
|
||||
private effectIndex = EFFECT_SEQUENCE.findIndex((effect) => effect.id === 'off');
|
||||
private readonly effectValues: Record<EffectId, number> = {
|
||||
reverb: 50,
|
||||
@@ -173,6 +174,14 @@ export class AudioEngine {
|
||||
return this.outputMode;
|
||||
}
|
||||
|
||||
setVoiceLayerEnabled(enabled: boolean): void {
|
||||
this.voiceLayerEnabled = enabled;
|
||||
}
|
||||
|
||||
isVoiceLayerEnabled(): boolean {
|
||||
return this.voiceLayerEnabled;
|
||||
}
|
||||
|
||||
toggleLoopback(): boolean {
|
||||
this.loopbackEnabled = !this.loopbackEnabled;
|
||||
this.rebuildOutboundEffectGraph();
|
||||
@@ -186,6 +195,7 @@ export class AudioEngine {
|
||||
): Promise<void> {
|
||||
await this.ensureContext();
|
||||
if (!this.audioCtx) return;
|
||||
this.cleanupPeerAudio(peer);
|
||||
|
||||
const audioElement = new Audio();
|
||||
audioElement.srcObject = stream;
|
||||
@@ -206,9 +216,13 @@ export class AudioEngine {
|
||||
let pannerNode: StereoPannerNode | undefined;
|
||||
if (this.supportsStereoPanner()) {
|
||||
pannerNode = this.audioCtx.createStereoPanner();
|
||||
gainNode.connect(pannerNode).connect(this.audioCtx.destination);
|
||||
if (this.voiceLayerEnabled) {
|
||||
gainNode.connect(pannerNode).connect(this.audioCtx.destination);
|
||||
}
|
||||
} else {
|
||||
gainNode.connect(this.audioCtx.destination);
|
||||
if (this.voiceLayerEnabled) {
|
||||
gainNode.connect(this.audioCtx.destination);
|
||||
}
|
||||
}
|
||||
|
||||
peer.audioElement = audioElement;
|
||||
@@ -313,9 +327,16 @@ export class AudioEngine {
|
||||
}
|
||||
|
||||
cleanupPeerAudio(peer: SpatialPeerRuntime): void {
|
||||
peer.audioElement?.remove();
|
||||
if (peer.audioElement) {
|
||||
peer.audioElement.pause();
|
||||
peer.audioElement.srcObject = null;
|
||||
peer.audioElement.remove();
|
||||
}
|
||||
peer.gain?.disconnect();
|
||||
peer.panner?.disconnect();
|
||||
peer.audioElement = undefined;
|
||||
peer.gain = undefined;
|
||||
peer.panner = undefined;
|
||||
}
|
||||
|
||||
private rebuildOutboundEffectGraph(): void {
|
||||
|
||||
@@ -9,8 +9,11 @@ type EmitOutput = {
|
||||
panner: StereoPannerNode | null;
|
||||
};
|
||||
|
||||
const ITEM_EMIT_BASE_GAIN = 0.3;
|
||||
|
||||
export class ItemEmitRuntime {
|
||||
private readonly outputs = new Map<string, EmitOutput>();
|
||||
private layerEnabled = true;
|
||||
|
||||
constructor(
|
||||
private readonly audio: AudioEngine,
|
||||
@@ -34,7 +37,20 @@ export class ItemEmitRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
async setLayerEnabled(enabled: boolean, items: Iterable<WorldItem>): Promise<void> {
|
||||
this.layerEnabled = enabled;
|
||||
if (!enabled) {
|
||||
this.cleanupAll();
|
||||
return;
|
||||
}
|
||||
await this.sync(items);
|
||||
}
|
||||
|
||||
async sync(items: Iterable<WorldItem>): Promise<void> {
|
||||
if (!this.layerEnabled) {
|
||||
this.cleanupAll();
|
||||
return;
|
||||
}
|
||||
const validIds = new Set<string>();
|
||||
await this.audio.ensureContext();
|
||||
const audioCtx = this.audio.context;
|
||||
@@ -81,6 +97,7 @@ export class ItemEmitRuntime {
|
||||
}
|
||||
|
||||
updateSpatialAudio(items: Map<string, WorldItem>, playerPosition: { x: number; y: number }): void {
|
||||
if (!this.layerEnabled) return;
|
||||
const audioCtx = this.audio.context;
|
||||
if (!audioCtx) return;
|
||||
|
||||
@@ -101,7 +118,7 @@ export class ItemEmitRuntime {
|
||||
gainValue = 1;
|
||||
panValue = 0;
|
||||
}
|
||||
output.gain.gain.linearRampToValueAtTime(gainValue, audioCtx.currentTime + 0.1);
|
||||
output.gain.gain.linearRampToValueAtTime(gainValue * ITEM_EMIT_BASE_GAIN, audioCtx.currentTime + 0.1);
|
||||
if (output.panner) {
|
||||
const resolvedPan = this.audio.getOutputMode() === 'mono' ? 0 : Math.max(-1, Math.min(1, panValue));
|
||||
output.panner.pan.linearRampToValueAtTime(resolvedPan, audioCtx.currentTime + 0.1);
|
||||
|
||||
@@ -110,6 +110,7 @@ function connectRadioChannelSource(
|
||||
export class RadioStationRuntime {
|
||||
private readonly sharedRadioSources = new Map<string, SharedRadioSource>();
|
||||
private readonly itemRadioOutputs = new Map<string, ItemRadioOutput>();
|
||||
private layerEnabled = true;
|
||||
|
||||
constructor(private readonly audio: AudioEngine) {}
|
||||
|
||||
@@ -148,7 +149,20 @@ export class RadioStationRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
async setLayerEnabled(enabled: boolean, items: Iterable<WorldItem>): Promise<void> {
|
||||
this.layerEnabled = enabled;
|
||||
if (!enabled) {
|
||||
this.cleanupAll();
|
||||
return;
|
||||
}
|
||||
await this.sync(items);
|
||||
}
|
||||
|
||||
async sync(items: Iterable<WorldItem>): Promise<void> {
|
||||
if (!this.layerEnabled) {
|
||||
this.cleanupAll();
|
||||
return;
|
||||
}
|
||||
const validIds = new Set<string>();
|
||||
for (const item of items) {
|
||||
if (item.type !== 'radio_station') continue;
|
||||
@@ -163,6 +177,7 @@ export class RadioStationRuntime {
|
||||
}
|
||||
|
||||
updateSpatialAudio(items: Map<string, WorldItem>, playerPosition: { x: number; y: number }): void {
|
||||
if (!this.layerEnabled) return;
|
||||
const audioCtx = this.audio.context;
|
||||
if (!audioCtx) return;
|
||||
for (const [itemId, output] of this.itemRadioOutputs.entries()) {
|
||||
|
||||
Reference in New Issue
Block a user