Skill: ivangdavila/audio (version 1.0.1)

--- name: Audio slug: audio version: 1.0.1 description: Process, enhance, and convert audio files with noise removal, normalization, format conversion, transcription, and podcast workflows. changelog: Declare required binaries (ffmpeg, ffprobe), add requirements section with optional deps, add explicit scope metadata: {"clawdbot":{"emoji":"🔊","requires":{"bins":["ffmpeg","ffprobe"]},"os":["linux","darwin","win32"]}} --- ## Requirements **Required:** - `ffmpeg` / `ffprobe` — core audio processing **Optional (for advanced features):** - `sox` — additional noise reduction - `whisper` — local transcription (or use API) - `demucs` — stem separation ## Quick Reference | Situation | Load | |-----------|------| | FFmpeg commands by task | `commands.md` | | Loudness standards by platform | `loudness.md` | | Podcast production workflow | `podcast.md` | | Transcription workflow | `transcription.md` | ## Core Capabilities | Task | Method | |------|--------| | Convert formats | FFmpeg (`-acodec`) | | Remove noise | FFmpeg filters or SoX | | Normalize loudness | `ffmpeg-normalize` or `-af loudnorm` | | Transcribe | Whisper → text, SRT, VTT | | Separate stems | Demucs (vocals, drums, bass, other) | ## Execution Pattern 1. **Clarify goal** — What format? What loudness? What platform? 2. **Analyze source** — `ffprobe` for codec, sample rate, channels, duration 3. **Process** — FFmpeg/SoX for transformation 4. **Verify** — Check output plays, meets specs, sounds correct 5. **Deliver** — Provide file to user ## Common Requests → Actions | User says | Agent does | |-----------|------------| | "Convert to MP3" | `-acodec libmp3lame -q:a 2` | | "Remove background noise" | Apply highpass/lowpass or dedicated denoiser | | "Normalize for podcast" | `-af loudnorm=I=-16:TP=-1.5:LRA=11` | | "Transcribe this" | Whisper → output SRT/VTT/TXT | | "Extract audio from video" | `-vn -acodec copy` or re-encode | | "Make it smaller" | Lower bitrate: `-b:a 128k` or `-b:a 96k` | | "Speed up 1.5x" | `-af atempo=1.5` | ## Format Quick Reference | Format | Use Case | Quality | |--------|----------|---------| | WAV | Master, editing | Lossless | | FLAC | Archive, audiophile | Lossless compressed | | MP3 | Universal sharing | Lossy, 128-320 kbps | | AAC/M4A | Apple, podcasts | Lossy, efficient | | OGG/Opus | WhatsApp, Discord | Lossy, very efficient | ## Quality Defaults - **Podcast:** -16 LUFS (Spotify), -19 LUFS (Apple) - **Music:** -14 LUFS (Spotify), -16 LUFS (Apple Music) - **MP3 quality:** VBR `-q:a 2` (~190 kbps) or CBR `-b:a 192k` - **Sample rate:** 44.1kHz for music, 48kHz for video sync ## Scope This skill: - Processes audio files user explicitly provides - Runs FFmpeg commands on user request - Does NOT access cloud services without user knowing - Does NOT store files persistently (user manages their files)

path: commands.md
content: # FFmpeg Audio Commands

## Inspect Audio
```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp3
```

---

## Format Conversion
```bash
# To MP3 (VBR, good quality)
ffmpeg -i input.wav -acodec libmp3lame -q:a 2 output.mp3

# To MP3 (CBR, specific bitrate)
ffmpeg -i input.wav -acodec libmp3lame -b:a 192k output.mp3

# To AAC/M4A
ffmpeg -i input.wav -acodec aac -b:a 192k output.m4a

# To FLAC (lossless)
ffmpeg -i input.wav -acodec flac output.flac

# To WAV (from any format)
ffmpeg -i input.mp3 -acodec pcm_s16le output.wav

# To Opus (small, efficient)
ffmpeg -i input.wav -acodec libopus -b:a 128k output.opus
```

---

