duplicate loop

This commit is contained in:
🪞👃🪞 2024-07-05 11:07:50 +03:00
parent 768c2337e7
commit 2e26fc2eaa
5 changed files with 141 additions and 29 deletions

View file

@ -19,7 +19,14 @@ handle!(App |self, e| {
const KEYMAP: &'static [KeyBinding<App>] = 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| {

View file

@ -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<AtomicBool>,
device: &Arc<Mutex<impl Handle + Send + 'static>>
) -> 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 {

View file

@ -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<usize> {
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<usize>,
pub track: Option<&'a Track>,
pub scene_id: Option<usize>,
pub scene: Option<&'a Scene>,
pub phrase_id: Option<usize>,
pub phrase: Option<&'a Phrase>,
}
struct SelectionMut<'a> {
pub track_id: Option<usize>,
pub track: Option<&'a mut Track>,
pub scene_id: Option<usize>,
pub scene: Option<&'a mut Scene>,
pub phrase_id: Option<usize>,
pub phrase: Option<&'a mut Phrase>,
}

View file

@ -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])

View file

@ -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,