Imported from Eason20000/videocanvas (
AGENTS.md). Install upstream withnpx skills add Eason20000/videocanvas. Copyright stays with the author.
AGENTS.md
Build
nix build . --option builders '' --print-build-logs
Do not run cargo build — cargo only exists inside nix develop (flake devShell).
nix build runs cargo test (checkPhase) then cargo clippy -- -D warnings (postCheck) — there is no CI, so this build is the only gate.
Nix source trap
src = self in package.nix uses lib.cleanSource, which only includes git-tracked files.
New or renamed source files are invisible to nix build until git added.
Lint
unwrap() and expect() are compile errors (unwrap_used/expect_used = deny in Cargo.toml).
All clippy warnings are fatal in postCheck. The crate is Rust edition 2024.
Architecture
src/main.rs CLI (clap derive) — entrypoint, imports lib crate
src/lib.rs Public API: convert_image, convert_video, stream_video, Options, DeviceMode
src/sysex.rs Roland SC sysex construction — pure Rust, zero deps
src/image.rs OTSU binarization + ffmpeg pixel extraction + interlace ordering
src/process.rs ffmpeg pipeline -> SMF or real-time MIDI port
sysex.rs has no external dependencies — safest to modify, easiest to test.
API gotchas (lib.rs)
convert_video(input, &options, &arena)returnsSmf<'a>borrowed from a caller-ownedmidly::Arena— the arena must outlive the SMF. There is noto_static().convert_imagetakes tight-packed GRAY8 pixels at the mode's resolution: 256 (SC-55), 10240 (SC-8850 160x64), or 8192 (SD-90 128x64) bytes.
CLI quirks (main.rs)
--sd90only applies together with--sc8850(Sd90 wins if both set).--edgetakes an optional threshold: bare--edgemeans 50,--edge 100explicit.--ditherand--edgeare SC-8850/SD-90 only;--interlaceis SC-8850 only.- Auto output path:
{stem}_s[i][e]f{framerate}.mid(e.g.Bad Apple!! PV_sif10.mid).
Features
midi-output (default: on) gates the midir crate and --midi-port / --list-ports CLI options.
Code gated with #[cfg(feature = "midi-output")].
Testing
Tests are #[cfg(test)] mod tests blocks in sysex.rs (10 tests) and image.rs (10 tests).
All pure functions — no fixtures, no ffmpeg, no MIDI hardware needed.
Run via nix build (automatic in checkPhase) or nix develop + cargo test.
Dependencies
| Crate | Purpose |
|---|---|
| ffmpeg-next 9 | Video decode, fps/scale filter (requires ffmpeg system lib) |
| midly 0.5 | MIDI SMF read/write |
| midir 0.10 | Real-time MIDI port output (optional, via midi-output feature) |
| clap 4 | CLI argument parsing |
No image crate — OTSU thresholding is a ~40-line pure Rust implementation.
Conventions
- All source characters are ASCII — no Unicode arrows, dashes, or multiplication signs.
- Errors use
crate::Result<T>(Box<dyn Error>); string errors via"msg".into()?/ok_or(...)?. ffmpeg::init()is wrapped instd::sync::OnceLock(fnensure_ffmpeg, process.rs:18).- Roland checksum:
(128 - sum % 128) & 0x7F(not128 - sum % 128 % 128— precedence bug fixed).