Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions components/stage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '@/components/ui/alert-dialog';
import { AlertTriangle } from 'lucide-react';
import { VisuallyHidden } from 'radix-ui';
import { toast } from 'sonner';

/**
* Stage Component
Expand Down Expand Up @@ -528,6 +529,12 @@ export function Stage({
}, 1500);
}
},
onBrowserTTSMuted: () => {
toast.warning(t('playback.browserTtsMuted'), {
duration: 5000,
id: 'browser-tts-muted',
});
},
});

engineRef.current = engine;
Expand Down
6 changes: 6 additions & 0 deletions lib/i18n/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export const commonZhCN = {
exportSuccess: '导出成功',
exportFailed: '导出失败',
},
playback: {
browserTtsMuted: '当前浏览器处于静音状态,无法听到语音。请打开系统或浏览器音量。',
},
} as const;

export const commonEnUS = {
Expand Down Expand Up @@ -78,4 +81,7 @@ export const commonEnUS = {
exportSuccess: 'Export successful',
exportFailed: 'Export failed',
},
playback: {
browserTtsMuted: 'Browser is muted. Please unmute your system or browser to hear the audio.',
},
} as const;
12 changes: 12 additions & 0 deletions lib/playback/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,19 @@ export class PlaybackEngine {
utterance.lang = cjkRatio > CJK_LANG_THRESHOLD ? 'zh-CN' : 'en-US';
}

// Track start time to detect abnormally fast completion (likely due to mute)
const startTime = performance.now();
const MIN_NORMAL_DURATION = 200; // ms - anything faster is suspicious

utterance.onend = () => {
const duration = performance.now() - startTime;

// Detect abnormally fast completion - likely browser/system is muted
if (duration < MIN_NORMAL_DURATION && chunkText.trim().length > 0) {
log.warn(`Browser TTS chunk ended too quickly (${duration}ms) - possible mute state`);
this.callbacks.onBrowserTTSMuted?.();
}

this.browserTTSChunkIndex++;
if (this.mode === 'playing') {
this.playBrowserTTSChunk(); // next chunk
Expand Down
3 changes: 3 additions & 0 deletions lib/playback/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ export interface PlaybackEngineCallbacks {
getPlaybackSpeed?: () => number;

onComplete?: () => void;

/** Browser TTS is muted - prompt user to unmute */
onBrowserTTSMuted?: () => void;
}
Loading