Metadata-Version: 2.4
Name: musicvisualizer
Version: 2.2.0
Summary: A plugin-based audio visualization tool that converts audio files into animated visualizer videos
Author-email: fredjt <61065187+fredjt@users.noreply.github.com>
License: MIT
Project-URL: Repository, https://github.com/fredjt/music-visualizer
Project-URL: Issues, https://github.com/fredjt/music-visualizer/issues
Project-URL: Changelog, https://github.com/fredjt/music-visualizer/blob/master/CHANGELOG.md
Keywords: audio,visualizer,ffmpeg,music,video
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: tqdm
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: pyright>=1.1; extra == "dev"

# Audio Visualizer

A plugin-based Python tool for converting audio files into animated visualizer videos.

## Features

- **Plugin Architecture**: Easily create, add, or remove visualizer plugins
- **Multiple Visualizer Modes**: Choose from several visualization styles
- **Hardware Acceleration**: NVENC (NVIDIA), QSV (Intel), VA-API (Linux) support
- **Multiple Output Formats**: MP4, MKV, WebM, AVI, MOV, FLV
- **Batch Processing**: Process entire directories of audio files
- **Progress Tracking**: Real-time progress bar with ETA
- **Customizable**: Adjust resolution, FPS, preset, CRF, and more

## Available Visualizers

- **Spectrum** (default) - Classic frequency spectrum bars
- **Waveform** - Audio waveform visualization
- **Bars** - Animated bar visualization
- **Radial** - Radial/circular spectrum display
- **Spectrogram** - Time-frequency spectrogram
- **Vectorscope** - Psychedelic circular visualization
- **Album Art** - Visualizer with album art overlay

## Requirements

