summaryrefslogtreecommitdiff
path: root/src/outputers.rs
diff options
context:
space:
mode:
authorWavy Harp <wavyharp@gmail.com>2023-05-07 23:04:53 -0600
committerKyle McFarland <tfkyle@gmail.com>2023-05-07 23:04:53 -0600
commit991849b32acf83dd14a5096540bb053d2572502a (patch)
tree279b59d75d4ad6081f5242cf77d843ae6b37fc3d /src/outputers.rs
downloadrustynotes-master.zip
rustynotes-master.tar.gz
rustynotes-master.tar.bz2
initial importHEADmaster
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~
Diffstat (limited to 'src/outputers.rs')
-rw-r--r--src/outputers.rs266
1 files changed, 266 insertions, 0 deletions
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<I: FftNum + NoteValue> {
+ fn setup(&mut self);
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>);
+}
+
+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<tui::backend::CrosstermBackend<std::io::Stdout>>,
+}
+
+impl<I: FftNum + NoteValue> Outputer<I> for SimpleOutputer {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ 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<I: FftNum + NoteValue>(&self, note_count: usize, notes: &ProcerNotes<I>) {
+ 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<I: FftNum + NoteValue>(&self, note_count: usize, notes: &ProcerNotes<I>) {
+ print!("\r");
+ // TODO: do grouping to avoid excessive control characters
+ // for now this just does the simplest thing though
+ for note in &notes.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<I: FftNum + NoteValue> Outputer<I> for LineLayout {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ 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<Self, std::io::Error> {
+ let stdout = std::io::stdout();
+ let backend = CrosstermBackend::new(stdout);
+ let mut terminal = Terminal::new(backend)?;
+ return Ok(Self {
+ terminal,
+ })
+ }
+}
+
+impl<I: FftNum + NoteValue> Outputer<I> for TuiOutputer {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ //self.terminal.draw(|frame| {})
+ }
+}
+
+
+impl<I: FftNum + NoteValue> Outputer<I> for Outputers {
+ fn setup(&mut self) {
+ match self {
+ Outputers::Simple(so) => <SimpleOutputer as Outputer<I>>::setup(so),
+ Outputers::LineLayout(llo) => <LineLayout as Outputer<I>>::setup(llo),
+ Outputers::Tui(to) => <TuiOutputer as Outputer<I>>::setup(to)
+ }
+ }
+
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ match self {
+ Outputers::Simple(so) => so.handle_pnotes(notes),
+ Outputers::LineLayout(llo) => llo.handle_pnotes(notes),
+ Outputers::Tui(to) => to.handle_pnotes(notes),
+ }
+ }
+}
+