wip(p64,e4)

This commit is contained in:
🪞👃🪞 2024-11-21 15:44:32 +01:00
parent f49823b7a7
commit fffd830e15
11 changed files with 194 additions and 217 deletions

View file

@ -4,12 +4,11 @@ use crate::*;
pub enum TransportCommand {
Focus(FocusCommand),
Clock(ClockCommand),
Playhead(PlayheadCommand),
}
impl<T: TransportControl> Command<T> for TransportCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use TransportCommand::{Focus, Clock, Playhead};
use TransportCommand::{Focus, Clock};
use FocusCommand::{Next, Prev};
use ClockCommand::{SetBpm, SetQuant, SetSync};
Ok(Some(match self {
@ -31,23 +30,21 @@ pub enum SequencerCommand {
Redo,
Clear,
Clock(ClockCommand),
Playhead(PlayheadCommand),
Phrases(PhrasesCommand),
Editor(PhraseCommand),
}
impl<T> Command<T> for SequencerCommand
where
T: PhrasesControl + PhraseEditorControl + PlayheadApi + FocusGrid<Item = SequencerFocus>
T: ClockApi + PhrasesControl + PhraseEditorControl + FocusGrid<Item = SequencerFocus>
{
fn execute (self, state: &mut T) -> Perhaps<Self> {
use SequencerCommand::*;
Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus),
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Playhead(cmd) => cmd.execute(state)?.map(Playhead),
Focus(cmd) => cmd.execute(state)?.map(Focus),
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Undo => { todo!() },
Redo => { todo!() },
Clear => { todo!() },
@ -63,7 +60,6 @@ pub enum ArrangerCommand {
Clear,
Color(ItemColor),
Clock(ClockCommand),
Playhead(PlayheadCommand),
Scene(ArrangerSceneCommand),
Track(ArrangerTrackCommand),
Clip(ArrangerClipCommand),
@ -85,7 +81,6 @@ impl Command<ArrangerTui> for ArrangerCommand {
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Playhead(cmd) => cmd.execute(state)?.map(Playhead),
Zoom(zoom) => { todo!(); },
Select(selected) => {
*state.selected_mut() = selected;

View file

@ -8,7 +8,7 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> {
lay!(
state.transport_selected().wrap(focused, TransportFocus::PlayPause, &Styled(
None,
match state.transport_state() {
match *state.transport_state().read().unwrap() {
Some(TransportState::Rolling) => "▶ PLAYING",
Some(TransportState::Starting) => "READY ...",
Some(TransportState::Stopped) => "⏹ STOPPED",

View file

@ -13,32 +13,22 @@ macro_rules! impl_jack_api {
macro_rules! impl_clock_api {
($Struct:ident $(:: $field:ident)*) => {
impl ClockApi for $Struct {
fn timebase (&self) -> &Arc<Timebase> {
&self$(.$field)*.current.timebase
}
fn quant (&self) -> &Quantize {
fn quant (&self) -> &Arc<Quantize> {
&self$(.$field)*.quant
}
fn sync (&self) -> &LaunchSync {
fn sync (&self) -> &Arc<LaunchSync> {
&self$(.$field)*.sync
}
}
}
}
macro_rules! impl_playhead_api {
($Struct:ident $(:: $field:ident)*) => {
impl PlayheadApi for $Struct {
fn current (&self) -> &Instant {
fn current (&self) -> &Arc<Instant> {
&self$(.$field)*.current
}
fn transport (&self) -> &jack::Transport {
fn transport_handle (&self) -> &Arc<Transport> {
&self$(.$field)*.transport
}
fn playing (&self) -> &RwLock<Option<TransportState>> {
fn transport_state (&self) -> &Arc<RwLock<Option<TransportState>>> {
&self$(.$field)*.playing
}
fn started (&self) -> &RwLock<Option<(usize, usize)>> {
fn transport_offset (&self) -> &Arc<RwLock<Option<(usize, usize)>>> {
&self$(.$field)*.started
}
}
@ -187,14 +177,9 @@ impl_jack_api!(ArrangerTui::jack);
impl_clock_api!(TransportTui::state);
impl_clock_api!(SequencerTui::transport);
impl_clock_api!(ArrangerTui::transport);
impl_clock_api!(PhrasePlayerModel::transport);
impl_clock_api!(ArrangerTrack);
impl_playhead_api!(TransportTui::state);
impl_playhead_api!(SequencerTui::transport);
impl_playhead_api!(ArrangerTui::transport);
impl_playhead_api!(PhrasePlayerModel::transport);
impl_playhead_api!(ArrangerTrack);
impl_has_phrases!(PhrasesModel::phrases);
impl_clock_api!(PhrasePlayerModel);
impl_clock_api!(ArrangerTrack::player);
impl_has_phrases!(PhrasesModel);
impl_has_phrases!(SequencerTui::phrases);
impl_has_phrases!(ArrangerTui::phrases);
impl_midi_player!(SequencerTui::player);

View file

@ -5,7 +5,7 @@ impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
use KeyCode::Char;
use ClockCommand::{SetBpm, SetQuant, SetSync};
use TransportFocus as Focused;
use TransportCommand::{Focus, Clock, Playhead};
use TransportCommand::{Focus, Clock};
let focused = state.transport_focused();
Some(match input.event() {
key!(Left) => Focus(FocusCommand::Prev),
@ -14,32 +14,32 @@ impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
Focused::Bpm => Clock(SetBpm(state.bpm().get() + 1.0)),
Focused::Quant => Clock(SetQuant(state.next_quant())),
Focused::Sync => Clock(SetSync(state.next_sync())),
Focused::PlayPause => Playhead(todo!()),
Focused::Clock => Playhead(todo!()),
Focused::PlayPause => Clock(todo!()),
Focused::Clock => Clock(todo!()),
_ => {todo!()}
},
key!(Char(',')) => match focused {
Focused::Bpm => Clock(SetBpm(state.bpm().get() - 1.0)),
Focused::Quant => Clock(SetQuant(state.prev_quant())),
Focused::Sync => Clock(SetSync(state.prev_sync())),
Focused::PlayPause => Playhead(todo!()),
Focused::Clock => Playhead(todo!()),
Focused::PlayPause => Clock(todo!()),
Focused::Clock => Clock(todo!()),
_ => {todo!()}
},
key!(Char('>')) => match focused {
Focused::Bpm => Clock(SetBpm(state.bpm().get() + 0.001)),
Focused::Quant => Clock(SetQuant(state.next_quant())),
Focused::Sync => Clock(SetSync(state.next_sync())),
Focused::PlayPause => Playhead(todo!()),
Focused::Clock => Playhead(todo!()),
Focused::PlayPause => Clock(todo!()),
Focused::Clock => Clock(todo!()),
_ => {todo!()}
},
key!(Char('<')) => match focused {
Focused::Bpm => Clock(SetBpm(state.bpm().get() - 0.001)),
Focused::Quant => Clock(SetQuant(state.prev_quant())),
Focused::Sync => Clock(SetSync(state.prev_sync())),
Focused::PlayPause => Playhead(todo!()),
Focused::Clock => Playhead(todo!()),
Focused::PlayPause => Clock(todo!()),
Focused::Clock => Clock(todo!()),
_ => {todo!()}
},
_ => return None
@ -49,7 +49,7 @@ impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
impl<T> InputToCommand<Tui, T> for SequencerCommand
where
T: SequencerControl + TransportControl + PhrasesControl + PhraseEditorControl + PlayheadApi
T: SequencerControl + TransportControl + PhrasesControl + PhraseEditorControl
+ HasFocus<Item = SequencerFocus>
+ FocusGrid<Item = SequencerFocus>
{
@ -67,10 +67,9 @@ where
key!(KeyCode::Right) => Some(Self::Focus(Right)),
_ => Some(match state.focused() {
SequencerFocus::Transport => {
use TransportCommand::{Clock, Playhead, Focus};
use TransportCommand::{Clock, Focus};
match TransportCommand::input_to_command(state, input)? {
Clock(command) => { todo!() },
Playhead(command) => { todo!() },
Focus(command) => { todo!() },
}
},
@ -99,14 +98,13 @@ impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
key!(KeyCode::Right) => Self::Focus(Right),
key!(KeyCode::Enter) => Self::Focus(Enter),
key!(KeyCode::Esc) => Self::Focus(Exit),
key!(KeyCode::Char(' ')) => Self::Playhead(PlayheadCommand::Play(None)),
key!(KeyCode::Char(' ')) => Self::Clock(ClockCommand::Play(None)),
_ => match state.focused() {
ArrangerFocus::Menu => { todo!() },
ArrangerFocus::Transport => {
use TransportCommand::{Clock, Playhead, Focus};
use TransportCommand::{Clock, Focus};
match TransportCommand::input_to_command(state, input)? {
Clock(command) => { todo!() },
Playhead(command) => { todo!() },
Focus(command) => { todo!() }
}
},

View file

@ -2,13 +2,13 @@ use crate::*;
impl Audio for TransportTui {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
PlayheadAudio(self).process(client, scope)
ClockAudio(self).process(client, scope)
}
}
impl Audio for SequencerTui {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
if PlayheadAudio(self).process(client, scope) == Control::Quit {
if ClockAudio(self).process(client, scope) == Control::Quit {
return Control::Quit
}
if PlayerAudio(

View file

@ -1,18 +1,18 @@
use crate::*;
pub struct TransportModel {
/// Playback state
pub(crate) playing: RwLock<Option<TransportState>>,
/// Global sample and usec at which playback started
pub(crate) started: RwLock<Option<(usize, usize)>>,
/// Current moment in time
pub(crate) current: Instant,
/// Note quantization factor
pub(crate) quant: Quantize,
/// Launch quantization factor
pub(crate) sync: LaunchSync,
/// JACK transport handle.
pub(crate) transport: jack::Transport,
pub(crate) transport: Arc<Transport>,
/// Playback state
pub(crate) playing: Arc<RwLock<Option<TransportState>>>,
/// Global sample and usec at which playback started
pub(crate) started: Arc<RwLock<Option<(usize, usize)>>>,
/// Current moment in time
pub(crate) current: Arc<Instant>,
/// Note quantization factor
pub(crate) quant: Arc<Quantize>,
/// Launch quantization factor
pub(crate) sync: Arc<LaunchSync>,
/// Enable metronome?
pub(crate) metronome: bool,
/// Selected transport component
@ -22,17 +22,17 @@ pub struct TransportModel {
}
impl From<jack::Transport> for TransportModel {
fn from (transport: jack::Transport) -> Self {
fn from (transport: Transport) -> Self {
Self {
current: Instant::default(),
metronome: false,
playing: RwLock::new(None),
quant: Quantize::default(),
started: RwLock::new(None),
sync: LaunchSync::default(),
current: Instant::default().into(),
focus: TransportFocus::PlayPause,
is_focused: true,
transport,
metronome: false,
playing: RwLock::new(None).into(),
quant: Quantize::default().into(),
started: RwLock::new(None).into(),
sync: LaunchSync::default().into(),
transport: transport.into(),
}
}
}
@ -40,6 +40,17 @@ impl From<jack::Transport> for TransportModel {
/// Contains state for playing a phrase
#[derive(Debug)]
pub struct PhrasePlayerModel {
/// Playback state
pub(crate) playing: Arc<RwLock<Option<TransportState>>>,
/// Global sample and usec at which playback started
pub(crate) started: Arc<RwLock<Option<(usize, usize)>>>,
/// Current moment in time
pub(crate) current: Arc<Instant>,
/// Note quantization factor
pub(crate) quant: Arc<Quantize>,
/// Launch quantization factor
pub(crate) sync: Arc<LaunchSync>,
/// Start time and phrase being played
pub(crate) play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
@ -65,6 +76,11 @@ pub struct PhrasePlayerModel {
impl Default for PhrasePlayerModel {
fn default () -> Self {
Self {
playing: RwLock::new(None).into(),
started: RwLock::new(None).into(),
current: Instant::default().into(),
quant: Quantize::default().into(),
sync: LaunchSync::default().into(),
midi_ins: vec![],
midi_outs: vec![],
reset: true,
@ -79,6 +95,19 @@ impl Default for PhrasePlayerModel {
}
}
impl From<&TransportModel> for PhrasePlayerModel {
fn from (transport: &TransportModel) -> Self {
Self {
playing: transport.playing.clone(),
started: transport.started.clone(),
current: transport.current.clone(),
quant: transport.quant.clone(),
sync: transport.sync.clone(),
..Default::default()
}
}
}
/// Contains state for viewing and editing a phrase
pub struct PhraseEditorModel {
/// Phrase being played
@ -252,13 +281,13 @@ impl ArrangerTracksApi<ArrangerTrack> for ArrangerTui {
#[derive(Debug)]
pub struct ArrangerTrack {
/// Name of track
pub(crate) name: Arc<RwLock<String>>,
pub(crate) name: Arc<RwLock<String>>,
/// Preferred width of track column
pub(crate) width: usize,
pub(crate) width: usize,
/// Identifying color of track
pub(crate) color: ItemColor,
pub(crate) color: ItemColor,
/// MIDI player state
pub(crate) player: PhrasePlayerModel,
pub(crate) player: PhrasePlayerModel,
}
impl ArrangerTrackApi for ArrangerTrack {

View file

@ -6,10 +6,9 @@ pub struct PhrasesView<'a, T: PhrasesViewState>(pub &'a T);
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
pub trait TransportViewState: ClockApi + PlayheadApi + Send + Sync {
pub trait TransportViewState: ClockApi + Send + Sync {
fn transport_selected (&self) -> TransportFocus;
fn transport_focused (&self) -> bool;
fn transport_state (&self) -> Option<TransportState>;
fn bpm_value (&self) -> f64 {
self.bpm().get()
}
@ -57,9 +56,6 @@ impl TransportViewState for TransportTui {
fn transport_focused (&self) -> bool {
true
}
fn transport_state (&self) -> Option<TransportState> {
*self.playing().read().unwrap()
}
}
impl TransportViewState for SequencerTui {
@ -69,9 +65,6 @@ impl TransportViewState for SequencerTui {
fn transport_focused (&self) -> bool {
self.focused() == SequencerFocus::Transport
}
fn transport_state (&self) -> Option<TransportState> {
*self.playing().read().unwrap()
}
}
impl TransportViewState for ArrangerTui {
@ -81,9 +74,6 @@ impl TransportViewState for ArrangerTui {
fn transport_focused (&self) -> bool {
self.focused() == ArrangerFocus::Transport
}
fn transport_state (&self) -> Option<TransportState> {
*self.playing().read().unwrap()
}
}
impl ArrangerViewState for ArrangerTui {