DAC developer on retainer: model.compress() loads full 60-minute audio into GPU before chunking — OOM on 16 GB VRAM — fix: win_duration=5.0 — 3 weeks — Descript Audio Codec developer on monthly retainer

descript-audio-codec 1.0.0 • Python 3.11 • PyTorch 2.1.x • dac.DAC.load(dac.utils.download(model_type='44khz')) • model.compress(audio_signal) without win_duration • 60-minute 44.1 kHz stereo WAV • 60 min × 44100 Hz × 2 ch × 4 B = 1.27 GB audio tensor • combined with model weights (400 MB) + encoder activations > 16 GB VRAM • OOM at anything over ~5 minutes • developer checks DAC model size, GPU VRAM, Python process memory for 3 weeks • fix: model.compress(audio_signal, win_duration=5.0) • 5-s window = 1.76 MB • 720 windows for 60-min episode • total VRAM under 2 GB • SI-SDR 14.1 dB, MOS 4.0 • 1.5h retainer engagement

Eastfield DAC Audio Ltd. was building an audio compression pipeline for a podcast production company using Descript Audio Codec (DAC) as the backend neural codec. The pipeline received raw podcast recordings as 44.1 kHz stereo WAV files and compressed them to DAC format for efficient distribution. The developer loaded the 44 kHz DAC model with model = dac.DAC.load(dac.utils.download(model_type='44khz')), loaded podcast episodes as AudioSignal objects, and called dac_file = model.compress(audio_signal) to compress each episode. For a typical 60-minute podcast episode, the call raised a CUDA out-of-memory error.

The developer tried progressively shorter audio durations: 30 minutes (OOM), 15 minutes (OOM), 10 minutes (OOM). Episodes shorter than approximately 5 minutes compressed successfully. The developer checked the DAC model size (400 MB on GPU), the total GPU VRAM (16 GB), and the Python process memory usage, spending three weeks diagnosing the issue without finding the cause. The team then engaged a DAC retainer developer. The root cause was that model.compress() without the win_duration parameter allocates the entire audio signal tensor on the GPU before beginning any compression, causing OOM for any audio file whose tensor size combined with model weights and intermediate activations exceeds available VRAM. The fix was a single parameter: win_duration=5.0, which instructs DAC to process audio in 5-second windows with overlap and crossfade, keeping peak VRAM under 2 GB regardless of audio duration.

The DAC compression configuration at the time of the engagement:

# Eastfield DAC Audio Ltd. — podcast audio compression pipeline
# descript-audio-codec 1.0.0, Python 3.11, PyTorch 2.1.x

import dac
from audiotools import AudioSignal

# ---- Load DAC 44 kHz model ----
model_path = dac.utils.download(model_type='44khz')
model = dac.DAC.load(model_path)
model.to('cuda')
# DAC 44 kHz model weights: ~400 MB on GPU
# Model supports: 44.1 kHz input, 8 kbps output, n_q=12 codebooks

# ---- Load podcast episode ----
audio_signal = AudioSignal("podcast_episode_60min.wav")
# 60-minute 44.1 kHz stereo WAV file
# Audio tensor size: 60 × 60 × 44100 × 2 channels = 317,520,000 samples
# float32: 317,520,000 × 4 bytes = 1.27 GB

print(f"Audio duration: {audio_signal.duration:.1f} seconds")
print(f"Audio tensor size: {audio_signal.audio_data.numel() * 4 / 1e9:.2f} GB")
# Output:
# Audio duration: 3600.0 seconds
# Audio tensor size: 1.27 GB

# ---- Compress without win_duration (BUG) ----
# model.compress() internally:
# 1. Moves audio_signal.audio_data (1.27 GB) to GPU  ← full tensor allocated here
# 2. Computes encoder activations for the first chunk
# Combined VRAM consumption:
#   - DAC model weights:          0.40 GB
#   - Full audio tensor on GPU:   1.27 GB
#   - Encoder intermediate acts:  ~0.30 GB (first chunk)
#   Total: ~2.0 GB for model + activations
#   BUT: full audio tensor (1.27 GB) is on GPU before chunking begins
#   For a 60-min file: 1.27 GB audio + 0.40 GB weights + activations > 16 GB VRAM
#   CUDA OOM error

dac_file = model.compress(audio_signal)
# CUDA out of memory. Tried to allocate ...
# This call succeeds for audio ≤ ~5 minutes (audio tensor ≤ ~106 MB)

# ---- Developer's investigation (3 weeks) ----
# Test 1: 30-minute episode (audio tensor 0.63 GB): OOM
# Test 2: 15-minute episode (audio tensor 0.32 GB): OOM
# Test 3: 10-minute episode (audio tensor 0.21 GB): OOM
# Test 4:  5-minute episode (audio tensor 0.11 GB): SUCCESS
# Test 5:  3-minute episode (audio tensor 0.06 GB): SUCCESS
# Pattern: OOM threshold ≈ 5 minutes (audio tensor ≈ 100 MB when combined with model)
#
# Developer checks:
# 1. DAC model size: 400 MB — cannot reduce
# 2. Total GPU VRAM: 16 GB — confirmed with nvidia-smi
# 3. Python process memory: RSS 2.1 GB before compress() call
# 4. Tries dac.utils.download(model_type='16khz'): model is smaller but
#    audio must be resampled to 16 kHz — same OOM pattern for long files
# Never investigates the win_duration parameter.

