···11+use crate::music::Note;
22+33+/// a simple oscilloscope/vectorscope for your terminal
44+#[derive(Debug)]
55+pub struct ScopeArgs {
66+ pub opts: SourceOptions,
77+ pub ui: UiOptions,
88+}
99+1010+#[derive(Debug, Clone)]
1111+pub struct UiOptions {
1212+ /// floating point vertical scale, from 0 to 1
1313+ pub scale: f32,
1414+1515+ /// use vintage looking scatter mode instead of line mode
1616+ pub scatter: bool,
1717+1818+ /// don't draw reference line
1919+ pub no_reference: bool,
2020+2121+ /// hide UI and only draw waveforms
2222+ pub no_ui: bool,
2323+2424+ /// don't use braille dots for drawing lines
2525+ pub no_braille: bool,
2626+}
2727+2828+#[derive(Debug, Clone)]
2929+pub struct SourceOptions {
3030+ /// number of channels to open
3131+ pub channels: usize,
3232+3333+ /// size of audio buffer, and width of scope
3434+ pub buffer: u32,
3535+3636+ /// sample rate to use
3737+ pub sample_rate: u32,
3838+3939+ /// tune buffer size to be in tune with given note (overrides buffer option)
4040+ pub tune: Option<String>,
4141+}
4242+4343+// TODO its convenient to keep this here but it's not really the best place...
4444+impl SourceOptions {
4545+ pub fn tune(&mut self) {
4646+ if let Some(txt) = &self.tune {
4747+ // TODO make it less jank
4848+ if let Ok(note) = txt.parse::<Note>() {
4949+ self.buffer = note.tune_buffer_size(self.sample_rate);
5050+ while self.buffer % (self.channels as u32 * 2) != 0 {
5151+ // TODO customizable bit depth
5252+ self.buffer += 1; // TODO jank but otherwise it doesn't align
5353+ }
5454+ } else {
5555+ eprintln!("[!] Unrecognized note '{}', ignoring option", txt);
5656+ }
5757+ }
5858+ }
5959+}
···11+pub type Matrix<T> = Vec<Vec<T>>;
22+33+/// separate a stream of alternating channels into a matrix of channel streams:
44+/// L R L R L R L R L R
55+/// becomes
66+/// L L L L L
77+/// R R R R R
88+pub fn stream_to_matrix<I, O>(
99+ stream: impl Iterator<Item = I>,
1010+ channels: usize,
1111+ norm: O,
1212+) -> Matrix<O>
1313+where
1414+ I: Copy + Into<O>,
1515+ O: Copy + std::ops::Div<Output = O>,
1616+{
1717+ let mut out = vec![vec![]; channels];
1818+ let mut channel = 0;
1919+ for sample in stream {
2020+ out[channel].push(sample.into() / norm);
2121+ channel = (channel + 1) % channels;
2222+ }
2323+ out
2424+}