mirror of
https://github.com/alemidev/scope-tui.git
synced 2024-11-14 18:59:19 +01:00
feat: allow tuning to musical notes
This commit is contained in:
parent
e83a326c89
commit
16c4fc3810
2 changed files with 93 additions and 3 deletions
24
src/main.rs
24
src/main.rs
|
@ -1,7 +1,8 @@
|
||||||
mod parser;
|
mod parser;
|
||||||
mod app;
|
mod app;
|
||||||
|
mod music;
|
||||||
|
|
||||||
use std::{io::{self, ErrorKind}, time::{Duration, SystemTime}};
|
use std::{io::{self, ErrorKind}, time::{Duration, Instant}};
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::{CrosstermBackend, Backend},
|
backend::{CrosstermBackend, Backend},
|
||||||
widgets::{Block, Chart, Axis, Dataset, GraphType},
|
widgets::{Block, Chart, Axis, Dataset, GraphType},
|
||||||
|
@ -23,6 +24,7 @@ use clap::Parser;
|
||||||
use parser::{SampleParser, Signed16PCM};
|
use parser::{SampleParser, Signed16PCM};
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
use crate::music::Note;
|
||||||
|
|
||||||
/// A simple oscilloscope/vectorscope for your terminal
|
/// A simple oscilloscope/vectorscope for your terminal
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -47,10 +49,18 @@ struct Args {
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
vectorscope: bool,
|
vectorscope: bool,
|
||||||
|
|
||||||
|
/// Tune buffer size to be in tune with given note (overrides buffer option)
|
||||||
|
#[arg(long)]
|
||||||
|
tune: Option<Note>,
|
||||||
|
|
||||||
/// Sample rate to use
|
/// Sample rate to use
|
||||||
#[arg(long, default_value_t = 44100)]
|
#[arg(long, default_value_t = 44100)]
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
|
|
||||||
|
/// Pulseaudio server buffer size, in block number
|
||||||
|
#[arg(long, default_value_t = 32)]
|
||||||
|
server_buffer: u32,
|
||||||
|
|
||||||
/// Don't draw reference line
|
/// Don't draw reference line
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
no_reference: bool,
|
no_reference: bool,
|
||||||
|
@ -86,6 +96,15 @@ fn data_set<'a>(
|
||||||
fn main() -> Result<(), io::Error> {
|
fn main() -> Result<(), io::Error> {
|
||||||
let mut args = Args::parse();
|
let mut args = Args::parse();
|
||||||
|
|
||||||
|
if let Some(note) = &args.tune { // TODO make it less jank
|
||||||
|
if note != &Note::INVALID {
|
||||||
|
args.buffer = note.tune_buffer_size(0, args.sample_rate);
|
||||||
|
while args.buffer % 4 != 0 {
|
||||||
|
args.buffer += 1; // TODO jank but otherwise it doesn't align
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setup terminal
|
// setup terminal
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
|
@ -113,7 +132,6 @@ fn main() -> Result<(), io::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io::Error> {
|
fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io::Error> {
|
||||||
// prepare globals
|
// prepare globals
|
||||||
let mut buffer : Vec<u8> = vec![0; args.buffer as usize];
|
let mut buffer : Vec<u8> = vec![0; args.buffer as usize];
|
||||||
|
@ -144,7 +162,7 @@ fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io
|
||||||
&spec, // Our sample format
|
&spec, // Our sample format
|
||||||
None, // Use default channel map
|
None, // Use default channel map
|
||||||
Some(&BufferAttr {
|
Some(&BufferAttr {
|
||||||
maxlength: 32 * args.buffer,
|
maxlength: args.server_buffer * args.buffer,
|
||||||
fragsize: args.buffer,
|
fragsize: args.buffer,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
72
src/music.rs
Normal file
72
src/music.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Note {
|
||||||
|
C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B,
|
||||||
|
INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From::<String> for Note {
|
||||||
|
fn from(txt: String) -> Self {
|
||||||
|
match txt.as_str() {
|
||||||
|
"C" => Note::C,
|
||||||
|
"Db" => Note::Db,
|
||||||
|
"D" => Note::D,
|
||||||
|
"Eb" => Note::Eb,
|
||||||
|
"E" => Note::E,
|
||||||
|
"F" => Note::F,
|
||||||
|
"Gb" => Note::Gb,
|
||||||
|
"G" => Note::G,
|
||||||
|
"Ab" => Note::Ab,
|
||||||
|
"A" => Note::A,
|
||||||
|
"Bb" => Note::Bb,
|
||||||
|
"B" => Note::B,
|
||||||
|
_ => Note::INVALID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Note {
|
||||||
|
pub fn freq(&self, octave: u32) -> f32 {
|
||||||
|
match octave {
|
||||||
|
0 => match self {
|
||||||
|
Note::C => 16.35,
|
||||||
|
Note::Db => 17.32,
|
||||||
|
Note::D => 18.35,
|
||||||
|
Note::Eb => 19.45,
|
||||||
|
Note::E => 20.60,
|
||||||
|
Note::F => 21.83,
|
||||||
|
Note::Gb => 23.12,
|
||||||
|
Note::G => 24.50,
|
||||||
|
Note::Ab => 25.96,
|
||||||
|
Note::A => 27.50,
|
||||||
|
Note::Bb => 29.14,
|
||||||
|
Note::B => 30.87,
|
||||||
|
Note::INVALID => 0.0,
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let mut freq = self.freq(0);
|
||||||
|
for _ in 0..octave {
|
||||||
|
freq *= 2.0;
|
||||||
|
}
|
||||||
|
freq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn all() -> Vec<Note> {
|
||||||
|
// vec![Note::C, Note::Db, Note::D, Note::Eb, Note::E, Note::F, Note::Gb, Note::G, Note::Ab, Note::A, Note::Bb, Note::B]
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn tune_buffer_size(&self, octave:u32, sample_rate: u32) -> u32 {
|
||||||
|
let t = 1.0 / self.freq(octave); // periodo ?
|
||||||
|
let buf = (sample_rate as f32) * t;
|
||||||
|
return (buf * 4.0).round() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn tune_sample_rate(&self, octave:u32, buffer_size: u32) -> u32 {
|
||||||
|
// // TODO does it just work the same way?
|
||||||
|
// let t = 1.0 / self.freq(octave); // periodo ?
|
||||||
|
// let buf = (buffer_size as f32) * t;
|
||||||
|
// return (buf * 4.0).round() as u32;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue