mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(voice): recalibrate stored participant and stream volumes too (#1790)
This commit is contained in:
1 parent
06c42ce02f
commit
d5daf61dbd
4 files changed
+74
-3
No files matched your search
@@ -12,6 +12,7 @@ import {
|
||||
clampVoiceTrackTotalGain,
|
||||
clampVoiceVolumePercent,
|
||||
composeVoiceVolumeGain,
|
||||
recalibrateStoredVoiceVolumes,
|
||||
} from '@app/features/voice/utils/VoiceVolumeUtils';
|
||||
import type {RemoteAudioTrack, RemoteParticipant, Room} from 'livekit-client';
|
||||
import {makeAutoObservable} from 'mobx';
|
||||
@@ -62,6 +63,7 @@ function getVoiceConnectionContext(): VoiceConnectionContextAccess {
|
||||
class ParticipantVolume {
|
||||
volumes: Record<string, number> = {};
|
||||
localMutes: Record<string, boolean> = {};
|
||||
outputVolumeRecalibratedV1 = false;
|
||||
connectionVolumesByLocalConnectionId: Record<string, Record<string, number>> = {};
|
||||
private listeners = new Set<() => void>();
|
||||
|
||||
@@ -81,7 +83,17 @@ class ParticipantVolume {
|
||||
}
|
||||
|
||||
private async initPersistence(): Promise<void> {
|
||||
await makePersistent(this, 'ParticipantVolume', ['volumes', 'localMutes']);
|
||||
await makePersistent(this, 'ParticipantVolume', ['volumes', 'localMutes', 'outputVolumeRecalibratedV1']);
|
||||
this.applyOutputVolumeRecalibration();
|
||||
}
|
||||
|
||||
private applyOutputVolumeRecalibration(): void {
|
||||
if (this.outputVolumeRecalibratedV1) {
|
||||
return;
|
||||
}
|
||||
this.volumes = recalibrateStoredVoiceVolumes(this.volumes);
|
||||
this.outputVolumeRecalibratedV1 = true;
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
setVolume(userId: string, volume: number): void {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {makePersistent} from '@app/features/platform/utils/MobXPersistence';
|
||||
import {clampVoiceVolumePercent} from '@app/features/voice/utils/VoiceVolumeUtils';
|
||||
import {clampVoiceVolumePercent, recalibrateStoredVoiceVolumePercent} from '@app/features/voice/utils/VoiceVolumeUtils';
|
||||
import {
|
||||
STREAM_AUDIO_PREFS_PRUNE_INTERVAL_MS,
|
||||
STREAM_AUDIO_PREFS_TOUCH_INTERVAL_MS,
|
||||
@@ -20,6 +20,7 @@ interface StreamAudioPrefsEntry {
|
||||
|
||||
class StreamAudioPrefs {
|
||||
entries: Record<string, StreamAudioPrefsEntry> = {};
|
||||
outputVolumeRecalibratedV1 = false;
|
||||
audioPrefsRevision = 0;
|
||||
private pruneIntervalId: number | null = null;
|
||||
private listeners = new Set<() => void>();
|
||||
@@ -42,12 +43,26 @@ class StreamAudioPrefs {
|
||||
}
|
||||
|
||||
private async initPersistence(): Promise<void> {
|
||||
await makePersistent(this, 'StreamAudioPrefs', ['entries']);
|
||||
await makePersistent(this, 'StreamAudioPrefs', ['entries', 'outputVolumeRecalibratedV1']);
|
||||
this.applyOutputVolumeRecalibration();
|
||||
if (this.hasAnyEntries()) {
|
||||
this.startPruneInterval();
|
||||
}
|
||||
}
|
||||
|
||||
private applyOutputVolumeRecalibration(): void {
|
||||
if (this.outputVolumeRecalibratedV1) {
|
||||
return;
|
||||
}
|
||||
this.entries = Object.fromEntries(
|
||||
Object.entries(this.entries).map(([streamKey, entry]) => [
|
||||
streamKey,
|
||||
{...entry, volume: recalibrateStoredVoiceVolumePercent(entry.volume)},
|
||||
]),
|
||||
);
|
||||
this.outputVolumeRecalibratedV1 = true;
|
||||
}
|
||||
|
||||
hasEntry(streamKey: string): boolean {
|
||||
return streamKey in this.entries;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
boostedVoiceVolumePercentToTrackVolume,
|
||||
clampVoiceTrackTotalGain,
|
||||
composeVoiceVolumeGain,
|
||||
recalibrateStoredVoiceVolumePercent,
|
||||
recalibrateStoredVoiceVolumes,
|
||||
VOICE_TRACK_MAX_TOTAL_GAIN,
|
||||
VOICE_VOLUME_MAX_GAIN,
|
||||
VOICE_VOLUME_MAX_PERCENT,
|
||||
@@ -70,3 +72,30 @@ describe('clampVoiceTrackTotalGain', () => {
|
||||
expect(clampVoiceTrackTotalGain(-1)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recalibrateStoredVoiceVolumePercent', () => {
|
||||
it('pulls every boosted stored value back to unity', () => {
|
||||
expect(recalibrateStoredVoiceVolumePercent(200)).toBe(100);
|
||||
expect(recalibrateStoredVoiceVolumePercent(150)).toBe(100);
|
||||
expect(recalibrateStoredVoiceVolumePercent(101)).toBe(100);
|
||||
});
|
||||
|
||||
it('leaves unity and quieter values untouched', () => {
|
||||
expect(recalibrateStoredVoiceVolumePercent(100)).toBe(100);
|
||||
expect(recalibrateStoredVoiceVolumePercent(40)).toBe(40);
|
||||
expect(recalibrateStoredVoiceVolumePercent(0)).toBe(0);
|
||||
});
|
||||
|
||||
it('keeps the same object when nothing needs recalibrating', () => {
|
||||
const volumes = {alice: 100, bob: 40};
|
||||
expect(recalibrateStoredVoiceVolumes(volumes)).toBe(volumes);
|
||||
});
|
||||
|
||||
it('caps only the boosted entries in a stored map', () => {
|
||||
expect(recalibrateStoredVoiceVolumes({alice: 200, bob: 60, carol: 150})).toEqual({
|
||||
alice: 100,
|
||||
bob: 60,
|
||||
carol: 100,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -37,6 +37,21 @@ export function composeVoiceVolumeGain(...volumePercents: Array<number>): number
|
||||
return volumePercents.reduce((gain, percent) => gain * boostedVoiceVolumePercentToTrackVolume(percent), 1);
|
||||
}
|
||||
|
||||
export function recalibrateStoredVoiceVolumePercent(value: number): number {
|
||||
if (!Number.isFinite(value) || value <= UNITY_GAIN_PERCENT) {
|
||||
return value;
|
||||
}
|
||||
return UNITY_GAIN_PERCENT;
|
||||
}
|
||||
|
||||
export function recalibrateStoredVoiceVolumes<T extends Record<string, number>>(volumes: T): T {
|
||||
const entries = Object.entries(volumes);
|
||||
if (!entries.some(([, value]) => recalibrateStoredVoiceVolumePercent(value) !== value)) {
|
||||
return volumes;
|
||||
}
|
||||
return Object.fromEntries(entries.map(([key, value]) => [key, recalibrateStoredVoiceVolumePercent(value)])) as T;
|
||||
}
|
||||
|
||||
export function clampVoiceTrackTotalGain(gain: number): number {
|
||||
if (!Number.isFinite(gain)) {
|
||||
return 1;
|
||||
|
||||
Reference in new issue
Block a user