mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
duplicate loop
This commit is contained in:
parent
768c2337e7
commit
2e26fc2eaa
5 changed files with 141 additions and 29 deletions
|
|
@ -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| {
|
||||
|
|
|
|||
33
src/core.rs
33
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<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 {
|
||||
|
|
|
|||
119
src/main.rs
119
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<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>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue