From: Huck Boles Date: Tue, 27 Jun 2023 00:10:58 +0000 (-0500) Subject: added: midi message channel X-Git-Url: https://git.huck.website/?a=commitdiff_plain;h=f1bd4062dcead807fb1ff62356bf3db90b20ab58;p=cellseq.git added: midi message channel --- diff --git a/src/lib.rs b/src/lib.rs index 8737153..aacac03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ mod midi; use map::*; use mask::*; -use midi::*; +pub use midi::*; pub type CellMap = FxHashSet; @@ -65,7 +65,7 @@ pub struct CellSeq { #[derive(Debug, Clone)] pub enum Message { - MidiMessage(MidiMessage), + Midi(MidiMessage), Map(map::Message), Mask(mask::Message), Tick(Instant), @@ -77,19 +77,21 @@ pub enum Message { SpeedChanged(usize), ToggleLoop, LoopLength(usize), + Quit, } impl Application for CellSeq { type Message = Message; type Theme = Theme; type Executor = executor::Default; - type Flags = (); + type Flags = MidiLink; - fn new(_flags: ()) -> (Self, Command) { + fn new(flags: Self::Flags) -> (Self, Command) { ( Self { bpm: 120, loop_len: 16, + midi: flags, ..Self::default() }, Command::none(), @@ -104,9 +106,9 @@ impl Application for CellSeq { match message { Message::Map(message) => self.map.update(message), Message::Mask(message) => self.mask.update(message), - Message::MidiMessage(message) => {} + Message::Midi(message) => self.midi.update(message), Message::Tick(_) => { - let life = if self.step_num == self.loop_len && self.is_looping { + let map = if self.is_looping && self.step_num > self.loop_len { self.step_num = 0; self.map.reset_loop() } else { @@ -114,8 +116,15 @@ impl Application for CellSeq { self.map.tick() }; - self.map.update(map::Message::Ticked(life.clone())); - self.mask.update(mask::Message::Tick(life)); + let midi = self.mask.tick(map); + let mut commands = Vec::new(); + for message in midi { + commands.push(Command::perform(async move { message }, |m| { + Message::Midi(m) + })); + } + + return Command::batch(commands); } Message::TogglePlayback => { self.is_playing = !self.is_playing; @@ -135,6 +144,7 @@ impl Application for CellSeq { } } Message::LoopLength(len) => self.loop_len = len, + Message::Quit => todo!(), } Command::none() @@ -220,6 +230,9 @@ fn view_controls<'a>( button("clear") .on_press(Message::Clear) .style(theme::Button::Destructive), + button("quit") + .on_press(Message::Quit) + .style(theme::Button::Destructive), ] .width(Length::Fill) .align_items(Alignment::Center) diff --git a/src/main.rs b/src/main.rs index 7f42c61..950a632 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,23 +4,38 @@ use iced::{window, Application, Settings}; use eyre::Result; use jack::{Client, ClientOptions, ClosureProcessHandler, Control, MidiOut, ProcessScope, RawMidi}; -use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::mpsc::channel; pub fn main() -> Result<()> { - let (midi_snd, midi_rcv) = channel::(256); + let (midi_snd, mut midi_rcv) = channel::>(256); // setting up jack client - let (jack_client, jack_status) = Client::new("cellseq", ClientOptions::empty())?; + let (jack_client, _status) = Client::new("cellseq", ClientOptions::empty())?; let mut midi_port = jack_client.register_port("cellseq_midi", MidiOut::default())?; let process_handler = ClosureProcessHandler::new(move |_: &Client, scope: &ProcessScope| { - let writer = midi_port.writer(scope); + let mut writer = midi_port.writer(scope); + let mut bytes = Vec::new(); + + while let Ok(Some(byte)) = midi_rcv.try_recv() { + bytes.push(byte); + } + + let time = scope.frames_since_cycle_start(); + writer + .write(&RawMidi { + time, + bytes: &bytes[0..bytes.len()], + }) + .unwrap(); Control::Continue }); let jack = jack_client.activate_async((), process_handler)?; + let midi = MidiLink::new(midi_snd); + // running the graphics window CellSeq::run(Settings { antialiasing: true, @@ -28,9 +43,9 @@ pub fn main() -> Result<()> { position: window::Position::Centered, ..window::Settings::default() }, + flags: midi, ..Settings::default() - }) - .map_err(|_| Ok(())); + })?; jack.deactivate()?; diff --git a/src/mask.rs b/src/mask.rs index cc3d4ab..c89920c 100644 --- a/src/mask.rs +++ b/src/mask.rs @@ -16,11 +16,12 @@ use rustc_hash::FxHashMap; pub enum Message { Check(Cell), Uncheck(Cell), + SetNote((Cell, Note)), Tick(CellMap), } #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum State { +enum OnOff { #[default] Off, On, @@ -28,22 +29,23 @@ pub enum State { #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Note { - value: usize, - state: State, + value: u8, + velocity: u8, + state: OnOff, } impl Note { pub fn is_on(&self) -> bool { match self.state { - State::On => true, - State::Off => false, + OnOff::On => true, + OnOff::Off => false, } } pub fn switch(&mut self) { self.state = match self.state { - State::On => State::Off, - State::Off => State::On, + OnOff::On => OnOff::Off, + OnOff::Off => OnOff::On, } } } @@ -75,6 +77,9 @@ impl Mask { self.mask_cache.clear(); } + Message::SetNote((cell, note)) => { + self.cells.insert(cell, note); + } } } @@ -92,6 +97,27 @@ impl Mask { .height(Length::Fixed(Cell::SIZE as f32 * 24.0)) .into() } + + pub fn tick(&mut self, life: CellMap) -> Vec { + let mut note_map: FxHashMap = FxHashMap::default(); + let mut messages = Vec::new(); + + for cell in life.iter() { + if let Some(note) = self.cells.get(cell) { + note_map.insert(note.value, note.velocity); + } + } + + for (note, vel) in note_map { + messages.push(MidiMessage::On { + note, + velocity: vel, + channel: 0, + }) + } + + messages + } } impl Program for Mask { diff --git a/src/midi.rs b/src/midi.rs index 3ee5fd9..4caf545 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -6,11 +6,21 @@ use tokio::sync::mpsc::Sender; #[derive(Clone, Debug)] pub struct MidiLink { buffer: VecDeque, - channel: Sender, + channel: Sender>, } -impl MidiLink { - pub fn new(channel: Sender) -> Self { +impl Default for MidiLink { + fn default() -> Self { + let (send, _) = tokio::sync::mpsc::channel(128); + Self { + buffer: VecDeque::default(), + channel: send, + } + } +} + +impl<'a> MidiLink { + pub fn new(channel: Sender>) -> Self { Self { buffer: VecDeque::default(), channel, @@ -27,9 +37,10 @@ impl MidiLink { pub async fn tick(&mut self) { for byte in self.buffer.iter() { - self.channel.send(*byte).await.unwrap(); + self.channel.send(Some(*byte)).await.unwrap(); } + self.channel.send(None).await.unwrap(); self.buffer.clear(); } }