mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
count time from start of playback
This commit is contained in:
parent
f6a7cbf38e
commit
52ea78a466
3 changed files with 309 additions and 285 deletions
310
src/main.rs
310
src/main.rs
|
|
@ -17,23 +17,36 @@ pub mod jack;
|
||||||
use crate::{core::*, model::*};
|
use crate::{core::*, model::*};
|
||||||
|
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
App::default().run(Some(|app: Arc<Mutex<App>>|{
|
let mut app = App::default();
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
let xdg = Arc::new(microxdg::XdgApp::new("tek")?);
|
||||||
|
app.xdg = Some(xdg.clone());
|
||||||
|
if crate::config::AppPaths::new(&xdg)?.should_create() {
|
||||||
|
app.modal = Some(Box::new(crate::config::SetupModal(Some(xdg.clone()))));
|
||||||
|
}
|
||||||
|
let midi_from = ["nanoKEY Studio.*capture.*"];
|
||||||
|
let audio_into = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"];
|
||||||
|
|
||||||
|
// Init
|
||||||
|
let ppq = app.timebase.ppq() as usize;
|
||||||
|
app.track_cursor = 1;
|
||||||
|
app.scene_cursor = 1;
|
||||||
|
app.note_start = 12;
|
||||||
|
app.time_zoom = 12;
|
||||||
|
app.quant = 24;
|
||||||
|
|
||||||
|
// Start main loop
|
||||||
|
app.run(Some(|app: Arc<Mutex<App>>|{
|
||||||
let mut state = app.lock().unwrap();
|
let mut state = app.lock().unwrap();
|
||||||
|
|
||||||
let xdg = Arc::new(microxdg::XdgApp::new("tek")?);
|
// Start JACK and setup device graph
|
||||||
state.xdg = Some(xdg.clone());
|
|
||||||
|
|
||||||
if crate::config::AppPaths::new(&xdg)?.should_create() {
|
|
||||||
state.modal = Some(Box::new(crate::config::SetupModal(Some(xdg.clone()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
let jack = jack_run("tek", &app)?;
|
let jack = jack_run("tek", &app)?;
|
||||||
let client = jack.as_client();
|
let client = jack.as_client();
|
||||||
state.transport = Some(client.transport());
|
state.transport = Some(client.transport());
|
||||||
state.playing = Some(TransportState::Stopped);
|
|
||||||
state.midi_in = Some(client.register_port("midi-in", MidiIn)?);
|
state.midi_in = Some(client.register_port("midi-in", MidiIn)?);
|
||||||
|
|
||||||
let _ = ["nanoKEY Studio.*capture.*"]
|
let _ = midi_from
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name|client
|
.map(|name|client
|
||||||
.ports(Some(name), None, PortFlags::empty())
|
.ports(Some(name), None, PortFlags::empty())
|
||||||
|
|
@ -47,15 +60,7 @@ pub fn main () -> Usually<()> {
|
||||||
.collect::<Usually<()>>())
|
.collect::<Usually<()>>())
|
||||||
.collect::<Usually<()>>()?;
|
.collect::<Usually<()>>()?;
|
||||||
|
|
||||||
let timebase = &state.timebase;
|
let audio_outs: Vec<_> = audio_into
|
||||||
let ppq = timebase.ppq() as usize;
|
|
||||||
state.track_cursor = 1;
|
|
||||||
state.scene_cursor = 1;
|
|
||||||
state.note_start = 12;
|
|
||||||
state.time_zoom = 12;
|
|
||||||
state.quant = 24;
|
|
||||||
|
|
||||||
let outputs: Vec<_> = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"]
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name|client
|
.map(|name|client
|
||||||
.ports(Some(name), None, PortFlags::empty())
|
.ports(Some(name), None, PortFlags::empty())
|
||||||
|
|
@ -85,10 +90,10 @@ pub fn main () -> Usually<()> {
|
||||||
)?, |track, device|{
|
)?, |track, device|{
|
||||||
device.connect_audio_in(0, &track.devices[0].audio_outs()?[0])?;
|
device.connect_audio_in(0, &track.devices[0].audio_outs()?[0])?;
|
||||||
device.connect_audio_in(0, &track.devices[0].audio_outs()?[1])?;
|
device.connect_audio_in(0, &track.devices[0].audio_outs()?[1])?;
|
||||||
if let Some(Some(left)) = outputs.get(0) {
|
if let Some(Some(left)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(0, left)?;
|
device.connect_audio_out(0, left)?;
|
||||||
}
|
}
|
||||||
if let Some(Some(right)) = outputs.get(0) {
|
if let Some(Some(right)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(1, right)?;
|
device.connect_audio_out(1, right)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -103,6 +108,7 @@ pub fn main () -> Usually<()> {
|
||||||
12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
||||||
}));
|
}));
|
||||||
track.add_phrase("D-Beat", ppq * 4, Some(phrase! {
|
track.add_phrase("D-Beat", ppq * 4, Some(phrase! {
|
||||||
|
00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
||||||
00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
||||||
04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
||||||
08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
||||||
|
|
@ -124,10 +130,10 @@ pub fn main () -> Usually<()> {
|
||||||
"file:///home/user/.lv2/Odin2.lv2"
|
"file:///home/user/.lv2/Odin2.lv2"
|
||||||
)?, |track, device|{
|
)?, |track, device|{
|
||||||
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
||||||
if let Some(Some(left)) = outputs.get(0) {
|
if let Some(Some(left)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(0, left)?;
|
device.connect_audio_out(0, left)?;
|
||||||
}
|
}
|
||||||
if let Some(Some(right)) = outputs.get(0) {
|
if let Some(Some(right)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(1, right)?;
|
device.connect_audio_out(1, right)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -153,10 +159,10 @@ pub fn main () -> Usually<()> {
|
||||||
"file:///home/user/.lv2/Helm.lv2"
|
"file:///home/user/.lv2/Helm.lv2"
|
||||||
)?, |track, device|{
|
)?, |track, device|{
|
||||||
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
||||||
if let Some(Some(left)) = outputs.get(0) {
|
if let Some(Some(left)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(0, left)?;
|
device.connect_audio_out(0, left)?;
|
||||||
}
|
}
|
||||||
if let Some(Some(right)) = outputs.get(0) {
|
if let Some(Some(right)) = audio_outs.get(0) {
|
||||||
device.connect_audio_out(1, right)?;
|
device.connect_audio_out(1, right)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -177,257 +183,5 @@ pub fn main () -> Usually<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct App {
|
|
||||||
/// Paths to user directories
|
|
||||||
pub xdg: Option<Arc<XdgApp>>,
|
|
||||||
/// Main JACK client.
|
|
||||||
pub jack: Option<DynamicAsyncClient>,
|
|
||||||
/// Main MIDI controller.
|
|
||||||
pub midi_in: Option<Port<MidiIn>>,
|
|
||||||
/// Main audio outputs.
|
|
||||||
pub audio_outs: Option<Vec<Port<AudioOut>>>,
|
|
||||||
/// JACK transport handle.
|
|
||||||
pub transport: Option<Transport>,
|
|
||||||
/// Transport status
|
|
||||||
pub playing: Option<TransportState>,
|
|
||||||
/// Current transport position
|
|
||||||
pub playhead: usize,
|
|
||||||
/// Current sample rate and tempo.
|
|
||||||
pub timebase: Arc<Timebase>,
|
|
||||||
/// Display mode of grid section
|
|
||||||
pub grid_mode: bool,
|
|
||||||
/// Display mode of chain section
|
|
||||||
pub chain_mode: bool,
|
|
||||||
/// Display mode of sequencer seciton
|
|
||||||
pub seq_mode: bool,
|
|
||||||
/// Optional modal dialog
|
|
||||||
pub modal: Option<Box<dyn Component>>,
|
|
||||||
/// Currently focused section
|
|
||||||
pub section: usize,
|
|
||||||
/// Whether the section is focused
|
|
||||||
pub entered: bool,
|
|
||||||
/// Current frame
|
|
||||||
pub metronome: bool,
|
|
||||||
/// Display position of cursor within note range
|
|
||||||
pub note_cursor: usize,
|
|
||||||
/// Range of notes to display
|
|
||||||
pub note_start: usize,
|
|
||||||
/// Display position of cursor within time range
|
|
||||||
pub time_cursor: usize,
|
|
||||||
/// PPQ per display unit
|
|
||||||
pub time_zoom: usize,
|
|
||||||
/// Range of time steps to display
|
|
||||||
pub time_start: usize,
|
|
||||||
/// Focused scene+1, 0 is track list
|
|
||||||
pub scene_cursor: usize,
|
|
||||||
/// Collection of scenes
|
|
||||||
pub scenes: Vec<Scene>,
|
|
||||||
/// Focused track+1, 0 is scene list
|
|
||||||
pub track_cursor: usize,
|
|
||||||
/// Collection of tracks
|
|
||||||
pub tracks: Vec<Track>,
|
|
||||||
|
|
||||||
pub chunk_size: usize,
|
|
||||||
|
|
||||||
pub quant: usize,
|
|
||||||
}
|
|
||||||
process!(App |self, _client, scope| {
|
|
||||||
let transport = self.transport.as_ref().unwrap().query().unwrap();
|
|
||||||
let mut panic = false;
|
|
||||||
if Some(transport.state) != self.playing {
|
|
||||||
panic = true;
|
|
||||||
}
|
|
||||||
self.playing = Some(transport.state);
|
|
||||||
self.playhead = transport.pos.frame() as usize;
|
|
||||||
self.chunk_size = scope.n_frames() as usize;
|
|
||||||
let CycleTimes {
|
|
||||||
current_frames,
|
|
||||||
current_usecs,
|
|
||||||
next_usecs,
|
|
||||||
period_usecs
|
|
||||||
} = scope.cycle_times().unwrap();
|
|
||||||
for track in self.tracks.iter_mut() {
|
|
||||||
track.process(
|
|
||||||
self.midi_in.as_ref().unwrap().iter(scope),
|
|
||||||
&self.timebase,
|
|
||||||
self.playing,
|
|
||||||
self.quant,
|
|
||||||
panic,
|
|
||||||
&scope,
|
|
||||||
(current_frames as usize, self.chunk_size),
|
|
||||||
(current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize),
|
|
||||||
period_usecs as f64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Control::Continue
|
|
||||||
});
|
|
||||||
impl App {
|
|
||||||
pub fn client (&self) -> &Client {
|
|
||||||
self.jack.as_ref().unwrap().as_client()
|
|
||||||
}
|
|
||||||
pub fn connect_ports <A: ::_jack::PortSpec, B: ::_jack::PortSpec> (
|
|
||||||
&self, a: &Port<A>, b: &Port<B>
|
|
||||||
) -> Usually<()> {
|
|
||||||
Ok(self.client().connect_ports(a, b)?)
|
|
||||||
}
|
|
||||||
pub fn toggle_play (&mut self) -> Usually<()> {
|
|
||||||
self.playing = match self.playing.expect("after jack init") {
|
|
||||||
TransportState::Stopped => {
|
|
||||||
self.transport.as_ref().unwrap().start()?;
|
|
||||||
Some(TransportState::Starting)
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
self.transport.as_ref().unwrap().stop()?;
|
|
||||||
self.transport.as_ref().unwrap().locate(0)?;
|
|
||||||
Some(TransportState::Stopped)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
|
||||||
let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?;
|
|
||||||
self.scenes.push(Scene::new(&name, vec![]));
|
|
||||||
self.scene_cursor = self.scenes.len();
|
|
||||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
|
||||||
}
|
|
||||||
pub fn add_track (
|
|
||||||
&mut self,
|
|
||||||
name: Option<&str>,
|
|
||||||
) -> Usually<&mut Track> {
|
|
||||||
let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?;
|
|
||||||
self.tracks.push(Track::new(&name, self.client(), None, None)?);
|
|
||||||
self.track_cursor = self.tracks.len();
|
|
||||||
Ok(&mut self.tracks[self.track_cursor - 1])
|
|
||||||
}
|
|
||||||
pub fn add_track_with_cb (
|
|
||||||
&mut self,
|
|
||||||
name: Option<&str>,
|
|
||||||
init: impl Fn(&Client, &mut Track)->Usually<()>,
|
|
||||||
) -> Usually<&mut Track> {
|
|
||||||
let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?;
|
|
||||||
let mut track = Track::new(&name, self.client(), None, None)?;
|
|
||||||
init(self.client(), &mut track)?;
|
|
||||||
self.tracks.push(track);
|
|
||||||
self.track_cursor = self.tracks.len();
|
|
||||||
Ok(&mut self.tracks[self.track_cursor - 1])
|
|
||||||
}
|
|
||||||
pub fn track (&self) -> Option<(usize, &Track)> {
|
|
||||||
match self.track_cursor { 0 => None, _ => {
|
|
||||||
let id = self.track_cursor as usize - 1;
|
|
||||||
self.tracks.get(id).map(|t|(id, t))
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
|
||||||
match self.track_cursor { 0 => None, _ => {
|
|
||||||
let id = self.track_cursor as usize - 1;
|
|
||||||
self.tracks.get_mut(id).map(|t|(id, t))
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
|
||||||
match self.scene_cursor { 0 => None, _ => {
|
|
||||||
let id = self.scene_cursor as usize - 1;
|
|
||||||
self.scenes.get(id).map(|t|(id, t))
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> {
|
|
||||||
match self.scene_cursor { 0 => None, _ => {
|
|
||||||
let id = self.scene_cursor as usize - 1;
|
|
||||||
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>,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
267
src/model.rs
267
src/model.rs
|
|
@ -12,3 +12,270 @@ pub use self::track::Track;
|
||||||
pub use self::sampler::{Sampler, Sample};
|
pub use self::sampler::{Sampler, Sample};
|
||||||
pub use self::mixer::Mixer;
|
pub use self::mixer::Mixer;
|
||||||
pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
|
pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
|
||||||
|
|
||||||
|
use crate::core::*;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct App {
|
||||||
|
/// Paths to user directories
|
||||||
|
pub xdg: Option<Arc<XdgApp>>,
|
||||||
|
/// Main JACK client.
|
||||||
|
pub jack: Option<DynamicAsyncClient>,
|
||||||
|
/// Main MIDI controller.
|
||||||
|
pub midi_in: Option<Port<MidiIn>>,
|
||||||
|
/// Main audio outputs.
|
||||||
|
pub audio_outs: Option<Vec<Port<AudioOut>>>,
|
||||||
|
/// JACK transport handle.
|
||||||
|
pub transport: Option<Transport>,
|
||||||
|
/// Current transport state
|
||||||
|
pub playing: Option<TransportState>,
|
||||||
|
/// Current position according to transport
|
||||||
|
pub playhead: usize,
|
||||||
|
/// Position of T0 for this playback within global timeline
|
||||||
|
pub play_started: Option<usize>,
|
||||||
|
/// Current sample rate and tempo.
|
||||||
|
pub timebase: Arc<Timebase>,
|
||||||
|
/// Display mode of grid section
|
||||||
|
pub grid_mode: bool,
|
||||||
|
/// Display mode of chain section
|
||||||
|
pub chain_mode: bool,
|
||||||
|
/// Display mode of sequencer seciton
|
||||||
|
pub seq_mode: bool,
|
||||||
|
/// Optional modal dialog
|
||||||
|
pub modal: Option<Box<dyn Component>>,
|
||||||
|
/// Currently focused section
|
||||||
|
pub section: usize,
|
||||||
|
/// Whether the section is focused
|
||||||
|
pub entered: bool,
|
||||||
|
/// Current frame
|
||||||
|
pub metronome: bool,
|
||||||
|
/// Display position of cursor within note range
|
||||||
|
pub note_cursor: usize,
|
||||||
|
/// Range of notes to display
|
||||||
|
pub note_start: usize,
|
||||||
|
/// Display position of cursor within time range
|
||||||
|
pub time_cursor: usize,
|
||||||
|
/// PPQ per display unit
|
||||||
|
pub time_zoom: usize,
|
||||||
|
/// Range of time steps to display
|
||||||
|
pub time_start: usize,
|
||||||
|
/// Focused scene+1, 0 is track list
|
||||||
|
pub scene_cursor: usize,
|
||||||
|
/// Collection of scenes
|
||||||
|
pub scenes: Vec<Scene>,
|
||||||
|
/// Focused track+1, 0 is scene list
|
||||||
|
pub track_cursor: usize,
|
||||||
|
/// Collection of tracks
|
||||||
|
pub tracks: Vec<Track>,
|
||||||
|
|
||||||
|
pub chunk_size: usize,
|
||||||
|
|
||||||
|
pub quant: usize,
|
||||||
|
}
|
||||||
|
process!(App |self, _client, scope| {
|
||||||
|
self.chunk_size = scope.n_frames() as usize;
|
||||||
|
let CycleTimes {
|
||||||
|
current_frames,
|
||||||
|
current_usecs,
|
||||||
|
next_usecs,
|
||||||
|
period_usecs
|
||||||
|
} = scope.cycle_times().unwrap();
|
||||||
|
let transport = self.transport.as_ref().unwrap().query().unwrap();
|
||||||
|
self.playhead = transport.pos.frame() as usize;
|
||||||
|
let mut send_all_off = false;
|
||||||
|
if self.playing != Some(transport.state) {
|
||||||
|
match transport.state {
|
||||||
|
TransportState::Rolling => {
|
||||||
|
self.play_started = Some(current_usecs as usize);
|
||||||
|
},
|
||||||
|
TransportState::Stopped => {
|
||||||
|
self.play_started = None;
|
||||||
|
send_all_off = true;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.playing = Some(transport.state);
|
||||||
|
for track in self.tracks.iter_mut() {
|
||||||
|
track.process(
|
||||||
|
self.midi_in.as_ref().unwrap().iter(scope),
|
||||||
|
&self.timebase,
|
||||||
|
self.playing,
|
||||||
|
self.play_started,
|
||||||
|
self.quant,
|
||||||
|
send_all_off,
|
||||||
|
&scope,
|
||||||
|
(current_frames as usize, self.chunk_size),
|
||||||
|
(current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize),
|
||||||
|
period_usecs as f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Control::Continue
|
||||||
|
});
|
||||||
|
impl App {
|
||||||
|
pub fn client (&self) -> &Client {
|
||||||
|
self.jack.as_ref().unwrap().as_client()
|
||||||
|
}
|
||||||
|
pub fn connect_ports <A: ::_jack::PortSpec, B: ::_jack::PortSpec> (
|
||||||
|
&self, a: &Port<A>, b: &Port<B>
|
||||||
|
) -> Usually<()> {
|
||||||
|
Ok(self.client().connect_ports(a, b)?)
|
||||||
|
}
|
||||||
|
pub fn toggle_play (&mut self) -> Usually<()> {
|
||||||
|
self.playing = match self.playing.expect("after jack init") {
|
||||||
|
TransportState::Stopped => {
|
||||||
|
self.transport.as_ref().unwrap().start()?;
|
||||||
|
Some(TransportState::Starting)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.transport.as_ref().unwrap().stop()?;
|
||||||
|
self.transport.as_ref().unwrap().locate(0)?;
|
||||||
|
Some(TransportState::Stopped)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||||
|
let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?;
|
||||||
|
self.scenes.push(Scene::new(&name, vec![]));
|
||||||
|
self.scene_cursor = self.scenes.len();
|
||||||
|
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||||
|
}
|
||||||
|
pub fn add_track (
|
||||||
|
&mut self,
|
||||||
|
name: Option<&str>,
|
||||||
|
) -> Usually<&mut Track> {
|
||||||
|
let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?;
|
||||||
|
self.tracks.push(Track::new(&name, self.client(), None, None)?);
|
||||||
|
self.track_cursor = self.tracks.len();
|
||||||
|
Ok(&mut self.tracks[self.track_cursor - 1])
|
||||||
|
}
|
||||||
|
pub fn add_track_with_cb (
|
||||||
|
&mut self,
|
||||||
|
name: Option<&str>,
|
||||||
|
init: impl Fn(&Client, &mut Track)->Usually<()>,
|
||||||
|
) -> Usually<&mut Track> {
|
||||||
|
let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?;
|
||||||
|
let mut track = Track::new(&name, self.client(), None, None)?;
|
||||||
|
init(self.client(), &mut track)?;
|
||||||
|
self.tracks.push(track);
|
||||||
|
self.track_cursor = self.tracks.len();
|
||||||
|
Ok(&mut self.tracks[self.track_cursor - 1])
|
||||||
|
}
|
||||||
|
pub fn track (&self) -> Option<(usize, &Track)> {
|
||||||
|
match self.track_cursor { 0 => None, _ => {
|
||||||
|
let id = self.track_cursor as usize - 1;
|
||||||
|
self.tracks.get(id).map(|t|(id, t))
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
||||||
|
match self.track_cursor { 0 => None, _ => {
|
||||||
|
let id = self.track_cursor as usize - 1;
|
||||||
|
self.tracks.get_mut(id).map(|t|(id, t))
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
||||||
|
match self.scene_cursor { 0 => None, _ => {
|
||||||
|
let id = self.scene_cursor as usize - 1;
|
||||||
|
self.scenes.get(id).map(|t|(id, t))
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> {
|
||||||
|
match self.scene_cursor { 0 => None, _ => {
|
||||||
|
let id = self.scene_cursor as usize - 1;
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ impl Track {
|
||||||
input: MidiIter,
|
input: MidiIter,
|
||||||
timebase: &Arc<Timebase>,
|
timebase: &Arc<Timebase>,
|
||||||
playing: Option<TransportState>,
|
playing: Option<TransportState>,
|
||||||
|
started: Option<usize>,
|
||||||
quant: usize,
|
quant: usize,
|
||||||
panic: bool,
|
panic: bool,
|
||||||
scope: &ProcessScope,
|
scope: &ProcessScope,
|
||||||
|
|
@ -136,15 +137,17 @@ impl Track {
|
||||||
// Play from phrase into output buffer
|
// Play from phrase into output buffer
|
||||||
let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten();
|
let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten();
|
||||||
if playing == Some(TransportState::Rolling) {
|
if playing == Some(TransportState::Rolling) {
|
||||||
|
if let Some(started) = started {
|
||||||
if let Some(phrase) = phrase {
|
if let Some(phrase) = phrase {
|
||||||
phrase.process_out(
|
phrase.process_out(
|
||||||
&mut self.midi_out_buf,
|
&mut self.midi_out_buf,
|
||||||
&mut self.notes_on,
|
&mut self.notes_on,
|
||||||
timebase,
|
timebase,
|
||||||
(usec0, usecs, period)
|
(usec0 - started, usecs, period)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Monitor and record input
|
// Monitor and record input
|
||||||
if self.recording || self.monitoring {
|
if self.recording || self.monitoring {
|
||||||
// For highlighting keys
|
// For highlighting keys
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue