Skip to content

Commit bc35110

Browse files
authored
Merge pull request #232 from scribear/anish_updated
Updated mic no audio detection/flags
2 parents a91ea64 + 91409a6 commit bc35110

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

src/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ function App() {
1616

1717
const scribearStatus = useSelector((state: RootState) => state.APIStatusReducer?.scribearServerStatus as number);
1818
const scribearMessage = useSelector((state: RootState) => (state.APIStatusReducer as any)?.scribearServerMessage as string | undefined);
19+
const listening = useSelector((state: RootState) => (state as any).ControlReducer?.listening === true);
20+
const micNoAudio = useSelector((state: RootState) => (state as any).ControlReducer?.micNoAudio === true);
1921

2022
const [snackbarOpen, setSnackbarOpen] = useState(false);
2123
const [snackbarMsg, setSnackbarMsg] = useState('');
@@ -39,6 +41,15 @@ function App() {
3941
}
4042
}, [scribearStatus]);
4143

44+
useEffect(() => {
45+
// Show a snackbar if we expect mic audio but none is coming through
46+
if (listening && micNoAudio) {
47+
setSnackbarMsg('No microphone audio detected. Please check permissions, input device, or mic level.');
48+
setSnackbarSeverity('warning');
49+
setSnackbarOpen(true);
50+
}
51+
}, [listening, micNoAudio]);
52+
4253
const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => {
4354
if (reason === 'clickaway') return;
4455
setSnackbarOpen(false);

src/components/api/scribearServer/scribearRecognizer.tsx

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export class ScribearRecognizer implements Recognizer {
3131
private language: string
3232
private recorder?: RecordRTC;
3333
private kSampleRate = 16000;
34+
private lastAudioTimestamp: number | null = null;
35+
private inactivityInterval: any = null;
3436

3537
urlParams = new URLSearchParams(window.location.search);
3638
mode = this.urlParams.get('mode');
@@ -50,21 +52,75 @@ export class ScribearRecognizer implements Recognizer {
5052
}
5153

5254
private async _startRecording() {
53-
let mic_stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
55+
let mic_stream: MediaStream;
56+
try {
57+
mic_stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
58+
} catch (e) {
59+
console.error('Failed to access microphone', e);
60+
try {
61+
store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: true });
62+
} catch (dispatchErr) {
63+
console.error('Failed to dispatch SET_MIC_INACTIVITY after mic access error', dispatchErr);
64+
}
65+
// Surface an API status error to prompt UI; recognizer encapsulates flag setting here
66+
try {
67+
store.dispatch({ type: 'CHANGE_API_STATUS', payload: { scribearServerStatus: STATUS.ERROR, scribearServerMessage: 'Microphone permission denied or unavailable' } });
68+
} catch (dispatchErr) {
69+
console.error('Failed to dispatch CHANGE_API_STATUS after mic access error', dispatchErr);
70+
}
71+
throw e;
72+
}
5473

5574
this.recorder = new RecordRTC(mic_stream, {
5675
type: 'audio',
5776
mimeType: 'audio/wav',
5877
desiredSampRate: this.kSampleRate,
5978
timeSlice: 50,
6079
ondataavailable: async (blob: Blob) => {
80+
// update last audio timestamp and mark that we've received at least one audio chunk
81+
this.lastAudioTimestamp = performance.now();
82+
try {
83+
const controlState = (store.getState() as any).ControlReducer;
84+
if (controlState?.micNoAudio === true) {
85+
store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: false });
86+
}
87+
} catch (e) {
88+
console.warn('Failed to clear mic inactivity', e);
89+
}
6190
this.socket?.send(blob);
6291
},
6392
recorderType: StereoAudioRecorder,
6493
numberOfAudioChannels: 1,
6594
});
6695

6796
this.recorder.startRecording();
97+
98+
// start inactivity monitor
99+
const thresholdMs = 3000;
100+
if (this.inactivityInterval == null) {
101+
this.inactivityInterval = setInterval(() => {
102+
try {
103+
const state: any = store.getState();
104+
const listening = state.ControlReducer?.listening === true;
105+
const micNoAudio = state.ControlReducer?.micNoAudio === true;
106+
if (listening) {
107+
if (!this.lastAudioTimestamp || (Date.now() - this.lastAudioTimestamp > thresholdMs)) {
108+
if (!micNoAudio) {
109+
store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: true });
110+
}
111+
} else {
112+
if (micNoAudio) {
113+
store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: false });
114+
}
115+
}
116+
} else {
117+
if (micNoAudio) store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: false });
118+
}
119+
} catch (e) {
120+
console.warn('Error in mic inactivity interval', e);
121+
}
122+
}, 1000);
123+
}
68124
}
69125

70126
/**
@@ -179,6 +235,15 @@ export class ScribearRecognizer implements Recognizer {
179235
if (!this.socket) { return; }
180236
this.socket.close();
181237
this.socket = null;
238+
if (this.inactivityInterval) {
239+
clearInterval(this.inactivityInterval);
240+
this.inactivityInterval = null;
241+
}
242+
try {
243+
store.dispatch({ type: 'SET_MIC_INACTIVITY', payload: false });
244+
} catch (e) {
245+
console.warn('Failed to clear mic inactivity on stop', e);
246+
}
182247
}
183248

184249
/**

0 commit comments

Comments
 (0)