## Extract Audio from Video
```bash
# Keep original codec (fastest)
ffmpeg -i video.mp4 -vn -acodec copy audio.aac

# Convert to MP3
ffmpeg -i video.mp4 -vn -acodec libmp3lame -q:a 2 audio.mp3

# Convert to WAV for editing
ffmpeg -i video.mp4 -vn -acodec pcm_s16le -ar 44100 audio.wav
```

---

## Trim/Cut
```bash
# Cut from 00:30 to 02:00
ffmpeg -i input.mp3 -ss 00:00:30 -to 00:02:00 -c copy output.mp3

# First 60 seconds
ffmpeg -i input.mp3 -t 60 -c copy output.mp3

# Remove first 10 seconds
ffmpeg -i input.mp3 -ss 10 -c copy output.mp3
```

---

## Merge Multiple Files
```bash
# Create list file
echo "file 'clip1.mp3'" > list.txt
echo "file 'clip2.mp3'" >> list.txt
echo "file 'clip3.mp3'" >> list.txt

# Concatenate
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp3
```

---

## Volume & Loudness

```bash
# Simple volume adjustment
ffmpeg -i input.mp3 -af "volume=1.5" output.mp3  # 1.5x louder
ffmpeg -i input.mp3 -af "volume=-3dB" output.mp3  # 3dB quieter

# Normalize to target loudness (podcast standard)
ffmpeg -i input.mp3 -af "loudnorm=I=-16:TP=-1.5:LRA=11" output.mp3

# Peak normalize (maximize without clipping)
ffmpeg -i input.mp3 -af "volume=0dB:precision=double:replaygain=track" output.mp3

# Using ffmpeg-normalize (more accurate, two-pass)
ffmpeg-normalize input.mp3 -o output.mp3 -t -16
```

---

## Noise Reduction

```bash
# Basic highpass (remove rumble <200Hz)
ffmpeg -i input.mp3 -af "highpass=f=80" output.mp3

# Basic lowpass (remove hiss >8kHz)
ffmpeg -i input.mp3 -af "lowpass=f=8000" output.mp3

# Combined for voice cleanup
ffmpeg -i input.mp3 -af "highpass=f=80,lowpass=f=8000" output.mp3

# Noise gate (reduce quiet noise between speech)
ffmpeg -i input.mp3 -af "afftdn=nf=-25" output.mp3
```

---

## Speed/Tempo

```bash
# Speed up 1.5x (changes pitch too)
ffmpeg -i input.mp3 -af "asetrate=44100*1.5,aresample=44100" output.mp3

# Speed up WITHOUT changing pitch
ffmpeg -i input.mp3 -af "atempo=1.5" output.mp3

# Slow down to 0.75x
ffmpeg -i input.mp3 -af "atempo=0.75" output.mp3

# Extreme speed (chain atempo, each max 2.0)
ffmpeg -i input.mp3 -af "atempo=2.0,atempo=2.0" output.mp3  # 4x speed
```

---

## Fade In/Out

```bash
# Fade in first 3 seconds
ffmpeg -i input.mp3 -af "afade=t=in:st=0:d=3" output.mp3

# Fade out last 3 seconds (need duration first)
duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp3)
start=$(echo "$duration - 3" | bc)
ffmpeg -i input.mp3 -af "afade=t=out:st=$start:d=3" output.mp3

# Both fades
ffmpeg -i input.mp3 -af "afade=t=in:st=0:d=2,afade=t=out:st=58:d=2" output.mp3
```

---

## Silence Removal

```bash
# Remove silence longer than 0.5s, threshold -50dB
ffmpeg -i input.mp3 -af "silenceremove=stop_periods=-1:stop_duration=0.5:stop_threshold=-50dB" output.mp3

# Remove silence from beginning
ffmpeg -i input.mp3 -af "silenceremove=start_periods=1:start_threshold=-50dB" output.mp3

# Trim silence from both ends
ffmpeg -i input.mp3 -af "silenceremove=start_periods=1:start_threshold=-50dB,areverse,silenceremove=start_periods=1:start_threshold=-50dB,areverse" output.mp3
```

---

## Channel Manipulation

```bash
# Stereo to mono
ffmpeg -i input.mp3 -ac 1 output.mp3

# Mono to stereo (duplicate channel)
ffmpeg -i input.mp3 -ac 2 output.mp3

# Extract left channel only
ffmpeg -i input.mp3 -af "pan=mono|c0=FL" output.mp3

# Extract right channel only
ffmpeg -i input.mp3 -af "pan=mono|c0=FR" output.mp3
```

