Files
singer/src/main.rs
2026-03-28 01:20:37 +02:00

175 lines
4.5 KiB
Rust

use anyhow::{Result, anyhow};
use macroquad::color::*;
use serialport::SerialPortType;
#[derive(Clone, Copy, Debug)]
pub enum Instruction {
NoteOn(u8),
NoteOff,
Modulation { depth: u8, rate: u8 },
Frequency(u16),
}
pub struct SerialDevice {
port: Box<dyn serialport::SerialPort>,
}
const CHANNEL_COUNT: u8 = 4;
impl SerialDevice {
pub fn new() -> Result<Self> {
let ports = serialport::available_ports()?;
let mut this: anyhow::Result<Self> = Err(anyhow!("No serial port found"));
for port in ports {
match port.port_type {
SerialPortType::UsbPort(_) => {
this = Ok(Self {
port: serialport::new(port.port_name, 115200).open()?,
});
break;
}
_ => {}
};
}
this
}
fn note_off(&mut self, ch: u8) -> Result<()> {
assert!((0..CHANNEL_COUNT).contains(&ch));
let bytes: Vec<u8> = vec![0x80 + ch];
self.port.write(&bytes)?;
Ok(())
}
fn note_on(&mut self, ch: u8, note: u8) -> Result<()> {
assert!((0..CHANNEL_COUNT).contains(&ch));
assert!((0..=127).contains(&note));
let bytes: Vec<u8> = vec![0x90 + ch, note];
self.port.write(&bytes)?;
Ok(())
}
fn modulation(&mut self, ch: u8, depth: u8, rate: u8) -> Result<()> {
assert!((0..CHANNEL_COUNT).contains(&ch));
assert!((0..=127).contains(&depth));
let bytes: Vec<u8> = vec![0xA0 + ch, depth, rate];
self.port.write(&bytes)?;
Ok(())
}
fn frequency_on(&mut self, ch: u8, freq: u16) -> Result<()> {
assert!((0..CHANNEL_COUNT).contains(&ch));
let bytes: Vec<u8> = vec![0xB0 + ch, (freq & 0xff) as u8, (freq >> 8) as u8];
self.port.write(&bytes)?;
Ok(())
}
pub fn stop(&mut self) -> Result<()> {
let bytes: Vec<u8> = vec![0xff];
self.port.write(&bytes)?;
Ok(())
}
pub fn execute(&mut self, ch: u8, instruction: Instruction) -> Result<()> {
match instruction {
Instruction::NoteOn(note) => self.note_on(ch, note),
Instruction::NoteOff => self.note_off(ch),
Instruction::Modulation { depth, rate } => self.modulation(ch, depth, rate),
Instruction::Frequency(freq) => self.frequency_on(ch, freq),
}
}
}
pub struct Pattern {
pub rows: Vec<[Option<Instruction>; CHANNEL_COUNT as usize]>,
}
impl Pattern {
pub fn new() -> Self {
let mut rows = Vec::new();
rows.resize_with(256, || [None; CHANNEL_COUNT as usize]);
Self { rows }
}
}
pub struct TimeSignature {
pub numerator: u8,
pub denominator: u8,
}
impl Default for TimeSignature {
fn default() -> Self {
Self {
numerator: 4,
denominator: 4,
}
}
}
pub struct Song {
pub bpm: f64,
pub time_signature: TimeSignature,
pub patterns: Vec<Pattern>,
pub pattern_order: Vec<usize>, // index into patterns
}
impl Song {
const DEFAULT_BPM: f64 = 120.0;
pub fn new(bpm: Option<f64>, time_signature: Option<TimeSignature>) -> Self {
Self {
bpm: bpm.unwrap_or(Self::DEFAULT_BPM),
time_signature: time_signature.unwrap_or_default(),
patterns: vec![Pattern::new()],
pattern_order: vec![0],
}
}
pub fn empty(bpm: Option<f64>, time_signature: Option<TimeSignature>) -> Self {
Self {
bpm: bpm.unwrap_or(Self::DEFAULT_BPM),
time_signature: time_signature.unwrap_or_default(),
patterns: Vec::new(),
pattern_order: Vec::new(),
}
}
}
#[derive(Default)]
struct App {}
impl App {
pub fn new() -> Self {
Self { ..App::default() }
}
pub async fn run(&self) {
loop {
self.update().await;
}
}
async fn update(&self) {
macroquad::window::clear_background(WHITE);
macroquad::shapes::draw_line(40.0, 40.0, 100.0, 200.0, 15.0, BLUE);
macroquad::shapes::draw_rectangle(
macroquad::window::screen_width() / 2.0 - 60.0,
100.0,
120.0,
60.0,
GREEN,
);
macroquad::text::draw_text("Hello, Macroquad!", 20.0, 20.0, 30.0, DARKGRAY);
macroquad::window::next_frame().await
}
}
#[macroquad::main("singer")]
async fn main() {
let app = App::new();
app.run().await;
}