Skip to content

Commit

Permalink
Initial commit, basic waveform rendering with tui
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmarsc committed Oct 26, 2021
0 parents commit c638bc8
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/target
*.wav
*.aif*
venv
.vscode
Cargo.lock
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "audeye"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
structopt = { version = "0.3", default-features = false }
hound = { version = "3.4"}
textplots = { version = "0.8" }
terminal_size = { version = "0.1.17"}
sndfile = { version = "0.0.4", features = ["ndarray_features"]}
rayon = "1.5.1"
tui = "0.16"
termion = "1.5"
75 changes: 75 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::io;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use termion::event::Key;
use termion::input::TermRead;

pub enum Event<I> {
Input(I),
Tick,
}

/// A small event handler that wrap termion input and tick events. Each event
/// type is handled in its own thread and returned to a common `Receiver`
pub struct Events {
rx: mpsc::Receiver<Event<Key>>,
input_handle: thread::JoinHandle<()>,
tick_handle: thread::JoinHandle<()>,
}

#[derive(Debug, Clone, Copy)]
pub struct Config {
pub tick_rate: Duration,
}

impl Default for Config {
fn default() -> Config {
Config {
tick_rate: Duration::from_millis(250),
}
}
}

impl Events {
pub fn new() -> Events {
Events::with_config(Config::default())
}

pub fn with_config(config: Config) -> Events {
let (tx, rx) = mpsc::channel();
let input_handle = {
let tx = tx.clone();
thread::spawn(move || {
let stdin = io::stdin();
for evt in stdin.keys() {
if let Ok(key) = evt {
if let Err(err) = tx.send(Event::Input(key)) {
eprintln!("{}", err);
return;
}
}
}
})
};
let tick_handle = {
thread::spawn(move || loop {
if let Err(err) = tx.send(Event::Tick) {
eprintln!("{}", err);
break;
}
thread::sleep(config.tick_rate);
})
};
Events {
rx,
input_handle,
tick_handle,
}
}

pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
self.rx.recv()
}
}
136 changes: 136 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

// use std::fs::File;
use structopt::StructOpt;
use hound;
use terminal_size::terminal_size;
use tui::layout;
use tui::style::Modifier;
use tui::widgets::Dataset;
use tui::widgets::GraphType;
use std::convert::From;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::thread::panicking;
use std::cmp::max;
extern crate sndfile;
use crate::render::waveform;
use crate::sndfile::SndFileIO;
use std::io::SeekFrom;



mod event;
use crate::event::{Event, Events};

mod render;
use crate::render::renderer::Renderer;
use crate::render::waveform::WaveformRenderer;
// use crate::util::


// use crate::util::event::{Config, Event, Events};
use std::{error::Error, io, time::Duration};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout, Rect},
style::Color,
style::Style,
widgets::{
canvas::{Canvas, Map, MapResolution, Rectangle},
Block, Borders, Chart, Axis
},
text::Span,
Terminal,
symbols
};



#[derive(StructOpt)]
struct CliArgs {
// The file to read
#[structopt(parse(from_os_str), help = "The path of the file to analyze")]
path: std::path::PathBuf,

// Normalize option
// unused for now
#[structopt(short = "n", long = "normalize")]
normalize: bool,
}


fn main() -> Result<(), io::Error> {
// Get some infos about the terminal
// let (width, height) = terminal_size().expect("Unable to get terminal size");

// Get cli args
let args = CliArgs::from_args();

// Setup UI
let stdout = io::stdout().into_raw_mode()?;
let stdout = MouseTerminal::from(stdout);
let stdout = AlternateScreen::from(stdout);
let backend = TermionBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;

let events = Events::new();


// Compute the size of each block to fit the screen
let block_count: u16 = 1920 / 4 ;


let mut waveform_render = WaveformRenderer::new(block_count, &args.path);
let channels = waveform_render.channels;
let chunk_count: u32 = max(2, channels).try_into().expect("");
let layout_constraints = vec![Constraint::Ratio(1, chunk_count); chunk_count as usize];


loop {
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(layout_constraints.as_ref())
.split(f.size());

for ch_idx in 0..channels {
match waveform_render.get_representation(ch_idx) {
Some(chart) => f.render_widget(chart, chunks[ch_idx]),
None => ()
}
}

})?;

let event = events.next().expect("");

match event {
Event::Input(input) => {
if input == Key::Char('q') {
break;
}
}
Event::Tick => {
continue;
}
}
}





// // let shape = Shape::Bars();
// let graph_height: u32 = u32::max(height.0.into(), 32);
// Chart::new_with_y_range((width.0).into(), graph_height, 0., block_count as f32, -1., 1.)
// .lineplot(&Shape::Lines(p_data.as_slice()))
// .lineplot(&Shape::Lines(n_data.as_slice()))
// .display();
// Chart::new(block_count.into(), graph_height, 0., block_count as f32)
// .lineplot(&Shape::Lines(p_data.as_slice()))
// .lineplot(&Shape::Lines(n_data.as_slice()))
// .display();

Ok(())
}
2 changes: 2 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod renderer;
pub mod waveform;
7 changes: 7 additions & 0 deletions src/render/renderer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use tui::widgets::Widget;


pub trait Renderer<'a, T : Widget> {
fn get_representation(&'a mut self, channel: usize) -> Option<T>;
}

Loading

0 comments on commit c638bc8

Please sign in to comment.