---

## Sample Rate Conversion

```bash
# Convert to 44.1kHz
ffmpeg -i input.mp3 -ar 44100 output.mp3

# Convert to 48kHz (for video)
ffmpeg -i input.mp3 -ar 48000 output.mp3

# Convert to 16kHz (for Whisper)
ffmpeg -i input.mp3 -ar 16000 output.wav
```

---

## Compression (Dynamics)

```bash
# Basic compression (reduce dynamic range)
ffmpeg -i input.mp3 -af "acompressor=threshold=-20dB:ratio=4:attack=5:release=50" output.mp3

# Limiter (prevent clipping)
ffmpeg -i input.mp3 -af "alimiter=limit=0.9" output.mp3

# Normalize + compress for podcast
ffmpeg -i input.mp3 -af "acompressor=threshold=-20dB:ratio=3:attack=5:release=100,loudnorm=I=-16:TP=-1.5:LRA=11" output.mp3
```

---

## EQ (Basic)

```bash
# Boost bass (below 200Hz)
ffmpeg -i input.mp3 -af "bass=g=5" output.mp3

# Boost treble (above 3kHz)
ffmpeg -i input.mp3 -af "treble=g=3" output.mp3

# Voice presence boost (3-4kHz)
ffmpeg -i input.mp3 -af "equalizer=f=3500:t=q:w=1:g=3" output.mp3

# De-ess (reduce sibilance around 6-8kHz)
ffmpeg -i input.mp3 -af "equalizer=f=7000:t=q:w=2:g=-4" output.mp3
```

---

## Create Ringtone

```bash
# 30-second clip with fades
ffmpeg -i song.mp3 -ss 60 -t 30 -af "afade=t=in:st=0:d=1,afade=t=out:st=29:d=1" -acodec libmp3lame -b:a 192k ringtone.mp3

# iPhone ringtone (M4R format)
ffmpeg -i song.mp3 -ss 60 -t 30 -af "afade=t=in:st=0:d=1,afade=t=out:st=29:d=1" -acodec aac -b:a 192k ringtone.m4r
```

---

## Batch Processing

```bash
# Convert all WAV to MP3
for f in *.wav; do ffmpeg -i "$f" -acodec libmp3lame -q:a 2 "${f%.wav}.mp3"; done

# Normalize all files in directory
for f in *.mp3; do ffmpeg -i "$f" -af "loudnorm=I=-16" "normalized_$f"; done
```


path: loudness.md
content: # Loudness Standards by Platform

## Podcasts

| Platform | Target LUFS | True Peak | Notes |
|----------|-------------|-----------|-------|
| Spotify | -14 LUFS | -1 dB | Will normalize louder files down |
| Apple Podcasts | -16 LUFS | -1 dB | Recommended by Apple |
| Google Podcasts | -16 LUFS | -1 dB | |
| YouTube | -14 LUFS | -1 dB | |
| General Standard | -16 LUFS | -1.5 dB | Safe for all platforms |

**Recommendation:** Target -16 LUFS for podcasts — it works everywhere.

```bash
# Podcast normalization
ffmpeg -i input.mp3 -af "loudnorm=I=-16:TP=-1.5:LRA=11" output.mp3

# Or with ffmpeg-normalize (more accurate)
ffmpeg-normalize input.mp3 -o output.mp3 -t -16 -tp -1.5
```

---

## Music Streaming

| Platform | Target LUFS | True Peak | Behavior |
|----------|-------------|-----------|----------|
| Spotify | -14 LUFS | -1 dB | Loud=normalizes down, quiet=normalizes up |
| Apple Music | -16 LUFS | -1 dB | Sound Check feature |
| YouTube Music | -14 LUFS | -1 dB | |
| Amazon Music | -14 LUFS | -2 dB | |
| Tidal | -14 LUFS | -1 dB | |
| Deezer | -15 LUFS | -1 dB | |

**Recommendation:** Master at -14 LUFS for streaming distribution.

---

## Broadcast

