fix: ensure audio is muted before recording state transition

This commit is contained in:
haritabh-z01 2026-01-17 13:23:17 +05:30
parent 395cf9fec6
commit 74764ac465
5 changed files with 29 additions and 10 deletions

View file

@ -167,7 +167,12 @@ export class RecordingManager extends EventEmitter {
public async onPTTRelease() {
// Hands-free mode ignores PTT release
if (this.recordingMode !== "ptt") return;
if (this.recordingState !== "recording") return;
if (
this.recordingState !== "recording" &&
this.recordingState !== "starting"
) {
return;
}
if (this.isQuickAction()) {
// Quick release - wait for potential double-tap before cancelling
@ -201,7 +206,10 @@ export class RecordingManager extends EventEmitter {
}
// Already recording
if (this.recordingState === "recording") {
if (
this.recordingState === "recording" ||
this.recordingState === "starting"
) {
// In PTT mode? Switch to hands-free
if (this.recordingMode === "ptt") {
logger.audio.info("Toggle in PTT mode, switching to hands-free");
@ -241,7 +249,7 @@ export class RecordingManager extends EventEmitter {
const startTime = performance.now();
logger.audio.info("RecordingManager: doStart called", { mode });
// Sync state broadcast
// Sync state broadcast - stay in "starting" until init completes
this.setState("starting");
this.setMode(mode);
this.terminationCode = null;
@ -252,14 +260,16 @@ export class RecordingManager extends EventEmitter {
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
this.currentSessionId = `session-${timestamp}`;
this.setState("recording");
this.startNoAudioTimer();
// Async init inside mutex
// Initialize session (includes muting) BEFORE changing to "recording"
// This ensures audio is muted before renderer starts capturing
this.initPromise = this.initializeSession();
await this.initPromise;
// Now safe to start recording - audio is muted
this.setState("recording");
this.startNoAudioTimer();
const totalDuration = performance.now() - startTime;
logger.audio.info("Recording started", {
sessionId: this.currentSessionId,
@ -297,7 +307,10 @@ export class RecordingManager extends EventEmitter {
code: TerminationCode | null = null,
): Promise<void> {
await this.lifecycleMutex.runExclusive(async () => {
if (this.recordingState !== "recording") {
if (
this.recordingState !== "recording" &&
this.recordingState !== "starting"
) {
logger.audio.warn("Cannot end recording - not recording", {
currentState: this.recordingState,
});
@ -745,7 +758,10 @@ export class RecordingManager extends EventEmitter {
* Signal to stop recording (called from tRPC)
*/
public async signalStop(): Promise<void> {
if (this.recordingState === "recording") {
if (
this.recordingState === "recording" ||
this.recordingState === "starting"
) {
await this.endRecording();
}
}
@ -755,7 +771,10 @@ export class RecordingManager extends EventEmitter {
this.clearTimers();
// Stop recording if active
if (this.recordingState === "recording") {
if (
this.recordingState === "recording" ||
this.recordingState === "starting"
) {
await this.endRecording();
}