Author: Moisés Horta Valenzuela, hexorcismos
Date: May 2026
A generative model that synthesises audio in CoDiCodec's continuous latent space using Conditional Flow Matching (CFM) on a block-causal DiT architecture.
The model targets musical continuation / improvising accompaniment: given a short audio prompt, it generates an arbitrarily long continuation in a chunk-causal, streaming fashion on the codec's ~11.7 Hz, 64-channel latent sequence.
- CoDiCodec (Pasini et al., 2025)
encodes 48 kHz stereo audio to summary embeddings at ~11.7 Hz with 64
channels (128x compression) and exposes a streaming
decode_next()API. The continuous latents, after the codec'satanh / sigma_rescale=0.8transform, are approximately unit-Gaussian — a direct fit for flow matching. - Block-causal Flow Matching DiT is the simplest architecture that:
- respects the codec's chunk structure,
- supports KV-caching for efficient streaming inference,
- has unconditional dropout-based classifier-free guidance for free.
- The whole pipeline is MPS-friendly so it can be trained and run in real-time on a 36 GB Apple Silicon laptop.
Try CoDiCodec-Flow directly in your browser using the Google Colab notebook in the colab/ directory. The notebook provides a step-by-step guide for:
- Cloning the repository and setting up the environment
- Preprocessing audio data to latents
- Training a model on your data
- Generating audio continuations
Click the badge at the top of this README to open the notebook in Colab.
codicodec-flow/
codicodec/ upstream codec package (do not modify)
flow/ this project
__init__.py
codec_wrapper.py MPS-safe wrapper around codicodec.EncoderDecoder
config.py dataclass-based config
utils.py device, masks, logging
data/
preencode.py audio dir -> per-file .pt latent shards
latent_dataset.py
model/
dit.py block-causal flow-matching DiT
cfm.py CFM loss + Euler/Heun samplers
ema.py
train.py training loop
sample.py offline sampling
smoke_test.py end-to-end sanity check
requirements.txt
README.md (this file)
# Create conda environment
conda create -n codicodec-flow python=3.10
conda activate codicodec-flow
# Install dependencies
pip install -r requirements.txt
# Install the upstream CoDiCodec package
pip install -e ./codicodec
# Verify codec works on your machine (downloads checkpoint on first run)
python -m flow.smoke_test --device mps # Use 'cuda' for NVIDIA GPUscodicodec-flow provides a user-friendly CLI wrapper that simplifies training, preprocessing, and generation without requiring python -m flow... commands.
# Preprocess audio data
python cli.py preprocess --in-dir ~/music/training --out-dir ./data/latents --device mps
# Train a model (TUI monitoring enabled by default)
python cli.py train --data-dir ./data/latents --out-dir ./runs/v0 --device mps
# Generate audio
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --out ./out.wav --device mps
# Stream audio indefinitely with live keyboard controls (press 'q' to quit)
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema --device mpsSee Real-time Streaming Generation for the full list of options and live keyboard controls.
Before training, you need to convert your audio files into latent shards using the CoDiCodec encoder.
python cli.py preprocess \
--in-dir /path/to/your/audio \
--out-dir ./data/latents \
--device mps \
--max-seconds 60Arguments:
--in-dir: Directory containing audio files (WAV, MP3, FLAC, etc.)--out-dir: Output directory for latent shards (.pt files)--device:mpsfor Apple Silicon,cudafor NVIDIA GPUs,cpuas fallback--max-seconds: Maximum duration per file (default: 300s). Longer files are split.
Output:
- Each audio file produces a
.ptfile containing the encoded latent representation - Latents are stored as
[T, 8, 64]tensors (T = number of 0.683s chunks) - Files are stored with metadata for the dataset loader
Tips:
- Use diverse audio for better generalization (different styles, instruments, tempos)
- 48 kHz stereo audio is recommended (CoDiCodec's native rate)
- Aim for several hours of audio for reasonable training
- Train for at least 100K steps for meaningful results; the v3_okachihuali model was trained for 6,860,000 steps
Train a block-causal Flow Matching DiT model on the preprocessed latents.
python cli.py train \
--data-dir ./data/latents \
--out-dir ./runs/v0 \
--device mps \
--batch-size 4 \
--grad-accum 2 \
--crop-tokens 512 \
--max-steps 200000Key Arguments:
--data-dir: Directory containing preprocessed latent shards--out-dir: Output directory for checkpoints and logs--device:mps,cuda, orcpu--batch-size: Batch size per GPU (default: 8, use 4 on MPS)--grad-accum: Gradient accumulation steps (effective batch = batch_size × grad_accum)--crop-tokens: Random crop length in tokens (default: 768, must be multiple of 8)--max-steps: Total training steps (default: 200000)--dtype:bf16for bfloat16 (faster, less memory) orfp32for float32--lr: Learning rate (default: 1e-4 with cosine decay)--ema-decay: EMA decay rate (default: 0.9999)
Model Size Configuration:
Default (~97M params, recommended for 36GB+ RAM):
python cli.py train --data-dir ./data/latents --out-dir ./runs/v0 \
--device mps --batch-size 4 --grad-accum 2 --crop-tokens 512 \
--dtype bf16 --max-steps 200000Smaller (~20M params, faster iteration):
python cli.py train --data-dir ./data/latents --out-dir ./runs/v0 \
--device mps --batch-size 8 --grad-accum 2 --crop-tokens 512 \
--dtype bf16 --max-steps 200000 \
--dim 384 --n-layers 8 --n-heads 6 --cond-dim 384Training Details:
- Checkpoints are saved every 50 steps:
last.pt(latest) andema.pt(EMA copy) - Periodic audio samples are generated during training (unconditional by default)
- Use
--audio-continuationto enable continuation sampling during training - Use
--audio-sample-every Nto control sampling frequency (0 to disable) - Logs include loss, learning rate, and sample metrics
Generate audio continuations using a trained checkpoint.
python cli.py sample \
--ckpt ./runs/v0/ema.pt \
--prompt-wav ./prompt.wav \
--duration-s 20 \
--nfe 8 \
--solver heun \
--out ./out.wav \
--device mpsArguments:
--ckpt: Path to checkpoint (useema.ptfor best quality,last.ptfor latest)--prompt-wav: Audio prompt file (WAV, 48 kHz stereo recommended)--duration-s: Duration of continuation in seconds (default: 20)--nfe: Number of function evaluations (sampling steps, default: 8)--solver: Sampler (defaultheun). Supported:euler: 1st-order ODE, 1 NFE/step (fastest baseline)heun: 2nd-order ODE, 2 NFE/step (default)midpoint: 2nd-order RK2, 2 NFE/steprk4: 4th-order Runge-Kutta, 4 NFE/step (highest quality at low NFE)dpmpp: DPM-Solver++ 2M for rectified flow, 1 NFE/step (strong at NFE 4-8)pingpong: stochastic SDE sampler, 1 NFE/step (recommended for distilled / few-step models)
--schedule: Time grid:linear(default) orshifted(logSNR shift)--schedule-shift: LogSNR shift exponent for--schedule shifted(e.g.1.0-3.0for long sequences)--out: Output audio file path--device:mps,cuda, orcpu--temperature: Sampling temperature (default: 1.0, higher = more diverse)--n-steps: Number of diffusion steps (default: 32)
python cli.py sample \
--ckpt ./runs/v0/ema.pt \
--duration-s 20 \
--nfe 8 \
--solver heun \
--out ./out_uncond.wav \
--device mpsOmit --prompt-wav for unconditional generation (no prompt context).
Higher quality with more sampling steps:
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --duration-s 30 --nfe 16 --solver heun --out ./out_high_quality.wav --device mpsFaster generation with fewer steps:
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --duration-s 20 --nfe 4 --solver euler --out ./out_fast.wav --device mpsAdjust temperature for diversity:
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --duration-s 20 --nfe 8 --solver heun --temperature 1.5 --out ./out_diverse.wav --device mpsDPM-Solver++ for very low NFE:
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --duration-s 20 --nfe 6 --solver dpmpp --out ./out_dpmpp.wav --device mpsRK4 for highest quality at low NFE:
python cli.py sample --ckpt ./runs/v0/ema.pt --prompt-wav ./prompt.wav --duration-s 20 --nfe 4 --solver rk4 --out ./out_rk4.wav --device mpsSampling Trade-offs:
- NFE (steps): More steps = better quality but slower. 4-8 is real-time, 16+ is high quality.
- Solver:
euler/dpmpp/pingponguse 1 NFE per step;heun/midpointuse 2;rk4uses 4.dpmppandrk4give the best quality at very low total NFE;pingpongadds stochasticity (only recommended for distilled models). - Schedule:
--schedule shifted --schedule-shift 1.0warps the time grid in log-SNR space (SD3-style); helpful for long sequences or low NFE. - Temperature: Higher values increase diversity but may reduce coherence.
The realtime subcommand spins up a streaming generator that plays audio
indefinitely out of the system's default sound device while the FlowDiT
model continually fills a sliding-window buffer in the background. Use it for
headless live-coding sessions, long-form ambient playback, or stress-testing a
checkpoint.
# Minimal: stream from a checkpoint until you press 'q'
python cli.py realtime --ckpt ./runs/v3_okachihuali/ema.pt --use-ema --device mps
# Same thing, but called directly as a module
python -m flow.realtime --ckpt ./runs/v3_okachihuali/ema.pt --use-ema --device mpsOn launch you'll see the engine load the model, prebuffer a couple of chunks (~1.4 s @ 48 kHz), and then start playback. The terminal stays in raw mode so single keypresses act as live controls — there is no need to press Enter.
# High-quality, slightly slower (8 ODE steps with Heun integrator)
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema \
--solver heun --nfe 8 --temperature 0.95
# Fast, low-NFE setup (good for live performance on a laptop)
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema \
--solver dpmpp --nfe 4
# Long context window for more coherent long-form output
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema \
--context-chunks 64 --prebuffer 4 --crossfade-chunks 8
# Capture the whole session to a WAV file while playing it live
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema \
--save ./session.wav
# Reproducible session: fix the initial seed
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema --seed 12345
# Stop automatically after 200 chunks (~137 s @ 48 kHz)
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema --max-chunks 200
# Use a converted CoreML model (auto-falls back to PyTorch on shape mismatch)
python cli.py realtime --ckpt ./runs/v0/ema.pt --use-ema \
--coreml-path ./runs/v0/model.mlpackage| Flag | Default | Description |
|---|---|---|
--ckpt PATH |
required | Checkpoint to load (last.pt or ema.pt). |
--use-ema |
off | Load EMA weights — strongly recommended for inference. |
--device DEV |
auto | mps, cuda, or cpu. Auto-detected if omitted. |
--coreml-path PATH |
none | Optional CoreML .mlpackage. Falls back to PyTorch when the variable sliding-window shape does not match the traced shape. |
--nfe N |
4 |
ODE steps per chunk. Higher = better quality, lower = lower latency. |
--solver NAME |
euler |
One of euler, heun, midpoint, rk4, dpmpp, pingpong. |
--temperature F |
1.0 |
Velocity scaling. <1 sharpens, >1 diffuses. |
--seed-scale F |
0.0 |
Shrinks initial noise toward zero (0 = standard N(0, I)). |
--context-chunks N |
32 |
Sliding-window length in codec chunks (1 chunk ≈ 0.683 s @ 48 kHz). Pass <= 0 to use the model's maximum safe context. |
--prebuffer N |
2 |
Chunks to render before playback starts. Higher = more glitch-resistant, more startup latency. |
--crossfade-chunks N |
4 |
Crossfade length used when switching seeds mid-stream. |
--max-chunks N |
unbounded | Stop after N chunks. Omit for indefinite playback. |
--save PATH |
none | Write the full session to a .wav file alongside live playback. |
--seed N |
time-based | Initial RNG seed. |
--summary-scale V |
1.0 |
Initial summary-latent scale. Scalar broadcasts to all 8 tokens; or pass 8 comma-separated floats (e.g. 1.0,0.9,1.1,...). |
--summary-bias V |
0.0 |
Same format as --summary-scale, added in normalized space. |
While the stream is playing, single keypresses adjust the engine in real
time. Press ? at any moment to print the up-to-date help in the terminal.
Global
| Key | Action |
|---|---|
q |
Quit. |
? |
Print the full keyboard-control help. |
r |
Reset everything to defaults (including summary & channel controls). |
Sampler
| Key | Action |
|---|---|
1 / 2 / 3 |
Set diffusion steps to 4 / 8 / 16 NFE. |
e / h |
Switch solver to euler / heun. |
< / > |
Decrease / increase temperature by 0.1. |
+ / - |
Increase / decrease context window by 4 chunks. |
Seeds & morphing
| Key | Action |
|---|---|
x |
Crossfade to a new random seed. |
X |
Hard cut to a new random seed. |
s / S |
Crossfade / hard-cut to a specific seed (type the digits, then Enter). |
a |
Toggle auto-cycle (periodically swap seeds). |
A |
Set the auto-cycle interval in chunks. |
[ / ] |
Decrease / increase crossfade length by 1 chunk. |
Summary-latent control (per-chunk, 8 tokens)
| Key | Action |
|---|---|
b / B |
Decrease / increase summary bias uniformly across all 8 tokens. |
g / G |
Decrease / increase summary scale uniformly. |
i |
Enter per-token edit mode (e.g. type b3 0.5 to set bias of token 3). |
o |
Reset summary controls only (scale=1, bias=0). |
n / N |
Randomize summary bias / scale uniformly in their valid range. |
m |
Randomize both summary scale and bias. |
Channel-latent control (per-chunk, 64 feature dims)
| Key | Action |
|---|---|
y / Y |
Randomize channel bias / scale (independent per dim). |
u |
Randomize both channel scale and bias. |
U |
Reset channel controls only. |
- The model generates one codec chunk per inference call. Each chunk is decoded to ~0.683 s of 48 kHz stereo audio.
- A sliding window of the last
--context-chunkschunks is fed back as context, so the model has memory of recent material. - A prebuffer of
--prebufferchunks is rendered before audio starts; after that, generation runs in lockstep with playback. If your machine generates faster than real-time, the engine throttles to avoid growing the buffer unbounded; if it's slower, you'll see underruns logged. - Each chunk prints an RT % metric:
audio_seconds / compute_seconds × 100. Values > 100% mean the model is generating faster than real-time (good), values < 100% mean it can't keep up and you'll eventually hit underruns. - Seed switching is crossfaded in latent space across
--crossfade-chunkschunks so transitions don't click.
- Audio underruns / glitches: lower
--nfe, switch to--solver euler, reduce--context-chunks, or increase--prebuffer. - Output sounds noisy or unstable: try
--temperature 0.9or lower, or use--solver heun --nfe 8for higher-quality steps. - Engine quits immediately with "checkpoint not found": pass an absolute
path to
--ckpt, or run the command from the repo root. - No sound: confirm your default output device is correct
(the engine uses
sounddevice's default). On macOS, you can change the default output inSystem Settings > Sound. - CoreML keeps falling back to PyTorch: this is expected — the traced CoreML graph has a fixed shape, but realtime uses a variable sliding window. CoreML is most useful for fixed-shape batch inference.
CoDiCodec-Flow includes experimental CoreML support for inference on Apple Silicon. CoreML can leverage the Apple Neural Engine (ANE) for potentially more power-efficient inference.
To convert a trained checkpoint to CoreML format:
# Install coremltools first
pip install coremltools
# Convert the checkpoint
python cli.py convert-coreml \
--ckpt runs/v3_okachihuali/ema.pt \
--out runs/v3_okachihuali/model.mlpackage \
--use-ema \
--context-chunks 32 \
--min-deployment-target macos13Important Limitations:
- CoreML models are traced with fixed input shapes. The
--context-chunksparameter determines the sequence length used during conversion. - Realtime audio generation uses variable sequence lengths (sliding window), which CoreML does not support natively. The CoreML backend will automatically fall back to PyTorch MPS when shapes don't match.
- For this reason, CoreML is not recommended for realtime generation. It may be useful for:
- Batch inference with fixed shapes
- Experimentation and performance comparison
To use the CoreML model for inference (with automatic fallback to PyTorch):
python -m flow.realtime \
--ckpt runs/v3_okachihuali/ema.pt \
--use-ema \
--coreml-path runs/v3_okachihuali/model.mlpackage \
--device mps \
--solver euler \
--nfe 4If CoreML inference fails (e.g., due to shape mismatch), the system automatically falls back to PyTorch MPS backend.
- MPS (PyTorch): Recommended for most use cases, especially realtime generation. Supports dynamic shapes and is well-optimized for Apple Silicon GPUs.
- CoreML: May offer better power efficiency for batch inference with fixed shapes. Not suitable for the dynamic sliding-window pattern used in realtime generation.
- Fallback: The implementation includes automatic fallback from CoreML to PyTorch, ensuring compatibility even if CoreML fails.
Sample audio generated by codicodec-flow is available in the examples/ directory, demonstrating the progression of the v3_okachihuali model during training:
okachihuali_v3_step_000000.wav- Generated at 0 training steps (initialization)okachihuali_v3_step_100000.wav- Generated at 100,000 training stepsokachihuali_v3_step_200000.wav- Generated at 200,000 training stepsokachihuali_v3_step_300000.wav- Generated at 300,000 training stepsokachihuali_v3_step_400000.wav- Generated at 400,000 training stepsokachihuali_v3_step_500000.wav- Generated at 500,000 training stepsokachihuali_v3_step_600000.wav- Generated at 600,000 training steps
The v3_okachihuali model was trained for approximately 700,000 steps on the Okachihuali dataset - a 60-track album by hexorcismos available at https://hexorcismos.bandcamp.com/album/--2. This dataset provides a diverse collection of musical material for training the generative model.
These examples demonstrate the model's ability to generate coherent musical continuations from unconditional generation.
CoDiCodec-Flow Architecture
- Moisés Horta Valenzuela, 2026
CoDiCodec
- The upstream CoDiCodec encoder/decoder is released by Sony CSL Paris under CC BY-NC 4.0
- Paper: Pasini et al., 2025 - CoDiCodec: UNIFYING CONTINUOUS AND DISCRETE COMPRESSED REPRESENTATIONS OF AUDIO
- Original repository: https://github.com/sony/codicodec
License
- This repository is licensed under CC BY-NC 4.0
- Code under
codicodec/is released under CC BY-NC 4.0 by Sony CSL Paris - The
flow/code is under the same license unless stated otherwise