audio: live mic gain preview with temporary loopback during edit
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// Maintainer-controlled web client version.
|
// Maintainer-controlled web client version.
|
||||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||||
window.CHGRID_WEB_VERSION = "2026.02.22 R158";
|
window.CHGRID_WEB_VERSION = "2026.02.22 R159";
|
||||||
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
||||||
window.CHGRID_TIME_ZONE = "America/Detroit";
|
window.CHGRID_TIME_ZONE = "America/Detroit";
|
||||||
|
|||||||
@@ -204,6 +204,18 @@ export class AudioEngine {
|
|||||||
return this.loopbackEnabled;
|
return this.loopbackEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns current loopback monitor state. */
|
||||||
|
isLoopbackEnabled(): boolean {
|
||||||
|
return this.loopbackEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets loopback monitor state directly. */
|
||||||
|
setLoopbackEnabled(enabled: boolean): boolean {
|
||||||
|
this.loopbackEnabled = enabled;
|
||||||
|
this.rebuildOutboundEffectGraph();
|
||||||
|
return this.loopbackEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
async attachRemoteStream(
|
async attachRemoteStream(
|
||||||
peer: SpatialPeerRuntime,
|
peer: SpatialPeerRuntime,
|
||||||
stream: MediaStream,
|
stream: MediaStream,
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ const itemEmitRuntime = new ItemEmitRuntime(audio, resolveIncomingSoundUrl, getI
|
|||||||
let internalClipboardText = '';
|
let internalClipboardText = '';
|
||||||
let replaceTextOnNextType = false;
|
let replaceTextOnNextType = false;
|
||||||
let pendingEscapeDisconnect = false;
|
let pendingEscapeDisconnect = false;
|
||||||
|
let micGainLoopbackRestoreState: boolean | null = null;
|
||||||
let helpViewerLines: string[] = [];
|
let helpViewerLines: string[] = [];
|
||||||
let helpViewerIndex = 0;
|
let helpViewerIndex = 0;
|
||||||
let audioLayers: AudioLayerState = {
|
let audioLayers: AudioLayerState = {
|
||||||
@@ -1026,6 +1027,15 @@ function describeMediaError(error: unknown): string {
|
|||||||
return mediaSession.describeMediaError(error);
|
return mediaSession.describeMediaError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Restores loopback state captured when entering microphone gain edit mode. */
|
||||||
|
function restoreLoopbackAfterMicGainEdit(): void {
|
||||||
|
if (micGainLoopbackRestoreState === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
audio.setLoopbackEnabled(micGainLoopbackRestoreState);
|
||||||
|
micGainLoopbackRestoreState = null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Builds dependencies shared by connect/disconnect flow helpers. */
|
/** Builds dependencies shared by connect/disconnect flow helpers. */
|
||||||
function getConnectionFlowDeps(): ConnectFlowDeps {
|
function getConnectionFlowDeps(): ConnectFlowDeps {
|
||||||
return {
|
return {
|
||||||
@@ -1066,6 +1076,7 @@ async function connect(): Promise<void> {
|
|||||||
function disconnect(): void {
|
function disconnect(): void {
|
||||||
runDisconnectFlow(getConnectionFlowDeps());
|
runDisconnectFlow(getConnectionFlowDeps());
|
||||||
pendingEscapeDisconnect = false;
|
pendingEscapeDisconnect = false;
|
||||||
|
restoreLoopbackAfterMicGainEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMessage = createOnMessageHandler({
|
const onMessage = createOnMessageHandler({
|
||||||
@@ -1199,6 +1210,8 @@ function handleNormalModeInput(code: string, shiftKey: boolean): void {
|
|||||||
state.nicknameInput = formatSteppedNumber(audio.getOutboundInputGain(), MIC_INPUT_GAIN_STEP);
|
state.nicknameInput = formatSteppedNumber(audio.getOutboundInputGain(), MIC_INPUT_GAIN_STEP);
|
||||||
state.cursorPos = state.nicknameInput.length;
|
state.cursorPos = state.nicknameInput.length;
|
||||||
replaceTextOnNextType = true;
|
replaceTextOnNextType = true;
|
||||||
|
micGainLoopbackRestoreState = audio.isLoopbackEnabled();
|
||||||
|
audio.setLoopbackEnabled(true);
|
||||||
updateStatus(`Set microphone gain: ${state.nicknameInput}`);
|
updateStatus(`Set microphone gain: ${state.nicknameInput}`);
|
||||||
audio.sfxUiBlip();
|
audio.sfxUiBlip();
|
||||||
return;
|
return;
|
||||||
@@ -1522,6 +1535,7 @@ function handleMicGainEditModeInput(code: string, key: string, ctrlKey: boolean)
|
|||||||
state.nicknameInput = formatSteppedNumber(next, MIC_INPUT_GAIN_STEP);
|
state.nicknameInput = formatSteppedNumber(next, MIC_INPUT_GAIN_STEP);
|
||||||
state.cursorPos = state.nicknameInput.length;
|
state.cursorPos = state.nicknameInput.length;
|
||||||
replaceTextOnNextType = false;
|
replaceTextOnNextType = false;
|
||||||
|
audio.setOutboundInputGain(next);
|
||||||
updateStatus(state.nicknameInput);
|
updateStatus(state.nicknameInput);
|
||||||
if (Math.abs(next - base) < 1e-9 || Math.abs(next - attempted) > 1e-9) {
|
if (Math.abs(next - base) < 1e-9 || Math.abs(next - attempted) > 1e-9) {
|
||||||
audio.sfxUiCancel();
|
audio.sfxUiCancel();
|
||||||
@@ -1549,6 +1563,7 @@ function handleMicGainEditModeInput(code: string, key: string, ctrlKey: boolean)
|
|||||||
persistMicInputGain(applied);
|
persistMicInputGain(applied);
|
||||||
state.mode = 'normal';
|
state.mode = 'normal';
|
||||||
replaceTextOnNextType = false;
|
replaceTextOnNextType = false;
|
||||||
|
restoreLoopbackAfterMicGainEdit();
|
||||||
updateStatus(`Microphone gain set to ${formatSteppedNumber(applied, MIC_INPUT_GAIN_STEP)}.`);
|
updateStatus(`Microphone gain set to ${formatSteppedNumber(applied, MIC_INPUT_GAIN_STEP)}.`);
|
||||||
audio.sfxUiConfirm();
|
audio.sfxUiConfirm();
|
||||||
return;
|
return;
|
||||||
@@ -1557,6 +1572,7 @@ function handleMicGainEditModeInput(code: string, key: string, ctrlKey: boolean)
|
|||||||
if (editAction === 'cancel') {
|
if (editAction === 'cancel') {
|
||||||
state.mode = 'normal';
|
state.mode = 'normal';
|
||||||
replaceTextOnNextType = false;
|
replaceTextOnNextType = false;
|
||||||
|
restoreLoopbackAfterMicGainEdit();
|
||||||
updateStatus('Cancelled.');
|
updateStatus('Cancelled.');
|
||||||
audio.sfxUiCancel();
|
audio.sfxUiCancel();
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user