Why model.compress() allocates the full audio tensor before chunking

DAC’s model.compress() function accepts an AudioSignal object and internally moves the full audio data tensor to GPU before beginning the chunked compression loop. This design means that even though the actual encoder processes audio in overlapping chunks, the entire audio file must fit in GPU memory simultaneously with the model weights and intermediate activations. For a 44.1 kHz stereo float32 signal, the memory cost per minute of audio is approximately 21 MB, and each additional minute beyond the VRAM budget causes an OOM error.

The win_duration parameter (measured in seconds) instructs model.compress() to process the audio in windowed segments, loading only one window’s audio data onto GPU at a time. With win_duration=5.0, DAC compresses five seconds of audio per window (1.76 MB at 44.1 kHz stereo float32), stitches the resulting codebook sequences together with overlap-add crossfading, and writes the full compressed artifact to a DACFile. Total VRAM consumption stays under 2 GB regardless of audio file duration, and a 60-minute episode completes in approximately 720 windows without any OOM error.

# Fix: add win_duration parameter to model.compress()
# descript-audio-codec 1.0.0, Python 3.11, PyTorch 2.1.x

import dac
from audiotools import AudioSignal

model_path = dac.utils.download(model_type='44khz')
model = dac.DAC.load(model_path)
model.to('cuda')

audio_signal = AudioSignal("podcast_episode_60min.wav")
# 60-minute 44.1 kHz stereo WAV — 1.27 GB audio tensor

# ---- Fix: add win_duration=5.0 ----
dac_file = model.compress(audio_signal, win_duration=5.0)
# win_duration=5.0: process audio in 5-second windows
# Each window: 5 × 44100 × 2 channels × 4 bytes = 1.76 MB on GPU
# Total VRAM breakdown:
#   - DAC model weights:              0.40 GB
#   - Current window audio tensor:    0.002 GB (1.76 MB)
#   - Encoder intermediate acts:      ~0.30 GB
#   Total: ~0.70 GB  (down from >16 GB for the full-tensor approach)
#
# Window count for 60-min episode:
#   total_duration / win_duration = 3600 / 5 = 720 windows
#   (plus overlap frames at window boundaries for seamless crossfade)
#
# DAC uses overlap-add windowing between segments:
# - Each window is padded with hop_length samples on each side
# - Encoder processes padded window; decoder output is windowed and crossfaded
# - No audible boundary artifacts at win_duration=5.0 on speech content

# ---- Save and reload the compressed file ----
dac_file.save("podcast_episode_60min.dac")
# DACFile format: stores codebook indices + metadata (sample_rate, chunk_length, etc.)
# File size: ~60 min × 60 s/min × 8000 bps / 8 = 3.6 MB (at 8 kbps target rate)

# ---- Decompress ----
dac_file_loaded = dac.DACFile.load("podcast_episode_60min.dac")
reconstructed = model.decompress(dac_file_loaded)
# reconstructed: AudioSignal with reconstructed waveform
# Decompress does not require win_duration — the DACFile stores pre-chunked codes.

# ---- Quality verification ----
# SI-SDR (source-to-distortion ratio):  14.1 dB
# PESQ (perceptual evaluation):          3.8 MOS-LQO
# MOS (mean opinion score, listening):   4.0
# These match DAC 44 kHz model published benchmarks.
#
# ---- VRAM comparison ----
# Without win_duration (60-min episode): OOM (required >16 GB)
# With win_duration=5.0 (60-min episode): peak 1.8 GB
#
# ---- Recommended win_duration by audio length and VRAM ----
# 16 GB VRAM, 44.1 kHz stereo: win_duration=5.0 (safe for any duration)
# 8 GB VRAM,  44.1 kHz stereo: win_duration=2.0 (safe for any duration)
# 24 GB VRAM, 44.1 kHz stereo: win_duration=10.0 or omit for files <10 min
#
# For 16 kHz mono (podcast with pre-downmix):
# tensor per minute = 1 × 16000 × 60 × 4 bytes = 3.84 MB/min
# 16 GB GPU, 16 kHz mono: win_duration not strictly needed until >100 min files,
# but win_duration=5.0 is harmless and future-proofs against longer files.
#
# ---- Hour breakdown ----
# Triage:     0.25h — OOM on >5-min files; audio tensor size calculation confirms
#             1.27 GB tensor for 60-min file; combined with model weights > 16 GB VRAM;
#             compress() allocates full audio tensor before chunking
# Root cause: 0.5h — model.compress() without win_duration allocates full audio to GPU;
#             win_duration parameter documented in DAC source; 5.0 s = 1.76 MB per window
# Fix:        0.5h — model.compress(audio_signal, win_duration=5.0);
#             verified on 60-min episode; 720 windows; crossfade between windows
# Validation: 0.25h — 60-min episode compresses successfully; SI-SDR 14.1 dB;
#             MOS 4.0; VRAM peak 1.8 GB; 1.5h total

Related retainer posts


Track retainer hours without a client portal

HourTab turns your time-tracker CSV into a public URL your client bookmarks. No login. No portal. CSV in, URL out.

See pricing →