tuitar 0.1.0

A TUI guitar tuner
Documentation
use std::sync::mpsc;

use ratatui::crossterm::event::{Event, KeyCode};
use ratatui::layout::{Margin, Offset};
use ratatui::style::Modifier;
use ratatui_fretboard::FretboardState;
use tui_big_text::PixelSize;
use tuitar_core::fps::FpsWidget;
use tuitar_core::state::State;
use tuitar_core::ui::*;

use crate::input::Recorder;
use crate::transform::Transform;

pub struct Application {
    pub is_running: bool,
    pub state: State<Transform>,
    pub receiver: mpsc::Receiver<Vec<i16>>,
    pub recorder: Recorder,
    pub fps_widget: FpsWidget,
    tab: usize,
}

impl Default for Application {
    fn default() -> Self {
        Self::new()
    }
}

impl Application {
    pub fn new() -> Self {
        let transform = Transform::new();

        let (tx, rx) = mpsc::channel::<Vec<i16>>();
        let recorder = Recorder::init("pipewire", move |data: &[i16], _| {
            tx.send(data.to_vec()).unwrap();
        });
        let state = State::new(
            transform,
            recorder.sample_rate() as usize,
            12,
            PixelSize::Full,
            5,
            Some(5),
        );

        Self {
            is_running: true,
            state,
            receiver: rx,
            recorder,
            fps_widget: FpsWidget::default()
                .with_label(true)
                .with_style(Modifier::ITALIC),
            tab: 0,
        }
    }

    pub fn start_recording(&mut self) {
        self.recorder.start();
    }

    pub fn switch_tab(&mut self) {
        self.tab = (self.tab + 1) % 3;
    }

    pub fn handle_event(&mut self, event: Event) {
        if let Event::Key(key) = event {
            match key.code {
                KeyCode::Tab => self.switch_tab(),
                KeyCode::Char('q') | KeyCode::Esc => self.is_running = false,
                _ => {}
            }
        }
    }

    pub fn render(&mut self, frame: &mut ratatui::Frame<'_>) {
        self.fps_widget.fps.tick();
        let area = frame.area();

        draw_cents(frame, area, &self.state);
        frame.render_widget(&self.fps_widget, frame.area());

        let area = area.inner(Margin {
            horizontal: 0,
            vertical: 1,
        });

        match self.tab {
            0 => draw_waveform(
                frame,
                area,
                &self.state,
                (i16::MIN as f64, i16::MAX as f64),
                ("Amplitudes", "Time(s)"),
            ),
            1 => {
                draw_frequency(frame, area, &self.state);
                draw_note_name(frame, area, &self.state);
            }
            2 => draw_dbfs_spectrum(frame, area, &self.state, ("Level (dBFS)", "Frequency (Hz)")),
            _ => {}
        }

        let fretboard_width = 51;
        let mut area = frame.area().offset(Offset {
            x: (frame.area().width as i32 - fretboard_width) / 2,
            y: (frame.area().height / 2) as i32 + 4,
        });
        area.width = fretboard_width.try_into().unwrap_or(0);
        draw_fretboard(frame, area, &self.state, &mut FretboardState::default());
    }
}