this repo has no description
3
fork

Configure Feed

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

⚗️ Huge W, first working version with midi sync

authored by

Gwenn Le Bihan and committed by
Ewen Le Bihan
7f872007 f15a4c24

+255 -221
+205
src/cli.rs
··· 1 + use docopt::Docopt; 2 + use serde::Deserialize; 3 + use serde_json; 4 + use shapemaker::{Canvas, ColorMapping}; 5 + use std::collections::HashMap; 6 + use std::fs::File; 7 + use std::io::BufReader; 8 + 9 + const USAGE: &'static str = " 10 + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 11 + █░▄▄█░████░▄▄▀█▀▄▄▀█░▄▄█░▄▀▄░█░▄▄▀█░█▀█░▄▄█░▄▄▀█ 12 + █▄▄▀█░▄▄░█░▀▀░█░▀▀░█░▄▄█░█▄█░█░▀▀░█░▄▀█░▄▄█░▀▀▄█ 13 + █▄▄▄█▄██▄█▄██▄█░████▄▄▄█▄███▄█▄██▄█▄█▄█▄▄▄█▄█▄▄█ 14 + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀v?.?.?▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ 15 + 16 + Usage: shapemaker (image|video) [options] [--color <mapping>...] <file> 17 + shapemaker --help 18 + shapemaker --version 19 + 20 + Options: 21 + --resolution <pixelcount> Size of the image (or frames)'s largest dimension in pixels [default: 1000] 22 + --colors <file> JSON file mapping color names to hex values 23 + The supported color names are: black, white, red, green, blue, yellow, orange, purple, brown, pink, gray, and cyan. 24 + -c --color <mapping> Color mapping in the form of <color>:<hex>. Can be used multiple times. 25 + --grid-size <WIDTHxHEIGHT> Size of the grid (number of anchor points) [default: 3x3] 26 + Putting one of the dimensions to 1 can cause a crash. 27 + --cell-size <size> Size of a cell in pixels [default: 50] 28 + --canvas-padding <size> Outter canvas padding between cells in pixels [default: 10] 29 + --line-width <size> Width of the lines in pixels [default: 2] 30 + --small-circle-radius <size> Radius of small circles in pixels [default: 5] 31 + --dot-radius <size> Radius of dots in pixels [default: 2] 32 + --empty-shape-stroke <size> Width of the stroke when a closed shape is not filled [default: 0.5] 33 + --render-grid Render the grid of anchor points 34 + --objects-count <range> Number of objects to render [default: 3..6] 35 + --polygon-vertices <range> Number of vertices for polygons [default: 2..6] 36 + 37 + Note: <range>s are inclusive on both ends 38 + 39 + Video-specific: 40 + --workers <number> Number of parallel threads to use for rendering [default: 8] 41 + --fps <fps> Frames per second [default: 30] 42 + --audio <file> Audio file to use for the video 43 + --sync-with <directory> Directory containing the audio files to sync to. 44 + The directory must contain: 45 + - stems/(instrument name).wav — stems 46 + - landmarks.json — JSON file mapping time in milliseconds to marker text (see ./landmarks.py) 47 + - full.mp3 — the complete audio file to use as the video's audio 48 + - bpm.txt — the BPM of the audio file (see ./landmarks.py) 49 + 50 + 51 + "; 52 + 53 + pub fn cli_args() -> Args { 54 + Docopt::new(USAGE.replace("?.?.?", env!("CARGO_PKG_VERSION"))) 55 + .and_then(|d| d.deserialize()) 56 + .unwrap_or_else(|e| e.exit()) 57 + } 58 + 59 + pub fn canvas_from_cli(args: &Args) -> Canvas { 60 + if args.flag_version { 61 + println!("shapemaker {}", env!("CARGO_PKG_VERSION")); 62 + std::process::exit(0); 63 + } 64 + 65 + let mut canvas = Canvas::new(vec![]); 66 + canvas.colormap = load_colormap(&args); 67 + set_canvas_settings_from_args(&args, &mut canvas); 68 + canvas 69 + } 70 + 71 + #[derive(Debug, Deserialize)] 72 + pub struct Args { 73 + pub cmd_image: bool, 74 + pub cmd_video: bool, 75 + pub arg_file: String, 76 + pub flag_version: bool, 77 + pub flag_color: Vec<String>, 78 + pub flag_colors: Option<String>, 79 + pub flag_grid_size: Option<String>, 80 + pub flag_cell_size: Option<usize>, 81 + pub flag_canvas_padding: Option<usize>, 82 + pub flag_line_width: Option<f32>, 83 + pub flag_small_circle_radius: Option<f32>, 84 + pub flag_dot_radius: Option<f32>, 85 + pub flag_empty_shape_stroke: Option<f32>, 86 + pub flag_render_grid: bool, 87 + pub flag_objects_count: Option<String>, 88 + pub flag_polygon_vertices: Option<String>, 89 + pub flag_fps: Option<usize>, 90 + pub flag_sync_with: Option<String>, 91 + pub flag_audio: Option<String>, 92 + pub flag_resolution: Option<usize>, 93 + pub flag_workers: Option<usize>, 94 + } 95 + 96 + fn set_canvas_settings_from_args(args: &Args, canvas: &mut Canvas) { 97 + if let Some(dimensions) = &args.flag_grid_size { 98 + let mut split = dimensions.split('x'); 99 + let width = split.next().unwrap().parse::<usize>().unwrap(); 100 + let height = split.next().unwrap().parse::<usize>().unwrap(); 101 + canvas.grid_size = (width, height); 102 + } 103 + if let Some(cell_size) = args.flag_cell_size { 104 + canvas.cell_size = cell_size; 105 + } 106 + if let Some(canvas_padding) = args.flag_canvas_padding { 107 + canvas.canvas_outter_padding = canvas_padding; 108 + } 109 + if let Some(line_width) = args.flag_line_width { 110 + canvas.object_sizes.line_width = line_width; 111 + } 112 + if let Some(small_circle_radius) = args.flag_small_circle_radius { 113 + canvas.object_sizes.small_circle_radius = small_circle_radius; 114 + } 115 + if let Some(dot_radius) = args.flag_dot_radius { 116 + canvas.object_sizes.dot_radius = dot_radius; 117 + } 118 + if let Some(empty_shape_stroke) = args.flag_empty_shape_stroke { 119 + canvas.object_sizes.empty_shape_stroke_width = empty_shape_stroke; 120 + } 121 + canvas.render_grid = args.flag_render_grid; 122 + if let Some(objects_count) = &args.flag_objects_count { 123 + let mut split = objects_count.split(".."); 124 + let min = split.next().unwrap().parse::<usize>().unwrap(); 125 + let max = split.next().unwrap().parse::<usize>().unwrap(); 126 + // +1 because the range is exclusive, using ..= raises a type error 127 + canvas.objects_count_range = min..(max + 1); 128 + } 129 + if let Some(polygon_vertices) = &args.flag_polygon_vertices { 130 + let mut split = polygon_vertices.split(".."); 131 + let min = split.next().unwrap().parse::<usize>().unwrap(); 132 + let max = split.next().unwrap().parse::<usize>().unwrap(); 133 + canvas.polygon_vertices_range = min..(max + 1); 134 + } 135 + } 136 + 137 + fn load_colormap(args: &Args) -> ColorMapping { 138 + if let Some(file) = &args.flag_colors { 139 + let file = File::open(file).unwrap(); 140 + let reader = BufReader::new(file); 141 + serde_json::from_reader(reader).unwrap() 142 + } else { 143 + let mut colormap: HashMap<String, String> = HashMap::new(); 144 + for mapping in &args.flag_color { 145 + if !mapping.contains(":") { 146 + println!("Invalid color mapping: {}", mapping); 147 + std::process::exit(1); 148 + } 149 + let mut split = mapping.split(':'); 150 + let color = split.next().unwrap(); 151 + let hex = split.next().unwrap(); 152 + colormap.insert(color.to_string(), hex.to_string()); 153 + } 154 + ColorMapping { 155 + black: colormap 156 + .get("black") 157 + .unwrap_or(&ColorMapping::default().black) 158 + .to_string(), 159 + white: colormap 160 + .get("white") 161 + .unwrap_or(&ColorMapping::default().white) 162 + .to_string(), 163 + red: colormap 164 + .get("red") 165 + .unwrap_or(&ColorMapping::default().red) 166 + .to_string(), 167 + green: colormap 168 + .get("green") 169 + .unwrap_or(&ColorMapping::default().green) 170 + .to_string(), 171 + blue: colormap 172 + .get("blue") 173 + .unwrap_or(&ColorMapping::default().blue) 174 + .to_string(), 175 + yellow: colormap 176 + .get("yellow") 177 + .unwrap_or(&ColorMapping::default().yellow) 178 + .to_string(), 179 + orange: colormap 180 + .get("orange") 181 + .unwrap_or(&ColorMapping::default().orange) 182 + .to_string(), 183 + purple: colormap 184 + .get("purple") 185 + .unwrap_or(&ColorMapping::default().purple) 186 + .to_string(), 187 + brown: colormap 188 + .get("brown") 189 + .unwrap_or(&ColorMapping::default().brown) 190 + .to_string(), 191 + pink: colormap 192 + .get("pink") 193 + .unwrap_or(&ColorMapping::default().pink) 194 + .to_string(), 195 + gray: colormap 196 + .get("gray") 197 + .unwrap_or(&ColorMapping::default().gray) 198 + .to_string(), 199 + cyan: colormap 200 + .get("cyan") 201 + .unwrap_or(&ColorMapping::default().cyan) 202 + .to_string(), 203 + } 204 + } 205 + }
+1 -1
src/lib.rs
··· 93 93 pub extra: AdditionalContext, 94 94 } 95 95 96 - const DURATION_OVERRIDE: Option<usize> = Some(30 * 1000); 96 + const DURATION_OVERRIDE: Option<usize> = Some(2 * 60 * 1000); 97 97 98 98 pub trait GetOrDefault { 99 99 type Item;
+49 -220
src/main.rs
··· 1 - use docopt::Docopt; 2 - use serde::Deserialize; 3 - use serde_json; 4 - use shapemaker::{Anchor, Canvas, CenterAnchor, Color, ColorMapping, Fill, Object, Video}; 5 - use std::collections::HashMap; 6 - use std::fs::File; 7 - use std::io::BufReader; 8 - use std::path::PathBuf; 9 - 10 - const USAGE: &'static str = " 11 - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 12 - █░▄▄█░████░▄▄▀█▀▄▄▀█░▄▄█░▄▀▄░█░▄▄▀█░█▀█░▄▄█░▄▄▀█ 13 - █▄▄▀█░▄▄░█░▀▀░█░▀▀░█░▄▄█░█▄█░█░▀▀░█░▄▀█░▄▄█░▀▀▄█ 14 - █▄▄▄█▄██▄█▄██▄█░████▄▄▄█▄███▄█▄██▄█▄█▄█▄▄▄█▄█▄▄█ 15 - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀v?.?.?▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ 16 - 17 - Usage: shapemaker (image|video) [options] [--color <mapping>...] <file> 18 - shapemaker --help 19 - shapemaker --version 20 - 21 - Options: 22 - --resolution <pixelcount> Size of the image (or frames)'s largest dimension in pixels [default: 1000] 23 - --colors <file> JSON file mapping color names to hex values 24 - The supported color names are: black, white, red, green, blue, yellow, orange, purple, brown, pink, gray, and cyan. 25 - -c --color <mapping> Color mapping in the form of <color>:<hex>. Can be used multiple times. 26 - --grid-size <WIDTHxHEIGHT> Size of the grid (number of anchor points) [default: 3x3] 27 - Putting one of the dimensions to 1 can cause a crash. 28 - --cell-size <size> Size of a cell in pixels [default: 50] 29 - --canvas-padding <size> Outter canvas padding between cells in pixels [default: 10] 30 - --line-width <size> Width of the lines in pixels [default: 2] 31 - --small-circle-radius <size> Radius of small circles in pixels [default: 5] 32 - --dot-radius <size> Radius of dots in pixels [default: 2] 33 - --empty-shape-stroke <size> Width of the stroke when a closed shape is not filled [default: 0.5] 34 - --render-grid Render the grid of anchor points 35 - --objects-count <range> Number of objects to render [default: 3..6] 36 - --polygon-vertices <range> Number of vertices for polygons [default: 2..6] 37 - 38 - Note: <range>s are inclusive on both ends 39 - 40 - Video-specific: 41 - --workers <number> Number of parallel threads to use for rendering [default: 8] 42 - --fps <fps> Frames per second [default: 30] 43 - --audio <file> Audio file to use for the video 44 - --sync-with <directory> Directory containing the audio files to sync to. 45 - The directory must contain: 46 - - stems/(instrument name).wav — stems 47 - - landmarks.json — JSON file mapping time in milliseconds to marker text (see ./landmarks.py) 48 - - full.mp3 — the complete audio file to use as the video's audio 49 - - bpm.txt — the BPM of the audio file (see ./landmarks.py) 50 - 51 - 52 - "; 1 + use shapemaker::{Anchor, Canvas, CenterAnchor, Color, Fill, Object, Video}; 2 + mod cli; 3 + pub use cli::{canvas_from_cli, cli_args}; 53 4 54 5 fn main() { 55 - let args: Args = Docopt::new(USAGE.replace("?.?.?", env!("CARGO_PKG_VERSION"))) 56 - .and_then(|d| d.deserialize()) 57 - .unwrap_or_else(|e| e.exit()); 58 - 59 - if args.flag_version { 60 - println!("shapemaker {}", env!("CARGO_PKG_VERSION")); 61 - std::process::exit(0); 62 - } 63 - 64 - let mut canvas = Canvas::new(vec![]); 65 - canvas.colormap = load_colormap(&args); 66 - set_canvas_settings_from_args(&args, &mut canvas); 6 + let args = cli_args(); 7 + let mut canvas = canvas_from_cli(&args); 67 8 68 9 if args.cmd_image && !args.cmd_video { 69 10 canvas.layers.push(canvas.random_layer("main")); ··· 92 33 canvas.random_color(), 93 34 ); 94 35 canvas.set_background(context.extra.3); 95 - context.dump_stems("stems_data".into()) 96 36 }) 97 37 .set_audio(args.flag_audio.unwrap().into()) 98 38 .sync_audio_with(&args.flag_sync_with.unwrap()) 99 - .each_beat(&|canvas, _| { 100 - let current_background = canvas.background; 101 - let mut background_to_use = canvas.random_color(); 102 - if let Some(bg) = current_background { 103 - while bg == background_to_use { 104 - background_to_use = canvas.random_color(); 39 + .on_stem( 40 + "bass", 41 + 0.7, 42 + &|canvas, _| { 43 + let mut layer = canvas.random_layer("root"); 44 + for obj in layer.objects.iter_mut() { 45 + if let Some(_) = obj.1 .1 { 46 + obj.1 .1 = Some(Fill::Solid(Color::Black)) 47 + } 105 48 } 106 - } 107 - canvas.set_background(canvas.random_color()); 108 - }) 49 + canvas.layers[0] = layer; 50 + }, 51 + &|_, _| {}, 52 + ) 109 53 .on_stem( 110 54 "anchor kick", 111 55 0.7, 112 - &|canvas, context| { 113 - println!( 114 - "anchor kick at {}: amplitude_relative is {}", 115 - context.timestamp, 116 - context.stem("anchor kick").amplitude_relative() 117 - ); 118 - canvas.root().add_object( 119 - "kick", 120 - Object::BigCircle(context.extra.1), 121 - Some(Fill::Solid(Color::Cyan)), 122 - ); 123 - }, 124 - &|canvas, _| canvas.remove_object("kick"), 56 + &|canvas, _| canvas.set_background(color_cycle(canvas.background.unwrap())), 57 + &|_, _| {}, 125 58 ) 59 + // .on_stem( 60 + // "bass", 61 + // 0.7, 62 + // &|canvas, context| { 63 + // println!( 64 + // "anchor kick at {}: amplitude_relative is {}", 65 + // context.timestamp, 66 + // context.stem("anchor kick").amplitude_relative() 67 + // ); 68 + // canvas.root().add_object( 69 + // "kick", 70 + // Object::BigCircle(context.extra.1), 71 + // Some(Fill::Solid(Color::Cyan)), 72 + // ); 73 + // }, 74 + // &|canvas, _| canvas.remove_object("kick"), 75 + // ) 126 76 .on_stem( 127 77 "clap", 128 78 0.7, ··· 151 101 .unwrap(); 152 102 } 153 103 154 - #[derive(Debug, Deserialize)] 155 - struct Args { 156 - cmd_image: bool, 157 - cmd_video: bool, 158 - arg_file: String, 159 - flag_version: bool, 160 - flag_color: Vec<String>, 161 - flag_colors: Option<String>, 162 - flag_grid_size: Option<String>, 163 - flag_cell_size: Option<usize>, 164 - flag_canvas_padding: Option<usize>, 165 - flag_line_width: Option<f32>, 166 - flag_small_circle_radius: Option<f32>, 167 - flag_dot_radius: Option<f32>, 168 - flag_empty_shape_stroke: Option<f32>, 169 - flag_render_grid: bool, 170 - flag_objects_count: Option<String>, 171 - flag_polygon_vertices: Option<String>, 172 - flag_fps: Option<usize>, 173 - flag_sync_with: Option<String>, 174 - flag_audio: Option<String>, 175 - flag_resolution: Option<usize>, 176 - flag_workers: Option<usize>, 177 - } 178 - 179 - fn set_canvas_settings_from_args(args: &Args, canvas: &mut Canvas) { 180 - if let Some(dimensions) = &args.flag_grid_size { 181 - let mut split = dimensions.split('x'); 182 - let width = split.next().unwrap().parse::<usize>().unwrap(); 183 - let height = split.next().unwrap().parse::<usize>().unwrap(); 184 - canvas.grid_size = (width, height); 185 - } 186 - if let Some(cell_size) = args.flag_cell_size { 187 - canvas.cell_size = cell_size; 188 - } 189 - if let Some(canvas_padding) = args.flag_canvas_padding { 190 - canvas.canvas_outter_padding = canvas_padding; 191 - } 192 - if let Some(line_width) = args.flag_line_width { 193 - canvas.object_sizes.line_width = line_width; 194 - } 195 - if let Some(small_circle_radius) = args.flag_small_circle_radius { 196 - canvas.object_sizes.small_circle_radius = small_circle_radius; 197 - } 198 - if let Some(dot_radius) = args.flag_dot_radius { 199 - canvas.object_sizes.dot_radius = dot_radius; 200 - } 201 - if let Some(empty_shape_stroke) = args.flag_empty_shape_stroke { 202 - canvas.object_sizes.empty_shape_stroke_width = empty_shape_stroke; 203 - } 204 - canvas.render_grid = args.flag_render_grid; 205 - if let Some(objects_count) = &args.flag_objects_count { 206 - let mut split = objects_count.split(".."); 207 - let min = split.next().unwrap().parse::<usize>().unwrap(); 208 - let max = split.next().unwrap().parse::<usize>().unwrap(); 209 - // +1 because the range is exclusive, using ..= raises a type error 210 - canvas.objects_count_range = min..(max + 1); 211 - } 212 - if let Some(polygon_vertices) = &args.flag_polygon_vertices { 213 - let mut split = polygon_vertices.split(".."); 214 - let min = split.next().unwrap().parse::<usize>().unwrap(); 215 - let max = split.next().unwrap().parse::<usize>().unwrap(); 216 - canvas.polygon_vertices_range = min..(max + 1); 217 - } 218 - } 219 - 220 - fn load_colormap(args: &Args) -> ColorMapping { 221 - if let Some(file) = &args.flag_colors { 222 - let file = File::open(file).unwrap(); 223 - let reader = BufReader::new(file); 224 - serde_json::from_reader(reader).unwrap() 225 - } else { 226 - let mut colormap: HashMap<String, String> = HashMap::new(); 227 - for mapping in &args.flag_color { 228 - if !mapping.contains(":") { 229 - println!("Invalid color mapping: {}", mapping); 230 - std::process::exit(1); 231 - } 232 - let mut split = mapping.split(':'); 233 - let color = split.next().unwrap(); 234 - let hex = split.next().unwrap(); 235 - colormap.insert(color.to_string(), hex.to_string()); 236 - } 237 - ColorMapping { 238 - black: colormap 239 - .get("black") 240 - .unwrap_or(&ColorMapping::default().black) 241 - .to_string(), 242 - white: colormap 243 - .get("white") 244 - .unwrap_or(&ColorMapping::default().white) 245 - .to_string(), 246 - red: colormap 247 - .get("red") 248 - .unwrap_or(&ColorMapping::default().red) 249 - .to_string(), 250 - green: colormap 251 - .get("green") 252 - .unwrap_or(&ColorMapping::default().green) 253 - .to_string(), 254 - blue: colormap 255 - .get("blue") 256 - .unwrap_or(&ColorMapping::default().blue) 257 - .to_string(), 258 - yellow: colormap 259 - .get("yellow") 260 - .unwrap_or(&ColorMapping::default().yellow) 261 - .to_string(), 262 - orange: colormap 263 - .get("orange") 264 - .unwrap_or(&ColorMapping::default().orange) 265 - .to_string(), 266 - purple: colormap 267 - .get("purple") 268 - .unwrap_or(&ColorMapping::default().purple) 269 - .to_string(), 270 - brown: colormap 271 - .get("brown") 272 - .unwrap_or(&ColorMapping::default().brown) 273 - .to_string(), 274 - pink: colormap 275 - .get("pink") 276 - .unwrap_or(&ColorMapping::default().pink) 277 - .to_string(), 278 - gray: colormap 279 - .get("gray") 280 - .unwrap_or(&ColorMapping::default().gray) 281 - .to_string(), 282 - cyan: colormap 283 - .get("cyan") 284 - .unwrap_or(&ColorMapping::default().cyan) 285 - .to_string(), 286 - } 104 + fn color_cycle(current_color: Color) -> Color { 105 + match current_color { 106 + Color::Blue => Color::Cyan, 107 + Color::Cyan => Color::Green, 108 + Color::Green => Color::Yellow, 109 + Color::Yellow => Color::Orange, 110 + Color::Orange => Color::Red, 111 + Color::Red => Color::Purple, 112 + Color::Purple => Color::Pink, 113 + Color::Pink => Color::White, 114 + Color::White => Color::Blue, 115 + _ => unreachable!(), 287 116 } 288 117 }