mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
google tts defaults!
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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/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>
|
<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>
|
<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">
|
<link rel="stylesheet" crossorigin href="/assets/index-CflZqP0e.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ function createHandlers({ sendSystemMessage }) {
|
|||||||
playTypingNote(roverId, TYPING_SEND_NOTE, socket?.id);
|
playTypingNote(roverId, TYPING_SEND_NOTE, socket?.id);
|
||||||
|
|
||||||
if (isPrivateClosedRoverId(message.roverId)) {
|
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);
|
maybeSpeak(socket, message, forcedTts);
|
||||||
cb({ success: true, privateOnly: true });
|
cb({ success: true, privateOnly: true });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ function normalizeTtsOptions(raw = {}) {
|
|||||||
const speak = raw.speak !== false;
|
const speak = raw.speak !== false;
|
||||||
if (!speak) return null;
|
if (!speak) return null;
|
||||||
const rawEngine = typeof raw.engine === 'string' ? raw.engine.toLowerCase() : '';
|
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;
|
const voice = typeof raw.voice === 'string' ? raw.voice.trim() : undefined;
|
||||||
let pitch = Number.isFinite(raw.pitch) ? raw.pitch : undefined;
|
let pitch = Number.isFinite(raw.pitch) ? raw.pitch : undefined;
|
||||||
let speed = Number.isFinite(raw.speed) ? raw.speed : undefined;
|
let speed = Number.isFinite(raw.speed) ? raw.speed : undefined;
|
||||||
|
|||||||
@@ -13,16 +13,37 @@ const pendingCommands = new Map(); // id -> { roverId }
|
|||||||
const lastDriveActivity = new Map(); // roverId -> { ts, socketId, direction, speed, isAdmin }
|
const lastDriveActivity = new Map(); // roverId -> { ts, socketId, direction, speed, isAdmin }
|
||||||
const driveCooldowns = new Map(); // roverId -> blockedUntil
|
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) {
|
function issueCommand(roverId, payload) {
|
||||||
const record = roverManager.rovers.get(roverId);
|
const record = roverManager.rovers.get(roverId);
|
||||||
if (!record || !record.ws) {
|
if (!record || !record.ws) {
|
||||||
throw new Error('Rover offline');
|
throw new Error('Rover offline');
|
||||||
}
|
}
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
const message = { ...payload, id };
|
const normalizedPayload = normalizeOutboundCommandPayload(payload);
|
||||||
|
const message = { ...normalizedPayload, id };
|
||||||
record.ws.send(JSON.stringify(message));
|
record.ws.send(JSON.stringify(message));
|
||||||
pendingCommands.set(id, { roverId, ts: Date.now(), type: payload.type });
|
pendingCommands.set(id, { roverId, ts: Date.now(), type: normalizedPayload.type });
|
||||||
logger.info('Issued command', roverId, payload.type, id);
|
logger.info('Issued command', roverId, normalizedPayload.type, id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
1. implement multitabbing prevention using the identity system
|
1. make google tts the default everywhere but roverd
|
||||||
2. make google tts the default everywhere but roverd
|
2. fix rover request spam queue cheat
|
||||||
3. fix rover request spam queue cheat
|
3. make alert feed.jsx show more alerts at once
|
||||||
4. make alert feed.jsx show more alerts at once
|
4. unify typing row and chat row, should be simple
|
||||||
5. unify typing row and chat row, should be simple
|
5. fix google TTS speeds
|
||||||
6. fix google TTS speeds
|
6. fix this:
|
||||||
7. 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]: /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]: cb({ error: err.message });
|
||||||
Jun 18 15:14:18 roombaserver.local node[216731]: ^
|
Jun 18 15:14:18 roombaserver.local node[216731]: ^
|
||||||
|
|||||||
@@ -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 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 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_VALUE = 1.0;
|
||||||
|
const DEFAULT_GOOGLE_TTS_VOICE = 'tpf';
|
||||||
|
|
||||||
const TTS_SETTINGS_DEFAULTS = {
|
const TTS_SETTINGS_DEFAULTS = {
|
||||||
engine: 'flite',
|
engine: 'chromegtts',
|
||||||
voice: 'rms',
|
voice: DEFAULT_GOOGLE_TTS_VOICE,
|
||||||
pitch: 50,
|
pitch: 50,
|
||||||
googlePitch: DEFAULT_GOOGLE_TTS_VALUE,
|
googlePitch: DEFAULT_GOOGLE_TTS_VALUE,
|
||||||
googleSpeed: 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) {
|
function resolveTtsSettings(settings) {
|
||||||
return {
|
return {
|
||||||
engine: settings?.engine || TTS_SETTINGS_DEFAULTS.engine,
|
engine: settings?.engine || TTS_SETTINGS_DEFAULTS.engine,
|
||||||
@@ -122,7 +131,7 @@ function TtsControls({
|
|||||||
const next = event.target.value;
|
const next = event.target.value;
|
||||||
const nextVoice =
|
const nextVoice =
|
||||||
next === 'chromegtts' && !CHROME_TTS_VOICES.includes(voice)
|
next === 'chromegtts' && !CHROME_TTS_VOICES.includes(voice)
|
||||||
? 'tpf'
|
? DEFAULT_GOOGLE_TTS_VOICE
|
||||||
: next === 'flite' && !FLITE_VOICES.includes(voice)
|
: next === 'flite' && !FLITE_VOICES.includes(voice)
|
||||||
? 'rms'
|
? 'rms'
|
||||||
: voice;
|
: voice;
|
||||||
@@ -176,7 +185,7 @@ function TtsControls({
|
|||||||
>
|
>
|
||||||
{GOOGLE_TTS_VALUES.map((value) => (
|
{GOOGLE_TTS_VALUES.map((value) => (
|
||||||
<option key={`speed-${value}`} value={value}>
|
<option key={`speed-${value}`} value={value}>
|
||||||
speed {value.toFixed(2)}
|
speed {formatGoogleSpeedLabel(value)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -28,8 +28,11 @@ function HudChatInput({ compact = false }) {
|
|||||||
const roverRoster = useSessionSelector((state) => state.session?.roster || []);
|
const roverRoster = useSessionSelector((state) => state.session?.roster || []);
|
||||||
const { sendMessage, onInputFocus, onInputBlur, blurChat, registerInputRef, setTypingActive } = useChatActions();
|
const { sendMessage, onInputFocus, onInputBlur, blurChat, registerInputRef, setTypingActive } = useChatActions();
|
||||||
const { value: ttsSettings } = useSettingsNamespace('tts', {
|
const { value: ttsSettings } = useSettingsNamespace('tts', {
|
||||||
engine: 'flite',
|
// HUD chat shares the normal browser TTS defaults, but it has no visible
|
||||||
voice: 'rms',
|
// 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,
|
pitch: 50,
|
||||||
googlePitch: 1,
|
googlePitch: 1,
|
||||||
googleSpeed: 1,
|
googleSpeed: 1,
|
||||||
@@ -46,7 +49,7 @@ function HudChatInput({ compact = false }) {
|
|||||||
const ttsPayload = useMemo(() => {
|
const ttsPayload = useMemo(() => {
|
||||||
if (!ttsSupported) return null;
|
if (!ttsSupported) return null;
|
||||||
const engine =
|
const engine =
|
||||||
ttsSettings?.engine === 'espeak' ? 'espeak' : ttsSettings?.engine === 'chromegtts' ? 'chromegtts' : 'flite';
|
ttsSettings?.engine === 'espeak' ? 'espeak' : ttsSettings?.engine === 'flite' ? 'flite' : 'chromegtts';
|
||||||
if (engine === 'espeak') {
|
if (engine === 'espeak') {
|
||||||
let pitch = Number.isFinite(ttsSettings?.pitch) ? Math.round(ttsSettings.pitch) : undefined;
|
let pitch = Number.isFinite(ttsSettings?.pitch) ? Math.round(ttsSettings.pitch) : undefined;
|
||||||
if (typeof pitch === 'number') {
|
if (typeof pitch === 'number') {
|
||||||
|
|||||||
Reference in New Issue
Block a user