Spaces:
Running
Running
Update ER_MAP/dashboard.py
Browse filesadded female voice to nurse by hardcoding it because ElevenLabs tokens were exhausting in 1-2 episodes and browser TTS was using same voices for all agents
- ER_MAP/dashboard.py +30 -2
ER_MAP/dashboard.py
CHANGED
|
@@ -1084,6 +1084,30 @@ HTML_PAGE = r"""<!DOCTYPE html>
|
|
| 1084 |
if (logEndRef.current) logEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
| 1085 |
}, [conversationLog]);
|
| 1086 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1087 |
// ---------------- Audio queue (drives card activation) ----------------
|
| 1088 |
const processQueue = useCallback(async () => {
|
| 1089 |
if (isPlayingRef.current || audioQueueRef.current.length === 0) return;
|
|
@@ -1102,8 +1126,10 @@ HTML_PAGE = r"""<!DOCTYPE html>
|
|
| 1102 |
if (synth) {
|
| 1103 |
const utter = new SpeechSynthesisUtterance(item.text);
|
| 1104 |
utter.rate = 1.05;
|
| 1105 |
-
utter.pitch = item.agent === 'patient' ? 0.9 : item.agent === 'nurse' ? 1.
|
| 1106 |
utter.volume = 1.0;
|
|
|
|
|
|
|
| 1107 |
setActiveAgent(item.agent);
|
| 1108 |
utter.onend = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
| 1109 |
utter.onerror = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
|
@@ -1146,8 +1172,10 @@ HTML_PAGE = r"""<!DOCTYPE html>
|
|
| 1146 |
if (synth) {
|
| 1147 |
const utter = new SpeechSynthesisUtterance(item.text);
|
| 1148 |
utter.rate = 1.05;
|
| 1149 |
-
utter.pitch = item.agent === 'patient' ? 0.9 : item.agent === 'nurse' ? 1.
|
| 1150 |
utter.volume = 1.0;
|
|
|
|
|
|
|
| 1151 |
setActiveAgent(item.agent);
|
| 1152 |
utter.onend = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
| 1153 |
utter.onerror = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
|
|
|
| 1084 |
if (logEndRef.current) logEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
| 1085 |
}, [conversationLog]);
|
| 1086 |
|
| 1087 |
+
// ---- Browser TTS voice picker (female for nurse/patient, male for doctor) ----
|
| 1088 |
+
const pickVoice = useCallback((agent) => {
|
| 1089 |
+
const synth = window.speechSynthesis;
|
| 1090 |
+
if (!synth) return null;
|
| 1091 |
+
const voices = synth.getVoices();
|
| 1092 |
+
if (!voices.length) return null;
|
| 1093 |
+
const wantFemale = (agent === 'nurse' || agent === 'patient');
|
| 1094 |
+
const femaleHints = ['female', 'zira', 'samantha', 'karen', 'victoria', 'fiona', 'google uk english female', 'woman'];
|
| 1095 |
+
const maleHints = ['male', 'david', 'mark', 'james', 'daniel', 'google uk english male', 'man'];
|
| 1096 |
+
const hints = wantFemale ? femaleHints : maleHints;
|
| 1097 |
+
let match = voices.find(v => hints.some(h => v.name.toLowerCase().includes(h)));
|
| 1098 |
+
if (!match && wantFemale) match = voices.find(v => /female/i.test(v.name));
|
| 1099 |
+
if (!match && !wantFemale) match = voices.find(v => /male/i.test(v.name) && !/female/i.test(v.name));
|
| 1100 |
+
return match || null;
|
| 1101 |
+
}, []);
|
| 1102 |
+
|
| 1103 |
+
// Preload voices (Chrome loads them async)
|
| 1104 |
+
useEffect(() => {
|
| 1105 |
+
if (window.speechSynthesis) {
|
| 1106 |
+
window.speechSynthesis.getVoices();
|
| 1107 |
+
window.speechSynthesis.onvoiceschanged = () => window.speechSynthesis.getVoices();
|
| 1108 |
+
}
|
| 1109 |
+
}, []);
|
| 1110 |
+
|
| 1111 |
// ---------------- Audio queue (drives card activation) ----------------
|
| 1112 |
const processQueue = useCallback(async () => {
|
| 1113 |
if (isPlayingRef.current || audioQueueRef.current.length === 0) return;
|
|
|
|
| 1126 |
if (synth) {
|
| 1127 |
const utter = new SpeechSynthesisUtterance(item.text);
|
| 1128 |
utter.rate = 1.05;
|
| 1129 |
+
utter.pitch = item.agent === 'patient' ? 0.9 : item.agent === 'nurse' ? 1.15 : 1.0;
|
| 1130 |
utter.volume = 1.0;
|
| 1131 |
+
const voice = pickVoice(item.agent);
|
| 1132 |
+
if (voice) utter.voice = voice;
|
| 1133 |
setActiveAgent(item.agent);
|
| 1134 |
utter.onend = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
| 1135 |
utter.onerror = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
|
|
|
| 1172 |
if (synth) {
|
| 1173 |
const utter = new SpeechSynthesisUtterance(item.text);
|
| 1174 |
utter.rate = 1.05;
|
| 1175 |
+
utter.pitch = item.agent === 'patient' ? 0.9 : item.agent === 'nurse' ? 1.15 : 1.0;
|
| 1176 |
utter.volume = 1.0;
|
| 1177 |
+
const voice = pickVoice(item.agent);
|
| 1178 |
+
if (voice) utter.voice = voice;
|
| 1179 |
setActiveAgent(item.agent);
|
| 1180 |
utter.onend = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|
| 1181 |
utter.onerror = () => { setActiveAgent(null); isPlayingRef.current = false; processQueue(); };
|