This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

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