google tts defaults!

This commit is contained in:
legop3
2026-06-21 16:44:32 -04:00
parent 9cf71e8df3
commit 49bd8acf7b
8 changed files with 67 additions and 28 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -78,7 +78,7 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-Cp0GiTXH.js"></script>
<script type="module" crossorigin src="/assets/index-CZg2vwtM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CflZqP0e.css">
</head>
<body>
+4 -1
View File
@@ -40,7 +40,10 @@ function createHandlers({ sendSystemMessage }) {
playTypingNote(roverId, TYPING_SEND_NOTE, socket?.id);
if (isPrivateClosedRoverId(message.roverId)) {
const forcedTts = ttsOptions || { speak: true, engine: 'flite' };
// Private-closed chat does not broadcast the text, so TTS is the only
// delivery path. Use the same Google speech default as normal chat when
// the sender did not provide explicit TTS settings.
const forcedTts = ttsOptions || { speak: true, engine: 'chromegtts' };
maybeSpeak(socket, message, forcedTts);
cb({ success: true, privateOnly: true });
return;
@@ -36,7 +36,11 @@ function normalizeTtsOptions(raw = {}) {
const speak = raw.speak !== false;
if (!speak) return null;
const rawEngine = typeof raw.engine === 'string' ? raw.engine.toLowerCase() : '';
const engine = rawEngine === 'espeak' ? 'espeak' : rawEngine === 'chromegtts' ? 'chromegtts' : 'flite';
// Chat payloads come from browsers and bridged command paths, so this is the
// chat-specific copy of the server TTS default. Unknown or missing engines
// become Google speech here instead of falling through to flite before the
// shared command service gets a chance to apply its own default.
const engine = rawEngine === 'espeak' ? 'espeak' : rawEngine === 'flite' ? 'flite' : 'chromegtts';
const voice = typeof raw.voice === 'string' ? raw.voice.trim() : undefined;
let pitch = Number.isFinite(raw.pitch) ? raw.pitch : undefined;
let speed = Number.isFinite(raw.speed) ? raw.speed : undefined;
+24 -3
View File
@@ -13,16 +13,37 @@ const pendingCommands = new Map(); // id -> { roverId }
const lastDriveActivity = new Map(); // roverId -> { ts, socketId, direction, speed, isAdmin }
const driveCooldowns = new Map(); // roverId -> blockedUntil
function normalizeOutboundCommandPayload(payload = {}) {
if (payload?.type !== 'tts') return payload;
const tts = payload.tts && typeof payload.tts === 'object' ? payload.tts : {};
const configuredEngine = typeof tts.engine === 'string' ? tts.engine.trim() : '';
if (configuredEngine) return payload;
// The server owns the default for commands it sends. Roverd still keeps its
// own local default for payloads that omit an engine from some other source,
// but normal server-issued TTS should prefer Google speech without requiring
// every caller to remember that policy.
return {
...payload,
tts: {
...tts,
engine: 'chromegtts',
},
};
}
function issueCommand(roverId, payload) {
const record = roverManager.rovers.get(roverId);
if (!record || !record.ws) {
throw new Error('Rover offline');
}
const id = uuidv4();
const message = { ...payload, id };
const normalizedPayload = normalizeOutboundCommandPayload(payload);
const message = { ...normalizedPayload, id };
record.ws.send(JSON.stringify(message));
pendingCommands.set(id, { roverId, ts: Date.now(), type: payload.type });
logger.info('Issued command', roverId, payload.type, id);
pendingCommands.set(id, { roverId, ts: Date.now(), type: normalizedPayload.type });
logger.info('Issued command', roverId, normalizedPayload.type, id);
return id;
}
+6 -7
View File
@@ -1,10 +1,9 @@
1. implement multitabbing prevention using the identity system
2. make google tts the default everywhere but roverd
3. fix rover request spam queue cheat
4. make alert feed.jsx show more alerts at once
5. unify typing row and chat row, should be simple
6. fix google TTS speeds
7. fix this:
1. make google tts the default everywhere but roverd
2. fix rover request spam queue cheat
3. make alert feed.jsx show more alerts at once
4. unify typing row and chat row, should be simple
5. fix google TTS speeds
6. fix this:
`Jun 18 15:14:18 roombaserver.local node[216731]: /home/daniel/MultiRoombaRover/server/src/services/roverManager/socketHandlers.js:92
Jun 18 15:14:18 roombaserver.local node[216731]: cb({ error: err.message });
Jun 18 15:14:18 roombaserver.local node[216731]: ^
+13 -4
View File
@@ -16,15 +16,24 @@ const CHROME_TTS_VOICES = ['sfg', 'iob', 'iog', 'iol', 'iom', 'tpc', 'tpd', 'tpf
const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10);
const GOOGLE_TTS_VALUES = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0];
const DEFAULT_GOOGLE_TTS_VALUE = 1.0;
const DEFAULT_GOOGLE_TTS_VOICE = 'tpf';
const TTS_SETTINGS_DEFAULTS = {
engine: 'flite',
voice: 'rms',
engine: 'chromegtts',
voice: DEFAULT_GOOGLE_TTS_VOICE,
pitch: 50,
googlePitch: DEFAULT_GOOGLE_TTS_VALUE,
googleSpeed: DEFAULT_GOOGLE_TTS_VALUE,
};
function formatGoogleSpeedLabel(engineSpeed) {
// Chrome's local Google TTS model treats this parameter like a duration
// multiplier, so larger raw values sound slower. Keep the raw saved value and
// rover payload unchanged, but show the reciprocal so the dropdown reads like
// a normal user-facing speed control where larger means faster.
return (1 / engineSpeed).toFixed(2);
}
function resolveTtsSettings(settings) {
return {
engine: settings?.engine || TTS_SETTINGS_DEFAULTS.engine,
@@ -122,7 +131,7 @@ function TtsControls({
const next = event.target.value;
const nextVoice =
next === 'chromegtts' && !CHROME_TTS_VOICES.includes(voice)
? 'tpf'
? DEFAULT_GOOGLE_TTS_VOICE
: next === 'flite' && !FLITE_VOICES.includes(voice)
? 'rms'
: voice;
@@ -176,7 +185,7 @@ function TtsControls({
>
{GOOGLE_TTS_VALUES.map((value) => (
<option key={`speed-${value}`} value={value}>
speed {value.toFixed(2)}
speed {formatGoogleSpeedLabel(value)}
</option>
))}
</select>
@@ -28,8 +28,11 @@ function HudChatInput({ compact = false }) {
const roverRoster = useSessionSelector((state) => state.session?.roster || []);
const { sendMessage, onInputFocus, onInputBlur, blurChat, registerInputRef, setTypingActive } = useChatActions();
const { value: ttsSettings } = useSettingsNamespace('tts', {
engine: 'flite',
voice: 'rms',
// HUD chat shares the normal browser TTS defaults, but it has no visible
// controls of its own. Keeping the fallback here on Google speech prevents
// the compact HUD path from silently reverting to flite for fresh users.
engine: 'chromegtts',
voice: 'tpf',
pitch: 50,
googlePitch: 1,
googleSpeed: 1,
@@ -46,7 +49,7 @@ function HudChatInput({ compact = false }) {
const ttsPayload = useMemo(() => {
if (!ttsSupported) return null;
const engine =
ttsSettings?.engine === 'espeak' ? 'espeak' : ttsSettings?.engine === 'chromegtts' ? 'chromegtts' : 'flite';
ttsSettings?.engine === 'espeak' ? 'espeak' : ttsSettings?.engine === 'flite' ? 'flite' : 'chromegtts';
if (engine === 'espeak') {
let pitch = Number.isFinite(ttsSettings?.pitch) ? Math.round(ttsSettings.pitch) : undefined;
if (typeof pitch === 'number') {