| Standard | Target LUFS | True Peak | Region |
|----------|-------------|-----------|--------|
| EBU R128 | -23 LUFS | -1 dB | Europe |
| ATSC A/85 | -24 LKFS | -2 dB | USA broadcast |
| ARIB TR-B32 | -24 LKFS | -1 dB | Japan |

---

## Social Media

| Platform | Recommendation | Notes |
|----------|----------------|-------|
| Instagram/Reels | -14 LUFS | Matches music streaming |
| TikTok | -14 LUFS | |
| Facebook | -14 LUFS | |
| Twitter/X | -14 LUFS | |

---

## Understanding LUFS

**LUFS** = Loudness Units Full Scale

- Measures *perceived* loudness, not just peak levels
- More accurate than peak normalization for consistent listening experience
- Negative values (like -16 LUFS) — closer to 0 = louder

**LRA** = Loudness Range
- Measures dynamic variation
- Lower LRA = more compressed sound
- Podcasts: LRA 5-10 is comfortable
- Music: LRA 8-15 is typical

**True Peak**
- The actual maximum amplitude
- Must stay below 0 dB to avoid clipping
- -1 dB to -1.5 dB provides safety margin

---

## Measuring Loudness

```bash
# Analyze loudness without processing
ffmpeg -i input.mp3 -af "loudnorm=print_format=summary" -f null -

# Output shows:
# Input Integrated: -20.5 LUFS
# Input True Peak: -0.3 dBTP
# Input LRA: 12.8 LU
```

---

## Common Mistakes

| Mistake | Problem | Solution |
|---------|---------|----------|
| Mastering too loud (-8 LUFS) | Platform turns it down anyway, loses dynamics | Target platform's standard |
| Not checking true peak | Digital clipping on some players | Keep TP below -1 dB |
| Inconsistent episode levels | Listeners adjust volume constantly | Use same target for all episodes |
| Only peak normalizing | Perceived loudness varies widely | Use LUFS-based normalization |


path: podcast.md
content: # Podcast Production Workflow

## Pre-Recording Checklist

- [ ] Room quiet (no AC, fans, outside noise)
- [ ] Mic positioned correctly (fist distance from mouth)
- [ ] Gain set properly (peaks around -12 dB)
- [ ] Recording software configured (44.1kHz or 48kHz, 16 or 24-bit)
- [ ] Headphones on to monitor

---

## Post-Production Pipeline

### 1. Backup Raw Recording
```bash
cp raw_episode.wav raw_episode_backup.wav
```

### 2. Noise Reduction (if needed)
```bash
# Basic cleanup
ffmpeg -i raw.wav -af "highpass=f=80,lowpass=f=12000" cleaned.wav

# For persistent background noise
ffmpeg -i raw.wav -af "afftdn=nf=-20" cleaned.wav
```

### 3. Remove Silence/Dead Air
```bash
# Compress long silences (>1s) to 0.5s
ffmpeg -i cleaned.wav -af "silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-40dB" trimmed.wav
```

### 4. Normalize Loudness
```bash
# Target -16 LUFS for podcast standard
ffmpeg -i trimmed.wav -af "loudnorm=I=-16:TP=-1.5:LRA=11" normalized.wav

# Or use ffmpeg-normalize for two-pass (more accurate)
ffmpeg-normalize trimmed.wav -o normalized.wav -t -16 -tp -1.5
```

### 5. Add Compression (Optional)
```bash
# Make quiet parts audible, prevent loud parts from blasting
ffmpeg -i normalized.wav -af "acompressor=threshold=-20dB:ratio=3:attack=5:release=100" compressed.wav
```

### 6. Add Intro/Outro
```bash
# Create list file
echo "file 'intro.wav'" > episode.txt
echo "file 'compressed.wav'" >> episode.txt
echo "file 'outro.wav'" >> episode.txt

# Concatenate
ffmpeg -f concat -safe 0 -i episode.txt -c copy episode_final.wav
```

### 7. Export for Distribution
```bash
# MP3 for hosting (most compatible)
ffmpeg -i episode_final.wav -acodec libmp3lame -b:a 128k -ar 44100 episode.mp3

# MP3 higher quality (if bandwidth not a concern)
ffmpeg -i episode_final.wav -acodec libmp3lame -b:a 192k -ar 44100 episode_hq.mp3

# Add metadata
ffmpeg -i episode.mp3 -metadata title="Episode 42: Topic" -metadata artist="Podcast Name" -metadata album="Season 2" -c copy episode_tagged.mp3
```

