mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(voice): stop stereo microphones publishing as mono (#2846)
This commit is contained in:
@@ -12,7 +12,6 @@ export const publishDefaults: TrackPublishDefaults = {
|
||||
audioPreset: AudioPresets.music,
|
||||
dtx: false,
|
||||
red: true,
|
||||
forceStereo: false,
|
||||
simulcast: true,
|
||||
screenShareEncoding: ScreenSharePresets.original.encoding,
|
||||
stopMicTrackOnMute: false,
|
||||
|
||||
@@ -622,7 +622,7 @@ export class VoiceEngineV2AppMediaExecutionAdapter extends Store {
|
||||
const guild = channel?.guildId ? Guilds.getGuild(channel.guildId) : null;
|
||||
const channelBitrate = resolveVoiceChannelBitrate(channel?.bitrate, guild?.features);
|
||||
const profile = this.resolveActiveMicrophoneProfile();
|
||||
return buildMicrophonePublishOptions(channelBitrate, profile.mode, profile.stereoCapture);
|
||||
return buildMicrophonePublishOptions(channelBitrate, profile.stereoCapture);
|
||||
}
|
||||
|
||||
async refreshMicrophonePublishSettings(room: Room | null, channelId: string | null): Promise<void> {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {VoiceTrackSource} from '@app/features/voice/engine/VoiceTrackSource';
|
||||
import type {VoiceProcessingMode} from '@app/features/voice/utils/VoiceProcessingProfile';
|
||||
import type {TrackPublishOptions} from 'livekit-client';
|
||||
|
||||
export const OPUS_MAX_AUDIO_BITRATE_BPS = 510000;
|
||||
export const VOICE_CHANNEL_MIN_AUDIO_BITRATE_BPS = 8000;
|
||||
export const STEREO_VOICE_MIN_AUDIO_BITRATE_BPS = 128000;
|
||||
export const STEREO_VOICE_MIN_AUDIO_BITRATE_BPS = 64000;
|
||||
|
||||
export function normaliseAudioBitrateBps(value: number | null | undefined): number | undefined {
|
||||
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) return undefined;
|
||||
@@ -17,27 +16,18 @@ export function normaliseAudioBitrateBps(value: number | null | undefined): numb
|
||||
|
||||
export function buildMicrophonePublishOptions(
|
||||
channelBitrate: number | null | undefined,
|
||||
processingMode: VoiceProcessingMode,
|
||||
stereoCapture = false,
|
||||
): TrackPublishOptions | undefined {
|
||||
const maxBitrate = normaliseAudioBitrateBps(channelBitrate);
|
||||
if (!maxBitrate) return undefined;
|
||||
const studioMode = processingMode === 'studio';
|
||||
const bitrateAllowsStereo = maxBitrate >= STEREO_VOICE_MIN_AUDIO_BITRATE_BPS;
|
||||
return {
|
||||
audioPreset: {
|
||||
maxBitrate,
|
||||
priority: 'high',
|
||||
},
|
||||
red: true,
|
||||
...(studioMode
|
||||
? {
|
||||
dtx: false,
|
||||
forceStereo: bitrateAllowsStereo,
|
||||
}
|
||||
: stereoCapture && !bitrateAllowsStereo
|
||||
? {forceStereo: false}
|
||||
: {}),
|
||||
red: true,
|
||||
forceStereo: stereoCapture && maxBitrate >= STEREO_VOICE_MIN_AUDIO_BITRATE_BPS,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
} from '@app/features/voice/utils/noise_suppression/NoiseSuppressionBackends';
|
||||
import {readEffectiveNoiseSuppression} from '@app/features/voice/utils/noise_suppression/NoiseSuppressionRuntime';
|
||||
import {readNoiseSuppressionRuntimeCapabilities} from '@app/features/voice/utils/noise_suppression/NoiseSuppressionSelection';
|
||||
import {legacyNoiseSuppressionBackend} from '@app/features/voice/utils/VoiceProcessingProfile';
|
||||
import {
|
||||
legacyNoiseSuppressionBackend,
|
||||
resolveVoiceProcessingFromState,
|
||||
} from '@app/features/voice/utils/VoiceProcessingProfile';
|
||||
|
||||
export const NOISE_SUPPRESSION_UI_SAMPLE_RATE = 48000;
|
||||
|
||||
@@ -67,8 +70,8 @@ export function isStereoMicrophoneChoiceAvailable(): boolean {
|
||||
const assignment = VoiceNoiseSuppressionRollout.assignment;
|
||||
if (!assignment.enabled || !assignment.stereo_enabled) return false;
|
||||
const effective = readEffectiveNoiseSuppression(NOISE_SUPPRESSION_UI_SAMPLE_RATE);
|
||||
if (!effective.rolloutApplied || effective.backend == null) return false;
|
||||
return getNoiseSuppressionBackendDescriptor(effective.backend).preservesInputChannels;
|
||||
const backend = effective.backend ?? resolveVoiceProcessingFromState(VoiceSettings).noiseSuppressionBackend;
|
||||
return getNoiseSuppressionBackendDescriptor(backend).preservesInputChannels;
|
||||
}
|
||||
|
||||
export function isStereoMicrophoneEnabled(): boolean {
|
||||
|
||||
@@ -44,7 +44,8 @@ export function resolveEffectiveNoiseSuppression(
|
||||
): EffectiveNoiseSuppression {
|
||||
const preference = isVoiceNoiseSuppressionBackend(userPreference) ? userPreference : null;
|
||||
const resolution = resolveVoiceNoiseSuppressionForCall(assignment, guildId, preference);
|
||||
if (resolution == null) return INERT_EFFECTIVE_NOISE_SUPPRESSION;
|
||||
const stereoAllowed = assignment.enabled && assignment.stereo_enabled && stereoPreference !== false;
|
||||
if (resolution == null) return {...INERT_EFFECTIVE_NOISE_SUPPRESSION, stereoEnabled: stereoAllowed};
|
||||
const backend = selectUsableNoiseSuppressionBackend(resolution.backend, capabilities);
|
||||
return {
|
||||
rolloutApplied: true,
|
||||
@@ -53,9 +54,7 @@ export function resolveEffectiveNoiseSuppression(
|
||||
source: resolution.source,
|
||||
suppressionStrength: resolution.suppressionStrength,
|
||||
stereoEnabled:
|
||||
resolution.stereoEnabled &&
|
||||
stereoPreference !== false &&
|
||||
getNoiseSuppressionBackendDescriptor(backend).preservesInputChannels,
|
||||
stereoAllowed && resolution.stereoEnabled && getNoiseSuppressionBackendDescriptor(backend).preservesInputChannels,
|
||||
configVersion: resolution.configVersion,
|
||||
};
|
||||
}
|
||||
@@ -78,12 +77,20 @@ export function getNoiseSuppressionScopeGuildId(): string | null {
|
||||
return activeScopeGuildId;
|
||||
}
|
||||
|
||||
export function resolveStereoCapture(
|
||||
effective: EffectiveNoiseSuppression,
|
||||
backend: VoiceNoiseSuppressionBackend,
|
||||
): boolean {
|
||||
return effective.stereoEnabled && getNoiseSuppressionBackendDescriptor(backend).preservesInputChannels;
|
||||
}
|
||||
|
||||
export function applyNoiseSuppressionOverride(
|
||||
profile: ResolvedVoiceProcessing,
|
||||
effective: EffectiveNoiseSuppression,
|
||||
): ResolvedVoiceProcessing {
|
||||
if (!effective.rolloutApplied || effective.backend == null) return profile;
|
||||
if (profile.mode === 'studio') return profile;
|
||||
if (!effective.rolloutApplied || effective.backend == null || profile.mode === 'studio') {
|
||||
return {...profile, stereoCapture: resolveStereoCapture(effective, profile.noiseSuppressionBackend)};
|
||||
}
|
||||
const backend = effective.backend;
|
||||
const descriptor = getNoiseSuppressionBackendDescriptor(backend);
|
||||
return {
|
||||
@@ -92,6 +99,6 @@ export function applyNoiseSuppressionOverride(
|
||||
deepFilter: backend === 'deep_filter',
|
||||
deepFilterNoiseReductionLevel: backend === 'deep_filter' ? effective.suppressionStrength : 0,
|
||||
noiseSuppressionBackend: backend,
|
||||
stereoCapture: effective.stereoEnabled,
|
||||
stereoCapture: resolveStereoCapture(effective, backend),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user