mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
wip: p.42, e=22, more intermediary trait layers
This commit is contained in:
parent
638298ad32
commit
dbf6e353b7
15 changed files with 937 additions and 688 deletions
|
|
@ -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 {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue