feat: guided light meditation + Edge TTS neural voice audio

- Add continuous guided light-body-scan meditation (16 segments, 97% coverage)
- Edge TTS API endpoint with Xiaoxiao neural voice at -40% rate
- Pre-generated chime WAVs and guidance MP3s in public/audio/
- Mute toggle in practice page, sequenced non-overlapping audio
- Fix middleware to serve /audio and /api/audio/tts without auth
- All LLM output localized to Chinese; fix blank home/settings pages
- DeepSeek API compat: json_object, increased max_tokens
- Analytics, cron reminders, onboarding flow fixes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jackyu66git
2026-06-17 20:08:29 +08:00
co-authored by Claude Opus 4.7
parent 00db3f6c96
commit 6530b20172
38 changed files with 1371 additions and 182 deletions
+109
View File
@@ -0,0 +1,109 @@
#!/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"