---

## Generate Transcript
```bash
# Extract audio for Whisper (16kHz mono)
ffmpeg -i episode.mp3 -ar 16000 -ac 1 episode_whisper.wav

# Transcribe
whisper episode_whisper.wav --model medium --output_format all

# Outputs: .txt, .srt, .vtt, .json
```

---

## Create Audiogram (Waveform Video)
```bash
# Generate waveform video for social media
ffmpeg -i highlight.mp3 -filter_complex \
  "[0:a]showwaves=s=1080x1920:mode=cline:colors=white[wave]; \
   color=c=black:s=1080x1920[bg]; \
   [bg][wave]overlay" \
  -c:v libx264 -c:a aac audiogram.mp4
```

---

## Extract Highlight Clips
```bash
# Cut 60-second clip starting at 15:30
ffmpeg -i episode.mp3 -ss 15:30 -t 60 -af "afade=t=in:d=1,afade=t=out:st=59:d=1" highlight.mp3
```

---

## Quality Checklist Before Publishing

- [ ] Loudness at -16 LUFS (check with `loudnorm=print_format=summary`)
- [ ] True peak below -1 dB
- [ ] No clipping or distortion
- [ ] Intro/outro attached
- [ ] Metadata (title, artist, album) added
- [ ] File plays correctly in different players
- [ ] File size reasonable for hosting limits

---

## Platform Export Specs

| Platform | Format | Bitrate | Sample Rate | Notes |
|----------|--------|---------|-------------|-------|
| Spotify | MP3 | 128-192 kbps | 44.1kHz | |
| Apple Podcasts | MP3/M4A | 128-192 kbps | 44.1kHz | |
| Google Podcasts | MP3 | 128+ kbps | 44.1kHz | |
| YouTube | AAC | 192+ kbps | 44.1/48kHz | Needs video for upload |

---

## Batch Episode Processing

```bash
#!/bin/bash
# Process all raw episodes in current directory

for raw in raw_*.wav; do
  name="${raw#raw_}"
  name="${name%.wav}"
  
  echo "Processing: $name"
  
  # Clean, normalize, export
  ffmpeg -i "$raw" \
    -af "highpass=f=80,loudnorm=I=-16:TP=-1.5:LRA=11" \
    -acodec libmp3lame -b:a 128k \
    "processed_${name}.mp3"
done
```


path: transcription.md
content: # Transcription Workflow

## Tools

| Tool | Use Case | Notes |
|------|----------|-------|
| **Whisper** | Local transcription | Free, accurate, many languages |
| **Whisper.cpp** | Fast local (C++ port) | Lower memory usage |
| **AssemblyAI** | API-based | Speaker diarization, paid |
| **Deepgram** | API-based | Fast, real-time capable |

---

## Whisper Local Transcription

### Install
```bash
pip install openai-whisper

# Or with conda
conda install -c conda-forge whisper
```

### Model Sizes

| Model | Size | Speed | Accuracy | VRAM |
|-------|------|-------|----------|------|
| tiny | 39M | Fastest | Lower | ~1GB |
| base | 74M | Fast | Good | ~1GB |
| small | 244M | Medium | Better | ~2GB |
| medium | 769M | Slow | Great | ~5GB |
| large | 1550M | Slowest | Best | ~10GB |
| turbo | 809M | Fast | Great | ~6GB |

**Recommendation:** Use `medium` or `turbo` for good balance.

### Basic Transcription
```bash
# Simple transcription (auto-detect language)
whisper audio.mp3 --model medium

# Specify language
whisper audio.mp3 --model medium --language en

# Output specific format
whisper audio.mp3 --model medium --output_format srt

# All formats at once
whisper audio.mp3 --model medium --output_format all
# Creates: .txt, .srt, .vtt, .json, .tsv
```

### Prepare Audio for Whisper
```bash
# Convert to 16kHz WAV (Whisper's preferred format)
ffmpeg -i input.mp3 -ar 16000 -ac 1 -acodec pcm_s16le whisper_ready.wav
```

