Skip to main content

session_digest/
session_digest.rs

1//! Prints everything a saved session depends on, so that two builds can be
2//! compared byte for byte.
3//!
4//! `cargo run -p phosphor-app --example session_digest -- sessions/*.phos`
5//!
6//! Not part of the product: this exists so that "a session saved before a
7//! change loads identically after it" can be *checked* rather than asserted,
8//! by running it in a worktree of the previous commit and diffing.
9
10use phosphor_app::preset::{layout_fingerprint, param_count};
11use phosphor_app::session;
12use phosphor_app::state::InstrumentType;
13
14fn main() {
15    println!("== instrument layouts ==");
16    for instrument in InstrumentType::ALL {
17        let key = session::instrument_key(*instrument);
18        println!(
19            "{key:<10} params={:<3} layout={} label={:?}",
20            param_count(*instrument),
21            layout_fingerprint(*instrument),
22            instrument.label()
23        );
24        // Every selector on the panel, and how many positions it has, because
25        // that is what a stored position is an index into.
26        let count = param_count(*instrument);
27        let mut selectors = Vec::new();
28        for param in 0..count {
29            if let Some(positions) = phosphor_app::discrete::positions(*instrument, param) {
30                selectors.push(format!("{param}:{}", positions.len()));
31            }
32        }
33        println!("           selectors {}", selectors.join(" "));
34    }
35
36    for path in std::env::args().skip(1) {
37        println!("\n== {path} ==");
38        let file = match session::load(std::path::Path::new(&path)) {
39            Ok(f) => f,
40            Err(e) => {
41                println!("  unreadable: {e}");
42                continue;
43            }
44        };
45        println!("  version {} tracks {}", file.version, file.tracks.len());
46        for track in &file.tracks {
47            let Some(instrument) = session::parse_instrument_type(&track.instrument_type) else {
48                println!("  {:<12} UNKNOWN {}", track.name, track.instrument_type);
49                continue;
50            };
51            let mut params = track.synth_params.clone();
52            let clamped = session::apply_selectors(instrument, &mut params, &track.discrete);
53            let digest: u64 = params.iter().fold(0xcbf2_9ce4_8422_2325u64, |h, v| {
54                v.to_bits().to_le_bytes().iter().fold(h, |h, b| {
55                    (h ^ u64::from(*b)).wrapping_mul(0x0000_0100_0000_01b3)
56                })
57            });
58            println!(
59                "  {:<12} {:<10} n={:<3} digest={digest:016x} clamped={clamped:?}",
60                track.name, track.instrument_type, params.len()
61            );
62        }
63    }
64}