- Python 3.10+
- [FFmpeg](https://ffmpeg.org/) ≥ 6.0 - Required for video encoding. The
  detected version is checked at startup (clear error below the project
  minimum); plugins may declare a higher `requires_ffmpeg_version`, and
  the selected modes are checked against the detected version before
  encoding starts.

Tested on Linux and macOS (macOS via the PR smoke subset). Windows is
not tested and is deferred to a later release.

## Installation

1. Clone the repository:
   ```bash
   git clone <repository-url>
   cd audio-visualizer
   ```

2. Install FFmpeg:
   - **Ubuntu/Debian**: `sudo apt install ffmpeg`
   - **macOS**: `brew install ffmpeg`
   - **Windows**: Download from [ffmpeg.org](https://ffmpeg.org/download.html)

3. Install the package:
   ```bash
   pip install .
   ```

## Usage

### Basic Usage

```bash
audio-visualizer input.mp3
```

### Specify a Visualizer Mode

```bash
audio-visualizer input.mp3 --mode waveform
audio-visualizer input.mp3 --mode radial

# Compose several visualizers side by side in one frame
audio-visualizer input.mp3 --mode spectrum waveform
```

Combo presets are available as a single `--mode` value — `--mode spectrum+waveform` is equivalent to `--mode spectrum waveform` — and `--list-plugins` shows the built-in presets under `[combos]`. Any combination of loaded modes works; unknown or duplicated names fail with the standard mode error.

### Per-Mode Options

Append `:key=value` pairs to a mode name to override that mode's options in a composite — repeat `--mode` (or list several specs) to pair several modes:

```bash
# spectrum on a navy background, vectorscope zoomed in 1.5x
audio-visualizer input.mp3 --mode "spectrum:bg_color=navy" --mode "vectorscope:zoom=1.5"
```

- Keys may be the mode's full option name (`spectrum-opacity`) or the bare name with the mode prefix stripped (`opacity`). Values are validated against that mode's option schema.
- `bg_color` and `colors` are accepted per mode (useful when two modes want different backgrounds); `opacity` (0.0–1.0) additionally works per mode on `--compose overlay` — the last mode is always opaque.
- Multiple pairs are comma-separated: `--mode "spectrum:bg_color=navy,colors=red,blue"` (commas inside a value are fine as long as they don't form a new `key=`).
- Precedence: **per-mode pair > global CLI flag > config file > default**. A global flag still applies to modes without a pair, and unpaired shared options resolve first-mode-wins (with a warning), as before.
- `--dry-run --json` surfaces the resolved per-mode values under `per_mode_options`.

### Tile Grid

`--compose tile` places modes in a row-major `ceil(sqrt(n)) x ceil(n/cols)` grid by default. `--compose-grid ROWSxCOLS` places the cells explicitly (e.g. `--compose tile --compose-grid 2x3 --mode spectrum waveform bars` puts the three modes in the first row of a 2x3 grid); a grid larger than the mode count pads the remaining cells with the background color, and a grid with fewer cells than modes is an error.

### Custom Output

```bash
audio-visualizer input.mp3 -o output.mp4 --resolution 1920x1080
```

### Hardware Acceleration

```bash
# Auto-detect (default)
audio-visualizer input.mp3

# Force NVIDIA encoding
audio-visualizer input.mp3 --accel nvenc

# Force Intel Quick Sync
audio-visualizer input.mp3 --accel qsv

# Force Linux VA-API
audio-visualizer input.mp3 --accel vaapi

# Disable hardware acceleration
audio-visualizer input.mp3 --accel disabled
```

### Output Format

```bash
# MP4 (default)
audio-visualizer input.mp3

# MKV
audio-visualizer input.mp3 --format mkv

# WebM (VP9)
audio-visualizer input.mp3 -f webm

# AVI
audio-visualizer input.mp3 -f avi
```

### Color Themes

`--theme` applies a built-in color palette to the visualizer; `--list-themes`
shows the available themes (with color swatches when the output is a tty and
the palette length, so you can compare them).

**Custom themes** — drop a JSON file in
`~/.config/audio-visualizer/themes/<name>.json`:

```json
{"palette": ["red", "orange", "gold", "white"]}
```

The `palette` is a list of 1–12 color names (or `#rrggbb` hex values). The
theme is then usable as `--theme <name>`, the `AUDIO_VISUALIZER_THEME` env
var, or `theme:` in the config file. A user theme with the same name as a
built-in overrides it; `--list-themes` shows built-in and user themes in
separate sections. Malformed theme files are skipped with a warning.

<!-- THEME-TABLE:BEGIN (generated by scripts/generate_flag_reference.py - do not edit by hand) -->
| Theme | Palette |
| --- | --- |
| `neon` | lime,magenta,cyan,yellow |
| `sunset` | red,orange,yellow,white |
| `ocean` | blue,cyan,teal,white |
| `forest` | green,lime,yellow,white |
| `dark` | purple,blue,gray,white |
| `light` | white,gray,blue,green |
<!-- THEME-TABLE:END -->

Precedence: an explicit `--colors` always wins; otherwise CLI `--theme` >
`AUDIO_VISUALIZER_THEME` env var > `theme` in the config file. Built-in and
custom themes may each carry up to 12 colors; visualizers that accept a
color list (e.g. `showfreqs`, `showwaves`) interpolate longer palettes.

### Batch Processing

Process all audio files in a directory:

```bash
# Process all audio files in a directory
audio-visualizer ./my-album/ --batch

# Short form
audio-visualizer ./my-album/ -b

# With custom settings
audio-visualizer ./my-album/ -b --mode waveform --resolution 1920x1080

# Parallel encode (N ffmpeg jobs; 'auto' = CPU count)
audio-visualizer ./my-album/ -b --parallel auto

# Recursive scan + glob filters (both repeatable)
audio-visualizer ./music/ -b --recursive --include "*.mp3" --exclude "*lossless*"

# From an .m3u/.m3u8 playlist - entries run in playlist order
audio-visualizer ./my-album/list.m3u8
audio-visualizer -b ./my-album/list.m3u8
```

A single input that is an `.m3u`/`.m3u8` file is expanded in order and
processed as a batch (the batch report is the result); a playlist may
also be passed directly as the `--batch` root. Comment/`#EXT*` lines are
skipped, entries resolve against the playlist's directory, and missing
entries get their own failure row in the report.

```bash
# Custom output naming - placeholders: {name} {ext} {mode} {width}
audio-visualizer ./my-album/ -b --output-template "{name}_{mode}.{ext}"
```

Discovery and naming:

- `--recursive` - scan subdirectories as well
- `--include PATTERN` / `--exclude PATTERN` - repeatable globs matched
  against the path relative to the batch directory; the built-in audio
  extension list is the default include
- `--output-template TEMPLATE` (default `{name}_visualizer.{ext}`)

Every batch run writes `<outdir>/batch_report.json` with per-file status,
output path, size, duration, failure reason, and timestamp. The exit code is
non-zero only when at least one file failed (clean run -> 0, failures -> 1),
so CI can gate on the batch exit code.

Supported formats: MP3, WAV, FLAC, OGG, M4A, AAC, WMA

### Environment Variables

All configuration options can be set via environment variables with the `AUDIO_VISUALIZER_` prefix. This is useful for Docker, CI/CD, or shell profiles.

```bash
# Set resolution and FPS via environment
export AUDIO_VISUALIZER_RESOLUTION=1920x1080
export AUDIO_VISUALIZER_FPS=60
export AUDIO_VISUALIZER_PRESET=fast

audio-visualizer input.mp3
```

Supported environment variables:

| Variable | Description | Example |
|---|---|---|
| `AUDIO_VISUALIZER_RESOLUTION` | Output resolution | `1920x1080` |
| `AUDIO_VISUALIZER_FORMAT` | Output format | `mkv` |
| `AUDIO_VISUALIZER_FPS` | Frame rate | `60` |
| `AUDIO_VISUALIZER_PRESET` | Encoding preset | `fast` |
| `AUDIO_VISUALIZER_CRF` | Quality (0-51) | `18` |
| `AUDIO_VISUALIZER_ACCEL` | Hardware acceleration | `nvenc` |
| `AUDIO_VISUALIZER_MODE` | Visualizer mode | `waveform` |
| `AUDIO_VISUALIZER_COLORS` | Custom colors | `red,blue,green` |
| `AUDIO_VISUALIZER_THEME` | Color theme | `neon` |
| `AUDIO_VISUALIZER_OUTPUT_DIR` | Output directory | `/path/to/output` |

**Precedence**: CLI arguments > environment variables > config file > defaults.

### Configuration File

Options can be persisted to `~/.audio_visualizer/config.yml`. Use `--save-config` to write the current options and `--config` to display the file:

```bash
audio-visualizer input.mp3 --save-config
audio-visualizer --config
```

Per-plugin options can be set in indented sections. Values are validated against each plugin's option schema (type, choices, range):

```yaml
# Global options
resolution: 1920x1080
fps: 60

# Per-plugin options
spectrum:
  mode: rainbow
  opacity: 0.8
waveform:
  mode: line
  splits: 2
```

Unknown plugins/options and values that fail validation are skipped with a warning, so a broken config file never crashes the tool.

### List Available Plugins

```bash
audio-visualizer --list-plugins
```

Add `--json` for a machine-readable payload (plugin metadata plus full
option schemas).

### Diagnose the Environment

```bash
audio-visualizer --doctor
```

Reports the ffmpeg version against the project minimum, whether each
visualizer's filters are present in your ffmpeg build, the hardware
encoders available, the config file path and validity, and the plugin
load report (failures include the reason). `--doctor --json` emits the
same report as JSON; the exit code is non-zero when anything is wrong,
so it is usable as a preflight check in CI.

### Install Custom Plugins

```bash
audio-visualizer --install-plugin my_plugin.py
```

Installs the plugin to `~/.audio_visualizer/plugins`, where it is
auto-discovered on every run.

### Load External Plugins

Plugins can be loaded without installing, either for one run or for all
runs:

```bash
# From the command line (repeatable, file or directory)
audio-visualizer --plugin-path ~/plugins/my_plugin.py --mode myviz input.mp3
audio-visualizer --plugin-path ~/plugins/ --list-plugins

# For all runs, via environment variable (path-separated list)
export AUDIO_VISUALIZER_PLUGIN_PATH=~/plugins/my_plugin.py:~/plugins/other/
```

Third-party distributions can ship plugins too: register them under the
`audio-visualizer.plugins` entry-point group (see *Creating a New Visualizer
Plugin*). Installed plugins and `--plugin-path` plugins shadow built-ins of
the same name, with a warning; `--list-plugins --verbose` shows each
plugin's source (builtin, entrypoint, installed, or path). Plugins from
`path` or `entrypoint` sources print a one-line trust warning on first
load — a plugin is arbitrary Python code, so review third-party code
before use (see the [security model](docs/PLUGIN_DEVELOPMENT.md#7-security-model)).

### Full Usage

```bash
audio-visualizer input.mp3 [options]
audio-visualizer ./directory/ --batch [options]
```

All options can also be set via environment variables (e.g.,
`AUDIO_VISUALIZER_RESOLUTION=1920x1080`) or in the config file (see
[Configuration File](#configuration-file)).

The tables below are generated from the live CLI parser by
[`scripts/generate_flag_reference.py`](scripts/generate_flag_reference.py) -
run `python scripts/generate_flag_reference.py` after changing any flag (CI
runs `--check` and fails when the tables drift from the parser).

<!-- FLAG-REF:BEGIN (generated by scripts/generate_flag_reference.py - do not edit by hand) -->
#### Positional arguments

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| `input` | str | — | Input MP3 (or audio) file, or an .m3u/.m3u8 playlist (processed as a batch) |

#### Global options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| -h`, `--help | flag (no value) | — | show this help message and exit |
| -o`, `--output | str | — | Output file (single) or output directory (batch mode) |
| --mode | str (one or more) | — | Visualization mode/plugin name(s); 2+ are composed into one frame (default: spectrum); combos like 'spectrum+waveform' also work. Per-mode option pairs: 'spectrum:bg_color=navy' overrides that mode's option (beats global flags); 'opacity' works on --compose overlay |
| --compose | one of: hstack, vstack, tile, overlay | 'hstack' | Layout for multi-mode composites (default: hstack) |
| --compose-scale | one of: fill, fit | 'fill' | Cell scaling for multi-mode composites: fill stretches, fit preserves aspect ratio (default: fill) |
| --compose-grid | str | — | Explicit ROWSxCOLS grid for --compose tile (default: row-major ceil(sqrt(n)) grid); a grid larger than the mode count pads the remainder with the background color |
| --resolution`, `-r | str | '1280x720' | Output resolution WxH or preset 360p/480p/720p/1080p/1440p/4k (default: 1280x720) |
| --fps | int | 30 | Frame rate (default: 30) |
| --preset | one of: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow | 'medium' | Encoding preset (default: medium) |
| --crf | one of: 0-51 | 23 | Quality (18=lossless, 23=default, 28=max). Lower=better (default: 23) |
| --verbose`, `-v | flag (no value) | False | Show detailed filter and command info |
| --quiet`, `-q | flag (no value) | False | Suppress non-error output |
| --log-file | str | — | Also write log output (debug level) to PATH |
| --debug`, `-d | flag (no value) | False | Show debug-level output |
| --list-plugins | flag (no value) | False | List all available visualizer plugins (with --json: machine-readable schemas) |
| --doctor | flag (no value) | False | Report ffmpeg/filters/hwaccel/config/plugin health (with --json for scripting) |
| --category | one of: composite, meter, scope, spectrum, waveform | — | Filter --list-plugins by plugin category |
| --install-plugin | str | — | Install a plugin from a Python file |
| --plugin-path | str | [] | Load external plugin(s) from a .py file or directory (repeatable; also AUDIO_VISUALIZER_PLUGIN_PATH, path-separated) |
| --image | str | — | Image file for image visualizer plugin (album art) |
| --border | flag (no value) | False | Add a colored border around the visualization |
| --border-color | str | 'ffffff' | Border color hex (default: ffffff) |
| --border-width | int | 2 | Border width in pixels (default: 2) |
| --aspect-ratio | one of: 16:9, 4:3, 1:1, 21:9 | — | Aspect ratio (auto-calculates resolution). Overrides --resolution |
| --visualizer-config | str | — | Save/load per-visualizer preset configuration file |
| --format`, `-f | one of: mp4, mkv, webm, avi, mov, flv | 'mp4' | Output format (default: mp4) |
| --accel | one of: auto, nvenc, qsv, vaapi, disabled | 'auto' | Hardware acceleration (default: auto) |
| --batch`, `-b | str | — | Process all audio files in a directory (or the entries of an .m3u/.m3u8 playlist) |
| --overwrite | flag (no value) | False | Overwrite existing output files in batch mode |
| --parallel`, `-j | str | '1' | Run N ffmpeg encodes concurrently in batch mode (default: 1 = sequential; 'auto' = CPU count) |
| --output-template | str | '{name}_visualizer.{ext}' | Batch output filename template with placeholders {name} {ext} {mode} {width} (default: '{name}_visualizer.{ext}') |
| --recursive | flag (no value) | False | Batch: also scan subdirectories |
| --include | str | [] | Batch: only process files whose path (relative to the batch dir) matches this glob (repeatable; default: built-in audio extensions) |
| --exclude | str | [] | Batch: skip files whose path matches this glob (repeatable) |
| --report | str | [] | Batch: also write the batch report (JSON) to this file, in addition to the default batch_report.json in the output directory (repeatable) |
| --retry-failed | str | — | Batch: re-run only the entries with status 'failed' from the given batch report (with this invocation's settings). Exits 0 when the retry pass completes, even if some entries are still failing; the new report lists them as failed |
| --resume | str | — | Batch: re-run only the entries of the given batch report that are not successful (successful = old status 'success' and the output file still exists); the new report merges the preserved entries with the re-run ones, and a copy of the old report is kept as batch_report.before-resume.json next to it |
| --colors | str | — | Comma-separated colors for visualizer (e.g., 'blue,green,white') |
| --bg-color | str | — | Background color (default: black). Accepts named colors or hex (e.g., 'navy', '#1a1a2e') |
| --title | str | — | Set the output video's title (overrides the source file's tag) |
| --artist | str | — | Set the output video's artist (overrides the source file's tag) |
| --album | str | — | Set the output video's album (overrides the source file's tag) |
| --comment | str | — | Set the output video's comment (overrides the source file's tag) |
| --no-metadata | flag (no value) | False | Strip all metadata tags from the output video |
| --theme | one of: dark, forest, light, neon, ocean, sunset | — | Color theme: built-in or user-defined (see --list-themes) |
| --list-themes | flag (no value) | False | List available color themes and exit |
| --png | str | — | With --list-themes: render one 1-second preview PNG per theme into DIR |
| --config | flag (no value) | False | Show current configuration file path |
| --save-config | flag (no value) | False | Save current options to configuration file |
| --validate-config | flag (no value) | False | Validate configuration and exit |
| --validate | flag (no value) | False | Validate all installed plugins and exit |
| --dry-run | flag (no value) | False | Show what would be done without encoding |
| --json | flag (no value) | False | With --dry-run: print machine-readable JSON instead of text |
| --version | flag (no value) | False | Print package and ffmpeg versions and exit |
| --info | str | — | Show detailed audio file information and exit |
| --retry`, `-R | int | 0 | Number of retries on ffmpeg failure (default: 0) |

#### [composite] Album Art Visualizer options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --albumart-effect | one of: glow, pulse, zoom | 'glow' | Background effect (glow, pulse, zoom) |
| --albumart-position | one of: bottom, top, left, right, fullscreen | 'bottom' | Spectrum overlay position (bottom, top, left, right, fullscreen) |
| --albumart-glow-intensity | float | 0.08 | Glow intensity (0.0-1.0) |
| --albumart-fit | one of: cover, contain, stretch | 'cover' | Image fit mode (cover, contain, stretch) |
| --albumart-blur | int | 0 | Gaussian blur radius (0-50) |

#### [meter] Beat Pulse options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --beatpulse-intensity | float | 1.0 | Pulse size multiplier (0.2-2.0) |
| --beatpulse-thickness | float | 1.0 | Radial falloff exponent: low = thin core, high = broad ring (0.2-3.0) |
| --beatpulse-color | str | 'cyan' | Pulse color (named color or hex) |

#### [meter] Volume Meter options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --showvolume-mode | one of: peak, rms | 'peak' | Volume mode (peak, rms) |
| --showvolume-orientation | one of: h, v | 'h' | Orientation (h, v) |
| --showvolume-scale | one of: lin, log | 'lin' | Display scale (lin, log) |

#### [meter] Psychedelic Vectorscope options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --vectorscope-mode | one of: frame, line, dot | 'frame' | Draw mode (frame, line, dot) |
| --vectorscope-scale | one of: lin, sqrt, cbrt, log | 'cbrt' | Amplitude scale (lin, sqrt, cbrt, log) |
| --vectorscope-zoom | float | 1.0 | Zoom level (0.1-10.0) |
| --vectorscope-trails | int | 100 | Trail persistence (0-255) |
| --vectorscope-framerate | int | 0 | Custom frame rate (0 = auto) |
| --vectorscope-border | flag (no value) | False | Enable border around scope |
| --vectorscope-color-index | int | 0 | Palette color index |

#### [scope] Band Lissajous options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --lissajous-low-band | int | 300 | X channel: lowpass cutoff in Hz (20-2000) |
| --lissajous-high-band | int | 3000 | Y channel: highpass cutoff in Hz (200-20000) |
| --lissajous-smooth | float | 0.7 | Trail persistence: 0.0 = crisp point, 1.0 = long trail (0.0-1.0) |

#### [spectrum] Classic Bars options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --bar-spacing | float | 0.5 | Bar spacing (0.0-1.0) |
| --bar-orientation | one of: horizontal, vertical | 'horizontal' | Bar orientation (horizontal, vertical) |

#### [spectrum] Circular Spectrum options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --circular-spectrum-mode | one of: channel, intensity, rainbow, moreland, nebulae, fire, fiery, fruit, cool, magma, green, viridis, plasma, cividis, terrain, colormapping | 'rainbow' | Colormap mode (rainbow, fire, magma, viridis, etc.) |
| --circular-spectrum-opacity | float | 1.0 | Spectrum opacity (0.0-1.0) |

#### [spectrum] CQT Spectrum options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --cqt-gamma | float | 3.0 | Sonogram gamma (1.0-7.0) |
| --cqt-basefreq | float | 20.0 | Base frequency in Hz |
| --cqt-endfreq | float | 20495.0 | End frequency in Hz |
| --cqt-attack | float | 0.0 | Attack time (0.0-1.0) |
| --cqt-timeclamp | float | 0.17 | Timeclamp value (0.002-1.0) |

#### [spectrum] Peak-Hold Bars options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --peakbars-hold | float | 0.1 | Seconds each peak is held at full height (0.0-2.0) |
| --peakbars-decay | float | 0.6 | Seconds the held peak takes to decay away (0.0-2.0) |

#### [spectrum] Mirrored Spectrum options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --mirror-axis | one of: x, y, xy | 'y' | Mirror axis (x, y, xy) |
| --mirror-gap | int | 0 | Gap pixels between original and mirrored halves |
| --mirror-opacity | float | 1.0 | Opacity of mirrored half (0.0-1.0) |

#### [spectrum] Showfreqs options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --showfreqs-mode | one of: bar, line, dot | 'bar' | Display mode (bar, line, dot) |
| --showfreqs-ascale | one of: lin, sqrt, cbrt, log | 'log' | Amplitude scale (lin, sqrt, cbrt, log) |
| --showfreqs-fscale | one of: lin, log, rlog | 'log' | Frequency scale (lin, log, rlog) |
| --showfreqs-winfunc | one of: rect, bartlett, hann, hanning, hamming, blackman, welch, flattop, bharris, bnuttall, bhann, sine, nuttall, lanczos, gauss, tukey, dolph, cauchy, parzen, poisson, bohman, kaiser | 'hann' | Window function (hann, hamming, blackman, etc.) |
| --showfreqs-overlap | float | 0.5 | Window overlap (0.0-1.0) |
| --showfreqs-channels | str | 'all' | Channels to draw (all, 0, 1, etc.) |

#### [spectrum] Spectrogram Waterfall options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --spectrogram-mode | one of: intensity, rain, fire, heat, ice, swirl | 'intensity' | Colormap mode (intensity, rain, fire, heat, ice, swirl) |
| --spectrogram-speed | float | 0.95 | Scroll speed (0.0-1.0, higher = slower) |
| --spectrogram-opacity | float | 1.0 | Waterfall opacity (0.0-1.0) |
| --spectrogram-transpose | flag (no value) | False | Rotate spectrogram 90 degrees |

#### [spectrum] Color Spectrum options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --spectrum-mode | one of: channel, intensity, rainbow, moreland, nebulae, fire, fiery, fruit, cool, magma, green, viridis, plasma, cividis, terrain, colormapping | 'rainbow' | Colormap mode (rainbow, fire, magma, viridis, etc.) |
| --spectrum-style | one of: replace, scroll, fill | 'scroll' | Slide mode (replace, scroll, fill) |
| --spectrum-opacity | float | 1.0 | Spectrum opacity (0.0-1.0) |
| --spectrum-start | float | 0 | Start frequency for display range (Hz) |
| --spectrum-stop | float | 0 | Stop frequency for display range (Hz) |

#### [waveform] Oscilloscope options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --oscilloscope-persist | float | 0.5 | Seconds of trace persistence/afterglow (0.0 disables) |
| --oscilloscope-scale | one of: lin, sqrt, cbrt, log, dbl, pwr | 'cbrt' | Amplitude scale (lin, sqrt, cbrt, log, dbl, pwr) |
| --oscilloscope-grid | float | 0.25 | Grid line spacing as a fraction of frame size; 0.0 disables the grid |

#### [waveform] Gradient Waveform options

| Flag | Takes | Default | Description |
| --- | --- | --- | --- |
| --waveform-mode | one of: point, line, p2p, cline, pict | 'line' | Waveform display mode (line, point, p2p, cline, pict) |
| --waveform-splits | int | 1 | Draw channels separately (e.g., stereo L/R) when > 1 |
| --waveform-frame | int | 0 | Audio samples per frame (0 = default) |
| --waveform-colors-per-channel | flag (no value) | False | Assign different colors per channel |
<!-- FLAG-REF:END -->

## Creating a New Visualizer Plugin

Scaffold a ready-to-edit plugin package (pyproject.toml with the
`audio-visualizer.plugins` entry point, a template plugin module that
doubles as the canonical minimal example, and a label-discipline test):

```bash
audio-visualizer new-plugin my-viz [--dir ~/plugins/my-viz]
pip install -e ~/plugins/my-viz
```

From scratch:

1. Create a Python file with a class inheriting from `VisualizerPlugin` in
   `audio_visualizer/visualizers/base.py`
2. Define a `metadata` (`PluginMetadata`) and an `options` list (`PluginOption`)
   — the CLI flags are auto-generated from this schema
3. Implement `build_filter()`
4. Choose how it gets discovered:
   - **Built-in**: place the file in the `visualizers/` directory
   - **Installed**: `audio-visualizer --install-plugin my_plugin.py`
   - **External path**: `--plugin-path` or `AUDIO_VISUALIZER_PLUGIN_PATH`
   - **Distribution**: ship a package registering an entry point, e.g.

     ```toml
     [project.entry-points."audio-visualizer.plugins"]
     myviz = "mymypkg.plugin:MyVizPlugin"
     ```

All discovery paths apply the same schema validation and isolation: a broken
plugin is skipped with a warning instead of crashing the run.

### The `build_filter()` contract

`build_filter()` returns the filter graph for your visualization. It must
produce the plugin's **main output** labeled with `get_output_label()`
(default: `viz`) and must **not** produce the final `[out]` label — the
base class composes that (overlaying your output on the background input,
or relabeling it) and then applies post-processing such as the border.

```python
def build_filter(self, width, height, fps, options, audio_index=0, video_index=1, **kwargs):
    return f"[{audio_index}:a] showspectrum=size={width}x{height}[viz]"
```

For multi-layer visualizations, return a `dict` mapping output names to
filter segments (the first key is the main output):

```python
def build_filter(self, width, height, fps, options, **kwargs):
    return {
        "viz": "[0:a] showvolume=w=1280:h=720[viz]",
        "spectrum": "[0:a] showspectrum=size=1280x720[spectrum]",
    }
```

Plugins that need custom inputs (image files, no background, etc.)
override `get_inputs()`, and plugins with a custom final composition
override `compose_final_filter()`. See `albumart.py`, `vectorscope.py`,
and `radial.py` for examples.

For the full guide — plugin anatomy, a worked example (the built-in
`peakbars`), packaging a plugin as an entry-point distribution, and
testing — see [docs/PLUGIN_DEVELOPMENT.md](docs/PLUGIN_DEVELOPMENT.md).

## Project Structure

```
audio_visualizer/
├── __init__.py
├── __main__.py              # Main application entry point
└── visualizers/
    ├── __init__.py
    ├── base.py              # Base plugin class
    ├── registry.py          # Plugin registry system
    ├── spectrum.py          # Spectrum visualizer
    ├── waveform.py          # Waveform visualizer
    ├── bars.py              # Bars visualizer
    ├── radial.py            # Radial visualizer
    ├── spectrogram.py       # Spectrogram visualizer
    ├── vectorscope.py       # Vectorscope visualizer
    └── albumart.py          # Album art visualizer
```

## License

MIT License
