64 lines
3.1 KiB
Rust
64 lines
3.1 KiB
Rust
//! Bounces stream the main mix or a bus to their file, exactly as rendered in memory.
|
|
|
|
use soundcraft_audio_io::{BitDepth, EncodeOptions, FileFormat};
|
|
use soundcraft_engine::io::{Bounce, bounce_stems};
|
|
use soundcraft_time::Range;
|
|
use std::path::PathBuf;
|
|
|
|
fn scratch(name: &str) -> PathBuf {
|
|
let dir = std::env::temp_dir().join(format!("soundcraft-{name}-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
dir
|
|
}
|
|
|
|
#[test]
|
|
fn bounces_write_exactly_the_mix_and_report_its_levels() {
|
|
let e = soundcraft_engine::demo::demo_engine();
|
|
let s = e.session();
|
|
let dir = scratch("bounce-exact");
|
|
let path = dir.join("Mix.wav");
|
|
let r = Range::new(s.sample_rate.samples(10.0) + 7, s.sample_rate.samples(13.0));
|
|
let opts = EncodeOptions { format: FileFormat::Wav, bit_depth: BitDepth::Float32, dither: false, bwf: None };
|
|
let verb = s.bus_by_name("Verb").unwrap().id;
|
|
for bus in [None, Some(verb)] {
|
|
let mut mix = match bus {
|
|
Some(b) => soundcraft_mix::render_bus(s, b, r, 1024),
|
|
None => soundcraft_mix::render_range(s, r, 1024),
|
|
};
|
|
assert!(mix.iter().flatten().any(|v| v.abs() > 1e-3), "{bus:?}");
|
|
for normalize in [false, true] {
|
|
let done = Bounce::new(e.session_arc(), &path, r, &opts, bus, normalize, false).unwrap().run().unwrap();
|
|
if normalize {
|
|
soundcraft_dsp::offline::normalize(&mut mix, -0.1, false);
|
|
}
|
|
let bytes = std::fs::read(&path).unwrap();
|
|
let (_, file) = soundcraft_audio_io::decode(&bytes, None).unwrap();
|
|
assert_eq!(file.channels, mix, "{bus:?} normalize: {normalize}");
|
|
let mut lm = soundcraft_dsp::meter::LoudnessMeter::new(s.sample_rate.as_f64() as f32, mix.len());
|
|
lm.process(&mix, mix[0].len());
|
|
let levels = (soundcraft_dsp::offline::peak_db(&mix), lm.integrated_lufs(), lm.true_peak_db());
|
|
assert_eq!((done.bytes, (done.peak_db, done.lufs, done.true_peak_db)), (bytes.len() as u64, levels));
|
|
}
|
|
}
|
|
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 1);
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
|
|
#[test]
|
|
fn a_bounce_too_big_for_its_format_fails_and_keeps_the_old_file() {
|
|
let e = soundcraft_engine::demo::demo_engine();
|
|
let s = e.session();
|
|
let dir = scratch("bounce-too-big");
|
|
let path = dir.join("Mix.aif");
|
|
std::fs::write(&path, b"old").unwrap();
|
|
let opts = EncodeOptions { format: FileFormat::Aiff, bit_depth: BitDepth::Float32, dither: false, bwf: None };
|
|
let err = Bounce::new(e.session_arc(), &path, Range::new(0, s.sample_rate.samples(3.9 * 3600.0)), &opts, None, false, false).err().unwrap();
|
|
assert!(err.to_string().contains("4 GiB"), "{err}");
|
|
assert_eq!(std::fs::read(&path).unwrap(), b"old");
|
|
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 1);
|
|
let err = bounce_stems(&e, &dir.to_string_lossy(), Range::new(0, s.sample_rate.samples(4.1 * 3600.0)), &opts).unwrap_err();
|
|
assert!(err.to_string().contains("4 hours"), "{err}");
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|