From 991849b32acf83dd14a5096540bb053d2572502a Mon Sep 17 00:00:00 2001 From: Wavy Harp Date: Sun, 7 May 2023 23:04:53 -0600 Subject: initial import currently everything is very tied to alsa and my system, for the moment you'll need to manually change the device names and maybe channels/period_size in src/main.rs, src/bin/smolguitar.rs and src/bin/loopbacker.rs, i'll fix that and add runtime period size/updater allocation soon, (and probably make a cpal backend as well so it can work on other platforms), but doing this initial commit to play around with stereo for now~ --- src/outputers.rs | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 src/outputers.rs (limited to 'src/outputers.rs') diff --git a/src/outputers.rs b/src/outputers.rs new file mode 100644 index 0000000..4c56e56 --- /dev/null +++ b/src/outputers.rs @@ -0,0 +1,266 @@ +use crate::notes::{Note, BaseNote, Notes, NoteValue, ProcerNote, ProcerNotes}; +use std::collections::VecDeque; +use std::io::Write; +use std::fmt::{Debug}; +use realfft::FftNum; +//use clap::{Args,Subcommand}; + +//use std::io; +use crossterm::terminal; +use crossterm::style::{Stylize, Color}; +use tui::{backend::CrosstermBackend, Terminal}; +use serde::{Serialize, Deserialize}; +use ansi_colours::rgb_from_ansi256; + + +trait ToRgbVal { + fn to_rgb_value(&self) -> (u8, u8, u8); +} + +impl ToRgbVal for Color { + fn to_rgb_value(&self) -> (u8, u8, u8) { + match self { + Color::Rgb {r, g, b} => (*r, *g, *b), + // I'll use the tango colours for the colour constants + // but the Ansi lookup table for the Ansi versions + Color::Reset => (0, 0, 0), + Color::Black => (0, 0, 0), + Color::DarkRed => (204, 0, 0), + Color::DarkGreen => (78, 154, 6), + Color::DarkYellow => (196, 160, 0), + Color::DarkBlue => (54, 101, 164), + Color::DarkMagenta => (117, 80, 123), + Color::DarkCyan => (6, 152, 154), + Color::Grey => (211, 215, 207), + Color::DarkGrey => (85, 87, 83), + Color::Red => (239, 41, 41), + Color::Green => (138, 226, 52), + Color::Yellow => (252, 233, 79), + Color::Blue => (114, 159, 207), + Color::Magenta => (173, 127, 168), + Color::Cyan => (52, 226, 226), + Color::White => (238, 238, 236), + Color::AnsiValue(ansi) => rgb_from_ansi256(*ansi), + } + } +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ColourScheme { + pub a: Color, pub as_: Color, + pub b: Color, + pub c: Color, pub cs: Color, + pub d: Color, pub ds: Color, + pub e: Color, + pub f: Color, pub fs: Color, + pub g: Color, pub gs: Color, +} + +impl ColourScheme { + fn get_colour(&self, note: Notes) -> Color { + match note { + Notes::C => self.c, + Notes::Cs => self.cs, + Notes::D => self.d, + Notes::Ds => self.ds, + Notes::E => self.e, + Notes::F => self.f, + Notes::Fs => self.fs, + Notes::G => self.g, + Notes::Gs => self.gs, + Notes::A => self.a, + Notes::As => self.as_, + Notes::B => self.b, + } + } +} + +impl Default for ColourScheme { + fn default() -> Self { + let c = Color::Rgb {r: 140, g: 255, b: 210}; + return Self { + a: c, as_: c, + b: c, + c: c, cs: c, + d: c, ds: c, + e: c, + f: c, fs: c, + g: c, gs: c, + } + } +} + +pub trait Outputer { + fn setup(&mut self); + fn handle_pnotes(&mut self, notes: &mut ProcerNotes); +} + +pub enum Outputers { + Simple(SimpleOutputer), + LineLayout(LineLayout), + Tui(TuiOutputer), +} + +pub struct SimpleOutputer; +pub struct LineLayout { + term_width: usize, + notes_per_line: usize, + last_line: String, + empty: String, + last_was_empty: bool, + use_colour: bool, + //max_c: (f32, f32, f32), + colours: ColourScheme, + max_threshold: f32, + //threshold: f32, +} +pub struct TuiOutputer { + terminal: Terminal>, +} + +impl Outputer for SimpleOutputer { + fn setup(&mut self) {} + fn handle_pnotes(&mut self, notes: &mut ProcerNotes) { + notes.0.sort_unstable_by(|pn1, pn2| (-(pn1.3)).partial_cmp(&-(pn2.3)).unwrap()); + print!("\r{:-80}", notes); + std::io::stdout().flush().unwrap(); + } +} + +pub fn line_note_char(note: &Note) -> char { + match note.note.0 { + Notes::C => 'c', + Notes::Cs => match note.octave % 10 { + 0 => '0', + 1 => '1', + 2 => '2', + 3 => '3', + 4 => '4', + 5 => '5', + 6 => '6', + 7 => '7', + 8 => '8', + 9 => '9', + _ => ' ', + }, + Notes::D => 'd', + Notes::Ds => ' ', + Notes::E => 'e', + Notes::F => 'f', + Notes::Fs => ' ', + Notes::G => 'g', + Notes::Gs => ' ', + Notes::A => 'a', + Notes::As => ' ', + Notes::B => 'b' + } +} + +impl LineLayout { + pub fn new(max_threshold: f32, use_colour: bool, colours: ColourScheme, notes: &[Note]) -> Self { + let term_width = terminal::size().unwrap().0 as usize; + let note_count = std::cmp::min(notes.len(), term_width); + return Self { + term_width, use_colour, colours, max_threshold, notes_per_line: note_count, + last_line: notes[0..note_count].iter().map(line_note_char).collect(), + empty: " ".repeat(term_width), + last_was_empty: true, + } + } + + pub fn line_bw(&self, note_count: usize, notes: &ProcerNotes) { + print!("\r"); + let itx: String = notes.0[0..note_count].iter().map(|note| { + if note.3 >= notes.1 { + '▉' + } else { + ' ' + } + }).collect(); + print!("{}\n{}", itx, self.last_line); + std::io::stdout().flush().unwrap(); + } + + pub fn line_colour(&self, note_count: usize, notes: &ProcerNotes) { + print!("\r"); + // TODO: do grouping to avoid excessive control characters + // for now this just does the simplest thing though + for note in ¬es.0[0..note_count] { + if note.3 >= notes.1 { + let mut bright: f32 = note.3.to_f32().unwrap() / self.max_threshold; + if bright > 1.0 { + bright = 1.0; + } + let (r, g, b) = self.colours.get_colour(note.0.note.0).to_rgb_value(); + print!("{}", ' '.on(Color::Rgb { + r: (r as f32 * bright) as u8, + g: (g as f32 * bright) as u8, + b: (b as f32 * bright) as u8, + })); + } else { + print!(" "); + } + } + print!("\n{}", self.last_line); + std::io::stdout().flush().unwrap(); + } +} + +impl Outputer for LineLayout { + fn setup(&mut self) {} + fn handle_pnotes(&mut self, notes: &mut ProcerNotes) { + let notes_len = notes.0.len(); + let note_count = std::cmp::min(notes_len, self.notes_per_line); + if notes.0[0..note_count].iter().all(|note| note.3 < notes.1) { + if self.last_was_empty { + return + } else { + self.last_was_empty = true; + } + } else { + self.last_was_empty = false; + } + match self.use_colour { + true => self.line_colour(note_count, notes), + false => self.line_bw(note_count, notes) + } + } +} + +impl TuiOutputer { + pub fn new() -> Result { + let stdout = std::io::stdout(); + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + return Ok(Self { + terminal, + }) + } +} + +impl Outputer for TuiOutputer { + fn setup(&mut self) {} + fn handle_pnotes(&mut self, notes: &mut ProcerNotes) { + //self.terminal.draw(|frame| {}) + } +} + + +impl Outputer for Outputers { + fn setup(&mut self) { + match self { + Outputers::Simple(so) => >::setup(so), + Outputers::LineLayout(llo) => >::setup(llo), + Outputers::Tui(to) => >::setup(to) + } + } + + fn handle_pnotes(&mut self, notes: &mut ProcerNotes) { + match self { + Outputers::Simple(so) => so.handle_pnotes(notes), + Outputers::LineLayout(llo) => llo.handle_pnotes(notes), + Outputers::Tui(to) => to.handle_pnotes(notes), + } + } +} + -- cgit v1.1