diff --git a/apps/soundcraft-cli/src/main.rs b/apps/soundcraft-cli/src/main.rs index 2c22b689..ccec6697 100644 --- a/apps/soundcraft-cli/src/main.rs +++ b/apps/soundcraft-cli/src/main.rs @@ -5,7 +5,8 @@ //! soundcraft-cli convert IN OUT [--bit-depth 16|24|32f] [--rate HZ] [--normalize] [--dither | --no-dither] //! dithers only when the output loses resolution, unless told //! soundcraft-cli run [--in S.scraft | --demo] [--cmd ID[=JSON]]… [--bounce OUT] [--start T --end T] [--save S.scraft] [--inspect] -//! soundcraft-cli script FILE [--in S | --demo] [--save S] [--bounce OUT] (one `ID JSON` per line) +//! soundcraft-cli script FILE [--in S | --demo] [--save S] [--bounce OUT [--start T --end T]] (one `ID JSON` per line; +//! without --start/--end, --bounce covers the whole session) //! soundcraft-cli commands [FILTER] list commands //! soundcraft-cli describe ID one command's details //! soundcraft-cli app [--port P] METHOD [JSON] call a running app's control channel @@ -309,6 +310,11 @@ fn finish(e: &mut Engine, args: &[String]) -> ExitCode { if let Some(s) = arg_value(args, "--end") { p["end"] = time_arg(&s); } + // No range given: bounce the whole session, not whatever edit selection the commands left. + if p.get("start").is_none() && p.get("end").is_none() { + p["start"] = json!(0); + p["end"] = json!(e.session().content_end().max(1)); + } match e.execute("file.bounce_mix", &p) { Ok(v) => outln!("{v}"), Err(err) => return fail(err), diff --git a/apps/soundcraft-cli/tests/it/cli.rs b/apps/soundcraft-cli/tests/it/cli.rs index ebc92cc5..b15f8da5 100644 --- a/apps/soundcraft-cli/tests/it/cli.rs +++ b/apps/soundcraft-cli/tests/it/cli.rs @@ -215,3 +215,28 @@ fn usage_lists_the_root_flags() { let help = String::from_utf8_lossy(&o.stdout).into_owned(); assert!(help.contains("--automation-read-root DIR") && help.contains("--automation-write-root DIR"), "{help}"); } + +/// Without `--start`/`--end`, `--bounce` covers the whole session, not a stray edit selection +/// left by the script (a bare `19.2` in command params is 19 samples). +#[test] +fn script_bounce_without_range_covers_the_session() { + let base = tmpdir("bounce-range"); + let script = base.join("cmds.txt"); + std::fs::write( + &script, + concat!( + "track.new {\"count\":1,\"kind\":\"instrument\",\"name\":\"Keys\"}\n", + "midi.new_clip {\"track\":\"Keys\",\"start\":0,\"end\":{\"seconds\":19.2}}\n", + "edit.select {\"start\":0,\"end\":19.2}\n", + ), + ) + .unwrap(); + let out = base.join("out.wav"); + let o = run(&["script", script.to_str().unwrap(), "--bounce", out.to_str().unwrap()]); + assert!(o.status.success(), "{}", String::from_utf8_lossy(&o.stderr)); + let stdout = String::from_utf8_lossy(&o.stdout).into_owned(); + let last: Value = serde_json::from_str(stdout.lines().last().unwrap()).unwrap(); + let secs = last["seconds"].as_f64().unwrap(); + assert!((secs - 19.2).abs() < 0.01, "bounced {secs} s: {stdout}"); + let _ = std::fs::remove_dir_all(base); +}