use crate::*; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Represents the tracks and scenes of the composition. pub struct Arranger { /// Name of arranger pub name: Arc>, /// Collection of tracks. pub tracks: Vec>, /// Collection of scenes. pub scenes: Vec, /// Currently selected element. pub selected: ArrangerFocus, /// Display mode of arranger pub mode: ArrangerViewMode, /// Slot for modal dialog displayed on top of app. pub modal: Option>>, /// Whether the arranger is currently focused pub focused: bool } impl Arranger { pub fn new (name: &str) -> Self { Self { name: Arc::new(RwLock::new(name.into())), mode: ArrangerViewMode::Vertical(2), selected: ArrangerFocus::Clip(0, 0), scenes: vec![], tracks: vec![], modal: None, focused: false } } pub fn activate (&mut self) { match self.selected { ArrangerFocus::Scene(s) => { for (track_index, track) in self.tracks.iter_mut().enumerate() { track.playing_phrase = self.scenes[s].clips[track_index]; track.reset = true; } }, ArrangerFocus::Clip(t, s) => { self.tracks[t].playing_phrase = self.scenes[s].clips[t]; self.tracks[t].reset = true; }, _ => {} } } pub fn sequencer (&self) -> Option<&Sequencer> { self.selected.track() .map(|track|self.tracks.get(track)) .flatten() } pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> { self.selected.track() .map(|track|self.tracks.get_mut(track)) .flatten() } pub fn show_phrase (&mut self) { let (scene, track) = (self.selected.scene(), self.selected.track()); if let (Some(scene_index), Some(track_index)) = (scene, track) { let scene = self.scenes.get(scene_index); let track = self.tracks.get_mut(track_index); if let (Some(scene), Some(track)) = (scene, track) { track.viewing_phrase = scene.clips[track_index] } } } pub fn is_first_row (&self) -> bool { let selected = self.selected; selected.is_mix() || selected.is_track() } pub fn is_last_row (&self) -> bool { let selected = self.selected; (self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected { ArrangerFocus::Scene(s) => s == self.scenes.len() - 1, ArrangerFocus::Clip(_, s) => s == self.scenes.len() - 1, _ => false } } pub fn track (&self) -> Option<&Sequencer> { self.selected.track().map(|t|self.tracks.get(t)).flatten() } pub fn track_mut (&mut self) -> Option<&mut Sequencer> { self.selected.track().map(|t|self.tracks.get_mut(t)).flatten() } pub fn track_next (&mut self) { self.selected.track_next(self.tracks.len() - 1) } pub fn track_prev (&mut self) { self.selected.track_prev() } pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer> { self.tracks.push(name.map_or_else( || Sequencer::new(&self.track_default_name()), |name| Sequencer::new(name), )); let index = self.tracks.len() - 1; Ok(&mut self.tracks[index]) } pub fn track_del (&mut self) { unimplemented!("Arranger::track_del"); } pub fn track_default_name (&self) -> String { format!("Track {}", self.tracks.len() + 1) } pub fn scene (&self) -> Option<&Scene> { self.selected.scene().map(|s|self.scenes.get(s)).flatten() } pub fn scene_mut (&mut self) -> Option<&mut Scene> { self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten() } pub fn scene_next (&mut self) { self.selected.scene_next(self.scenes.len() - 1) } pub fn scene_prev (&mut self) { self.selected.scene_prev() } pub fn scene_add (&mut self, name: Option<&str>) -> Usually<&mut Scene> { let clips = vec![None;self.tracks.len()]; self.scenes.push(match name { Some(name) => Scene::new(name, clips), None => Scene::new(&self.scene_default_name(), clips), }); let index = self.scenes.len() - 1; Ok(&mut self.scenes[index]) } pub fn scene_del (&mut self) { unimplemented!("Arranger::scene_del"); } pub fn scene_default_name (&self) -> String { format!("Scene {}", self.scenes.len() + 1) } pub fn phrase (&self) -> Option<&Arc>> { let track_id = self.selected.track()?; self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?) } pub fn phrase_next (&mut self) { let track_index = self.selected.track(); let scene_index = self.selected.scene(); track_index .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) .and_then(|(track_index, track)|{ let phrases = track.phrases.len(); scene_index .and_then(|index|self.scenes.get_mut(index)) .and_then(|scene|{ if let Some(phrase_index) = scene.clips[track_index] { if phrase_index >= phrases - 1 { scene.clips[track_index] = None; } else { scene.clips[track_index] = Some(phrase_index + 1); } } else if phrases > 0 { scene.clips[track_index] = Some(0); } Some(()) }) }); } pub fn phrase_prev (&mut self) { let track_index = self.selected.track(); let scene_index = self.selected.scene(); track_index .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) .and_then(|(track_index, track)|{ let phrases = track.phrases.len(); scene_index .and_then(|index|self.scenes.get_mut(index)) .and_then(|scene|{ if let Some(phrase_index) = scene.clips[track_index] { scene.clips[track_index] = if phrase_index == 0 { None } else { Some(phrase_index - 1) }; } else if phrases > 0 { scene.clips[track_index] = Some(phrases - 1); } Some(()) }) }); } } /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(PartialEq, Clone, Copy)] /// Represents the current user selection in the arranger pub enum ArrangerFocus { /** The whole mix is selected */ Mix, /// A track is selected. Track(usize), /// A scene is selected. Scene(usize), /// A clip (track × scene) is selected. Clip(usize, usize), } /// Focus identification methods impl ArrangerFocus { pub fn description ( &self, tracks: &Vec>, scenes: &Vec, ) -> String { format!("Selected: {}", match self { Self::Mix => format!("Everything"), Self::Track(t) => if let Some(track) = tracks.get(*t) { format!("T{t}: {}", &track.name.read().unwrap()) } else { format!("T??") }, Self::Scene(s) => if let Some(scene) = scenes.get(*s) { format!("S{s}: {}", &scene.name.read().unwrap()) } else { format!("S??") }, Self::Clip(t, s) => if let (Some(track), Some(scene)) = ( tracks.get(*t), scenes.get(*s), ) { if let Some(Some(slot)) = scene.clips.get(*t) { if let Some(clip) = track.phrases.get(*slot) { format!("T{t} S{s} C{slot} ({})", &clip.read().unwrap().name.read().unwrap()) } else { format!("T{t} S{s}: Empty") } } else { format!("T{t} S{s}: Empty") } } else { format!("T{t} S{s}: Empty") } }) } pub fn is_mix (&self) -> bool { match self { Self::Mix => true, _ => false } } pub fn is_track (&self) -> bool { match self { Self::Track(_) => true, _ => false } } pub fn is_scene (&self) -> bool { match self { Self::Scene(_) => true, _ => false } } pub fn is_clip (&self) -> bool { match self { Self::Clip(_, _) => true, _ => false } } pub fn track (&self) -> Option { match self { Self::Clip(t, _) => Some(*t), Self::Track(t) => Some(*t), _ => None } } pub fn track_next (&mut self, last_track: usize) { *self = match self { Self::Mix => Self::Track(0), Self::Track(t) => Self::Track(last_track.min(*t + 1)), Self::Scene(s) => Self::Clip(0, *s), Self::Clip(t, s) => Self::Clip(last_track.min(*t + 1), *s), } } pub fn track_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Scene(s) => Self::Scene(*s), Self::Track(t) => if *t == 0 { Self::Mix } else { Self::Track(*t - 1) }, Self::Clip(t, s) => if *t == 0 { Self::Scene(*s) } else { Self::Clip(t.saturating_sub(1), *s) } } } pub fn scene (&self) -> Option { match self { Self::Clip(_, s) => Some(*s), Self::Scene(s) => Some(*s), _ => None } } pub fn scene_next (&mut self, last_scene: usize) { *self = match self { Self::Mix => Self::Scene(0), Self::Track(t) => Self::Clip(*t, 0), Self::Scene(s) => Self::Scene(last_scene.min(*s + 1)), Self::Clip(t, s) => Self::Clip(*t, last_scene.min(*s + 1)), } } pub fn scene_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Track(t) => Self::Track(*t), Self::Scene(s) => if *s == 0 { Self::Mix } else { Self::Scene(*s - 1) }, Self::Clip(t, s) => if *s == 0 { Self::Track(*t) } else { Self::Clip(*t, s.saturating_sub(1)) } } } } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Display mode of arranger #[derive(PartialEq)] pub enum ArrangerViewMode { Horizontal, Vertical(usize) } /// Arranger display mode can be cycled impl ArrangerViewMode { /// Cycle arranger display mode pub fn to_next (&mut self) { *self = match self { Self::Horizontal => Self::Vertical(1), Self::Vertical(1) => Self::Vertical(2), Self::Vertical(2) => Self::Vertical(2), Self::Vertical(0) => Self::Horizontal, Self::Vertical(_) => Self::Vertical(0), } } } /////////////////////////////////////////////////////////////////////////////////////////////////// pub struct VerticalArranger<'a, E: Engine>( pub &'a Arranger, pub usize ); pub struct VerticalArrangerGrid<'a>( pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)] ); pub struct VerticalArrangerCursor<'a>( pub bool, pub ArrangerFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)], ); /////////////////////////////////////////////////////////////////////////////////////////////////// pub struct HorizontalArranger<'a, E: Engine>( pub &'a Arranger ); /////////////////////////////////////////////////////////////////////////////////////////////////// /// Appears on first run (i.e. if state dir is missing). pub struct ArrangerRenameModal { _engine: std::marker::PhantomData, pub done: bool, pub target: ArrangerFocus, pub value: String, pub result: Arc>, pub cursor: usize } impl ArrangerRenameModal { pub fn new (target: ArrangerFocus, value: &Arc>) -> Self { Self { _engine: Default::default(), done: false, value: value.read().unwrap().clone(), cursor: value.read().unwrap().len(), result: value.clone(), target, } } } impl Exit for ArrangerRenameModal { fn exited (&self) -> bool { self.done } fn exit (&mut self) { self.done = true } } ////////////////////////////////////////////////////////////////////////////////////////////////// /// A collection of phrases to play on each track. #[derive(Default)] pub struct Scene { pub name: Arc>, pub clips: Vec>, } impl Scene { pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { let mut name = None; let mut clips = vec![]; edn!(edn in args { Edn::Map(map) => { let key = map.get(&Edn::Key(":name")); if let Some(Edn::Str(n)) = key { name = Some(*n); } else { panic!("unexpected key in scene '{name:?}': {key:?}") } }, Edn::Symbol("_") => { clips.push(None); }, Edn::Int(i) => { clips.push(Some(*i as usize)); }, _ => panic!("unexpected in scene '{name:?}': {edn:?}") }); let scene = Self::new(name.unwrap_or(""), clips); Ok(scene) } pub fn new (name: impl AsRef, clips: impl AsRef<[Option]>) -> Self { let name = Arc::new(RwLock::new(name.as_ref().into())); let clips = clips.as_ref().iter().map(|x|x.clone()).collect(); Self { name, clips, } } /// Returns the pulse length of the longest phrase in the scene pub fn pulses (&self, tracks: &[Sequencer]) -> usize { self.clips.iter().enumerate() .filter_map(|(i, c)|c .map(|c|tracks .get(i) .map(|track|track .phrases .get(c)))) .filter_map(|p|p) .filter_map(|p|p) .fold(0, |a, p|a.max(p.read().unwrap().length)) } /// Returns true if all phrases in the scene are currently playing pub fn is_playing (&self, tracks: &[Sequencer]) -> bool { self.clips.iter().enumerate() .all(|(track_index, phrase_index)|match phrase_index { Some(i) => tracks .get(track_index) .map(|track|track.playing_phrase == Some(*i)) .unwrap_or(false), None => true }) } } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Phrase editor. pub struct Sequencer { pub name: Arc>, pub mode: bool, pub focused: bool, pub entered: bool, pub phrase: Option>>, pub transport: Option>>>, /// The full piano roll is rendered to this buffer pub buffer: BigBuffer, /// The full piano keys is rendered to this buffer pub keys: Buffer, /// Highlight input keys pub keys_in: [bool; 128], /// Highlight output keys pub keys_out: [bool; 128], /// Current point in playing phrase pub now: usize, /// Temporal resolution (default 96) pub ppq: usize, /// Scroll/room in pitch axis pub note_axis: FixedAxis, /// Scroll/room in time axis pub time_axis: ScaledAxis, /// Play input through output. pub monitoring: bool, /// Write input to sequence. pub recording: bool, /// Overdub input to sequence. pub overdub: bool, /// Map: tick -> MIDI events at tick pub phrases: Vec>>, /// Phrase currently being played pub playing_phrase: Option, /// Phrase currently being viewed pub viewing_phrase: Option, /// Output from current sequence. pub midi_out: Option>, /// MIDI output buffer midi_out_buf: Vec>>, /// Send all notes off pub reset: bool, // TODO?: after Some(nframes) /// Highlight keys on piano roll. pub notes_in: [bool;128], /// Highlight keys on piano roll. pub notes_out: [bool;128], } impl Sequencer { pub fn new (name: &str) -> Self { Self { name: Arc::new(RwLock::new(name.into())), monitoring: false, recording: false, overdub: true, phrases: vec![], viewing_phrase: None, playing_phrase: None, midi_out: None, midi_out_buf: vec![Vec::with_capacity(16);16384], reset: true, notes_in: [false;128], notes_out: [false;128], buffer: Default::default(), keys: keys_vert(), entered: false, focused: false, mode: false, keys_in: [false;128], keys_out: [false;128], phrase: None, now: 0, ppq: 96, transport: None, note_axis: FixedAxis { start: 12, point: Some(36) }, time_axis: ScaledAxis { start: 0, scale: 24, point: Some(0) }, } } pub fn toggle_monitor (&mut self) { self.monitoring = !self.monitoring; } pub fn toggle_record (&mut self) { self.recording = !self.recording; } pub fn toggle_overdub (&mut self) { self.overdub = !self.overdub; } pub fn process ( &mut self, input: Option, timebase: &Arc, playing: Option, started: Option<(usize, usize)>, quant: usize, reset: bool, scope: &ProcessScope, (frame0, frames): (usize, usize), (_usec0, _usecs): (usize, usize), period: f64, ) { if self.midi_out.is_some() { // Clear the section of the output buffer that we will be using for frame in &mut self.midi_out_buf[0..frames] { frame.clear(); } // Emit "all notes off" at start of buffer if requested if self.reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; } else if reset { all_notes_off(&mut self.midi_out_buf); } } if let ( Some(TransportState::Rolling), Some((start_frame, _)), Some(phrase) ) = ( playing, started, self.playing_phrase.and_then(|id|self.phrases.get_mut(id)) ) { phrase.read().map(|phrase|{ if self.midi_out.is_some() { phrase.process_out( &mut self.midi_out_buf, &mut self.notes_out, timebase, (frame0.saturating_sub(start_frame), frames, period) ); } }).unwrap(); let mut phrase = phrase.write().unwrap(); let length = phrase.length; // Monitor and record input if input.is_some() && (self.recording || self.monitoring) { // For highlighting keys and note repeat for (frame, event, bytes) in parse_midi_input(input.unwrap()) { match event { LiveEvent::Midi { message, .. } => { if self.monitoring { self.midi_out_buf[frame].push(bytes.to_vec()) } if self.recording { phrase.record_event({ let pulse = timebase.frame_to_pulse( (frame0 + frame - start_frame) as f64 ); let quantized = ( pulse / quant as f64 ).round() as usize * quant; let looped = quantized % length; looped }, message); } match message { MidiMessage::NoteOn { key, .. } => { self.notes_in[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { self.notes_in[key.as_int() as usize] = false; }, _ => {} } }, _ => {} } } } } else if input.is_some() && self.midi_out.is_some() && self.monitoring { for (frame, event, bytes) in parse_midi_input(input.unwrap()) { self.process_monitor_event(frame, &event, bytes) } } if let Some(out) = &mut self.midi_out { write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames); } } #[inline] fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) { match event { LiveEvent::Midi { message, .. } => { self.write_to_output_buffer(frame, bytes); self.process_monitor_message(&message); }, _ => {} } } #[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) { self.midi_out_buf[frame].push(bytes.to_vec()); } #[inline] fn process_monitor_message (&mut self, message: &MidiMessage) { match message { MidiMessage::NoteOn { key, .. } => { self.notes_in[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { self.notes_in[key.as_int() as usize] = false; }, _ => {} } } } /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut MIDIChunk) { let mut buf = vec![]; let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() }; let evt = LiveEvent::Midi { channel: 0.into(), message: msg }; evt.write(&mut buf).unwrap(); output[0].push(buf); } /// Return boxed iterator of MIDI events pub fn parse_midi_input (input: MidiIter) -> Box + '_> { Box::new(input.map(|RawMidi { time, bytes }|( time as usize, LiveEvent::parse(bytes).unwrap(), bytes ))) } /// Write to JACK port from output buffer (containing notes from sequence and/or monitor) pub fn write_midi_output (writer: &mut MidiWriter, output: &MIDIChunk, frames: usize) { for time in 0..frames { for event in output[time].iter() { writer.write(&RawMidi { time: time as u32, bytes: &event }) .expect(&format!("{event:?}")); } } } /// MIDI message serialized to bytes pub type MIDIMessage = Vec; /// Collection of serialized MIDI messages pub type MIDIChunk = [Vec]; /////////////////////////////////////////////////////////////////////////////////////////////////// /// A collection of MIDI messages. pub type PhraseData = Vec>; /// A MIDI sequence. #[derive(Debug)] pub struct Phrase { pub name: Arc>, pub length: usize, pub notes: PhraseData, pub looped: Option<(usize, usize)>, /// All notes are displayed with minimum length pub percussive: bool } impl Default for Phrase { fn default () -> Self { Self::new("", 0, None) } } impl Phrase { pub fn new (name: &str, length: usize, notes: Option) -> Self { Self { name: Arc::new(RwLock::new(name.into())), length, notes: notes.unwrap_or(vec![Vec::with_capacity(16);length]), looped: Some((0, length)), percussive: true, } } pub fn record_event (&mut self, pulse: usize, message: MidiMessage) { if pulse >= self.length { panic!("extend phrase first") } self.notes[pulse].push(message); } /// Check if a range `start..end` contains MIDI Note On `k` pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool { //panic!("{:?} {start} {end}", &self); for events in self.notes[start.max(0)..end.min(self.notes.len())].iter() { for event in events.iter() { match event { MidiMessage::NoteOn {key,..} => { if *key == k { return true } } _ => {} } } } return false } /// Write a chunk of MIDI events to an output port. pub fn process_out ( &self, output: &mut MIDIChunk, notes_on: &mut [bool;128], timebase: &Arc, (frame0, frames, _): (usize, usize, f64), ) { let mut buf = Vec::with_capacity(8); for (time, tick) in Ticks(timebase.pulse_per_frame()).between_frames( frame0, frame0 + frames ) { let tick = tick % self.length; for message in self.notes[tick].iter() { buf.clear(); let channel = 0.into(); let message = *message; LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); output[time as usize].push(buf.clone()); match message { MidiMessage::NoteOn { key, .. } => notes_on[key.as_int() as usize] = true, MidiMessage::NoteOff { key, .. } => notes_on[key.as_int() as usize] = false, _ => {} } } } } pub fn from_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { let mut phrase = Self::default(); let mut name = String::new(); let mut beats = 0usize; let mut steps = 0usize; edn!(edn in args { Edn::Map(map) => { if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { name = String::from(*n); } if let Some(Edn::Int(b)) = map.get(&Edn::Key(":beats")) { beats = *b as usize; phrase.length = ppq * beats; for _ in phrase.notes.len()..phrase.length { phrase.notes.push(Vec::with_capacity(16)) } } if let Some(Edn::Int(s)) = map.get(&Edn::Key(":steps")) { steps = *s as usize; } }, Edn::List(args) => { let time = (match args.get(0) { Some(Edn::Key(text)) => text[1..].parse::()?, Some(Edn::Int(i)) => *i as f64, Some(Edn::Double(f)) => f64::from(*f), _ => panic!("unexpected in phrase '{name}': {:?}", args.get(0)), } * beats as f64 * ppq as f64 / steps as f64) as usize; for edn in args[1..].iter() { match edn { Edn::List(args) => if let ( Some(Edn::Int(key)), Some(Edn::Int(vel)), ) = ( args.get(0), args.get(1), ) { let (key, vel) = ( u7::from((*key as u8).min(127)), u7::from((*vel as u8).min(127)), ); phrase.notes[time].push(MidiMessage::NoteOn { key, vel }) } else { panic!("unexpected list in phrase '{name}'") }, _ => panic!("unexpected in phrase '{name}': {edn:?}") } } }, _ => panic!("unexpected in phrase '{name}': {edn:?}"), }); *phrase.name.write().unwrap() = name; Ok(phrase) } } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores and displays time-related state. pub struct TransportToolbar { _engine: PhantomData, /// Enable metronome? pub metronome: bool, /// Current sample rate, tempo, and PPQ. pub timebase: Arc, /// JACK client handle (needs to not be dropped for standalone mode to work). pub jack: Option, /// JACK transport handle. pub transport: Option, /// Quantization factor /// Global frame and usec at which playback started pub started: Option<(usize, usize)>, pub focused: bool, pub focus: TransportToolbarFocus, pub playing: Option, pub bpm: f64, pub quant: usize, pub sync: usize, pub frame: usize, pub pulse: usize, pub ppq: usize, pub usecs: usize, } impl TransportToolbar { pub fn standalone () -> Usually>> where Self: 'static { let jack = JackClient::Inactive( Client::new("tek_transport", ClientOptions::NO_START_SERVER)?.0 ); let mut transport = Self::new(Some(jack.transport())); transport.focused = true; let transport = Arc::new(RwLock::new(transport)); transport.write().unwrap().jack = Some( jack.activate( &transport.clone(), |state: &Arc>>, client, scope| { state.write().unwrap().process(client, scope) } )? ); Ok(transport) } pub fn new (transport: Option) -> Self { let timebase = Arc::new(Timebase::default()); Self { _engine: Default::default(), focused: false, focus: TransportToolbarFocus::PlayPause, playing: Some(TransportState::Stopped), bpm: timebase.bpm(), quant: 24, sync: timebase.ppq() as usize * 4, frame: 0, pulse: 0, ppq: 0, usecs: 0, metronome: false, started: None, jack: None, transport, timebase, } } pub fn toggle_play (&mut self) -> Usually<()> { let transport = self.transport.as_ref().unwrap(); self.playing = match self.playing.expect("1st frame has not been processed yet") { TransportState::Stopped => { transport.start()?; Some(TransportState::Starting) }, _ => { transport.stop()?; transport.locate(0)?; Some(TransportState::Stopped) }, }; Ok(()) } pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) { let times = scope.cycle_times().unwrap(); let CycleTimes { current_frames, current_usecs, next_usecs, period_usecs } = times; let chunk_size = scope.n_frames() as usize; let transport = self.transport.as_ref().unwrap().query().unwrap(); self.frame = transport.pos.frame() as usize; let mut reset = false; if self.playing != Some(transport.state) { match transport.state { TransportState::Rolling => { self.started = Some((current_frames as usize, current_usecs as usize)); }, TransportState::Stopped => { self.started = None; reset = true; }, _ => {} } } self.playing = Some(transport.state); ( reset, current_frames as usize, chunk_size as usize, current_usecs as usize, next_usecs as usize, period_usecs as f64 ) } pub fn bpm (&self) -> usize { self.timebase.bpm() as usize } pub fn ppq (&self) -> usize { self.timebase.ppq() as usize } pub fn pulse (&self) -> usize { self.timebase.frame_to_pulse(self.frame as f64) as usize } pub fn usecs (&self) -> usize { self.timebase.frame_to_usec(self.frame as f64) as usize } pub fn quant (&self) -> usize { self.quant } pub fn sync (&self) -> usize { self.sync } } impl Audio for TransportToolbar { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { self.update(&scope); Control::Continue } } /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Clone, Copy, PartialEq)] pub enum TransportToolbarFocus { PlayPause, Bpm, Quant, Sync, Clock, } impl TransportToolbarFocus { pub fn next (&mut self) { *self = match self { Self::PlayPause => Self::Bpm, Self::Bpm => Self::Quant, Self::Quant => Self::Sync, Self::Sync => Self::Clock, Self::Clock => Self::PlayPause, } } pub fn prev (&mut self) { *self = match self { Self::PlayPause => Self::Clock, Self::Bpm => Self::PlayPause, Self::Quant => Self::Bpm, Self::Sync => Self::Quant, Self::Clock => Self::Sync, } } } ///////////////////////////////////////////////////////////////////////////////////////////////////