use crate::*; impl Sampler { pub fn view_grid (&self) -> impl Draw + Layout + use<'_> { let cells_x = 8u16; let cells_y = 8u16; let cell_width = 10u16; let cell_height = 2u16; //let width = cells_x * cell_width; //let height = cells_y * cell_height; //let cols = Map::east( //cell_width, //move||0..cells_x, //move|x, _|Map::south( //cell_height, //move||0..cells_y, //move|y, _|self.view_grid_cell("........", x, y, cell_width, cell_height) //) //); //cols //Thunk::new(|to: &mut TuiOut|{ //}) "TODO" } pub fn view_grid_cell <'a> ( &'a self, name: &'a str, x: u16, y: u16, w: u16, h: u16 ) -> impl Draw + use<'a> { let cursor = self.cursor(); let hi_fg = Color::Rgb(64, 64, 64); let hi_bg = if y == 0 { Color::Reset } else { Color::Rgb(64, 64, 64) /*prev*/ }; let tx_fg = if let Some((index, _)) = self.recording && index % 8 == x as usize && index / 8 == y as usize { Color::Rgb(255, 64, 0) } else { Color::Rgb(255, 255, 255) }; let tx_bg = if x as usize == cursor.0 && y as usize == cursor.1 { Color::Rgb(96, 96, 96) } else { Color::Rgb(64, 64, 64) }; let lo_fg = Color::Rgb(64, 64, 64); let lo_bg = if y == 7 { Color::Reset } else { tx_bg }; Fixed::xy(w, h, Bsp::s( Fixed::y(1, Tui::fg_bg(hi_fg, hi_bg, RepeatH(Phat::<()>::LO))), Bsp::n( Fixed::y(1, Tui::fg_bg(lo_fg, lo_bg, RepeatH(Phat::<()>::HI))), Fill::x(Fixed::y(1, Tui::fg_bg(tx_fg, tx_bg, name))), ), )) } const _EMPTY: &[(f64, f64)] = &[(0., 0.), (1., 1.), (2., 2.), (0., 2.), (2., 0.)]; pub fn view_list <'a, T: NotePoint + NoteRange> ( &'a self, compact: bool, editor: &T ) -> impl Draw + 'a { let note_lo = editor.get_note_lo(); let note_pt = editor.get_note_pos(); let note_hi = editor.get_note_hi(); Fixed::x(if compact { 4 } else { 12 }, Map::south( 1, move||(note_lo..=note_hi).rev(), move|note, _index| { //let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a)))); let mut bg = if note == note_pt { Tui::g(64) } else { Color::Reset }; let mut fg = Tui::g(160); if let Some(mapped) = &self.mapped[note] { let sample = mapped.read().unwrap(); fg = if note == note_pt { sample.color.lightest.rgb } else { Tui::g(224) }; bg = if note == note_pt { sample.color.light.rgb } else { sample.color.base.rgb }; } if let Some((index, _)) = self.recording { if note == index { bg = if note == note_pt { Color::Rgb(96,24,0) } else { Color::Rgb(64,16,0) }; fg = Color::Rgb(224,64,32) } } Tui::fg_bg(fg, bg, format!("{note:3} {}", self.view_list_item(note, compact))) })) } pub fn view_list_item (&self, note: usize, compact: bool) -> String { if compact { String::default() } else { draw_list_item(&self.mapped[note]) } } pub fn view_sample (&self, note_pt: usize) -> impl Draw + use<'_> { Outer(true, Style::default().fg(Tui::g(96))) .enclose(Fill::xy(draw_viewer(if let Some((_, Some(sample))) = &self.recording { Some(sample) } else if let Some(sample) = &self.mapped[note_pt] { Some(sample) } else { None }))) } pub fn view_sample_info (&self, note_pt: usize) -> impl Draw + use<'_> { Fill::x(Fixed::y(1, draw_info(if let Some((_, Some(sample))) = &self.recording { Some(sample) } else if let Some(sample) = &self.mapped[note_pt] { Some(sample) } else { None }))) } pub fn view_sample_status (&self, note_pt: usize) -> impl Draw + use<'_> { Fixed::x(20, draw_info_v(if let Some((_, Some(sample))) = &self.recording { Some(sample) } else if let Some(sample) = &self.mapped[note_pt] { Some(sample) } else { None })) } pub fn view_status (&self, index: usize) -> impl Draw { draw_status(self.mapped[index].as_ref()) } pub fn view_meters_input (&self) -> impl Draw + use<'_> { draw_meters(&self.input_meters) } pub fn view_meters_output (&self) -> impl Draw + use<'_> { draw_meters(&self.output_meters) } } fn draw_meters (meters: &[f32]) -> impl Draw + use<'_> { Tui::bg(Black, Fixed::x(2, Map::east(1, ||meters.iter(), |value, _index|{ Fill::y(RmsMeter(*value)) }))) } fn draw_list_item (sample: &Option>>) -> String { if let Some(sample) = sample { let sample = sample.read().unwrap(); format!("{:8}", sample.name) //format!("{:8} {:3} {:6}-{:6}/{:6}", //sample.name, //sample.gain, //sample.start, //sample.end, //sample.channels[0].len() //) } else { String::from("........") } } fn draw_viewer (sample: Option<&Arc>>) -> impl Draw + Layout + use<'_> { let min_db = -64.0; Thunk::new(move|to: &mut TuiOut|{ let [x, y, width, height] = to.area(); let area = Rect { x, y, width, height }; if let Some(sample) = &sample { let sample = sample.read().unwrap(); let start = sample.start as f64; let end = sample.end as f64; let length = end - start; let step = length / width as f64; let mut t = start; let mut lines = vec![]; while t < end { let chunk = &sample.channels[0][t as usize..((t + step) as usize).min(sample.end)]; let total: f32 = chunk.iter().map(|x|x.abs()).sum(); let count = chunk.len() as f32; let meter = 10. * (total / count).log10(); let x = t as f64; let y = meter as f64; lines.push(Line::new(x, min_db, x, y, Color::Green)); t += step / 2.; } Canvas::default() .x_bounds([sample.start as f64, sample.end as f64]) .y_bounds([min_db, 0.]) .paint(|ctx| { for line in lines.iter() { ctx.draw(line); } //FIXME: proportions //let text = "press record to finish sampling"; //ctx.print( //(width - text.len() as u16) as f64 / 2.0, //height as f64 / 2.0, //text.red() //); }).render(area, &mut to.buffer); } else { Canvas::default() .x_bounds([0.0, width as f64]) .y_bounds([0.0, height as f64]) .paint(|_ctx| { //let text = "press record to begin sampling"; //ctx.print( //(width - text.len() as u16) as f64 / 2.0, //height as f64 / 2.0, //text.red() //); }) .render(area, &mut to.buffer); } }) } fn draw_info (sample: Option<&Arc>>) -> impl Draw + Layout + use<'_> { When(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; to.place(&row!( FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), FieldH(theme, "Start", format!("{:<8}", sample.start)), FieldH(theme, "End", format!("{:<8}", sample.end)), FieldH(theme, "Trans", "0"), FieldH(theme, "Gain", format!("{}", sample.gain)), )) })) } fn draw_info_v (sample: Option<&Arc>>) -> impl Draw + Layout + use<'_> { Either::new(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; to.place(&Fixed::x(20, col!( Fill::x(Align::w(FieldH(theme, "Name ", format!("{:<10}", sample.name.clone())))), Fill::x(Align::w(FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())))), Fill::x(Align::w(FieldH(theme, "Start ", format!("{:<8}", sample.start)))), Fill::x(Align::w(FieldH(theme, "End ", format!("{:<8}", sample.end)))), Fill::x(Align::w(FieldH(theme, "Trans ", "0"))), Fill::x(Align::w(FieldH(theme, "Gain ", format!("{}", sample.gain)))), ))) }), Thunk::new(|to: &mut TuiOut|to.place(&Tui::fg(Red, col!( Tui::bold(true, "× No sample."), "[r] record", "[Shift-F9] import", ))))) } fn draw_status (sample: Option<&Arc>>) -> impl Draw + Layout { Tui::bold(true, Tui::fg(Tui::g(224), sample .map(|sample|{ let sample = sample.read().unwrap(); format!("Sample {}-{}", sample.start, sample.end) }) .unwrap_or_else(||"No sample".to_string()))) } fn draw_sample ( to: &mut TuiOut, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool ) -> Usually { let style = if focus { Style::default().green() } else { Style::default() }; if focus { to.blit(&"🬴", x+1, y, Some(style.bold())); } let label1 = format!("{:3} {:12}", note.map(|n|n.to_string()).unwrap_or(String::default()), sample.name); let label2 = format!("{:>6} {:>6} +0.0", sample.start, sample.end); to.blit(&label1, x+2, y, Some(style.bold())); to.blit(&label2, x+3+label1.len()as u16, y, Some(style)); Ok(label1.len() + label2.len() + 4) }