mindcraft/src/agent/speak.js

80 lines
2.6 KiB
JavaScript
Raw Normal View History

2025-04-20 18:20:44 +01:00
import { exec, spawn } from 'child_process';
import settings from '../../settings.js';
2025-04-21 08:06:19 +01:00
import { sendAudioRequest } from '../models/pollinations.js';
2025-02-12 16:26:48 +00:00
let speakingQueue = [];
let isSpeaking = false;
2025-04-20 18:20:44 +01:00
export function say(text) {
speakingQueue.push(text);
if (!isSpeaking) processQueue();
}
2025-04-20 18:20:44 +01:00
async function processQueue() {
if (speakingQueue.length === 0) {
isSpeaking = false;
return;
}
isSpeaking = true;
2025-04-20 18:20:44 +01:00
const txt = speakingQueue.shift();
const isWin = process.platform === 'win32';
const isMac = process.platform === 'darwin';
2025-04-21 08:06:19 +01:00
const model = settings.speak_model || 'pollinations/openai-audio/echo';
2025-02-12 16:26:48 +00:00
2025-04-20 18:20:44 +01:00
if (model === 'system') {
// system TTS
const cmd = isWin
? `powershell -NoProfile -Command "Add-Type -AssemblyName System.Speech; \
$s=New-Object System.Speech.Synthesis.SpeechSynthesizer; $s.Rate=2; \
$s.Speak('${txt.replace(/'/g,"''")}'); $s.Dispose()"`
: isMac
? `say "${txt.replace(/"/g,'\\"')}"`
: `espeak "${txt.replace(/"/g,'\\"')}"`;
exec(cmd, err => {
if (err) console.error('TTS error', err);
processQueue();
});
2025-02-12 16:26:48 +00:00
} else {
2025-04-20 18:20:44 +01:00
// remote audio provider
const [prov, mdl, voice] = model.split('/');
if (prov !== 'pollinations') throw new Error(`Unknown provider: ${prov}`);
2025-02-12 16:26:48 +00:00
2025-04-20 18:20:44 +01:00
try {
2025-04-21 08:06:19 +01:00
let audioData = await sendAudioRequest(txt, mdl, voice);
if (!audioData) {
audioData = "SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU5LjI3LjEwMAAAAAAAAAAAAAAA/+NAwAAAAAAAAAAAAEluZm8AAAAPAAAAAAAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExhdmM1OS4zNwAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAeowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
// ^ 0 second silent audio clip
}
2025-04-20 18:20:44 +01:00
if (isWin) {
const ps = `
Add-Type -AssemblyName presentationCore;
$p=New-Object System.Windows.Media.MediaPlayer;
$p.Open([Uri]::new("data:audio/mp3;base64,${audioData}"));
$p.Play();
Start-Sleep -Seconds [math]::Ceiling($p.NaturalDuration.TimeSpan.TotalSeconds);
`;
spawn('powershell', ['-NoProfile','-Command', ps], {
stdio: 'ignore', detached: true
}).unref();
processQueue();
} else {
const player = spawn('ffplay', ['-nodisp','-autoexit','pipe:0'], {
stdio: ['pipe','ignore','ignore']
});
player.stdin.write(Buffer.from(audioData, 'base64'));
player.stdin.end();
player.on('exit', processQueue);
}
} catch (e) {
console.error('Audio error', e);
processQueue();
2025-03-13 14:40:18 -05:00
}
2025-04-20 18:20:44 +01:00
}
2025-02-12 16:26:48 +00:00
}