round from from Claude Opus 4.8

This commit is contained in:
2026-05-30 15:11:07 -06:00
parent 981106d3df
commit d71fcbab25
12 changed files with 1294 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
# Python Soundboard — Claude Code Build Plan
## Overview
A desktop soundboard application for Windows 11 that routes audio through VB-Cable so it
appears as a microphone input to other applications (Discord, OBS, Teams, etc.). Users can
add and remove audio files from the board at runtime via a simple GUI.
---
## Tech Stack
| Layer | Choice | Reason |
|---|---|---|
| Language | Python 3.11+ | Easy iteration, good audio library support |
| GUI | `tkinter` | Stdlib, no install needed, sufficient for this use case |
| Audio playback | `sounddevice` + `soundfile` | Device selection by name, supports WAV/FLAC natively |
| Audio decoding | `pydub` + `ffmpeg` | Converts MP3/OGG/etc. to raw PCM for sounddevice |
| Config persistence | `json` | Store board layout and device preference between sessions |
### Install Requirements (`requirements.txt`)
```
sounddevice
soundfile
pydub
numpy
```
> ffmpeg must also be installed and on PATH (used by pydub for MP3 decoding).
> Recommend: `winget install Gyan.FFmpeg`
---
## Project Structure
```
soundboard/
├── main.py # Entry point, launches GUI
├── audio_engine.py # Handles device selection and playback
├── board.py # Board state: buttons, file mappings
├── config.py # Load/save config to JSON
├── config.json # Persisted state (auto-created on first run)
├── requirements.txt
└── sounds/ # Optional default folder for audio files
```
---
## Feature Spec
### 1. Device Selection (Startup)
- On launch, scan available output devices via `sounddevice.query_devices()`
- Show a dropdown/listbox to select the output device
- Default: auto-select first device whose name contains `"CABLE Input"` (VB-Cable)
- Selection is saved to `config.json` and restored on next launch
### 2. Soundboard Grid
- Display a grid of buttons (default: 4 columns, expandable rows)
- Each button shows:
- The audio file's base name (e.g. `airhorn.mp3`)
- A colored indicator when playing
- Clicking a button plays that sound through the selected output device
- Playing a sound does NOT stop other sounds (overlapping play supported)
- Optional: right-click a button → Stop this sound
### 3. Add Audio Files
- "Add Sound" button opens a file picker dialog
- Supported formats: `.wav`, `.mp3`, `.ogg`, `.flac`, `.aiff`
- File is registered on the board (path stored in config); it is NOT copied
- New button appears immediately in the grid
- Board layout is saved to `config.json` automatically
### 4. Remove Audio Files
- Right-click any button → context menu with "Remove" option
- Confirmation prompt before removal
- Button is removed from the grid; entry deleted from config
- Does not delete the file from disk
### 5. Rename Buttons
- Right-click → "Rename" option
- Simple text entry dialog
- Custom label saved to config (file path unchanged)
### 6. Volume Control
- A global volume slider (0100%) in the toolbar
- Scales the numpy audio array before playback
- Per-button volume is a stretch goal (not in v1)
### 7. Stop All
- "Stop All" button immediately halts all active sounddevice streams
### 8. Config Persistence (`config.json` schema)
```json
{
"output_device": "CABLE Input (VB-Audio Virtual Cable)",
"volume": 80,
"buttons": [
{
"id": "btn_001",
"label": "Air Horn",
"file_path": "C:/Users/user/sounds/airhorn.mp3"
},
{
"id": "btn_002",
"label": "Sad Trombone",
"file_path": "C:/Users/user/sounds/sad_trombone.wav"
}
]
}
```
---
## Key Implementation Details for Claude Code
### Audio Playback (audio_engine.py)
```python
import sounddevice as sd
import soundfile as sf
import numpy as np
from pydub import AudioSegment
import io
def get_device_id(device_name: str) -> int:
"""Find output device index by name substring match."""
devices = sd.query_devices()
for i, dev in enumerate(devices):
if device_name.lower() in dev['name'].lower() and dev['max_output_channels'] > 0:
return i
raise ValueError(f"Device not found: {device_name}")
def load_audio(file_path: str) -> tuple[np.ndarray, int]:
"""Load any supported audio format, return (samples_array, samplerate)."""
ext = file_path.lower().split('.')[-1]
if ext in ('wav', 'flac', 'aiff'):
data, sr = sf.read(file_path, dtype='float32')
else:
# Use pydub for MP3/OGG/etc.
seg = AudioSegment.from_file(file_path)
seg = seg.set_channels(2).set_frame_rate(44100)
samples = np.array(seg.get_array_of_samples(), dtype=np.float32)
samples = samples.reshape((-1, 2)) / 32768.0
data, sr = samples, 44100
return data, sr
def play_sound(file_path: str, device_id: int, volume: float = 1.0):
"""Play audio non-blocking on the specified device."""
data, sr = load_audio(file_path)
data = data * volume # apply volume scaling
sd.play(data, samplerate=sr, device=device_id, blocking=False)
```
### Threading Note
- `sd.play()` is non-blocking by default but is not thread-safe when called rapidly
- Use a `threading.Thread` per button press to avoid GUI freezes on slow file loads
- Keep a list of active streams if stop-all is needed
### GUI Layout (main.py / board.py)
- Use `tkinter.ttk` for slightly nicer widgets
- Grid of `tk.Button` widgets built dynamically from config
- `tkinter.filedialog.askopenfilename()` for file picker
- `tkinter.simpledialog.askstring()` for rename
- `tkinter.Menu` for right-click context menus
---
## VB-Cable Setup Instructions (include in README)
1. Download and install VB-Cable from https://vb-audio.com/Cable
2. Reboot after installation
3. In the soundboard, select **"CABLE Input (VB-Audio Virtual Cable)"** as the output device
4. In Discord / OBS / Teams, set the microphone input to **"CABLE Output (VB-Audio Virtual Cable)"**
5. Audio played on the soundboard will now appear as microphone input
---
## Claude Code Prompt (paste this to start the build)
```
Build a Python soundboard desktop app for Windows 11 using the spec in soundboard_plan.md.
Start with this order:
1. requirements.txt
2. audio_engine.py — device listing, audio loading, play/stop functions
3. config.py — load/save JSON config
4. board.py — board state management (add, remove, rename buttons)
5. main.py — tkinter GUI that wires everything together
Requirements:
- Output device is selectable from a dropdown at the top (auto-selects VB-Cable if found)
- Buttons play sounds non-blocking using threads so the GUI doesn't freeze
- Right-click context menu on each button: Rename, Remove
- "Add Sound" button opens a file picker (wav, mp3, ogg, flac)
- Global volume slider 0-100%
- "Stop All" button
- All state saved/loaded from config.json automatically
- No third-party GUI frameworks — tkinter only
```
---
## Stretch Goals (v2)
- Hotkey support (bind sounds to keyboard shortcuts)
- Per-button color customization
- Drag-to-reorder buttons
- Loop toggle per button
- Waveform preview thumbnail on each button
- System tray icon so the window can be minimized