From 2e26fc2eaa72f6475f282c8193cf01e01f1a5365 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 5 Jul 2024 11:07:50 +0300 Subject: [PATCH] duplicate loop --- src/control.rs | 9 +++- src/core.rs | 33 +++++++------ src/main.rs | 119 ++++++++++++++++++++++++++++++++++++++++++--- src/model/track.rs | 5 +- src/view.rs | 4 +- 5 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/control.rs b/src/control.rs index 34cf2577..5bfb4052 100644 --- a/src/control.rs +++ b/src/control.rs @@ -19,7 +19,14 @@ handle!(App |self, e| { const KEYMAP: &'static [KeyBinding] = keymap!(App { [Char('x'), NONE, "extend", "double the current clip", |app: &mut App| { - // TODO: This duplicates the current clip's contents and doubles the length. + if let Some(phrase) = app.phrase_mut() { + let mut notes = BTreeMap::new(); + for (time, events) in phrase.notes.iter() { + notes.insert(time + phrase.length, events.clone()); + } + phrase.notes.append(&mut notes); + phrase.length = phrase.length * 2; + } Ok(true) }], [Char('l'), NONE, "toggle_loop", "toggle looping", |app: &mut App| { diff --git a/src/core.rs b/src/core.rs index 3a26abb8..7474b042 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,19 +1,22 @@ // Stdlib dependencies: -pub use std::error::Error; -pub use std::io::{stdout, Stdout, Write}; -pub use std::thread::{spawn, JoinHandle}; -pub use std::time::Duration; -pub use std::collections::BTreeMap; -pub use std::sync::{Arc, Mutex, MutexGuard}; -pub use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize}; -pub use std::sync::mpsc::{channel, Sender, Receiver}; +pub(crate) use std::error::Error; +pub(crate) use std::io::{stdout}; +pub(crate) use std::thread::{spawn, JoinHandle}; +pub(crate) use std::time::Duration; +pub(crate) use std::collections::BTreeMap; +pub(crate) use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize}; +pub(crate) use std::sync::{ + Arc, + Mutex, MutexGuard, + RwLock, LockResult, TryLockResult, RwLockReadGuard, RwLockWriteGuard +}; // Non-stdlib dependencies: -pub use microxdg::XdgApp; -pub use ratatui::prelude::*; -pub use midly::{MidiMessage, live::LiveEvent, num::u7}; -pub use crossterm::{ExecutableCommand, QueueableCommand}; -pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; +pub(crate) use microxdg::XdgApp; +pub(crate) use ratatui::prelude::*; +pub(crate) use midly::{MidiMessage, live::LiveEvent, num::u7}; +pub(crate) use crossterm::{ExecutableCommand}; +pub(crate) use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; use better_panic::{Settings, Verbosity}; use crossterm::terminal::{ EnterAlternateScreen, LeaveAlternateScreen, @@ -108,7 +111,7 @@ pub fn main_thread ( let exited = exited.clone(); let device = device.clone(); let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; - let sleep = std::time::Duration::from_millis(16); + let sleep = Duration::from_millis(16); Ok(spawn(move || loop { terminal.draw(|frame|{ @@ -133,7 +136,7 @@ pub fn input_thread ( exited: &Arc, device: &Arc> ) -> JoinHandle<()> { - let poll = std::time::Duration::from_millis(100); + let poll = Duration::from_millis(100); let exited = exited.clone(); let device = device.clone(); spawn(move || loop { diff --git a/src/main.rs b/src/main.rs index 9b97db4c..8bf408a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,6 +132,7 @@ pub fn main () -> Usually<()> { Ok(()) })?; track.sequence = Some(0); // FIXME + track.add_phrase("Custom", ppq * 4, None); track.add_phrase("Offbeat", ppq * 4, Some(phrase! { 00 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() }, 02 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, @@ -145,13 +146,32 @@ pub fn main () -> Usually<()> { Ok(()) })?; + state.add_track_with_cb(Some("Lead"), |_, track|{ + track.add_device_with_cb(Plugin::lv2( + "Helm", + "file:///home/user/.lv2/Helm.lv2" + )?, |track, device|{ + device.connect_midi_in(0, &track.midi_out.clone_unowned())?; + if let Some(Some(left)) = outputs.get(0) { + device.connect_audio_out(0, left)?; + } + if let Some(Some(right)) = outputs.get(0) { + device.connect_audio_out(1, right)?; + } + Ok(()) + })?; + track.sequence = Some(0); // FIXME + track.add_phrase("Custom", ppq * 4, None); + Ok(()) + })?; + state.scenes = vec![ - Scene::new("Intro", vec![None, Some(0), None, None]), - Scene::new("Hook", vec![Some(0), Some(0), None, None]), - Scene::new("Verse", vec![Some(1), Some(0), None, None]), - Scene::new("Chorus", vec![Some(0), Some(0), None, None]), - Scene::new("Bridge", vec![Some(2), Some(0), None, None]), - Scene::new("Outro", vec![None, Some(0), None, None]), + Scene::new("Intro", vec![None, Some(0), None, None]), + Scene::new("Hook", vec![Some(0), Some(1), None, None]), + Scene::new("Verse", vec![Some(1), Some(0), Some(0), None]), + Scene::new("Chorus", vec![Some(0), Some(1), None, None]), + Scene::new("Bridge", vec![Some(2), Some(0), Some(0), None]), + Scene::new("Outro", vec![None, Some(1), None, None]), ]; Ok(()) @@ -306,9 +326,96 @@ impl App { self.scenes.get_mut(id).map(|t|(id, t)) } } } + pub fn phrase (&self) -> Option<&Phrase> { + let (track_id, track) = self.track()?; + let (_, scene) = self.scene()?; + track.phrases.get((*scene.clips.get(track_id)?)?) + } + pub fn phrase_mut (&mut self) -> Option<&mut Phrase> { + let (track_id, _) = self.track()?; + let (_, scene) = self.scene()?; + let clip = (*scene.clips.get(track_id)?)?; + self.track_mut()?.1.phrases.get_mut(clip) + } pub fn phrase_id (&self) -> Option { let (track_id, _) = self.track()?; let (_, scene) = self.scene()?; *scene.clips.get(track_id)? } + + pub fn selection <'a> (&'a self) -> Selection<'a> { + let track_id = if self.track_cursor == 0 { None } else { Some(self.track_cursor - 1) }; + let track = (&track_id).map(|id|self.tracks.get(id)).flatten(); + let scene_id = if self.scene_cursor == 0 { None } else { Some(self.scene_cursor - 1) }; + let scene = (&scene_id).map(|id|self.scenes.get(id)).flatten(); + let phrase_id = if let (Some(scene), Some(id)) = (scene, track_id) { + if let Some(Some(id)) = scene.clips.get(id) { + Some(*id) + } else { + None + } + } else { + None + }; + let phrase = if let (Some(id), Some(track)) = (phrase_id, track) { + track.phrases.get(id) + } else { + None + }; + Selection { + track_id, + track, + scene_id, + scene, + phrase_id, + phrase + } + } + + //pub fn selection_mut <'a> (&'a mut self) -> SelectionMut<'a> { + //let track_id = if self.track_cursor == 0 { None } else { Some(self.track_cursor - 1) }; + //let track = (&track_id).map(|id|self.tracks.get_mut(id)).flatten(); + //let scene_id = if self.scene_cursor == 0 { None } else { Some(self.scene_cursor - 1) }; + //let scene = (&scene_id).map(|id|self.scenes.get_mut(id)).flatten(); + //let phrase_id = if let (Some(scene), Some(id)) = (scene, track_id) { + //if let Some(Some(id)) = scene.clips.get_mut(id) { + //Some(*id) + //} else { + //None + //} + //} else { + //None + //}; + //let phrase = if let (Some(id), Some(track)) = (phrase_id, track) { + //track.phrases.get_mut(id) + //} else { + //None + //}; + //SelectionMut { + //track_id, + //track, + //scene_id, + //scene, + //phrase_id, + //phrase + //} + //} +} + +struct Selection<'a> { + pub track_id: Option, + pub track: Option<&'a Track>, + pub scene_id: Option, + pub scene: Option<&'a Scene>, + pub phrase_id: Option, + pub phrase: Option<&'a Phrase>, +} + +struct SelectionMut<'a> { + pub track_id: Option, + pub track: Option<&'a mut Track>, + pub scene_id: Option, + pub scene: Option<&'a mut Scene>, + pub phrase_id: Option, + pub phrase: Option<&'a mut Phrase>, } diff --git a/src/model/track.rs b/src/model/track.rs index ddf9fd94..4e7f921c 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -123,10 +123,7 @@ impl Track { self.notes_on = notes_on; write_midi_output(&mut self.midi_out.writer(scope), &output, frames); } - pub fn add_device ( - &mut self, - mut device: JackDevice, - ) -> Usually<&mut JackDevice> { + pub fn add_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { self.devices.push(device); let index = self.devices.len() - 1; Ok(&mut self.devices[index]) diff --git a/src/view.rs b/src/view.rs index df3483be..260ee4be 100644 --- a/src/view.rs +++ b/src/view.rs @@ -51,9 +51,7 @@ render!(App |self, buf, area| { } .render(buf, Rect { x, y: y + height - height / 3 - 1, width, height: height / 3 })?.height; - let phrase = self.phrase_id() - .map(|id|track.phrases.get(id)) - .flatten(); + let phrase = self.phrase(); let seq_area = SequencerView { focused: self.section == 2,