feat: Archipelago demo stack (lightweight)
This commit is contained in:
77
neode-ui/scripts/generate-welcome-speech.js
Normal file
77
neode-ui/scripts/generate-welcome-speech.js
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generate "Welcome Noderunner" speech using ElevenLabs AI voice.
|
||||
* Slower, softer, sci-fi style with reverb/echo effects.
|
||||
*
|
||||
* Usage:
|
||||
* ELEVENLABS_API_KEY=your_key node scripts/generate-welcome-speech.js
|
||||
*
|
||||
* Optional voice ID (browse https://elevenlabs.io/voice-library/sensual):
|
||||
* ELEVENLABS_VOICE_ID=voice_id node scripts/generate-welcome-speech.js
|
||||
*/
|
||||
|
||||
import { writeFileSync, mkdirSync, readFileSync, unlinkSync } from 'fs'
|
||||
import { dirname, join } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { execSync } from 'child_process'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const API_KEY = process.env.ELEVENLABS_API_KEY
|
||||
// Sarah - mature, reassuring, confident female (softer than Rachel)
|
||||
const VOICE_ID = process.env.ELEVENLABS_VOICE_ID || 'EXAVITQu4vr4xnSDxMaL'
|
||||
const OUTPUT_PATH = join(__dirname, '../public/assets/audio/welcome-noderunner.mp3')
|
||||
const RAW_PATH = join(__dirname, '../public/assets/audio/welcome-noderunner-raw.mp3')
|
||||
|
||||
if (!API_KEY) {
|
||||
console.error('Set ELEVENLABS_API_KEY (get a free key at elevenlabs.io)')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Slower (0.78), softer (higher stability 0.65), more expressive (style 0.6)
|
||||
const res = await fetch(
|
||||
`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}?output_format=mp3_44100_128`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'xi-api-key': API_KEY,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text: 'Welcome Noderunner',
|
||||
model_id: 'eleven_multilingual_v2',
|
||||
voice_settings: {
|
||||
stability: 0.65,
|
||||
similarity_boost: 0.8,
|
||||
style: 0.6,
|
||||
use_speaker_boost: true,
|
||||
speed: 0.7,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text()
|
||||
console.error('ElevenLabs API error:', res.status, err)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const buf = Buffer.from(await res.arrayBuffer())
|
||||
mkdirSync(dirname(OUTPUT_PATH), { recursive: true })
|
||||
writeFileSync(RAW_PATH, buf)
|
||||
|
||||
// Add sci-fi reverb: dense short delays that blend (no distinct echo)
|
||||
try {
|
||||
execSync(
|
||||
`ffmpeg -y -i "${RAW_PATH}" -af "aecho=0.6:0.15:25|45|70:0.55|0.45|0.35,highpass=f=80,equalizer=f=4000:t=q:w=1:g=-1" -q:a 2 "${OUTPUT_PATH}" 2>/dev/null`,
|
||||
{ stdio: 'pipe' }
|
||||
)
|
||||
unlinkSync(RAW_PATH)
|
||||
} catch {
|
||||
writeFileSync(OUTPUT_PATH, buf)
|
||||
try { unlinkSync(RAW_PATH) } catch {}
|
||||
}
|
||||
|
||||
console.log('Generated:', OUTPUT_PATH)
|
||||
console.log('Add this file to git and deploy.')
|
||||
Reference in New Issue
Block a user