fix: write and read audio with soundfile instead of torchaudio

torchaudio 2.11 routes torchaudio.save()/load() through TorchCodec and
ignores the `backend` argument, so every generation died at the save
step with "ImportError: TorchCodec is required for save_with_torchcodec"
after the diffusion had already finished. Reference-audio loading
(audio2audio, repaint, extend) and the training dataset loader hit the
same wall.

soundfile is already a required dependency and covers all four output
formats the UI offers, so use it directly rather than pulling in
TorchCodec and its native FFmpeg stack:

- pipeline_ace_step.save_wav_file(): sf.write(), transposing
  (channels, samples) -> (samples, channels); drops the now-unused
  torchaudio import
- MusicDCAE.load_audio() and text2music_dataset: sf.read(dtype=float32,
  always_2d=True), transposed back to (channels, samples)

torchaudio is still used for Resample/MelScale transforms, which are
unaffected.

Verified end to end: 10s generation on an RTX 3060 in 9.7s, output is
valid non-silent 48kHz stereo; load_audio round-trips it; wav/mp3/ogg/
flac all write and read back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-09-08 19:03:29 +03:00
co-authored by Claude Opus 5
parent 6e3273d049
commit 0584397884
3 changed files with 26 additions and 12 deletions
+12 -8
View File
@@ -12,6 +12,7 @@ import os
import re
import torch
import soundfile as sf
from loguru import logger
from tqdm import tqdm
import json
@@ -46,7 +47,6 @@ from acestep.apg_guidance import (
cfg_zero_star,
cfg_double_condition_forward,
)
import torchaudio
from .cpu_offload import cpu_offload
@@ -1405,13 +1405,17 @@ class ACEStepPipeline:
else:
output_path_wav = save_path
target_wav = target_wav.float()
backend = "soundfile"
if format == "ogg":
backend = "sox"
logger.info(f"Saving audio to {output_path_wav} using backend {backend}")
torchaudio.save(
output_path_wav, target_wav, sample_rate=sample_rate, format=format, backend=backend
target_wav = target_wav.float().cpu()
logger.info(f"Saving audio to {output_path_wav}")
# Write with soundfile rather than torchaudio.save(): since torchaudio
# 2.11 the latter ignores the `backend` argument and routes everything
# through TorchCodec, an extra native dependency we do not require.
# soundfile expects (samples, channels), torch tensors are (channels, samples).
sf.write(
output_path_wav,
target_wav.transpose(0, 1).numpy(),
sample_rate,
format=format.upper(),
)
return output_path_wav