This repository has no description
1use super::animation::{AnimationUpdateFunction, LayerAnimationUpdateFunction};
2use super::hooks::{LaterHook, LaterRenderFunction};
3use super::Animation;
4use crate::synchronization::audio::{Note, StemAtInstant};
5use crate::synchronization::sync::SyncData;
6use itertools::Itertools;
7use nanoid::nanoid;
8use std::fs::{self};
9use std::path::PathBuf;
10
11pub struct Context<'a, AdditionalContext = ()> {
12 pub frame: usize,
13 pub beat: usize,
14 pub beat_fractional: f32,
15 pub timestamp: String,
16 pub ms: usize,
17 pub bpm: usize,
18 pub syncdata: &'a SyncData,
19 pub audiofile: PathBuf,
20 pub later_hooks: Vec<LaterHook<AdditionalContext>>,
21 pub extra: AdditionalContext,
22 pub duration_override: Option<usize>,
23}
24
25impl<C> Context<'_, C> {
26 pub fn stem(&self, name: &str) -> StemAtInstant {
27 let stems = &self.syncdata.stems;
28 if !stems.contains_key(name) {
29 panic!(
30 "No stem named {:?} found. Available stems:\n{}\n",
31 name,
32 stems
33 .keys()
34 .sorted()
35 .fold(String::new(), |acc, k| format!("{acc}\n\t{k}"))
36 );
37 }
38 StemAtInstant {
39 amplitude: *stems[name].amplitude_db.get(self.ms).unwrap_or(&0.0),
40 amplitude_max: stems[name].amplitude_max,
41 velocity_max: stems[name]
42 .notes
43 .get(&self.ms)
44 .iter()
45 .map(|notes| {
46 notes.iter().map(|note| note.velocity).max().unwrap_or(0)
47 })
48 .max()
49 .unwrap_or(0),
50 duration: stems[name].duration_ms,
51 notes: stems[name].notes.get(&self.ms).cloned().unwrap_or(vec![]),
52 }
53 }
54
55 pub fn notes_of_stem(&self, name: &str) -> impl Iterator<Item = Note> + '_ {
56 let stem = &self.syncdata.stems[name];
57 stem.notes
58 .get(&self.ms)
59 .into_iter()
60 .flat_map(|notes| notes.iter().cloned())
61 }
62
63 pub fn dump_syncdata(&self, to: PathBuf) -> anyhow::Result<()> {
64 Ok(serde_cbor::to_writer(fs::File::create(to)?, self.syncdata)?)
65 }
66
67 pub fn marker(&self) -> String {
68 self.syncdata
69 .markers
70 .get(&self.ms)
71 .unwrap_or(&"".to_string())
72 .to_string()
73 }
74
75 pub fn duration_ms(&self) -> usize {
76 match self.duration_override {
77 Some(duration) => duration,
78 None => self
79 .syncdata
80 .stems
81 .values()
82 .map(|stem| stem.duration_ms)
83 .max()
84 .unwrap(),
85 }
86 }
87
88 pub fn later_frames(
89 &mut self,
90 delay: usize,
91 render_function: &'static LaterRenderFunction,
92 ) {
93 let current_frame = self.frame;
94
95 self.later_hooks.insert(
96 0,
97 LaterHook {
98 once: true,
99 when: Box::new(move |_, context, _previous_beat| {
100 context.frame >= current_frame + delay
101 }),
102 render_function: Box::new(render_function),
103 },
104 );
105 }
106
107 pub fn later_ms(
108 &mut self,
109 delay: usize,
110 render_function: &'static LaterRenderFunction,
111 ) {
112 let current_ms = self.ms;
113
114 self.later_hooks.insert(
115 0,
116 LaterHook {
117 once: true,
118 when: Box::new(move |_, context, _previous_beat| {
119 context.ms >= current_ms + delay
120 }),
121 render_function: Box::new(render_function),
122 },
123 );
124 }
125
126 pub fn later_beats(
127 &mut self,
128 delay: f32,
129 render_function: &'static LaterRenderFunction,
130 ) {
131 let current_beat = self.beat;
132
133 self.later_hooks.insert(
134 0,
135 LaterHook {
136 once: true,
137 when: Box::new(move |_, context, _previous_beat| {
138 context.beat_fractional >= current_beat as f32 + delay
139 }),
140 render_function: Box::new(render_function),
141 },
142 );
143 }
144
145 /// duration is in milliseconds
146 pub fn start_animation(&mut self, duration: usize, animation: Animation) {
147 let start_ms = self.ms;
148 let ms_range = start_ms..(start_ms + duration);
149
150 self.later_hooks.push(LaterHook {
151 once: false,
152 when: Box::new(move |_, ctx, _| ms_range.contains(&ctx.ms)),
153 render_function: Box::new(move |canvas, ms| {
154 let t = (ms - start_ms) as f32 / duration as f32;
155 (animation.update)(t, canvas, ms)
156 }),
157 })
158 }
159
160 /// duration is in milliseconds
161 pub fn animate(
162 &mut self,
163 duration: usize,
164 f: &'static AnimationUpdateFunction,
165 ) {
166 self.start_animation(
167 duration,
168 Animation::new(format!("unnamed animation {}", nanoid!()), f),
169 );
170 }
171
172 pub fn animate_layer(
173 &mut self,
174 layer: &'static str,
175 duration: usize,
176 f: &'static LayerAnimationUpdateFunction,
177 ) {
178 let animation = Animation {
179 name: format!("unnamed animation {}", nanoid!()),
180 update: Box::new(move |progress, canvas, ms| {
181 (f)(progress, canvas.layer(layer), ms)?;
182 canvas.layer(layer).flush();
183 Ok(())
184 }),
185 };
186
187 self.start_animation(duration, animation);
188 }
189}