FormaLau commited on
Commit
874ece0
·
1 Parent(s): 3ba9b54

Stabilize Reachy speech voice

Browse files
app/src/game/emotions/emotionController.js CHANGED
@@ -3,30 +3,40 @@ import { EMOTIONS, loadReachyEmotion } from "./emotions.js";
3
  import { playUrl } from "../audio.js";
4
  import { signed } from "../signed.js";
5
 
 
 
6
  export function reachySay(text, reachy = null) {
7
  try { globalThis.dispatchEvent?.(new CustomEvent("reachy-speech-start", { detail: { text: String(text) } })); } catch { /* recording hook is optional */ }
8
  if (!globalThis.speechSynthesis || !globalThis.SpeechSynthesisUtterance) return Promise.resolve();
9
  reachy?.setWobbler?.(true, "speech");
10
  return new Promise((resolve) => {
11
- const utterance = new SpeechSynthesisUtterance(text);
12
- utterance.lang = "en-US";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  const voices = speechSynthesis.getVoices?.() ?? [];
14
- // Prefer a stable, traditionally masculine English system voice. Voice
15
- // names vary by browser/OS, so keep a small ordered set of known names
16
- // and fall back to any English voice when none is installed.
17
- const maleHints = /david|mark|guy|daniel|alex|arthur|thomas|george|ryan|google uk english male|microsoft (david|mark|ryan)/i;
18
- const voice = voices.find((candidate) => maleHints.test(candidate.name) && /^en[-_]gb/i.test(candidate.lang))
19
- ?? voices.find((candidate) => maleHints.test(candidate.name) && /^en[-_]/i.test(candidate.lang))
20
- ?? voices.find((candidate) => /^en[-_]gb/i.test(candidate.lang))
21
- ?? voices.find((candidate) => /^en[-_]/i.test(candidate.lang));
22
- if (voice) utterance.voice = voice;
23
- utterance.pitch = 1.12;
24
- utterance.rate = 0.92;
25
- const done = () => { reachy?.setWobbler?.(false, "speech"); resolve(); };
26
- utterance.onend = done;
27
- utterance.onerror = done;
28
- speechSynthesis.cancel();
29
- speechSynthesis.speak(utterance);
30
  });
31
  }
32
 
 
3
  import { playUrl } from "../audio.js";
4
  import { signed } from "../signed.js";
5
 
6
+ let selectedReachyVoice = null;
7
+
8
  export function reachySay(text, reachy = null) {
9
  try { globalThis.dispatchEvent?.(new CustomEvent("reachy-speech-start", { detail: { text: String(text) } })); } catch { /* recording hook is optional */ }
10
  if (!globalThis.speechSynthesis || !globalThis.SpeechSynthesisUtterance) return Promise.resolve();
11
  reachy?.setWobbler?.(true, "speech");
12
  return new Promise((resolve) => {
13
+ let spoken = false;
14
+ const speak = () => {
15
+ if (spoken) return;
16
+ spoken = true;
17
+ const utterance = new SpeechSynthesisUtterance(text);
18
+ utterance.lang = "en-US";
19
+ const voices = speechSynthesis.getVoices?.() ?? [];
20
+ const maleHints = /david|mark|guy|daniel|alex|arthur|thomas|george|ryan|google uk english male|microsoft (david|mark|ryan)/i;
21
+ const voice = selectedReachyVoice ?? voices.find((candidate) => maleHints.test(candidate.name) && /^en[-_]gb/i.test(candidate.lang))
22
+ ?? voices.find((candidate) => maleHints.test(candidate.name) && /^en[-_]/i.test(candidate.lang))
23
+ ?? voices.find((candidate) => /^en[-_]gb/i.test(candidate.lang))
24
+ ?? voices.find((candidate) => /^en[-_]/i.test(candidate.lang));
25
+ if (voice) { selectedReachyVoice = voice; utterance.voice = selectedReachyVoice; }
26
+ utterance.pitch = 0.72;
27
+ utterance.rate = 0.92;
28
+ const done = () => { reachy?.setWobbler?.(false, "speech"); resolve(); };
29
+ utterance.onend = done;
30
+ utterance.onerror = done;
31
+ speechSynthesis.cancel();
32
+ speechSynthesis.speak(utterance);
33
+ };
34
  const voices = speechSynthesis.getVoices?.() ?? [];
35
+ if (voices.length) speak();
36
+ else {
37
+ speechSynthesis.addEventListener?.("voiceschanged", speak, { once: true });
38
+ setTimeout(speak, 250);
39
+ }
 
 
 
 
 
 
 
 
 
 
 
40
  });
41
  }
42