#!/bin/bash # Generate practice audio assets. # Prerequisites: Node.js, Python 3, edge-tts (pip install edge-tts) set -e AUDIO_DIR="$(cd "$(dirname "$0")/../public/audio" && pwd)" mkdir -p "$AUDIO_DIR" echo "=== Generating chime sounds (Node.js) ===" node -e " const fs = require('fs'); function writeWav(filename, buildSamples, sampleRate, durationSec) { const numSamples = Math.floor(sampleRate * durationSec); const samples = new Float32Array(numSamples); buildSamples(samples, sampleRate, durationSec); // Convert to 16-bit PCM const buffer = Buffer.alloc(44 + numSamples * 2); buffer.write('RIFF', 0); buffer.writeUInt32LE(36 + numSamples * 2, 4); buffer.write('WAVE', 8); buffer.write('fmt ', 12); buffer.writeUInt32LE(16, 16); // chunk size buffer.writeUInt16LE(1, 20); // PCM buffer.writeUInt16LE(1, 22); // mono buffer.writeUInt32LE(sampleRate, 24); buffer.writeUInt32LE(sampleRate * 2, 28); // byte rate buffer.writeUInt16LE(2, 32); // block align buffer.writeUInt16LE(16, 34); // bits per sample buffer.write('data', 36); buffer.writeUInt32LE(numSamples * 2, 40); for (let i = 0; i < numSamples; i++) { const s = Math.max(-1, Math.min(1, samples[i])); buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2); } fs.writeFileSync(filename, buffer); console.log(' Created:', filename); } // Bell-like chime: two sine harmonics (fundamental + octave) with quick decay function bellTone(t, freq1, freq2, tStart, duration) { if (t < tStart || t > tStart + duration) return 0; const localT = (t - tStart) / duration; const env = Math.exp(-localT * 6); // quick exponential decay return 0.35 * env * ( Math.sin(2 * Math.PI * freq1 * t) * 0.6 + Math.sin(2 * Math.PI * freq2 * t) * 0.4 ); } // Start chime — welcoming ascending bell, 2s writeWav('$AUDIO_DIR/start-chime.wav', (samples, sr, dur) => { for (let i = 0; i < samples.length; i++) { const t = i / sr; let v = 0; v += bellTone(t, 523.25, 1046.5, 0, 0.9); // C5 v += bellTone(t, 659.25, 1318.5, 0.15, 0.8); // E5 v += bellTone(t, 783.99, 1568.0, 0.3, 0.7); // G5 samples[i] = v; } }, 44100, 1.8); // Soft tick — very short click, 0.08s writeWav('$AUDIO_DIR/tick.wav', (samples, sr) => { for (let i = 0; i < samples.length; i++) { const t = i / sr; const env = Math.exp(-t * 80); samples[i] = 0.12 * env * Math.sin(2 * Math.PI * 1200 * t); } }, 44100, 0.08); // End chime — resonant closing chord, 3s writeWav('$AUDIO_DIR/end-chime.wav', (samples, sr, dur) => { for (let i = 0; i < samples.length; i++) { const t = i / sr; let v = 0; v += bellTone(t, 261.63, 523.25, 0, 1.5); // C4 (deeper) v += bellTone(t, 329.63, 659.25, 0.12, 1.3); // E4 v += bellTone(t, 392.00, 783.99, 0.24, 1.1); // G4 v += bellTone(t, 523.25, 1046.5, 0.36, 0.9); // C5 samples[i] = v * 0.9; } }, 44100, 3.0); console.log('Chimes done.'); " echo "" echo "=== Generating voice guidance (Edge TTS neural voices → mp3) ===" # Chinese — Xiaoxiao (warm, mature female) python3 -m edge_tts --voice "zh-CN-XiaoxiaoNeural" --rate="-40%" --text "继续专注,你做得很好。" --write-media "$AUDIO_DIR/mid-guidance-zh.mp3" echo " Created: mid-guidance-zh.mp3" python3 -m edge_tts --voice "zh-CN-XiaoxiaoNeural" --rate="-40%" --text "练习完成。请记录你的感受。" --write-media "$AUDIO_DIR/end-guidance-zh.mp3" echo " Created: end-guidance-zh.mp3" # English — Aria (neutral, professional female) python3 -m edge_tts --voice "en-US-AriaNeural" --rate="-40%" --text "Stay focused. You're doing well." --write-media "$AUDIO_DIR/mid-guidance-en.mp3" echo " Created: mid-guidance-en.mp3" python3 -m edge_tts --voice "en-US-AriaNeural" --rate="-40%" --text "Practice complete. Take a moment to reflect." --write-media "$AUDIO_DIR/end-guidance-en.mp3" echo " Created: end-guidance-en.mp3" echo "" echo "=== All audio assets generated in $AUDIO_DIR ===" ls -lh "$AUDIO_DIR"