mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-05-01 22:40:13 +02:00
extract midi_pool.rs
This commit is contained in:
parent
1408c0c3ce
commit
e80b9419ae
191
.scratch.rs
191
.scratch.rs
|
@ -120,3 +120,194 @@
|
|||
//PhraseView,
|
||||
//PhraseEdit,
|
||||
//}
|
||||
|
||||
//let focused = true;
|
||||
//let _tracks = view.tracks();
|
||||
//lay!(
|
||||
//focused.then_some(Background(TuiTheme::border_bg())),
|
||||
//row!(
|
||||
//// name
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks, selected) = self;
|
||||
////let yellow = Some(Style::default().yellow().bold().not_dim());
|
||||
////let white = Some(Style::default().white().bold().not_dim());
|
||||
////let area = to.area();
|
||||
////let area = [area.x(), area.y(), 3 + 5.max(track_name_max_len(tracks)) as u16, area.h()];
|
||||
////let offset = 0; // track scroll offset
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
////to.blit(&"Mixer", area.x() + 1, area.y() + y, Some(DIM))?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2 + offset;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let selected = selected.track() == Some(index);
|
||||
////let style = if selected { yellow } else { white };
|
||||
////to.blit(&format!(" {index:>02} "), area.x(), area.y() + y, style)?;
|
||||
////to.blit(&*track.name.read().unwrap(), area.x() + 4, area.y() + y, style)?;
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// monitor
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().green().bold());
|
||||
////let off = Some(DIM);
|
||||
////area.x += 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" MON ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let style = if track.monitoring { on } else { off };
|
||||
////to.blit(&" MON ", area.x(), area.y() + y, style)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// record
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().red().bold());
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x += 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" REC ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let style = if track.recording { on } else { off };
|
||||
////to.blit(&" REC ", area.x(), area.y() + y, style)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// overdub
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().yellow().bold());
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" OVR ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////to.blit(&" OVR ", area.x(), area.y() + y, if track.overdub {
|
||||
////on
|
||||
////} else {
|
||||
////off
|
||||
////})?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// erase
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" DEL ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(_) = tracks.get(index) {
|
||||
////to.blit(&" DEL ", area.x(), area.y() + y, off)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// gain
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x() + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" GAIN ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(_) = tracks.get(index) {
|
||||
////to.blit(&" +0.0 ", area.x(), area.y() + y, off)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 7;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// scenes
|
||||
//Widget::new(|_|{todo!()}, |to: &mut TuiOutput|{
|
||||
//let [x, y, _, height] = to.area();
|
||||
//let mut x2 = 0;
|
||||
//Ok(for (scene_index, scene) in view.scenes().iter().enumerate() {
|
||||
//let active_scene = view.selected.scene() == Some(scene_index);
|
||||
//let sep = Some(if active_scene {
|
||||
//Style::default().yellow().not_dim()
|
||||
//} else {
|
||||
//Style::default().dim()
|
||||
//});
|
||||
//for y in y+1..y+height {
|
||||
//to.blit(&"│", x + x2, y, sep);
|
||||
//}
|
||||
//let name = scene.name.read().unwrap();
|
||||
//let mut x3 = name.len() as u16;
|
||||
//to.blit(&*name, x + x2, y, sep);
|
||||
//for (i, clip) in scene.clips.iter().enumerate() {
|
||||
//let active_track = view.selected.track() == Some(i);
|
||||
//if let Some(clip) = clip {
|
||||
//let y2 = y + 2 + i as u16 * 2;
|
||||
//let label = format!("{}", clip.read().unwrap().name);
|
||||
//to.blit(&label, x + x2, y2, Some(if active_track && active_scene {
|
||||
//Style::default().not_dim().yellow().bold()
|
||||
//} else {
|
||||
//Style::default().not_dim()
|
||||
//}));
|
||||
//x3 = x3.max(label.len() as u16)
|
||||
//}
|
||||
//}
|
||||
//x2 = x2 + x3 + 1;
|
||||
//})
|
||||
//}),
|
||||
//)
|
||||
//)
|
||||
//}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ pub(crate) mod midi_note; pub(crate) use midi_note::*;
|
|||
pub(crate) mod midi_out; pub(crate) use midi_out::*;
|
||||
pub(crate) mod midi_phrase; pub(crate) use midi_phrase::*;
|
||||
pub(crate) mod midi_play; pub(crate) use midi_play::*;
|
||||
pub(crate) mod midi_pool; pub(crate) use midi_pool::*;
|
||||
pub(crate) mod midi_rec; pub(crate) use midi_rec::*;
|
||||
|
||||
/// Add "all notes off" to the start of a buffer.
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait HasPhrases {
|
||||
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>>;
|
||||
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_phrases {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasPhrases for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn phrases (&$self) -> &Vec<Arc<RwLock<Phrase>>> { &$cb }
|
||||
fn phrases_mut (&mut $self) -> &mut Vec<Arc<RwLock<Phrase>>> { &mut$cb }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasPhrase {
|
||||
fn phrase (&self) -> &Arc<RwLock<Phrase>>;
|
||||
}
|
||||
|
@ -26,84 +12,6 @@ pub trait HasPhrase {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PhrasePoolCommand {
|
||||
Add(usize, Phrase),
|
||||
Delete(usize),
|
||||
Swap(usize, usize),
|
||||
Import(usize, PathBuf),
|
||||
Export(usize, PathBuf),
|
||||
SetName(usize, String),
|
||||
SetLength(usize, usize),
|
||||
SetColor(usize, ItemColor),
|
||||
}
|
||||
|
||||
impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
|
||||
fn execute (self, model: &mut T) -> Perhaps<Self> {
|
||||
use PhrasePoolCommand::*;
|
||||
Ok(match self {
|
||||
Add(mut index, phrase) => {
|
||||
let phrase = Arc::new(RwLock::new(phrase));
|
||||
let phrases = model.phrases_mut();
|
||||
if index >= phrases.len() {
|
||||
index = phrases.len();
|
||||
phrases.push(phrase)
|
||||
} else {
|
||||
phrases.insert(index, phrase);
|
||||
}
|
||||
Some(Self::Delete(index))
|
||||
},
|
||||
Delete(index) => {
|
||||
let phrase = model.phrases_mut().remove(index).read().unwrap().clone();
|
||||
Some(Self::Add(index, phrase))
|
||||
},
|
||||
Swap(index, other) => {
|
||||
model.phrases_mut().swap(index, other);
|
||||
Some(Self::Swap(index, other))
|
||||
},
|
||||
Import(index, path) => {
|
||||
let bytes = std::fs::read(&path)?;
|
||||
let smf = Smf::parse(bytes.as_slice())?;
|
||||
let mut t = 0u32;
|
||||
let mut events = vec![];
|
||||
for track in smf.tracks.iter() {
|
||||
for event in track.iter() {
|
||||
t += event.delta.as_int();
|
||||
if let TrackEventKind::Midi { channel, message } = event.kind {
|
||||
events.push((t, channel.as_int(), message));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut phrase = Phrase::new("imported", true, t as usize + 1, None, None);
|
||||
for event in events.iter() {
|
||||
phrase.notes[event.0 as usize].push(event.2);
|
||||
}
|
||||
Self::Add(index, phrase).execute(model)?
|
||||
},
|
||||
Export(_index, _path) => {
|
||||
todo!("export phrase to midi file");
|
||||
},
|
||||
SetName(index, name) => {
|
||||
let mut phrase = model.phrases()[index].write().unwrap();
|
||||
let old_name = phrase.name.clone();
|
||||
phrase.name = name;
|
||||
Some(Self::SetName(index, old_name))
|
||||
},
|
||||
SetLength(index, length) => {
|
||||
let mut phrase = model.phrases()[index].write().unwrap();
|
||||
let old_len = phrase.length;
|
||||
phrase.length = length;
|
||||
Some(Self::SetLength(index, old_len))
|
||||
},
|
||||
SetColor(index, color) => {
|
||||
let mut color = ItemPalette::from(color);
|
||||
std::mem::swap(&mut color, &mut model.phrases()[index].write().unwrap().color);
|
||||
Some(Self::SetColor(index, color.base))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A MIDI sequence.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Phrase {
|
||||
|
|
93
crates/tek/src/midi/midi_pool.rs
Normal file
93
crates/tek/src/midi/midi_pool.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait HasPhrases {
|
||||
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>>;
|
||||
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_phrases {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasPhrases for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn phrases (&$self) -> &Vec<Arc<RwLock<Phrase>>> { &$cb }
|
||||
fn phrases_mut (&mut $self) -> &mut Vec<Arc<RwLock<Phrase>>> { &mut$cb }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PhrasePoolCommand {
|
||||
Add(usize, Phrase),
|
||||
Delete(usize),
|
||||
Swap(usize, usize),
|
||||
Import(usize, PathBuf),
|
||||
Export(usize, PathBuf),
|
||||
SetName(usize, String),
|
||||
SetLength(usize, usize),
|
||||
SetColor(usize, ItemColor),
|
||||
}
|
||||
|
||||
impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
|
||||
fn execute (self, model: &mut T) -> Perhaps<Self> {
|
||||
use PhrasePoolCommand::*;
|
||||
Ok(match self {
|
||||
Add(mut index, phrase) => {
|
||||
let phrase = Arc::new(RwLock::new(phrase));
|
||||
let phrases = model.phrases_mut();
|
||||
if index >= phrases.len() {
|
||||
index = phrases.len();
|
||||
phrases.push(phrase)
|
||||
} else {
|
||||
phrases.insert(index, phrase);
|
||||
}
|
||||
Some(Self::Delete(index))
|
||||
},
|
||||
Delete(index) => {
|
||||
let phrase = model.phrases_mut().remove(index).read().unwrap().clone();
|
||||
Some(Self::Add(index, phrase))
|
||||
},
|
||||
Swap(index, other) => {
|
||||
model.phrases_mut().swap(index, other);
|
||||
Some(Self::Swap(index, other))
|
||||
},
|
||||
Import(index, path) => {
|
||||
let bytes = std::fs::read(&path)?;
|
||||
let smf = Smf::parse(bytes.as_slice())?;
|
||||
let mut t = 0u32;
|
||||
let mut events = vec![];
|
||||
for track in smf.tracks.iter() {
|
||||
for event in track.iter() {
|
||||
t += event.delta.as_int();
|
||||
if let TrackEventKind::Midi { channel, message } = event.kind {
|
||||
events.push((t, channel.as_int(), message));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut phrase = Phrase::new("imported", true, t as usize + 1, None, None);
|
||||
for event in events.iter() {
|
||||
phrase.notes[event.0 as usize].push(event.2);
|
||||
}
|
||||
Self::Add(index, phrase).execute(model)?
|
||||
},
|
||||
Export(_index, _path) => {
|
||||
todo!("export phrase to midi file");
|
||||
},
|
||||
SetName(index, name) => {
|
||||
let mut phrase = model.phrases()[index].write().unwrap();
|
||||
let old_name = phrase.name.clone();
|
||||
phrase.name = name;
|
||||
Some(Self::SetName(index, old_name))
|
||||
},
|
||||
SetLength(index, length) => {
|
||||
let mut phrase = model.phrases()[index].write().unwrap();
|
||||
let old_len = phrase.length;
|
||||
phrase.length = length;
|
||||
Some(Self::SetLength(index, old_len))
|
||||
},
|
||||
SetColor(index, color) => {
|
||||
let mut color = ItemPalette::from(color);
|
||||
std::mem::swap(&mut color, &mut model.phrases()[index].write().unwrap().color);
|
||||
Some(Self::SetColor(index, color.base))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -192,58 +192,55 @@ input_to_command!(ArrangerCommand: <Tui>|state: ArrangerTui, input|{
|
|||
key_pat!(Ctrl-Char('k')) => { todo!("keyboard") },
|
||||
// Transport: Play/pause
|
||||
key_pat!(Char(' ')) =>
|
||||
Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }),
|
||||
Self::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }),
|
||||
// Transport: Play from start or rewind to start
|
||||
key_pat!(Shift-Char(' ')) =>
|
||||
Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }),
|
||||
Self::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }),
|
||||
key_pat!(Char('e')) =>
|
||||
Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))),
|
||||
Self::Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))),
|
||||
key_pat!(Char('l')) =>
|
||||
Clip(ArrangerClipCommand::SetLoop(false)),
|
||||
Self::Clip(ArrangerClipCommand::SetLoop(false)),
|
||||
key_pat!(Ctrl-Char('a')) =>
|
||||
Scene(ArrangerSceneCommand::Add),
|
||||
Self::Scene(ArrangerSceneCommand::Add),
|
||||
key_pat!(Ctrl-Char('t')) =>
|
||||
Track(ArrangerTrackCommand::Add),
|
||||
Self::Track(ArrangerTrackCommand::Add),
|
||||
key_pat!(Char('0')) => match state.selected() {
|
||||
Selected::Mix => StopAll,
|
||||
Selected::Track(t) => return None,
|
||||
Selected::Scene(s) => return None,
|
||||
Selected::Clip(t, s) => return None,
|
||||
},
|
||||
key_pat!(Char('q')) => match state.selected() {
|
||||
Selected::Mix => return None,
|
||||
Selected::Track(t) => return None,
|
||||
Selected::Scene(s) => return None,
|
||||
Selected::Clip(t, s) => return None,
|
||||
key_pat!(Char('g')) => if let Selected::Clip(t, s) = state.selected() {
|
||||
Self::Phrases(PhrasesCommand::Select(0))
|
||||
} else {
|
||||
return None
|
||||
},
|
||||
key_pat!(Char('g')) => match state.selected() {
|
||||
Selected::Mix => return None,
|
||||
Selected::Track(t) => return None,
|
||||
Selected::Scene(s) => return None,
|
||||
Selected::Clip(t, s) => return None,
|
||||
key_pat!(Char('p')) => if let Selected::Clip(t, s) = state.selected() {
|
||||
Self::Clip(ArrangerClipCommand::Select(0))
|
||||
} else {
|
||||
return None
|
||||
},
|
||||
key_pat!(Char('p')) => match state.selected() {
|
||||
Selected::Mix => return None,
|
||||
Selected::Track(t) => return None,
|
||||
Selected::Scene(s) => return None,
|
||||
Selected::Clip(t, s) => return None,
|
||||
key_pat!(Char('q')) => if let Selected::Clip(t, s) = state.selected() {
|
||||
todo!("enqueue clip")
|
||||
} else {
|
||||
return None
|
||||
},
|
||||
// Tab: Toggle visibility of phrase pool column
|
||||
key_pat!(Tab) =>
|
||||
ShowPool(!state.show_pool),
|
||||
_ => match state.selected() {
|
||||
Selected::Mix => to_arranger_mix_command(input),
|
||||
Selected::Track(t) => to_arranger_track_command(input,
|
||||
t, state.tracks.len()),
|
||||
Selected::Scene(s) => to_arranger_scene_command(input,
|
||||
s, state.scenes.len()),
|
||||
Selected::Clip(t, s) => to_arranger_clip_command(input,
|
||||
t, state.tracks.len(),
|
||||
s, state.scenes.len()),
|
||||
Self::ShowPool(!state.show_pool),
|
||||
_ => {
|
||||
let t_len = state.tracks.len();
|
||||
let s_len = state.scenes.len();
|
||||
match state.selected() {
|
||||
Selected::Mix => to_arranger_mix_command(input),
|
||||
Selected::Track(t) => to_arranger_track_command(input, t, t_len),
|
||||
Selected::Scene(s) => to_arranger_scene_command(input, s, s_len),
|
||||
Selected::Clip(t, s) => to_arranger_clip_command(input, t, t_len, s, s_len),
|
||||
}
|
||||
}.or_else(||if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) {
|
||||
Some(Editor(command))
|
||||
Some(Self::Editor(command))
|
||||
} else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) {
|
||||
Some(Phrases(command))
|
||||
Some(Self::Phrases(command))
|
||||
} else {
|
||||
None
|
||||
})?
|
||||
|
@ -368,264 +365,3 @@ impl ArrangerMode {
|
|||
fn any_size <E: Engine> (_: E::Size) -> Perhaps<E::Size>{
|
||||
Ok(Some([0.into(),0.into()].into()))
|
||||
}
|
||||
|
||||
//impl<T: ArrangerApi> Command<T> for ArrangerClipCommand {
|
||||
//fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
//match self {
|
||||
//_ => todo!()
|
||||
//}
|
||||
//Ok(None)
|
||||
//}
|
||||
//}
|
||||
//let focused = true;
|
||||
//let _tracks = view.tracks();
|
||||
//lay!(
|
||||
//focused.then_some(Background(TuiTheme::border_bg())),
|
||||
//row!(
|
||||
//// name
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks, selected) = self;
|
||||
////let yellow = Some(Style::default().yellow().bold().not_dim());
|
||||
////let white = Some(Style::default().white().bold().not_dim());
|
||||
////let area = to.area();
|
||||
////let area = [area.x(), area.y(), 3 + 5.max(track_name_max_len(tracks)) as u16, area.h()];
|
||||
////let offset = 0; // track scroll offset
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
////to.blit(&"Mixer", area.x() + 1, area.y() + y, Some(DIM))?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2 + offset;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let selected = selected.track() == Some(index);
|
||||
////let style = if selected { yellow } else { white };
|
||||
////to.blit(&format!(" {index:>02} "), area.x(), area.y() + y, style)?;
|
||||
////to.blit(&*track.name.read().unwrap(), area.x() + 4, area.y() + y, style)?;
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// monitor
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().green().bold());
|
||||
////let off = Some(DIM);
|
||||
////area.x += 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" MON ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let style = if track.monitoring { on } else { off };
|
||||
////to.blit(&" MON ", area.x(), area.y() + y, style)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// record
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().red().bold());
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x += 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" REC ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////let style = if track.recording { on } else { off };
|
||||
////to.blit(&" REC ", area.x(), area.y() + y, style)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// overdub
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let on = Some(Style::default().not_dim().yellow().bold());
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" OVR ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(track) = tracks.get(index) {
|
||||
////to.blit(&" OVR ", area.x(), area.y() + y, if track.overdub {
|
||||
////on
|
||||
////} else {
|
||||
////off
|
||||
////})?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// erase
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" DEL ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(_) = tracks.get(index) {
|
||||
////to.blit(&" DEL ", area.x(), area.y() + y, off)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 4;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// gain
|
||||
//Widget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||
//todo!()
|
||||
////let Self(tracks) = self;
|
||||
////let mut area = to.area();
|
||||
////let off = Some(Style::default().dim());
|
||||
////area.x = area.x() + 1;
|
||||
////for y in 0..area.h() {
|
||||
////if y == 0 {
|
||||
//////" GAIN ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||
////} else if y % 2 == 0 {
|
||||
////let index = (y as usize - 2) / 2;
|
||||
////if let Some(_) = tracks.get(index) {
|
||||
////to.blit(&" +0.0 ", area.x(), area.y() + y, off)?;
|
||||
////} else {
|
||||
////area.height = y;
|
||||
////break
|
||||
////}
|
||||
////}
|
||||
////}
|
||||
////area.width = 7;
|
||||
////Ok(Some(area))
|
||||
//}),
|
||||
//// scenes
|
||||
//Widget::new(|_|{todo!()}, |to: &mut TuiOutput|{
|
||||
//let [x, y, _, height] = to.area();
|
||||
//let mut x2 = 0;
|
||||
//Ok(for (scene_index, scene) in view.scenes().iter().enumerate() {
|
||||
//let active_scene = view.selected.scene() == Some(scene_index);
|
||||
//let sep = Some(if active_scene {
|
||||
//Style::default().yellow().not_dim()
|
||||
//} else {
|
||||
//Style::default().dim()
|
||||
//});
|
||||
//for y in y+1..y+height {
|
||||
//to.blit(&"│", x + x2, y, sep);
|
||||
//}
|
||||
//let name = scene.name.read().unwrap();
|
||||
//let mut x3 = name.len() as u16;
|
||||
//to.blit(&*name, x + x2, y, sep);
|
||||
//for (i, clip) in scene.clips.iter().enumerate() {
|
||||
//let active_track = view.selected.track() == Some(i);
|
||||
//if let Some(clip) = clip {
|
||||
//let y2 = y + 2 + i as u16 * 2;
|
||||
//let label = format!("{}", clip.read().unwrap().name);
|
||||
//to.blit(&label, x + x2, y2, Some(if active_track && active_scene {
|
||||
//Style::default().not_dim().yellow().bold()
|
||||
//} else {
|
||||
//Style::default().not_dim()
|
||||
//}));
|
||||
//x3 = x3.max(label.len() as u16)
|
||||
//}
|
||||
//}
|
||||
//x2 = x2 + x3 + 1;
|
||||
//})
|
||||
//}),
|
||||
//)
|
||||
//)
|
||||
//}
|
||||
|
||||
//impl Command<ArrangerModel> for ArrangerSceneCommand {
|
||||
//}
|
||||
//Edit(phrase) => { state.state.phrase = phrase.clone() },
|
||||
//ToggleViewMode => { state.state.mode.to_next(); },
|
||||
//Delete => { state.state.delete(); },
|
||||
//Activate => { state.state.activate(); },
|
||||
//ZoomIn => { state.state.zoom_in(); },
|
||||
//ZoomOut => { state.state.zoom_out(); },
|
||||
//MoveBack => { state.state.move_back(); },
|
||||
//MoveForward => { state.state.move_forward(); },
|
||||
//RandomColor => { state.state.randomize_color(); },
|
||||
//Put => { state.state.phrase_put(); },
|
||||
//Get => { state.state.phrase_get(); },
|
||||
//AddScene => { state.state.scene_add(None, None)?; },
|
||||
//AddTrack => { state.state.track_add(None, None)?; },
|
||||
//ToggleLoop => { state.state.toggle_loop() },
|
||||
//pub fn zoom_in (&mut self) {
|
||||
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::V(factor + 1)
|
||||
//}
|
||||
//}
|
||||
//pub fn zoom_out (&mut self) {
|
||||
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::V(factor.saturating_sub(1))
|
||||
//}
|
||||
//}
|
||||
//pub fn move_back (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s > 0 {
|
||||
//self.scenes.swap(s, s - 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s - 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t > 0 {
|
||||
//self.tracks.swap(t, t - 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t - 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
//_ => todo!("arrangement: move forward")
|
||||
//}
|
||||
//}
|
||||
//pub fn move_forward (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s < self.scenes.len().saturating_sub(1) {
|
||||
//self.scenes.swap(s, s + 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s + 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t < self.tracks.len().saturating_sub(1) {
|
||||
//self.tracks.swap(t, t + 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t + 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
//_ => todo!("arrangement: move forward")
|
||||
//}
|
||||
//}
|
||||
|
|
|
@ -123,3 +123,65 @@ impl ArrangerTui {
|
|||
self.selected.scene().map(|s|self.scenes_mut().get_mut(s)).flatten()
|
||||
}
|
||||
}
|
||||
//impl Command<ArrangerModel> for ArrangerSceneCommand {
|
||||
//}
|
||||
//Edit(phrase) => { state.state.phrase = phrase.clone() },
|
||||
//ToggleViewMode => { state.state.mode.to_next(); },
|
||||
//Delete => { state.state.delete(); },
|
||||
//Activate => { state.state.activate(); },
|
||||
//ZoomIn => { state.state.zoom_in(); },
|
||||
//ZoomOut => { state.state.zoom_out(); },
|
||||
//MoveBack => { state.state.move_back(); },
|
||||
//MoveForward => { state.state.move_forward(); },
|
||||
//RandomColor => { state.state.randomize_color(); },
|
||||
//Put => { state.state.phrase_put(); },
|
||||
//Get => { state.state.phrase_get(); },
|
||||
//AddScene => { state.state.scene_add(None, None)?; },
|
||||
//AddTrack => { state.state.track_add(None, None)?; },
|
||||
//ToggleLoop => { state.state.toggle_loop() },
|
||||
//pub fn zoom_in (&mut self) {
|
||||
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::V(factor + 1)
|
||||
//}
|
||||
//}
|
||||
//pub fn zoom_out (&mut self) {
|
||||
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::V(factor.saturating_sub(1))
|
||||
//}
|
||||
//}
|
||||
//pub fn move_back (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s > 0 {
|
||||
//self.scenes.swap(s, s - 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s - 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t > 0 {
|
||||
//self.tracks.swap(t, t - 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t - 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
//_ => todo!("arrangement: move forward")
|
||||
//}
|
||||
//}
|
||||
//pub fn move_forward (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s < self.scenes.len().saturating_sub(1) {
|
||||
//self.scenes.swap(s, s + 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s + 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t < self.tracks.len().saturating_sub(1) {
|
||||
//self.tracks.swap(t, t + 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t + 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
//_ => todo!("arrangement: move forward")
|
||||
//}
|
||||
//}
|
||||
|
|
|
@ -104,7 +104,7 @@ from!(|state:&ArrangerTui|ArrangerStatus = {
|
|||
});
|
||||
render!(<Tui>|self: ArrangerStatus|Fixed::h(2, lay!([
|
||||
Self::help(),
|
||||
Fill::wh(Align::se({Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats())})),
|
||||
Fill::wh(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
])));
|
||||
impl ArrangerStatus {
|
||||
fn help () -> impl Render<Tui> {
|
||||
|
@ -120,9 +120,10 @@ impl ArrangerStatus {
|
|||
single("SPACE", "play/pause"),
|
||||
single(" Ctrl", " scroll"),
|
||||
single(" wsad", " cell"),
|
||||
double(("p", "put"), ("g", "get")),
|
||||
double(("q", "enqueue"), ("e", "edit")),
|
||||
single(" ▲▼▶◀", " note"),
|
||||
double(("a", "append"), ("s", "set note"),),
|
||||
double(("a", "append"), ("s", "set"),),
|
||||
double((",.", "length"), ("<>", "triplet"),),
|
||||
double(("[]", "phrase"), ("{}", "order"),),
|
||||
]))
|
||||
|
|
Loading…
Reference in a new issue