Summary
VoiceServer/server.ts hardcodes /usr/bin/afplay for audio playback. afplay is macOS-only and does not exist on Linux, causing all TTS notifications to fail silently with ENOENT. Because the startup catchphrase is fired as a detached fire-and-forget curl, the error is never surfaced to the user — the voice server appears to work but produces no audio.
Affected Files
VoiceServer/server.ts — playAudio() function (~line 379)
VoiceServer/start.sh, stop.sh, restart.sh — use launchctl (macOS-only), fail silently on Linux
Error
{"status":"error","message":"TTS failed: ENOENT: no such file or directory, posix_spawn '/usr/bin/afplay'"}
Platform
- Linux (tested on Fedora 43, kernel 6.18)
- macOS unaffected
What Breaks
- Startup catchphrase never plays
- Algorithm phase voice announcements never play
- All
/notify POST calls return status: error
- No visible error to the user (fire-and-forget curl swallows it)
Regression Vector
Every PAI migration/reinstall that overwrites VoiceServer/server.ts from the upstream repo reverts any local Linux fix, breaking voice again silently.
Proposed Fix
In playAudio(), detect the platform and use ffplay (part of ffmpeg, widely available on Linux) instead of afplay:
// Play audio — afplay on macOS, ffplay on Linux
async function playAudio(audioBuffer: ArrayBuffer, volume: number = FALLBACK_VOLUME): Promise<void> {
const tempFile = `/tmp/voice-${Date.now()}.mp3`;
await Bun.write(tempFile, audioBuffer);
const isMac = process.platform === 'darwin';
const player = isMac ? '/usr/bin/afplay' : '/usr/bin/ffplay';
const args = isMac
? ['-v', volume.toString(), tempFile]
: ['-nodisp', '-autoexit', '-volume', Math.round(volume * 100).toString(), tempFile];
return new Promise((resolve, reject) => {
const proc = spawn(player, args);
proc.on('error', (error) => { reject(error); });
proc.on('exit', (code) => {
spawn('/bin/rm', [tempFile]);
if (code === 0) resolve();
else reject(new Error(`${isMac ? 'afplay' : 'ffplay'} exited with code ${code}`));
});
});
}
ffplay is available at /usr/bin/ffplay on most Linux distros via the ffmpeg package. For distros without ffmpeg, mpg123 is a lighter alternative for MP3 playback.
The start.sh/stop.sh/restart.sh scripts similarly need Linux-compatible process management (e.g. PID file approach) instead of launchctl.
Summary
VoiceServer/server.tshardcodes/usr/bin/afplayfor audio playback.afplayis macOS-only and does not exist on Linux, causing all TTS notifications to fail silently withENOENT. Because the startup catchphrase is fired as a detached fire-and-forgetcurl, the error is never surfaced to the user — the voice server appears to work but produces no audio.Affected Files
VoiceServer/server.ts—playAudio()function (~line 379)VoiceServer/start.sh,stop.sh,restart.sh— uselaunchctl(macOS-only), fail silently on LinuxError
Platform
What Breaks
/notifyPOST calls returnstatus: errorRegression Vector
Every PAI migration/reinstall that overwrites
VoiceServer/server.tsfrom the upstream repo reverts any local Linux fix, breaking voice again silently.Proposed Fix
In
playAudio(), detect the platform and useffplay(part of ffmpeg, widely available on Linux) instead ofafplay:ffplayis available at/usr/bin/ffplayon most Linux distros via theffmpegpackage. For distros without ffmpeg,mpg123is a lighter alternative for MP3 playback.The
start.sh/stop.sh/restart.shscripts similarly need Linux-compatible process management (e.g. PID file approach) instead oflaunchctl.