This repository has no description
0

Configure Feed

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

🎨 Format code

+257 -256
+255 -255
src/video/engine.rs
··· 1 - use super::{context::Context, Video}; 2 - use crate::rendering::svg; 3 - use crate::{Canvas, SVGRenderable}; 4 - use anyhow::Result; 5 - use measure_time::debug_time; 6 - use std::sync::mpsc::SyncSender; 7 - 8 - /// What data is sent to the output by the rendering engine for each rendered frame 9 - pub enum EngineOutput { 10 - Finished, 11 - Frame(EngineProgression, svg::Node), 12 - } 13 - 14 - pub struct EngineProgression { 15 - pub ms: usize, 16 - pub timestamp: String, 17 - pub scene_name: Option<String>, 18 - } 19 - 20 - impl<'a, C: Default> Context<'a, C> { 21 - pub fn engine_progression(&self) -> EngineProgression { 22 - EngineProgression { 23 - ms: self.ms, 24 - timestamp: self.timestamp(), 25 - scene_name: self.current_scene.clone(), 26 - } 27 - } 28 - } 29 - 30 - impl<AdditionalContext: Default> Video<AdditionalContext> { 31 - pub fn render( 32 - &self, 33 - output: SyncSender<EngineOutput>, 34 - controller: impl Fn(&Context<AdditionalContext>) -> EngineControl, 35 - ) -> Result<usize> { 36 - debug_time!("render"); 37 - 38 - let mut rendered_frames_count: usize = 0; 39 - let mut context = Context { 40 - ms: self.start_rendering_at, 41 - current_scene: None, 42 - fps: self.fps, 43 - syncdata: &self.syncdata, 44 - extra: AdditionalContext::default(), 45 - later_hooks: vec![], 46 - audiofile: self.audiofile.clone(), 47 - duration_override: self.duration_override, 48 - scene_started_at_ms: None, 49 - bpm: self 50 - .syncdata 51 - .bpm 52 - .expect("No sync source could determine the BPM"), 53 - }; 54 - 55 - let mut canvas = self.initial_canvas.clone(); 56 - 57 - let mut previous_rendered_beat = 0; 58 - let mut previous_rendered_frame = 0; 59 - 60 - let render_ms_range = self.start_rendering_at + 0..self.duration_ms(); 61 - 62 - self.progress_bar.set_length(render_ms_range.len() as u64); 63 - 64 - for _ in render_ms_range { 65 - context.ms += 1_usize; 66 - 67 - let control = controller(&context); 68 - 69 - let (stop_before, stop_after, skip_rendering, skip_hooks) = ( 70 - control.stop_rendering_beforehand(), 71 - control.stop_rendering_afterwards(), 72 - !control.render_this_one(), 73 - !control.run_hooks_on_this_one(), 74 - ); 75 - 76 - if stop_before { 77 - break; 78 - } 79 - 80 - if skip_hooks { 81 - continue; 82 - } 83 - 84 - if let EngineControl::RenderFromCanvas(new_canvas) = control { 85 - canvas = new_canvas; 86 - } 87 - 88 - if context.marker().starts_with(':') { 89 - let marker_text = context.marker(); 90 - let commandline = marker_text.trim_start_matches(':').to_string(); 91 - 92 - for command in &self.commands { 93 - if commandline.starts_with(&command.name) { 94 - let args = commandline 95 - .trim_start_matches(&command.name) 96 - .trim() 97 - .to_string(); 98 - (command.action)(args, &mut canvas, &mut context)?; 99 - } 100 - } 101 - } 102 - 103 - // Render later hooks first, so that for example animations that aren't finished yet get overwritten by next frame's hook, if the next frames touches the same object 104 - // This is way better to cancel early animations such as fading out an object that appears on every note of a stem, if the next note is too close for the fade-out to finish. 105 - 106 - let mut later_hooks_to_delete: Vec<usize> = vec![]; 107 - 108 - for (i, hook) in context.later_hooks.iter().enumerate() { 109 - if (hook.when)(&canvas, &context, previous_rendered_beat) { 110 - (hook.render_function)(&mut canvas, context.ms)?; 111 - if hook.once { 112 - later_hooks_to_delete.push(i); 113 - } 114 - } else if !hook.once { 115 - later_hooks_to_delete.push(i); 116 - } 117 - } 118 - 119 - for i in later_hooks_to_delete { 120 - if i < context.later_hooks.len() { 121 - context.later_hooks.remove(i); 122 - } 123 - } 124 - 125 - for hook in &self.hooks { 126 - if (hook.when)( 127 - &canvas, 128 - &context, 129 - previous_rendered_beat, 130 - previous_rendered_frame, 131 - ) { 132 - (hook.render_function)(&mut canvas, &mut context)?; 133 - } 134 - } 135 - 136 - if !skip_rendering && context.frame() != previous_rendered_frame { 137 - output.send(EngineOutput::Frame( 138 - context.engine_progression(), 139 - canvas.render_to_svg( 140 - canvas.colormap.clone(), 141 - canvas.cell_size, 142 - canvas.object_sizes, 143 - "", 144 - )?, 145 - ))?; 146 - 147 - rendered_frames_count += 1; 148 - 149 - previous_rendered_beat = context.beat(); 150 - previous_rendered_frame = context.frame(); 151 - } 152 - 153 - if stop_after { 154 - println!( 155 - "Stopping rendering as requested after frame {}", 156 - context.frame() 157 - ); 158 - break; 159 - } 160 - } 161 - 162 - output.send(EngineOutput::Finished)?; 163 - 164 - Ok(rendered_frames_count) 165 - } 166 - 167 - pub fn render_single_frame( 168 - &self, 169 - frame_no: usize, 170 - ) -> Result<(String, svg::Node)> { 171 - debug_time!("render_single_frame"); 172 - let (tx, rx) = std::sync::mpsc::sync_channel::<EngineOutput>(2); 173 - 174 - self.render(tx, |ctx| { 175 - if ctx.frame() == frame_no { 176 - EngineControl::Finish 177 - } else if ctx.frame() < frame_no { 178 - EngineControl::Skip 179 - } else { 180 - EngineControl::Stop 181 - } 182 - })?; 183 - 184 - println!("Waiting for rendered frame..."); 185 - for output in rx.iter() { 186 - match output { 187 - EngineOutput::Finished => break, 188 - EngineOutput::Frame(progression, svg) => { 189 - return Ok((progression.timestamp, svg)) 190 - } 191 - } 192 - } 193 - 194 - return Err(anyhow::format_err!( 195 - "Renderer did not output any non-empty frames" 196 - )); 197 - } 198 - 199 - pub fn render_all_frames( 200 - &self, 201 - output: SyncSender<EngineOutput>, 202 - ) -> Result<usize> { 203 - self.render(output, |_| EngineControl::Render) 204 - } 205 - } 206 - 207 - /// Tells the rendering engine what to do with a frame 208 - pub enum EngineControl { 209 - /// Don't run hooks or anything on this frame 210 - Ignore, 211 - /// Skip to the next frame, don't render this one 212 - Skip, 213 - /// Render this frame as usual 214 - Render, 215 - /// Render this frame and stop rendering afterwards 216 - Finish, 217 - /// Don't render this frame and stop rendering 218 - Stop, 219 - /// Set canvas and then render this frame 220 - RenderFromCanvas(Canvas), 221 - } 222 - 223 - impl EngineControl { 224 - pub fn render_this_one(&self) -> bool { 225 - match self { 226 - EngineControl::RenderFromCanvas(_) 227 - | EngineControl::Render 228 - | EngineControl::Finish => true, 229 - EngineControl::Ignore | EngineControl::Skip | EngineControl::Stop => { 230 - false 231 - } 232 - } 233 - } 234 - 235 - pub fn run_hooks_on_this_one(&self) -> bool { 236 - match self { 237 - EngineControl::Ignore => false, 238 - _ => true, 239 - } 240 - } 241 - 242 - pub fn stop_rendering_beforehand(&self) -> bool { 243 - match self { 244 - EngineControl::Stop => true, 245 - _ => false, 246 - } 247 - } 248 - 249 - pub fn stop_rendering_afterwards(&self) -> bool { 250 - match self { 251 - EngineControl::Finish => true, 252 - _ => false, 253 - } 254 - } 255 - } 1 + use super::{context::Context, Video}; 2 + use crate::rendering::svg; 3 + use crate::{Canvas, SVGRenderable}; 4 + use anyhow::Result; 5 + use measure_time::debug_time; 6 + use std::sync::mpsc::SyncSender; 7 + 8 + /// What data is sent to the output by the rendering engine for each rendered frame 9 + pub enum EngineOutput { 10 + Finished, 11 + Frame(EngineProgression, svg::Node), 12 + } 13 + 14 + pub struct EngineProgression { 15 + pub ms: usize, 16 + pub timestamp: String, 17 + pub scene_name: Option<String>, 18 + } 19 + 20 + impl<'a, C: Default> Context<'a, C> { 21 + pub fn engine_progression(&self) -> EngineProgression { 22 + EngineProgression { 23 + ms: self.ms, 24 + timestamp: self.timestamp(), 25 + scene_name: self.current_scene.clone(), 26 + } 27 + } 28 + } 29 + 30 + impl<AdditionalContext: Default> Video<AdditionalContext> { 31 + pub fn render( 32 + &self, 33 + output: SyncSender<EngineOutput>, 34 + controller: impl Fn(&Context<AdditionalContext>) -> EngineControl, 35 + ) -> Result<usize> { 36 + debug_time!("render"); 37 + 38 + let mut rendered_frames_count: usize = 0; 39 + let mut context = Context { 40 + ms: self.start_rendering_at, 41 + current_scene: None, 42 + fps: self.fps, 43 + syncdata: &self.syncdata, 44 + extra: AdditionalContext::default(), 45 + later_hooks: vec![], 46 + audiofile: self.audiofile.clone(), 47 + duration_override: self.duration_override, 48 + scene_started_at_ms: None, 49 + bpm: self 50 + .syncdata 51 + .bpm 52 + .expect("No sync source could determine the BPM"), 53 + }; 54 + 55 + let mut canvas = self.initial_canvas.clone(); 56 + 57 + let mut previous_rendered_beat = 0; 58 + let mut previous_rendered_frame = 0; 59 + 60 + let render_ms_range = self.start_rendering_at + 0..self.duration_ms(); 61 + 62 + self.progress_bar.set_length(render_ms_range.len() as u64); 63 + 64 + for _ in render_ms_range { 65 + context.ms += 1_usize; 66 + 67 + let control = controller(&context); 68 + 69 + let (stop_before, stop_after, skip_rendering, skip_hooks) = ( 70 + control.stop_rendering_beforehand(), 71 + control.stop_rendering_afterwards(), 72 + !control.render_this_one(), 73 + !control.run_hooks_on_this_one(), 74 + ); 75 + 76 + if stop_before { 77 + break; 78 + } 79 + 80 + if skip_hooks { 81 + continue; 82 + } 83 + 84 + if let EngineControl::RenderFromCanvas(new_canvas) = control { 85 + canvas = new_canvas; 86 + } 87 + 88 + if context.marker().starts_with(':') { 89 + let marker_text = context.marker(); 90 + let commandline = marker_text.trim_start_matches(':').to_string(); 91 + 92 + for command in &self.commands { 93 + if commandline.starts_with(&command.name) { 94 + let args = commandline 95 + .trim_start_matches(&command.name) 96 + .trim() 97 + .to_string(); 98 + (command.action)(args, &mut canvas, &mut context)?; 99 + } 100 + } 101 + } 102 + 103 + // Render later hooks first, so that for example animations that aren't finished yet get overwritten by next frame's hook, if the next frames touches the same object 104 + // This is way better to cancel early animations such as fading out an object that appears on every note of a stem, if the next note is too close for the fade-out to finish. 105 + 106 + let mut later_hooks_to_delete: Vec<usize> = vec![]; 107 + 108 + for (i, hook) in context.later_hooks.iter().enumerate() { 109 + if (hook.when)(&canvas, &context, previous_rendered_beat) { 110 + (hook.render_function)(&mut canvas, context.ms)?; 111 + if hook.once { 112 + later_hooks_to_delete.push(i); 113 + } 114 + } else if !hook.once { 115 + later_hooks_to_delete.push(i); 116 + } 117 + } 118 + 119 + for i in later_hooks_to_delete { 120 + if i < context.later_hooks.len() { 121 + context.later_hooks.remove(i); 122 + } 123 + } 124 + 125 + for hook in &self.hooks { 126 + if (hook.when)( 127 + &canvas, 128 + &context, 129 + previous_rendered_beat, 130 + previous_rendered_frame, 131 + ) { 132 + (hook.render_function)(&mut canvas, &mut context)?; 133 + } 134 + } 135 + 136 + if !skip_rendering && context.frame() != previous_rendered_frame { 137 + output.send(EngineOutput::Frame( 138 + context.engine_progression(), 139 + canvas.render_to_svg( 140 + canvas.colormap.clone(), 141 + canvas.cell_size, 142 + canvas.object_sizes, 143 + "", 144 + )?, 145 + ))?; 146 + 147 + rendered_frames_count += 1; 148 + 149 + previous_rendered_beat = context.beat(); 150 + previous_rendered_frame = context.frame(); 151 + } 152 + 153 + if stop_after { 154 + println!( 155 + "Stopping rendering as requested after frame {}", 156 + context.frame() 157 + ); 158 + break; 159 + } 160 + } 161 + 162 + output.send(EngineOutput::Finished)?; 163 + 164 + Ok(rendered_frames_count) 165 + } 166 + 167 + pub fn render_single_frame( 168 + &self, 169 + frame_no: usize, 170 + ) -> Result<(String, svg::Node)> { 171 + debug_time!("render_single_frame"); 172 + let (tx, rx) = std::sync::mpsc::sync_channel::<EngineOutput>(2); 173 + 174 + self.render(tx, |ctx| { 175 + if ctx.frame() == frame_no { 176 + EngineControl::Finish 177 + } else if ctx.frame() < frame_no { 178 + EngineControl::Skip 179 + } else { 180 + EngineControl::Stop 181 + } 182 + })?; 183 + 184 + println!("Waiting for rendered frame..."); 185 + for output in rx.iter() { 186 + match output { 187 + EngineOutput::Finished => break, 188 + EngineOutput::Frame(progression, svg) => { 189 + return Ok((progression.timestamp, svg)) 190 + } 191 + } 192 + } 193 + 194 + return Err(anyhow::format_err!( 195 + "Renderer did not output any non-empty frames" 196 + )); 197 + } 198 + 199 + pub fn render_all_frames( 200 + &self, 201 + output: SyncSender<EngineOutput>, 202 + ) -> Result<usize> { 203 + self.render(output, |_| EngineControl::Render) 204 + } 205 + } 206 + 207 + /// Tells the rendering engine what to do with a frame 208 + pub enum EngineControl { 209 + /// Don't run hooks or anything on this frame 210 + Ignore, 211 + /// Skip to the next frame, don't render this one 212 + Skip, 213 + /// Render this frame as usual 214 + Render, 215 + /// Render this frame and stop rendering afterwards 216 + Finish, 217 + /// Don't render this frame and stop rendering 218 + Stop, 219 + /// Set canvas and then render this frame 220 + RenderFromCanvas(Canvas), 221 + } 222 + 223 + impl EngineControl { 224 + pub fn render_this_one(&self) -> bool { 225 + match self { 226 + EngineControl::RenderFromCanvas(_) 227 + | EngineControl::Render 228 + | EngineControl::Finish => true, 229 + EngineControl::Ignore | EngineControl::Skip | EngineControl::Stop => { 230 + false 231 + } 232 + } 233 + } 234 + 235 + pub fn run_hooks_on_this_one(&self) -> bool { 236 + match self { 237 + EngineControl::Ignore => false, 238 + _ => true, 239 + } 240 + } 241 + 242 + pub fn stop_rendering_beforehand(&self) -> bool { 243 + match self { 244 + EngineControl::Stop => true, 245 + _ => false, 246 + } 247 + } 248 + 249 + pub fn stop_rendering_afterwards(&self) -> bool { 250 + match self { 251 + EngineControl::Finish => true, 252 + _ => false, 253 + } 254 + } 255 + }
+2 -1
src/video/hooks.rs
··· 133 133 ) -> Self { 134 134 self.with_hook(Hook { 135 135 when: Box::new(move |_, context, _, previous_rendered_frame| { 136 - context.frame() != previous_rendered_frame && context.frame() % n == 0 136 + context.frame() != previous_rendered_frame 137 + && context.frame() % n == 0 137 138 }), 138 139 render_function: Box::new(render_function), 139 140 })