/** * TTS HTTP client — speaks text via a running tts-server.mjs instance. * * Usage: * const tts = new Tts_Client() * await tts.speak('Hello world.') * await tts.speak('Hello.', { audio_prompt: '/path/to/voice.wav' }) * const { voices, current } = await tts.list_voices() * await tts.set_voice('rommie') * * The server URL defaults to TTS_URL env var, then http://192.168.2.99:11500. */ const DEFAULT_URL = process.env.TTS_URL ?? 'http://192.168.2.99:11500' export class Tts_Client { constructor({ url = DEFAULT_URL } = {}) { this._url = url } async speak(text, opts = {}) { const res = await fetch(`${this._url}/speak`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, ...opts }), }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.error ?? `HTTP ${res.status}`) } } async list_voices() { const res = await fetch(`${this._url}/voices`) if (!res.ok) throw new Error(`HTTP ${res.status}`) return res.json() // { voices: [{name, description, active}], current } } async chime(name) { const res = await fetch(`${this._url}/chime`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.error ?? `HTTP ${res.status}`) } } async set_voice(name) { const res = await fetch(`${this._url}/voice`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.error ?? `HTTP ${res.status}`) } return res.json() // { ok, name, path } } }