Spaces:
Running on Zero
Scale a result past full scale down to fit, return an AIFF input as WAV, and reword the interface text
Browse filesThe result could overshoot full scale, and writing it to a 16-bit file flattened every peak above, which the validation measured as clipping. A result whose peak exceeds full scale is now scaled down to fit, and any other is left as it is.
HARP accepts .aif, the extension Logic Pro gives AIFF, but soundfile cannot write a file named with it, so such an input failed when its result was written, inside the GPU call. Gradio also converts an AIFF output to 16-bit WAV, since browsers cannot play AIFF, so keeping that container gained nothing. Lossless inputs now keep WAV and FLAC alone, and AIFF comes back as WAV at the bit depth this Space writes.
Some of the description and help text joined clauses with a semicolon or ended a sentence with a preposition, and each such sentence is reworded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- app.py +2 -2
- unison/pipelines/infer.py +9 -2
|
@@ -337,7 +337,7 @@ def run(
|
|
| 337 |
# A lossless input keeps its container. A lossy one comes back as WAV, since encoding
|
| 338 |
# the edit lossily again would expose the artifacts of the first encode and add a second.
|
| 339 |
input_ext = Path(input_audio_path).suffix.lower()
|
| 340 |
-
if input_ext in {".flac"
|
| 341 |
data, rate = sf.read(out_path, always_2d=True)
|
| 342 |
out_path = str(Path(out_path).with_suffix(input_ext))
|
| 343 |
sf.write(out_path, data, rate)
|
|
@@ -421,7 +421,7 @@ if __name__ == "__main__":
|
|
| 421 |
gr.Textbox(label="Prompt",
|
| 422 |
info="Generate: describe the sound, or what's said. Edit: describe the change. Clone Voice: text to speak."),
|
| 423 |
gr.Textbox(label="Background (optional)",
|
| 424 |
-
info="\"Speech + Background\" sound type only: the background sound to mix
|
| 425 |
gr.Textbox(label="Reference Transcript (optional)",
|
| 426 |
info="Only used in Clone Voice mode. Leave blank to auto-transcribe the input audio."),
|
| 427 |
gr.Dropdown(choices=list(VARIANTS), value="Balanced (44kHz)", label="Model",
|
|
|
|
| 337 |
# A lossless input keeps its container. A lossy one comes back as WAV, since encoding
|
| 338 |
# the edit lossily again would expose the artifacts of the first encode and add a second.
|
| 339 |
input_ext = Path(input_audio_path).suffix.lower()
|
| 340 |
+
if input_ext in {".flac"}:
|
| 341 |
data, rate = sf.read(out_path, always_2d=True)
|
| 342 |
out_path = str(Path(out_path).with_suffix(input_ext))
|
| 343 |
sf.write(out_path, data, rate)
|
|
|
|
| 421 |
gr.Textbox(label="Prompt",
|
| 422 |
info="Generate: describe the sound, or what's said. Edit: describe the change. Clone Voice: text to speak."),
|
| 423 |
gr.Textbox(label="Background (optional)",
|
| 424 |
+
info="\"Speech + Background\" sound type only: the background sound to mix with the speech."),
|
| 425 |
gr.Textbox(label="Reference Transcript (optional)",
|
| 426 |
info="Only used in Clone Voice mode. Leave blank to auto-transcribe the input audio."),
|
| 427 |
gr.Dropdown(choices=list(VARIANTS), value="Balanced (44kHz)", label="Model",
|
|
@@ -252,6 +252,13 @@ def sample_latents(
|
|
| 252 |
# -------------------------
|
| 253 |
# Decode & save
|
| 254 |
# -------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
@torch.no_grad()
|
| 256 |
def decode_and_save(audio_vae, latents, durations, output_paths, sample_rate=None):
|
| 257 |
if sample_rate is None:
|
|
@@ -269,7 +276,7 @@ def decode_and_save(audio_vae, latents, durations, output_paths, sample_rate=Non
|
|
| 269 |
wav = wav.mean(0)
|
| 270 |
wav = wav[..., :max_smp]
|
| 271 |
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
| 272 |
-
sf.write(path, wav.float().numpy(), sample_rate)
|
| 273 |
print(f" Saved: {path} ({dur:.1f}s)")
|
| 274 |
|
| 275 |
|
|
@@ -320,7 +327,7 @@ def decode_and_save_full(audio_vae, latents_full, ref_samples,
|
|
| 320 |
if fade_len > 1:
|
| 321 |
target_wav[:fade_len] *= torch.linspace(0.0, 1.0, fade_len)
|
| 322 |
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
| 323 |
-
sf.write(path, target_wav.float().numpy(), sample_rate)
|
| 324 |
print(f" Saved (target): {path} ({dur:.1f}s, crop@{crop_start} = {crop_start_s:.3f}s, fade={fade_in_ms}ms)")
|
| 325 |
|
| 326 |
# Optionally save the truncated ref waveform used as conditioning
|
|
|
|
| 252 |
# -------------------------
|
| 253 |
# Decode & save
|
| 254 |
# -------------------------
|
| 255 |
+
def _within_full_scale(wav: torch.Tensor) -> torch.Tensor:
|
| 256 |
+
"""Scale a result whose peak exceeds full scale down to fit, since writing it to a 16-bit
|
| 257 |
+
file would flatten every peak above."""
|
| 258 |
+
peak = wav.abs().max()
|
| 259 |
+
return wav / peak if peak > 1.0 else wav
|
| 260 |
+
|
| 261 |
+
|
| 262 |
@torch.no_grad()
|
| 263 |
def decode_and_save(audio_vae, latents, durations, output_paths, sample_rate=None):
|
| 264 |
if sample_rate is None:
|
|
|
|
| 276 |
wav = wav.mean(0)
|
| 277 |
wav = wav[..., :max_smp]
|
| 278 |
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
| 279 |
+
sf.write(path, _within_full_scale(wav.float()).numpy(), sample_rate)
|
| 280 |
print(f" Saved: {path} ({dur:.1f}s)")
|
| 281 |
|
| 282 |
|
|
|
|
| 327 |
if fade_len > 1:
|
| 328 |
target_wav[:fade_len] *= torch.linspace(0.0, 1.0, fade_len)
|
| 329 |
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
| 330 |
+
sf.write(path, _within_full_scale(target_wav.float()).numpy(), sample_rate)
|
| 331 |
print(f" Saved (target): {path} ({dur:.1f}s, crop@{crop_start} = {crop_start_s:.3f}s, fade={fade_in_ms}ms)")
|
| 332 |
|
| 333 |
# Optionally save the truncated ref waveform used as conditioning
|