Skip to main content

sim_lib_music_core/
freeze.rs

1use sim_kernel::Symbol;
2
3use crate::{ChainPlacementPlan, ChainTraceRecord, PlayContext, PlayEvent};
4
5/// Reproducibility metadata for a frozen player chain.
6///
7/// Records the identity and hashes that pin a render to its inputs, so a freeze
8/// can be matched against the chain, context, and output it was produced from.
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct FreezeMeta {
11    /// Identifier of the source the chain rendered.
12    pub source_id: Symbol,
13    /// Stable hash of the rendering chain.
14    pub chain_hash: String,
15    /// Stable hash of the play context.
16    pub context_hash: String,
17    /// Random seed used for the render.
18    pub seed: u64,
19    /// Placement plan the chain resolved to.
20    pub placement: ChainPlacementPlan,
21    /// Stable hash of the rendered events and traces.
22    pub output_hash: String,
23}
24
25/// Identifying record for a recorded source, without its rendered output.
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct SourceRecording {
28    /// Identifier of the recorded source.
29    pub source_id: Symbol,
30    /// Stable hash of the rendering chain.
31    pub chain_hash: String,
32    /// Stable hash of the play context.
33    pub context_hash: String,
34    /// Random seed used for the render.
35    pub seed: u64,
36    /// Placement plan the chain resolved to.
37    pub placement: ChainPlacementPlan,
38}
39
40/// A directly captured render: metadata plus its events and traces.
41#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct DirectRecording {
43    /// Reproducibility metadata for the render.
44    pub meta: FreezeMeta,
45    /// Rendered play events.
46    pub events: Vec<PlayEvent>,
47    /// Chain trace records produced during the render.
48    pub traces: Vec<ChainTraceRecord>,
49}
50
51/// A frozen player chain: cached render output keyed by its [`FreezeMeta`].
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct FrozenPlayerChain {
54    /// Reproducibility metadata for the frozen render.
55    pub meta: FreezeMeta,
56    /// Frozen play events.
57    pub events: Vec<PlayEvent>,
58    /// Chain trace records captured with the freeze.
59    pub traces: Vec<ChainTraceRecord>,
60}
61
62impl SourceRecording {
63    pub(crate) fn new(
64        source_id: Symbol,
65        chain_hash: String,
66        context_hash: String,
67        seed: u64,
68        placement: ChainPlacementPlan,
69    ) -> Self {
70        Self {
71            source_id,
72            chain_hash,
73            context_hash,
74            seed,
75            placement,
76        }
77    }
78}
79
80pub(crate) fn freeze_meta(
81    source_id: Symbol,
82    chain_hash: String,
83    cx: &PlayContext,
84    placement: ChainPlacementPlan,
85    events: &[PlayEvent],
86    traces: &[ChainTraceRecord],
87) -> FreezeMeta {
88    FreezeMeta {
89        source_id,
90        chain_hash,
91        context_hash: context_hash(cx),
92        seed: cx.seed,
93        placement,
94        output_hash: stable_hash("player-output", &(events, traces)),
95    }
96}
97
98pub(crate) fn context_hash(cx: &PlayContext) -> String {
99    stable_hash(
100        "play-context",
101        &(
102            &cx.transport,
103            &cx.tempo,
104            cx.sample_rate,
105            cx.ppq,
106            cx.range,
107            cx.seed,
108            &cx.capabilities,
109            cx.site,
110        ),
111    )
112}
113
114pub(crate) fn stable_hash<T: core::fmt::Debug>(label: &str, value: &T) -> String {
115    let mut hash = 0xcbf29ce484222325u64;
116    for byte in format!("{label}:{value:?}").bytes() {
117        hash ^= u64::from(byte);
118        hash = hash.wrapping_mul(0x100000001b3);
119    }
120    format!("fnv1a64:{hash:016x}")
121}