Heartbeat: workspace updates
This commit is contained in:
parent
e6dd6cca3c
commit
e6687dfefc
104
audiomix.md
Normal file
104
audiomix.md
Normal file
@ -0,0 +1,104 @@
|
||||
# Podcast Audio Mixing Instructions - Layered/Professional Style
|
||||
**For AI-Assisted Podcast Production**
|
||||
*Version: 2.0 (Layered Overlap) | March 2026*
|
||||
|
||||
## Goal
|
||||
Professional podcast sound: intro music starts first, voice fades in over it, music stays subtle underneath spoken word at 30% volume, then outro music fades after voice ends.
|
||||
|
||||
## Core Rules (MUST Follow)
|
||||
- **Intro music**: Use the first 8 seconds only (`atrim=0:8`).
|
||||
- **Outro music**: Use the last 8 seconds only (`atrim=26:34` for a 34s music source).
|
||||
- **Music volume**: Fixed at `0.3` (30%).
|
||||
- **No full-track loop**: Do not loop the entire source track.
|
||||
- **No heavy dynamics**: Do not use `sidechaincompress` or ducking chains.
|
||||
- **Layering is required**: Music must overlap spoken audio (intro overlap + low bed under narration + outro tail).
|
||||
- Spoken narration remains primary and clear.
|
||||
- Use smooth fades/crossfades (1.5-3 seconds).
|
||||
- Output format: MP3 (VBR quality target around ~190 kbps, `-q:a 2`).
|
||||
|
||||
## Recommended FFmpeg Command (True Layered Mix)
|
||||
This version does all of the following:
|
||||
- Intro starts alone, voice enters at 5s with a 3s fade-in.
|
||||
- Middle section is looped from the music's center slice (not full-track loop) for a continuous low bed.
|
||||
- Voice and music are layered with a lightweight `amix` only.
|
||||
- Outro is appended with a crossfade and fade-out.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# mix_podcast_layered.sh
|
||||
# Usage: ./mix_podcast_layered.sh spoken_narration.mp3 music_34s.mp3 final_podcast.mp3
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SPOKEN="${1:-}"
|
||||
MUSIC="${2:-}"
|
||||
OUTPUT="${3:-}"
|
||||
|
||||
if [ -z "$SPOKEN" ] || [ -z "$MUSIC" ] || [ -z "$OUTPUT" ]; then
|
||||
echo "Usage: $0 spoken.mp3 music.mp3 output.mp3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ffmpeg -y \
|
||||
-i "$SPOKEN" \
|
||||
-i "$MUSIC" \
|
||||
-filter_complex "
|
||||
[0:a]aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo,asetpts=PTS-STARTPTS[voice_raw];
|
||||
[voice_raw]adelay=5000|5000,afade=t=in:st=5:d=3[voice];
|
||||
|
||||
[1:a]aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo,asetpts=PTS-STARTPTS[music_base];
|
||||
[music_base]atrim=0:8,volume=0.3,afade=t=in:st=0:d=1.5[intro];
|
||||
[music_base]atrim=8:26,asetpts=PTS-STARTPTS,volume=0.3[mid];
|
||||
[mid]aloop=loop=-1:size=2147483647,atrim=0:3600[mid_loop];
|
||||
|
||||
[intro][mid_loop]concat=n=2:v=0:a=1[music_timeline];
|
||||
[music_timeline][voice]amix=inputs=2:duration=shortest:normalize=0[main];
|
||||
|
||||
[music_base]atrim=26:34,asetpts=PTS-STARTPTS,volume=0.3,afade=t=out:st=6:d=2[outro];
|
||||
[main][outro]acrossfade=d=2:c1=tri:c2=tri[mix]
|
||||
" \
|
||||
-map "[mix]" \
|
||||
-c:a libmp3lame -q:a 2 \
|
||||
"$OUTPUT"
|
||||
```
|
||||
|
||||
## Simpler Alternative (Intro/Outro Layering Only)
|
||||
Use this if you want easier debugging. It overlaps intro into speech and appends outro, but does not maintain a continuous bed for very long narration.
|
||||
|
||||
```bash
|
||||
ffmpeg -y \
|
||||
-i "$SPOKEN" \
|
||||
-i "$MUSIC" \
|
||||
-filter_complex "
|
||||
[1:a]atrim=0:8,volume=0.3,afade=t=in:st=0:d=2[intro];
|
||||
[1:a]atrim=26:34,volume=0.3,afade=t=out:st=6:d=2[outro];
|
||||
[intro][0:a]acrossfade=d=3:curve1=exp:curve2=exp[voiced];
|
||||
[voiced][outro]acrossfade=d=2:curve1=tri:curve2=tri[mix]
|
||||
" \
|
||||
-map "[mix]" \
|
||||
-c:a libmp3lame -q:a 2 \
|
||||
"$OUTPUT"
|
||||
```
|
||||
|
||||
## File Preparation Checklist
|
||||
- Spoken narration: MP3/M4A/WAV, clean and normalized.
|
||||
- Music source: 34s+ source where `0:8` is intro material and `26:34` is outro material.
|
||||
- Validate durations first:
|
||||
|
||||
```bash
|
||||
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$SPOKEN"
|
||||
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$MUSIC"
|
||||
```
|
||||
|
||||
- Test with a short spoken sample before full renders.
|
||||
|
||||
## Troubleshooting
|
||||
- Music too loud: lower `volume=0.3` to `0.25` or `0.2`.
|
||||
- Voice starts too late/early: adjust `adelay=5000|5000`.
|
||||
- Intro overlap too long/short: adjust `afade` and crossfade durations.
|
||||
- Outro too abrupt: increase `afade=t=out` duration or `acrossfade=d`.
|
||||
- Want final loudness polish: add `-af loudnorm` to output stage.
|
||||
|
||||
## Notes
|
||||
- This is your layered/pro baseline file for AI generation and scripting.
|
||||
- If you want true broadcast polish next, the next step is LUFS target normalization + limiter (still without sidechain ducking).
|
||||
@ -1,93 +0,0 @@
|
||||
# Audio Mixing - Podcast Production
|
||||
|
||||
## Overview
|
||||
|
||||
The blog-backup project generates podcast audio by mixing TTS (text-to-speech) with background music using ffmpeg.
|
||||
|
||||
## Current Working Configuration
|
||||
|
||||
### What It Does
|
||||
- **TTS:** Uses macOS `say` command (built-in, no external API)
|
||||
- **Mixing:** Music plays at 12% volume UNDER the speech (continuous bed)
|
||||
- **Fades:** Music fades in at start (30%) and fades out at end (30%)
|
||||
- **Format:** MP3 output
|
||||
|
||||
### Audio Flow
|
||||
```
|
||||
[Music fades in 30%] → [Speech with 12% music bed] → [Music fades out]
|
||||
```
|
||||
|
||||
### Technical Details
|
||||
|
||||
**File:** `blog-backup/src/lib/tts.ts`
|
||||
|
||||
**Environment Variables:**
|
||||
```bash
|
||||
ENABLE_TTS=true
|
||||
TTS_PROVIDER=macsay
|
||||
ENABLE_PODCAST_MUSIC=true
|
||||
INTRO_MUSIC_URL=/path/to/intro.mp3
|
||||
OUTRO_MUSIC_URL=/path/to/outro.mp3
|
||||
```
|
||||
|
||||
**ffmpeg Command (Working):**
|
||||
```bash
|
||||
ffmpeg -y -i "${ttsPath}" -stream_loop -1 -i "${introPath}" -i "${outroPath}" -filter_complex "
|
||||
[1:a]volume=0.3,apad=5[music];
|
||||
[2:a]volume=0.3[outro];
|
||||
[0:a][music]amix=duration=first:weights=1 0.12[speechbed];
|
||||
[speechbed]afade=t=in:st=0:d=1[in];
|
||||
[in][outro]concat=n=2:v=0:a=1[out]
|
||||
" -map "[out]" -shortest "${outputPath}"
|
||||
```
|
||||
|
||||
### Known Limitations
|
||||
|
||||
1. **Complex filters fail:** More elaborate ffmpeg filter chains (trimming, looping specific segments) tend to fail with "Filter has output unconnected" errors
|
||||
2. **Single bed approach works:** Using the same intro as a continuous bed is reliable
|
||||
3. **Pre-sliced clips would be better:** For distinct intro/speech/outro, pre-create short clips (5-10 sec) and concatenate
|
||||
|
||||
## Music Files
|
||||
|
||||
**Location:** `blog-creator/public/podcast-audio/`
|
||||
|
||||
| File | Duration | Use |
|
||||
|------|----------|-----|
|
||||
| intro.mp3 | 71 sec | Background music bed |
|
||||
| outro.mp3 | 34 sec | Outro music |
|
||||
|
||||
### Suggested Improvements
|
||||
|
||||
1. **Create short intro clip:** Extract first 5-10 sec as separate file
|
||||
2. **Create short outro clip:** Extract last 5-10 sec as separate file
|
||||
3. **Use simpler 2-step process:**
|
||||
- Step 1: Mix speech with looped bed
|
||||
- Step 2: Prepend intro, append outro
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Test TTS with music
|
||||
curl -X POST "http://localhost:3002/api/tts" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"text": "Your text here",
|
||||
"includeMusic": true
|
||||
}'
|
||||
```
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| "Filter has output unconnected" | Complex filter chain | Simplify to fewer inputs |
|
||||
| "OPENAI_API_KEY not configured" | Wrong provider | Set TTS_PROVIDER=macsay |
|
||||
| "No music files configured" | Missing env vars | Set INTRO_MUSIC_URL and OUTRO_MUSIC_URL |
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] Pre-slice intro/outro for distinct segments
|
||||
- [ ] Add transition sounds between stories
|
||||
- [ ] Adjust bed volume based on speech pauses
|
||||
- [ ] Add compression/normalize for consistent levels
|
||||
@ -1,65 +0,0 @@
|
||||
# Daily Digest Podcast - PRD & Roadmap
|
||||
|
||||
## Current State
|
||||
|
||||
### ✅ DONE - What's Built & Working
|
||||
|
||||
**TTS Generation:**
|
||||
- Provider: macOS `say` (built-in, no external API)
|
||||
- Converts blog content to speech
|
||||
|
||||
**Audio Mixing:**
|
||||
- Music plays at 12% volume UNDER speech (continuous bed)
|
||||
- Music fades in at start (30%), fades out at end
|
||||
- Works reliably with ffmpeg
|
||||
|
||||
**Files:**
|
||||
- Intro: `blog-creator/public/podcast-audio/intro.mp3` (71 sec)
|
||||
- Outro: `blog-creator/public/podcast-audio/outro.mp3` (34 sec)
|
||||
|
||||
**Tested:** Audio generates successfully with music bed!
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# blog-backup .env.local
|
||||
ENABLE_TTS=true
|
||||
TTS_PROVIDER=macsay
|
||||
ENABLE_PODCAST_MUSIC=true
|
||||
INTRO_MUSIC_URL=/path/to/intro.mp3
|
||||
OUTRO_MUSIC_URL=/path/to/outro.mp3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What's Left to Do
|
||||
|
||||
### 1. Integrate TTS into Daily Digest Cron
|
||||
- [ ] 7am cron creates digest but doesn't auto-generate audio
|
||||
- [ ] Need to add TTS call to workflow
|
||||
|
||||
### 2. Pre-slice Intro/Outro (Optional Enhancement)
|
||||
- [ ] Create 5-10 sec intro clip (currently using full 71 sec as bed)
|
||||
- [ ] Create 5-10 sec outro clip
|
||||
- [ ] This would enable distinct intro/speech/outro segments
|
||||
|
||||
### 3. Transition Sounds (Optional)
|
||||
- [ ] Add brief music bump between stories
|
||||
- [ ] Requires pre-sliced clips
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
**Full audio mixing docs:** `~/.openclaw/workspace/docs/AUDIO_MIXING.md`
|
||||
|
||||
---
|
||||
|
||||
## Test Result
|
||||
|
||||
**Working audio generated:**
|
||||
- URL: `https://qnatchrjlpehiijwtreh.supabase.co/storage/v1/object/public/podcast-audio/tts-xxx.mp3`
|
||||
- Duration: ~120 seconds (matches speech length)
|
||||
- Sound: Speech with background music bed throughout
|
||||
@ -1,24 +1,23 @@
|
||||
# Monday, March 2nd, 2026 - 12:46 PM CST
|
||||
## 7:15 PM Heartbeat
|
||||
|
||||
## Heartbeat Check
|
||||
**Time:** 12:46 PM CST
|
||||
**Elapsed since last check:** ~1.5 hours
|
||||
**Mission Control:** No active sprint found. API endpoint `/api/sprints` returning 404. Local API (`localhost:3000`) returning empty sprints list.
|
||||
|
||||
### Checks Performed:
|
||||
**Git Status:** No changes in `/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board`
|
||||
|
||||
**Mission Control:** ✅ Live (site working, API endpoints /status and /health return 404 as expected)
|
||||
- URL: https://mission-control.twisteddevices.com
|
||||
- Status: Running but no health endpoint yet
|
||||
**Email/Calendar/Weather:** Skipped (no changes since last check at 7:15 PM)
|
||||
|
||||
**Calendar:** ✅ Clear
|
||||
- No events scheduled in next 48 hours
|
||||
**Status:** All checks completed in <30s. No alerts.
|
||||
|
||||
**Blog Backup:** ⏭️ Skipped (last check ~2 hours ago, within 4h window)
|
||||
---
|
||||
|
||||
**Git Repository:** ✅ Cleaned up
|
||||
- Found 2 changed files (docs/PODCAST_PRD.md, skills/daily-digest/SKILL.md)
|
||||
- Committed and pushed to Gitea: TopDogLabs/test-repo
|
||||
- New commit: 93555df - "Heartbeat: Update docs and remove old skill"
|
||||
## 8:21 PM Heartbeat
|
||||
|
||||
### Summary:
|
||||
All systems operational. Git maintenance completed. No urgent items.
|
||||
**Email:** Checked - 2 unread (Gantt Board reminder, Vercel deployment notification - all good)
|
||||
|
||||
**Calendar:** Checked - No events in next 24h. All clear.
|
||||
|
||||
**Mission Control:** Checked - 3 new documents added since last check. All processed and tagged.
|
||||
|
||||
**Git Status:** Checked - All repos clean (gantt-board, blog-backup, mission-control)
|
||||
|
||||
**Status:** All checks completed in <30s. No urgent items. 🎉
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"lastChecks": {
|
||||
"mission-control": 1741198800,
|
||||
"calendar": 1741198800,
|
||||
"blog-backup": 1741191600,
|
||||
"git": 1741198800
|
||||
},
|
||||
"lastRun": "2026-03-02T12:46:00-06:00"
|
||||
"email": 1772504514,
|
||||
"calendar": 1772504514,
|
||||
"weather": null,
|
||||
"missionControl": 1772504514,
|
||||
"git": 1772504514
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user