* Build: use craft-media for bitstream and isobmff
soundcraft-video re-exports craft_media::{bitstream, isobmff} from
craft-libs instead of carrying a copy of FilmCraft's crates. The module
paths stay, so crate::bitstream and crate::isobmff resolve as before.
The shared copy is newer: it adds APV, .mp2 as MPEG audio, AAC channel
configurations 11-14, SampleEntry::av1/flac, Mp4Writer::set_sample_entry
and the se(v) fix for i32::MIN.
* Build: pin craft-media to craft-libs f0ad6c1e
* security: move the release workflow's write permission to the job level (SonarCloud githubactions:S8233)
* style: replace wildcard imports with explicit uses (SonarCloud rust:S2208)
Rewrites every glob `use` Sonar flags into explicit imports, so what each
command module consumes is visible at the top of the file.
* release: the git-sources gate moves into craft-common-actions (PR #78)
The check is shared at release.yml@main now; keep only the reusable call.
---------
Co-authored-by: Dylan Taylor <dylanmtaylor@users.noreply.github.com>
* Build: line tables only for workspace crates, no debug info for dependencies
Full debug info made most of the dev and test target size and link time. Line tables keep file and line numbers in backtraces; dependency frames lose them.
* Tests: build each crate's integration tests as one binary
Each tests/*.rs file was its own test binary: 98 binaries, each linking the crate and its dependencies. The files are now modules of tests/it/main.rs, one binary per crate, which cuts link time and target size. Filter by module: `cargo test -p <crate> --test it <module>::`.
Binaries with their own main (harness = false) in clap-host and vst3-host stay separate, as do the ui-egui tests that switch the process-wide Light/Dark palette and recovery.rs, whose autosave locks leak into child processes other tests start.
* Xtask: run tests with nextest, doctests apart, and no examples in the test build
cargo xtask ci runs the tests with cargo nextest when it is installed, else cargo test with a notice, and doctests with cargo test --doc. The test build leaves out examples (--lib --bins --tests); clippy --all-targets still compiles them. Test binaries with harness = false run under cargo test: their own main cannot answer nextest's --list. clippy and the wasm check keep going past a failing crate. cargo xtask test runs the test step alone.
* Audio: check the WAVE behind a RIFF form the probe skips
The probe skips a RIFF form other than WAVE and reads a WAVE behind it, so
the marker search skips it too and the INFO check runs on the WAVE the
probe reads. The mismatched-container test moves to an input that still
mismatches: a hinted FLAC behind a WAVE.
* Dependabot: hold egui, eframe and egui_kittest at 0.36
craft-ui stays on egui 0.36 until storytold/craft-libs#199 lands; the daily
group would otherwise propose 0.37 again.
* Sessions: use resolve_absolute for stored source paths
* CI: check that git dependencies come from storytold
* CI: release only with git dependencies from storytold
* Parse Cargo.lock as TOML when checking git sources
* Use the shared git-sources check in CI
* CI: parse cargo config, including the legacy .cargo/config, in the git-sources check
With /tmp on tmpfs, four autosave and recovery tests failed:
- Autosave folders were named by millisecond and process id, and created
with create_dir_all, so two sessions started in the same millisecond
shared one folder and the second overwrote the first's unsaved work
(recovering_keeps_the_unsaved_work_it_replaces,
closing_the_session_or_quitting_drops_its_autosave). Autosave::create
now creates the folder exclusively and adds a -2, -3... suffix when
the name is taken.
- leftovers() offers an autosave only when it is strictly newer than its
session file. A save and the autosave right after it can share one
coarse mtime, so the tests that save and then autosave now move the
saved file's mtime a second back
(unsaved_work_is_offered_once_its_session_closes,
an_exit_without_quit_offers_unsaved_changes_at_the_next_launch).
* Audio: migrate file decoding to symphonia 0.6
Upstream bumped symphonia to 0.6 without adapting crates/audio-io, so
`soundcraft-audio-io` no longer built: the probe module, codec parameters
and decoded-buffer APIs all moved.
- Probe with `default::get_probe().probe(&Hint, …)`. The 0.5 prober's
detection-only path is gone, so scan for the container marker ourselves
before instantiating a reader, and keep running the existing size checks
(CAF/MP4/FLAC/WAV) on the first marker — the damaged-file regression
tests depend on those checks firing before any allocation.
- Skip leading ID3v2 tags with `ReadBytes::ignore_bytes` as before.
- Read `Track::codec_params` (now an `Option`), decode through
`make_audio_decoder` and `GenericAudioBufferRef::copy_to_vecs_planar`,
and read the new public `Packet` fields.
- Keep the per-packet frame limit and the sample sanitisation.
* Playback: migrate the audio device layer to cpal 0.18
- Sample rates are plain `u32` now: drop `.0` and the `cpal::SampleRate`
constructor.
- One `cpal::Error` replaces the build/stream/play/pause error types;
detect `DeviceNotAvailable` through `error.kind()`.
- Stream builders take `StreamConfig` by value, and device names come
from `Device::description()` (the deprecated `name()` is gone).
- The test `Fake` implements the new `StreamTrait::{buffer_size, now}`.
* Deps: pin egui back to 0.36 until craft-ui supports 0.37
The dependabot group bump (#767) moved the workspace to egui, eframe and
egui_kittest 0.37, but the pinned `craft-ui` revision still builds against
egui 0.36, so the graph carries two incompatible egui versions and
`soundcraft-ui-egui` does not compile. craft-libs#199 updates craft-ui to
0.37 and is still open; pin 0.36 and refresh the lockfile meanwhile. The
other version bumps from #767 stay.
Re-land egui 0.37 together with the craft-ui update.
* Audio: refuse a size-checked container the probe did not validate
The dependency-migration review showed the preflight size checks can
validate one container marker while symphonia's probe selects a different
container, so a misleading marker could skip the FLAC/MP4/CAF/WAVE checks.
Probe 0.6 reports the selected format only through the reader it returns,
so `open()` now records which container's checks ran (including the
RF64/BW64 RIFF forms) and refuses the stream when a size-checked reader
was chosen without its checks, before any packet is decoded. Markers found
in audio data of non-container streams (MP3, Ogg, AIFF) do not reject
valid files.
Adds a regression test with a fake RIFF prefix followed by a valid FLAC
stream, and doc comments for the functions touched by the dependency
migration (the review's docstring-coverage warning).
0.490_392_64 and 0.49039264 are the same f32 value; the underscored form
reads as a numeric suffix to the analyzer.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Found while testing crates/model/src/automation.rs:73
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Widen the magnitude and drop-frame label arithmetic to i128 so i64::MIN stays exact and extreme drop-frame counts cannot overflow. Add round-trip regressions for all frame rates and both drop-frame variants.
Found while testing crates/model/src/automation.rs:190-203
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On a 63,424-clip drum edit the UI thread spent ~187 ms per frame, so with a plugin editor open
(a frame every 30 ms) it never went idle and the editor windows, which Windows only paints
when the thread's queue is empty, stopped redrawing. Now ~11 ms of CPU per frame:
- Overlap shadows: each drawn clip checked every clip of its track; on a sorted track the
clips starting inside it are found by binary search (sortedness is checked once per track
and frame, and an unsorted track is still scanned).
- Fade curves: a fade took at least 65 points, even under a pixel wide. A narrow one takes
~4 * its width in physical pixels; the chord error stays under a quarter pixel (tested).
- Clip List: rows are listed and filtered once per document revision and filter text, and
only the rows in view are laid out (show_rows).
- Universe: each track's clips are merged into same-colour runs covering exactly the same
pixels, cached per revision and area (5,000 clips become a handful of rects).
- Session::content_end, which walks every clip, is worked out once per revision.
- Alternate playlist lanes skip clips outside the view, like the main lane, and clip names
are not laid out where no glyph would show.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`edit.nudge`'s "Plus/Minus" shortcut only matched `Key::Plus`, which a
real keyboard without a dedicated numpad `+` can never send: typing
"+" needs Shift, and Shift+=/- is already a separate, intentional
shortcut (Grid value). So nudge-right was unreachable on most
keyboards, while nudge-left worked fine ("-" needs no shift). Fixes
#736.
Add `Key::Equals` (the bare, unshifted key) to the nudge match arm,
the same way it already works for `Key::Minus`. Also exclude `m.alt`
from the arm, since `Alt+=` is the separate, already-registered
`window.event_list` shortcut that would otherwise be shadowed.
Verified live against a running demo session over the control channel
before writing the fix (plain "-" moved a clip; plain "=" and
Shift+"=" both did nothing), and again after. New regression test
covers bare Equals/Plus/Minus nudging, Shift+Equals still stepping the
Grid value instead, and Alt+Equals still opening the MIDI Event List
instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>