Add configurable speech voice settings
This commit is contained in:
@@ -1,2 +1,7 @@
|
|||||||
EXPO_PUBLIC_OLLAMA_BASE_URL=http://localhost:11434
|
EXPO_PUBLIC_OLLAMA_BASE_URL=http://localhost:11434
|
||||||
EXPO_PUBLIC_OLLAMA_MODEL=llama3.2
|
EXPO_PUBLIC_OLLAMA_MODEL=llama3.2
|
||||||
|
EXPO_PUBLIC_SPEECH_LANGUAGE=en-US
|
||||||
|
EXPO_PUBLIC_SPEECH_RATE=0.85
|
||||||
|
EXPO_PUBLIC_SPEECH_PITCH=1
|
||||||
|
# Optional iOS voice identifier. Leave empty to use the first matching iPhone voice.
|
||||||
|
# EXPO_PUBLIC_SPEECH_VOICE=com.apple.voice.compact.en-US.Samantha
|
||||||
|
|||||||
6
src/config/speech.ts
Normal file
6
src/config/speech.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const speechConfig = {
|
||||||
|
language: process.env.EXPO_PUBLIC_SPEECH_LANGUAGE ?? 'en-US',
|
||||||
|
preferredVoice: process.env.EXPO_PUBLIC_SPEECH_VOICE,
|
||||||
|
pitch: Number(process.env.EXPO_PUBLIC_SPEECH_PITCH ?? 1),
|
||||||
|
rate: Number(process.env.EXPO_PUBLIC_SPEECH_RATE ?? 0.85),
|
||||||
|
};
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
import * as Speech from 'expo-speech';
|
import * as Speech from 'expo-speech';
|
||||||
|
|
||||||
export function speakText(text: string) {
|
import { speechConfig } from '../config/speech';
|
||||||
|
|
||||||
|
let cachedVoice: string | undefined;
|
||||||
|
|
||||||
|
export async function speakText(text: string) {
|
||||||
Speech.stop();
|
Speech.stop();
|
||||||
|
|
||||||
|
const voice = await getPreferredVoice();
|
||||||
|
|
||||||
Speech.speak(stripThinkingBlocks(text), {
|
Speech.speak(stripThinkingBlocks(text), {
|
||||||
language: 'en-US',
|
language: speechConfig.language,
|
||||||
pitch: 1,
|
pitch: speechConfig.pitch,
|
||||||
rate: 0.9,
|
rate: speechConfig.rate,
|
||||||
|
voice,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,3 +24,20 @@ export function stopSpeaking() {
|
|||||||
function stripThinkingBlocks(text: string) {
|
function stripThinkingBlocks(text: string) {
|
||||||
return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getPreferredVoice() {
|
||||||
|
if (cachedVoice) {
|
||||||
|
return cachedVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speechConfig.preferredVoice) {
|
||||||
|
cachedVoice = speechConfig.preferredVoice;
|
||||||
|
return cachedVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
const voices = await Speech.getAvailableVoicesAsync();
|
||||||
|
const matchingVoice = voices.find((voice) => voice.language === speechConfig.language);
|
||||||
|
cachedVoice = matchingVoice?.identifier;
|
||||||
|
|
||||||
|
return cachedVoice;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user