---

## Subtitle Formats

### SRT (SubRip)
```
1
00:00:01,000 --> 00:00:04,500
This is the first subtitle line.

2
00:00:05,000 --> 00:00:08,000
This is the second line.
```

### VTT (WebVTT)
```
WEBVTT

00:00:01.000 --> 00:00:04.500
This is the first subtitle line.

00:00:05.000 --> 00:00:08.000
This is the second line.
```

### Convert Between Formats
```bash
# SRT to VTT
ffmpeg -i subtitles.srt subtitles.vtt

# VTT to SRT
ffmpeg -i subtitles.vtt subtitles.srt
```

---

## Speaker Diarization

Whisper alone doesn't identify speakers. Options:

### Option 1: pyannote.audio (Local)
```bash
pip install pyannote.audio

# Requires Hugging Face token for models
```

### Option 2: WhisperX (Whisper + diarization)
```bash
pip install whisperx

whisperx audio.mp3 --diarize --hf_token YOUR_TOKEN
```

### Option 3: AssemblyAI API
```bash
# Upload and transcribe with speakers
curl -X POST "https://api.assemblyai.com/v2/transcript" \
  -H "[REDACTED_SECRET]" \
  -d '{"audio_url": "URL", "speaker_labels": true}'
```

---

## Subtitle Timing Adjustments

```bash
# Shift all subtitles by 2 seconds later
ffmpeg -i subtitles.srt -itsoffset 2 subtitles_shifted.srt

# Speed up subtitles (for faster video)
# Multiply timestamps by factor (e.g., 0.5 for 2x speed)
# Requires external tool or script
```

### Python Script for Timing Adjustment
```python
import re

def shift_srt(input_file, output_file, shift_ms):
    """Shift all SRT timestamps by shift_ms milliseconds."""
    with open(input_file, 'r') as f:
        content = f.read()
    
    def shift_time(match):
        h, m, s, ms = map(int, match.groups())
        total_ms = h*3600000 + m*60000 + s*1000 + ms + shift_ms
        h = total_ms // 3600000
        m = (total_ms % 3600000) // 60000
        s = (total_ms % 60000) // 1000
        ms = total_ms % 1000
        return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
    
    shifted = re.sub(r'(\d{2}):(\d{2}):(\d{2}),(\d{3})', shift_time, content)
    
    with open(output_file, 'w') as f:
        f.write(shifted)

# Usage: shift_srt('input.srt', 'output.srt', 2000)  # +2 seconds
```

---

## Burn Subtitles into Video

```bash
# Basic burn-in
ffmpeg -i video.mp4 -vf "subtitles=captions.srt" output.mp4

# Styled subtitles
ffmpeg -i video.mp4 -vf "subtitles=captions.srt:force_style='FontSize=24,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,Outline=2'" output.mp4

# TikTok/Reels style (centered, bold)
ffmpeg -i video.mp4 -vf "subtitles=captions.srt:force_style='FontName=Arial Bold,FontSize=28,Alignment=2,MarginV=50'" output.mp4
```

---

## Transcription Quality Tips

1. **Clean audio first** — Remove noise before transcribing
   ```bash
   ffmpeg -i input.mp3 -af "highpass=f=80,lowpass=f=8000" cleaned.mp3
   whisper cleaned.mp3 --model medium
   ```

2. **Use appropriate model** — Larger models = better accuracy for unclear audio

3. **Specify language** — Don't rely on auto-detect for better results
   ```bash
   whisper audio.mp3 --language es --model medium
   ```

4. **Initial prompt for context** — Help Whisper with jargon or names
   ```bash
   whisper audio.mp3 --initial_prompt "Podcast about machine learning with hosts Alice and Bob"
   ```

---

## Common Issues & Fixes

| Issue | Cause | Fix |
|-------|-------|-----|
| Wrong language detected | Auto-detect confused | Specify `--language` |
| Hallucinations (repeated text) | Silence or noise | Clean audio, use larger model |
| Missing words | Audio too quiet | Normalize first |
| Wrong punctuation | Model limitation | Post-process transcript |
| Speaker confusion | No diarization | Use WhisperX or API |


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)