wip: p.42, e=22, more intermediary trait layers

This commit is contained in:
🪞👃🪞 2024-11-16 21:31:04 +01:00
parent 638298ad32
commit dbf6e353b7
15 changed files with 937 additions and 688 deletions

View file

@ -6,17 +6,25 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp<Tui> {
Ok(Self::new(ArrangerView {
name: Arc::new(RwLock::new(String::new())),
phrases: vec![],
phrase: 0,
scenes: vec![],
tracks: vec![],
metronome: false,
playing: None.into(),
started: None.into(),
transport: jack.read().unwrap().transport(),
current: Instant::default(),
jack: jack.clone(),
selected: ArrangerSelection::Clip(0, 0),
mode: ArrangerMode::Vertical(2),
color: Color::Rgb(28, 35, 25).into(),
size: Measure::new(),
focused: false,
entered: false,
quant: Default::default(),
sync: Default::default(),
splits: [20, 20],
note_buf: vec![],
midi_buf: vec![],
}.into(), None, None))
}
}
@ -28,9 +36,14 @@ pub type ArrangerApp<E: Engine> = AppView<
ArrangerStatusBar
>;
impl<E: Engine> Audio for ArrangerApp<E> {
impl Audio for ArrangerApp<Tui> {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
ArrangerRefAudio(self.app).process(client, scope)
TracksAudio(
&mut self.app.tracks,
&mut self.app.note_buf,
&mut self.app.midi_buf,
Default::default(),
).process(client, scope)
}
}
@ -78,39 +91,35 @@ impl InputToCommand<Tui, ArrangerApp<Tui>> for ArrangerAppCommand {
Self::App(Playhead(PlayheadCommand::Play(None)))
},
_ => Self::App(match view.focused() {
Content(ArrangerViewFocus::Transport) => {
match TransportCommand::input_to_command(&view.app.transport, input)? {
Focus(command) => {
Content(ArrangerFocus::Transport) => {
use TransportCommand::{Clock, Playhead};
match TransportCommand::input_to_command(view, input)? {
Clock(command) => {
todo!()
},
App(Clock(command)) => {
todo!()
},
App(Playhead(command)) => {
Playhead(command) => {
todo!()
},
}
},
Content(ArrangerViewFocus::PhraseEditor) => Editor(
Content(ArrangerFocus::PhraseEditor) => Editor(
PhraseEditorCommand::input_to_command(&view.app.editor, input)?
),
Content(ArrangerViewFocus::PhrasePool) => match input.event() {
Content(ArrangerFocus::PhrasePool) => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
Some(view.app.phrases.phrase().clone())
Some(view.app.phrase().clone())
),
_ => Phrases(
PhrasePoolViewCommand::input_to_command(&view.app.phrases, input)?
PhrasePoolViewCommand::input_to_command(view, input)?
)
},
Content(ArrangerViewFocus::Arranger) => {
Content(ArrangerFocus::Arranger) => {
use ArrangerSelection as Select;
use ArrangerTrackCommand as Track;
use ArrangerClipCommand as Clip;
use ArrangerSceneCommand as Scene;
match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
view.selected_phrase()
),
key!(KeyCode::Char('e')) => EditPhrase(view.phrase()),
_ => match input.event() {
// FIXME: boundary conditions
@ -250,7 +259,7 @@ impl Command<ArrangerApp<Tui>> for ArrangerViewCommand {
Select(selected) => { state.selected = selected; Ok(None) },
EditPhrase(phrase) => {
state.editor.phrase = phrase.clone();
state.focus(ArrangerViewFocus::PhraseEditor);
state.focus(ArrangerFocus::PhraseEditor);
state.focus_enter();
Ok(None)
}
@ -277,10 +286,10 @@ pub struct ArrangerView<E: Engine> {
selected: ArrangerSelection,
mode: ArrangerMode,
color: ItemColor,
editor: PhraseEditor<E>,
focused: bool,
entered: bool,
size: Measure<E>,
note_buf: Vec<u8>,
midi_buf: Vec<Vec<Vec<u8>>>,
}
impl HasJack for ArrangerView<Tui> {
@ -332,15 +341,27 @@ impl HasTracks<ArrangerTrack> for ArrangerView<Tui> {
fn tracks_mut (&mut self) -> &mut Vec<ArrangerTrack> {
&mut self.tracks
}
}
impl ArrangerTracksApi<ArrangerTrack> for ArrangerView<Tui> {
fn track_add (&mut self, name: Option<&str>, color: Option<ItemColor>)
-> Usually<&mut ArrangerTrack>
{
let name = name.map_or_else(||self.track_default_name(), |x|x.to_string());
let track = ArrangerTrack {
name: Arc::new(name.into()),
color: color.unwrap_or_else(||ItemColor::random()),
width: name.len() + 2,
//player: MIDIPlayer::new(&self.jack(), &self.clock(), name.as_str())?,
width: name.len() + 2,
name: Arc::new(name.into()),
color: color.unwrap_or_else(||ItemColor::random()),
midi_ins: vec![],
midi_outs: vec![],
reset: true,
recording: false,
monitoring: false,
overdub: false,
play_phrase: None,
next_phrase: None,
notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(),
};
self.tracks_mut().push(track);
let index = self.tracks().len() - 1;
@ -387,7 +408,7 @@ pub enum ArrangerMode {
/// Sections in the arranger app that may be focused
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArrangerViewFocus {
pub enum ArrangerFocus {
/// The transport (toolbar) is focused
Transport,
/// The arrangement (grid) is focused
@ -444,9 +465,9 @@ impl Content for ArrangerView<Tui> {
fn content (&self) -> impl Widget<Engine = Tui> {
Split::up(
1,
widget(&self.transport),
widget(&TransportRef(self)),
Split::down(
self.split,
self.splits[0],
lay!(
Layers::new(move |add|{
match self.mode {
@ -471,9 +492,9 @@ impl Content for ArrangerView<Tui> {
.push_x(1),
),
Split::right(
self.split,
self.splits[1],
widget(&self.phrases),
widget(&self.editor),
widget(&PhraseEditorRef(self)),
)
)
)
@ -481,7 +502,7 @@ impl Content for ArrangerView<Tui> {
}
/// General methods for arranger
impl<E: Engine> ArrangerView<E> {
impl ArrangerView<Tui> {
/// Focus the editor with the current phrase
pub fn show_phrase (&mut self) {
@ -562,6 +583,30 @@ impl<E: Engine> ArrangerView<E> {
}
}
impl TransportViewState for ArrangerView<Tui> {
fn focus (&self) -> TransportViewFocus {
self.focus
}
fn focused (&self) -> bool {
self.focused
}
fn transport_state (&self) -> Option<TransportState> {
*self.playing().read().unwrap()
}
fn bpm_value (&self) -> f64 {
self.bpm().get()
}
fn sync_value (&self) -> f64 {
self.sync().get()
}
fn format_beat (&self) -> String {
self.current().format_beat()
}
fn format_msu (&self) -> String {
self.current().usec.format_msu()
}
}
impl<E: Engine> Audio for ArrangerView<E> {
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
if self.process(client, scope) == Control::Quit {
@ -635,7 +680,7 @@ impl<E: Engine> Audio for ArrangerView<E> {
//self.arrangement.phrase_put();
//}
//self.show_phrase();
//self.focus(ArrangerViewFocus::PhraseEditor);
//self.focus(ArrangerFocus::PhraseEditor);
//self.editor.entered = true;
//}
@ -672,18 +717,10 @@ impl<E: Engine> Audio for ArrangerView<E> {
//self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
//}
/// Focus layout of arranger app
impl FocusGrid for ArrangerApp<Tui> {
type Item = AppViewFocus<ArrangerViewFocus>;
fn cursor (&self) -> (usize, usize) {
self.cursor
}
fn cursor_mut (&mut self) -> &mut (usize, usize) {
&mut self.cursor
}
impl FocusEnter for ArrangerApp<Tui> {
fn focus_enter (&mut self) {
use AppViewFocus::*;
use ArrangerViewFocus::*;
use ArrangerFocus::*;
let focused = self.focused();
if !self.entered {
self.entered = focused == Content(Arranger);
@ -698,16 +735,27 @@ impl FocusGrid for ArrangerApp<Tui> {
self.app.phrases.entered = false;
}
}
fn entered (&self) -> Option<Self::Item> {
fn focus_entered (&self) -> Option<Self::Item> {
if self.entered {
Some(self.focused())
} else {
None
}
}
fn layout (&self) -> &[&[Self::Item]] {
}
/// Focus layout of arranger app
impl FocusGrid for ArrangerApp<Tui> {
type Item = AppViewFocus<ArrangerFocus>;
fn focus_cursor (&self) -> (usize, usize) {
self.cursor
}
fn focus_cursor_mut (&mut self) -> &mut (usize, usize) {
&mut self.cursor
}
fn focus_layout (&self) -> &[&[Self::Item]] {
use AppViewFocus::*;
use ArrangerViewFocus::*;
use ArrangerFocus::*;
&[
&[Menu, Menu ],
&[Content(Transport), Content(Transport) ],
@ -715,19 +763,15 @@ impl FocusGrid for ArrangerApp<Tui> {
&[Content(PhrasePool), Content(PhraseEditor)],
]
}
fn update_focus (&mut self) {
fn focus_update (&mut self) {
use AppViewFocus::*;
use ArrangerViewFocus::*;
use ArrangerFocus::*;
let focused = self.focused();
self.app.focused = focused == Content(Arranger);
self.app.transport.focused = focused == Content(Transport);
self.app.phrases.focused = focused == Content(PhrasePool);
self.app.editor.focused = focused == Content(PhraseEditor);
if let Some(mut status_bar) = self.status_bar {
status_bar.update(&(
self.focused(),
self.app.selected,
self.app.editor.entered
focused == Content(PhraseEditor) && self.entered
))
}
}
@ -827,7 +871,7 @@ impl ArrangerSelection {
impl StatusBar for ArrangerStatusBar {
type State = (AppViewFocus<ArrangerViewFocus>, ArrangerSelection, bool);
type State = (AppViewFocus<ArrangerFocus>, ArrangerSelection, bool);
fn hotkey_fg () -> Color where Self: Sized {
TuiTheme::hotkey_fg()
@ -836,15 +880,15 @@ impl StatusBar for ArrangerStatusBar {
use AppViewFocus::*;
if let Content(focused) = focused {
*self = match focused {
ArrangerViewFocus::Transport => ArrangerStatusBar::Transport,
ArrangerViewFocus::Arranger => match selected {
ArrangerFocus::Transport => ArrangerStatusBar::Transport,
ArrangerFocus::Arranger => match selected {
ArrangerSelection::Mix => ArrangerStatusBar::ArrangerMix,
ArrangerSelection::Track(_) => ArrangerStatusBar::ArrangerTrack,
ArrangerSelection::Scene(_) => ArrangerStatusBar::ArrangerScene,
ArrangerSelection::Clip(_, _) => ArrangerStatusBar::ArrangerClip,
},
ArrangerViewFocus::PhrasePool => ArrangerStatusBar::PhrasePool,
ArrangerViewFocus::PhraseEditor => match entered {
ArrangerFocus::PhrasePool => ArrangerStatusBar::PhrasePool,
ArrangerFocus::PhraseEditor => match entered {
true => ArrangerStatusBar::PhraseEdit,
false => ArrangerStatusBar::PhraseView,
},
@ -1386,182 +1430,3 @@ pub fn arranger_content_horizontal (
)
)
}
#[derive(Default, Debug, Clone)]
pub struct ArrangerScene {
/// Name of scene
pub name: Arc<RwLock<String>>,
/// Clips in scene, one per track
pub clips: Vec<Option<Arc<RwLock<Phrase>>>>,
/// Identifying color of scene
pub color: ItemColor,
}
impl ArrangerSceneApi for ArrangerScene {
fn name (&self) -> &Arc<RwLock<String>> {
&self.name
}
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
&self.clips
}
fn color (&self) -> ItemColor {
self.color
}
}
#[derive(Debug)]
pub struct ArrangerTrack {
/// Name of track
pub name: Arc<RwLock<String>>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: ItemColor,
/// Start time and phrase being played
play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Play input through output.
monitoring: bool,
/// Write input to sequence.
recording: bool,
/// Overdub input to sequence.
overdub: bool,
/// Send all notes off
reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence.
midi_inputs: Vec<Port<MidiIn>>,
/// Play from current sequence to MIDI ports
midi_outputs: Vec<Port<MidiOut>>,
/// MIDI output buffer
midi_note: Vec<u8>,
/// MIDI output buffer
midi_chunk: Vec<Vec<Vec<u8>>>,
/// Notes currently held at input
notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output
notes_out: Arc<RwLock<[bool; 128]>>,
}
impl ArrangerTrackApi for ArrangerTrack {
/// Name of track
fn name (&self) -> &Arc<RwLock<String>> {
&self.name
}
/// Preferred width of track column
fn width (&self) -> usize {
self.width
}
/// Preferred width of track column
fn width_mut (&mut self) -> &mut usize {
&mut self.width
}
/// Identifying color of track
fn color (&self) -> ItemColor {
self.color
}
}
impl HasMidiBuffer for ArrangerTrack {
fn midi_buffer (&self) -> &Vec<Vec<Vec<u8>>> {
todo!()
}
fn midi_buffer_mut (&self) -> &mut Vec<Vec<Vec<u8>>> {
todo!()
}
fn reset (&self) -> bool {
todo!()
}
fn reset_mut (&mut self) -> &mut bool {
todo!()
}
}
impl HasPhrase for ArrangerTrack {
fn phrase (&self) -> &Option<(Instant, Option<Arc<RwLock<Phrase>>>)> {
todo!()
}
fn phrase_mut (&self) -> &mut Option<(Instant, Option<Arc<RwLock<Phrase>>>)> {
todo!()
}
fn next_phrase (&self) -> &Option<(Instant, Option<Arc<RwLock<Phrase>>>)> {
todo!()
}
fn next_phrase_mut (&mut self) -> &mut Option<(Instant, Option<Arc<RwLock<Phrase>>>)> {
todo!()
}
}
impl MidiInputApi for ArrangerTrack {
fn midi_ins(&self) -> &Vec<Port<jack::MidiIn>> {
todo!()
}
fn midi_ins_mut(&self) -> &mut Vec<Port<jack::MidiIn>> {
todo!()
}
fn recording(&self) -> bool {
todo!()
}
fn recording_mut(&mut self) -> &mut bool {
todo!()
}
fn monitoring(&self) -> bool {
todo!()
}
fn monitoring_mut(&mut self) -> &mut bool {
todo!()
}
fn overdub(&self) -> bool {
todo!()
}
fn overdub_mut(&mut self) -> &mut bool {
todo!()
}
fn notes_in(&self) -> &Arc<RwLock<[bool; 128]>> {
todo!()
}
}
impl MidiOutputApi for ArrangerTrack {
fn midi_outs (&self) -> &Vec<Port<jack::MidiOut>> {
todo!()
}
fn midi_outs_mut (&mut self) -> &mut Vec<Port<jack::MidiOut>> {
todo!()
}
fn midi_note (&mut self) -> &mut Vec<u8> {
todo!()
}
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
todo!()
}
}
impl ClockApi for ArrangerTrack {
fn timebase (&self) -> &Arc<Timebase> {
todo!()
}
fn quant (&self) -> &Quantize {
todo!()
}
fn sync (&self) -> &LaunchSync {
todo!()
}
}
impl PlayheadApi for ArrangerTrack {
fn current(&self) -> &Instant {
todo!()
}
fn transport(&self) -> &Transport {
todo!()
}
fn playing(&self) -> &RwLock<Option<TransportState>> {
todo!()
}
fn started(&self) -> &RwLock<Option<(usize, usize)>> {
todo!()
}
}
impl PlayerApi for ArrangerTrack {}