mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: refactor pt.21: 48 errors
This commit is contained in:
parent
2188bccd63
commit
b8708d6b2d
15 changed files with 313 additions and 355 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Arrangement {
|
||||
pub struct ArrangerModel {
|
||||
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
||||
pub jack: Arc<RwLock<JackClient>>,
|
||||
/// Global timebase
|
||||
|
|
@ -9,15 +9,15 @@ pub struct Arrangement {
|
|||
/// Name of arranger
|
||||
pub name: Arc<RwLock<String>>,
|
||||
/// Collection of phrases.
|
||||
pub phrases: Arc<RwLock<Vec<Phrase>>>,
|
||||
pub phrases: Arc<RwLock<PhrasePool>>,
|
||||
/// Collection of tracks.
|
||||
pub tracks: Vec<ArrangementTrack>,
|
||||
pub tracks: Vec<ArrangerTrack>,
|
||||
/// Collection of scenes.
|
||||
pub scenes: Vec<ArrangementScene>,
|
||||
pub scenes: Vec<ArrangerScene>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArrangementTrack {
|
||||
pub struct ArrangerTrack {
|
||||
/// Name of track
|
||||
pub name: Arc<RwLock<String>>,
|
||||
/// Preferred width of track column
|
||||
|
|
@ -29,7 +29,7 @@ pub struct ArrangementTrack {
|
|||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct ArrangementScene {
|
||||
pub struct ArrangerScene {
|
||||
/// Name of scene
|
||||
pub name: Arc<RwLock<String>>,
|
||||
/// Clips in scene, one per track
|
||||
|
|
@ -38,7 +38,7 @@ pub struct ArrangementScene {
|
|||
pub color: ItemColor,
|
||||
}
|
||||
|
||||
impl Arrangement {
|
||||
impl ArrangerModel {
|
||||
pub fn is_stopped (&self) -> bool {
|
||||
*self.clock.playing.read().unwrap() == Some(TransportState::Stopped)
|
||||
}
|
||||
|
|
@ -50,9 +50,9 @@ impl Arrangement {
|
|||
}
|
||||
pub fn track_add (
|
||||
&mut self, name: Option<&str>, color: Option<ItemColor>
|
||||
) -> Usually<&mut ArrangementTrack> {
|
||||
) -> Usually<&mut ArrangerTrack> {
|
||||
let name = name.map_or_else(||self.track_default_name(), |x|x.to_string());
|
||||
self.tracks.push(ArrangementTrack {
|
||||
self.tracks.push(ArrangerTrack {
|
||||
width: name.len() + 2,
|
||||
color: color.unwrap_or_else(||ItemColor::random()),
|
||||
player: MIDIPlayer::new(&self.jack, &self.clock, name.as_str())?,
|
||||
|
|
@ -63,9 +63,9 @@ impl Arrangement {
|
|||
}
|
||||
pub fn scene_add (
|
||||
&mut self, name: Option<&str>, color: Option<ItemColor>
|
||||
) -> Usually<&mut ArrangementScene> {
|
||||
) -> Usually<&mut ArrangerScene> {
|
||||
let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string());
|
||||
self.scenes.push(ArrangementScene {
|
||||
self.scenes.push(ArrangerScene {
|
||||
name: Arc::new(name.into()),
|
||||
clips: vec![None;self.tracks.len()],
|
||||
color: color.unwrap_or_else(||ItemColor::random()),
|
||||
|
|
@ -84,7 +84,7 @@ impl Arrangement {
|
|||
}
|
||||
}
|
||||
|
||||
impl ArrangementScene {
|
||||
impl ArrangerScene {
|
||||
pub fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> {
|
||||
let mut total = 0;
|
||||
if factor == 0 {
|
||||
|
|
@ -113,7 +113,7 @@ impl ArrangementScene {
|
|||
|
||||
/// Returns true if all phrases in the scene are
|
||||
/// currently playing on the given collection of tracks.
|
||||
pub fn is_playing (&self, tracks: &[ArrangementTrack]) -> bool {
|
||||
pub fn is_playing (&self, tracks: &[ArrangerTrack]) -> bool {
|
||||
self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate()
|
||||
.all(|(track_index, clip)|match clip {
|
||||
Some(clip) => tracks
|
||||
|
|
@ -153,7 +153,7 @@ impl ArrangementScene {
|
|||
//},
|
||||
//_ => panic!("unexpected in scene '{name:?}': {edn:?}")
|
||||
//});
|
||||
//Ok(ArrangementScene {
|
||||
//Ok(ArrangerScene {
|
||||
//name: Arc::new(name.unwrap_or("").to_string().into()),
|
||||
//color: ItemColor::random(),
|
||||
//clips,
|
||||
|
|
@ -161,7 +161,7 @@ impl ArrangementScene {
|
|||
//}
|
||||
}
|
||||
|
||||
impl ArrangementTrack {
|
||||
impl ArrangerTrack {
|
||||
pub fn longest_name (tracks: &[Self]) -> usize {
|
||||
tracks.iter().map(|s|s.name.read().unwrap().len()).fold(0, usize::max)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ArrangementCommand {
|
||||
pub enum ArrangerCommand {
|
||||
Clear,
|
||||
Export,
|
||||
Import,
|
||||
StopAll,
|
||||
Scene(ArrangementSceneCommand),
|
||||
Track(ArrangementTrackCommand),
|
||||
Clip(ArrangementClipCommand),
|
||||
Scene(ArrangerSceneCommand),
|
||||
Track(ArrangerTrackCommand),
|
||||
Clip(ArrangerClipCommand),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ArrangementSceneCommand {
|
||||
pub enum ArrangerSceneCommand {
|
||||
Add,
|
||||
Delete(usize),
|
||||
RandomColor,
|
||||
|
|
@ -23,7 +23,7 @@ pub enum ArrangementSceneCommand {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ArrangementTrackCommand {
|
||||
pub enum ArrangerTrackCommand {
|
||||
Add,
|
||||
Delete(usize),
|
||||
RandomColor,
|
||||
|
|
@ -34,7 +34,7 @@ pub enum ArrangementTrackCommand {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ArrangementClipCommand {
|
||||
pub enum ArrangerClipCommand {
|
||||
Play,
|
||||
Get(usize, usize),
|
||||
Set(usize, usize, Option<Arc<RwLock<Phrase>>>),
|
||||
|
|
@ -43,8 +43,8 @@ pub enum ArrangementClipCommand {
|
|||
RandomColor,
|
||||
}
|
||||
|
||||
impl Command<Arrangement> for ArrangementCommand {
|
||||
fn execute (self, state: &mut Arrangement) -> Perhaps<Self> {
|
||||
impl Command<ArrangerModel> for ArrangerCommand {
|
||||
fn execute (self, state: &mut ArrangerModel) -> Perhaps<Self> {
|
||||
match self {
|
||||
Self::Scene(command) => { return Ok(command.execute(state)?.map(Self::Scene)) },
|
||||
Self::Track(command) => { return Ok(command.execute(state)?.map(Self::Track)) },
|
||||
|
|
@ -55,8 +55,8 @@ impl Command<Arrangement> for ArrangementCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl Command<Arrangement> for ArrangementSceneCommand {
|
||||
fn execute (self, state: &mut Arrangement) -> Perhaps<Self> {
|
||||
impl Command<ArrangerModel> for ArrangerSceneCommand {
|
||||
fn execute (self, state: &mut ArrangerModel) -> Perhaps<Self> {
|
||||
match self {
|
||||
Self::Delete(index) => { state.scene_del(index); },
|
||||
_ => todo!()
|
||||
|
|
@ -65,8 +65,8 @@ impl Command<Arrangement> for ArrangementSceneCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl Command<Arrangement> for ArrangementTrackCommand {
|
||||
fn execute (self, state: &mut Arrangement) -> Perhaps<Self> {
|
||||
impl Command<ArrangerModel> for ArrangerTrackCommand {
|
||||
fn execute (self, state: &mut ArrangerModel) -> Perhaps<Self> {
|
||||
match self {
|
||||
Self::Delete(index) => { state.track_del(index); },
|
||||
_ => todo!()
|
||||
|
|
@ -75,8 +75,8 @@ impl Command<Arrangement> for ArrangementTrackCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl Command<Arrangement> for ArrangementClipCommand {
|
||||
fn execute (self, state: &mut Arrangement) -> Perhaps<Self> {
|
||||
impl Command<ArrangerModel> for ArrangerClipCommand {
|
||||
fn execute (self, state: &mut ArrangerModel) -> Perhaps<Self> {
|
||||
match self {
|
||||
_ => todo!()
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ impl Command<Arrangement> for ArrangementClipCommand {
|
|||
}
|
||||
}
|
||||
|
||||
//impl Command<Arrangement> for ArrangementSceneCommand {
|
||||
//impl Command<ArrangerModel> for ArrangerSceneCommand {
|
||||
//}
|
||||
//Edit(phrase) => { state.state.phrase = phrase.clone() },
|
||||
//ToggleViewMode => { state.state.mode.to_next(); },
|
||||
|
|
@ -101,27 +101,27 @@ impl Command<Arrangement> for ArrangementClipCommand {
|
|||
//AddTrack => { state.state.track_add(None, None)?; },
|
||||
//ToggleLoop => { state.state.toggle_loop() },
|
||||
//pub fn zoom_in (&mut self) {
|
||||
//if let ArrangementEditorMode::Vertical(factor) = self.mode {
|
||||
//self.mode = ArrangementEditorMode::Vertical(factor + 1)
|
||||
//if let ArrangerEditorMode::Vertical(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::Vertical(factor + 1)
|
||||
//}
|
||||
//}
|
||||
//pub fn zoom_out (&mut self) {
|
||||
//if let ArrangementEditorMode::Vertical(factor) = self.mode {
|
||||
//self.mode = ArrangementEditorMode::Vertical(factor.saturating_sub(1))
|
||||
//if let ArrangerEditorMode::Vertical(factor) = self.mode {
|
||||
//self.mode = ArrangerEditorMode::Vertical(factor.saturating_sub(1))
|
||||
//}
|
||||
//}
|
||||
//pub fn move_back (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangementEditorFocus::Scene(s) => {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s > 0 {
|
||||
//self.scenes.swap(s, s - 1);
|
||||
//self.selected = ArrangementEditorFocus::Scene(s - 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s - 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangementEditorFocus::Track(t) => {
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t > 0 {
|
||||
//self.tracks.swap(t, t - 1);
|
||||
//self.selected = ArrangementEditorFocus::Track(t - 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t - 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
|
|
@ -130,16 +130,16 @@ impl Command<Arrangement> for ArrangementClipCommand {
|
|||
//}
|
||||
//pub fn move_forward (&mut self) {
|
||||
//match self.selected {
|
||||
//ArrangementEditorFocus::Scene(s) => {
|
||||
//ArrangerEditorFocus::Scene(s) => {
|
||||
//if s < self.scenes.len().saturating_sub(1) {
|
||||
//self.scenes.swap(s, s + 1);
|
||||
//self.selected = ArrangementEditorFocus::Scene(s + 1);
|
||||
//self.selected = ArrangerEditorFocus::Scene(s + 1);
|
||||
//}
|
||||
//},
|
||||
//ArrangementEditorFocus::Track(t) => {
|
||||
//ArrangerEditorFocus::Track(t) => {
|
||||
//if t < self.tracks.len().saturating_sub(1) {
|
||||
//self.tracks.swap(t, t + 1);
|
||||
//self.selected = ArrangementEditorFocus::Track(t + 1);
|
||||
//self.selected = ArrangerEditorFocus::Track(t + 1);
|
||||
//// FIXME: also swap clip order in scenes
|
||||
//}
|
||||
//},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::*;
|
||||
|
||||
/// Contains all phrases in a project
|
||||
#[derive(Debug)]
|
||||
pub struct PhrasePool {
|
||||
/// Phrases in the pool
|
||||
pub phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||
|
|
|
|||
|
|
@ -22,10 +22,7 @@ impl ArrangerCli {
|
|||
/// Run the arranger TUI from CLI arguments.
|
||||
fn run (&self) -> Usually<()> {
|
||||
Tui::run(JackClient::new("tek_arranger")?.activate_with(|jack|{
|
||||
let transport = TransportView::new(jack, None);
|
||||
let phrases = Arc::new(RwLock::new(PhrasePool::new()));
|
||||
let mut arrangement = Arrangement::new(&jack, &transport.clock, "", &phrases);
|
||||
let transport = Arc::new(RwLock::new(transport));
|
||||
let mut app = TransportApp::try_from(jack)?;
|
||||
if let Some(name) = self.name.as_ref() {
|
||||
*arrangement.name.write().unwrap() = name.clone();
|
||||
}
|
||||
|
|
@ -45,13 +42,8 @@ impl ArrangerCli {
|
|||
Some(scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32))
|
||||
)?;
|
||||
}
|
||||
Ok(ArrangerView::new(
|
||||
jack,
|
||||
self.transport.then_some(transport),
|
||||
arrangement,
|
||||
phrases,
|
||||
))
|
||||
})?)?;
|
||||
Ok(app)
|
||||
})?)?
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub struct SequencerCli {
|
|||
impl SequencerCli {
|
||||
fn run (&self) -> Usually<()> {
|
||||
Tui::run(JackClient::new("tek_sequencer")?.activate_with(|jack|{
|
||||
let transport = TransportView::new(jack, None);
|
||||
let mut app = SequencerApp::try_from(jack)?;
|
||||
if let Some(_) = self.name.as_ref() {
|
||||
// TODO: sequencer.name = Arc::new(RwLock::new(name.clone()));
|
||||
}
|
||||
|
|
@ -32,16 +32,7 @@ impl SequencerCli {
|
|||
//phrase.write().unwrap().length = length;
|
||||
//}
|
||||
}
|
||||
Ok(SequencerView {
|
||||
jack: jack.clone(),
|
||||
focus_cursor: (1, 1),
|
||||
entered: false,
|
||||
phrases: Arc::new(RwLock::new(PhrasePool::new())),
|
||||
editor: PhraseEditor::new(),
|
||||
clock: transport.clock.clone(),
|
||||
player: PhrasePlayer::new(jack, &transport.clock, "tek_sequencer")?,
|
||||
transport: self.transport.then_some(Arc::new(RwLock::new(transport))),
|
||||
})
|
||||
Ok(app)
|
||||
})?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ use tek_core::clap::{self, Parser};
|
|||
|
||||
/// Application entrypoint.
|
||||
pub fn main () -> Usually<()> {
|
||||
Tui::run(JackClient::new("tek_transport")?.activate_with(TransportApp::run)?)?
|
||||
Tui::run(JackClient::new("tek_transport")?.activate_with(TransportApp::try_from)?)?
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct ArrangerAudio(pub Arc<RwLock<Arrangement>>);
|
||||
pub struct ArrangerAudio(pub Arc<RwLock<ArrangerModel>>);
|
||||
|
||||
impl Audio for ArrangerAudio {
|
||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
|
|
@ -8,7 +8,7 @@ impl Audio for ArrangerAudio {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ArrangerRefAudio<'a>(pub &'a mut Arrangement);
|
||||
pub struct ArrangerRefAudio<'a>(pub &'a mut ArrangerModel);
|
||||
|
||||
impl<'a> Audio for ArrangerRefAudio<'a> {
|
||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ pub type ArrangerApp = AppContainer<
|
|||
ArrangerStatusBar
|
||||
>;
|
||||
|
||||
impl ArrangerApp {
|
||||
pub fn run <'a> (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
let clock = Arc::new(Clock::from(Instant::default()));
|
||||
|
||||
let transport = Arc::new(RwLock::new(tek_api::Transport {
|
||||
|
|
@ -20,30 +21,22 @@ impl ArrangerApp {
|
|||
clock: clock.clone()
|
||||
}));
|
||||
|
||||
let phrases = Arc::new(RwLock::new(PhrasePool {
|
||||
phrases: vec![]
|
||||
}));
|
||||
let phrases = Arc::new(RwLock::new(PhrasePool { phrases: vec![] }));
|
||||
|
||||
let player = Arc::new(RwLock::new(MIDIPlayer::new(jack, &clock, "preview")?));
|
||||
|
||||
let arrangement = Arc::new(RwLock::new(Arrangement {
|
||||
jack: jack.clone(),
|
||||
clock: clock.clone(),
|
||||
name: Arc::new(RwLock::new(String::new())),
|
||||
phrases: phrases.read().unwrap().phrases.clone(), // FIXME
|
||||
tracks: vec![],
|
||||
scenes: vec![],
|
||||
}));
|
||||
|
||||
let sequencer = Arc::new(RwLock::new(SequencerModel {
|
||||
transport: transport.clone(),
|
||||
phrases: phrases.clone(),
|
||||
player: player.clone(),
|
||||
}));
|
||||
|
||||
let model = Arc::new(RwLock::new(ArrangerModel {
|
||||
arrangement: arrangement.clone(),
|
||||
sequencer: sequencer.clone(),
|
||||
let model = Arc::new(RwLock::new(Arranger {
|
||||
arrangement: Arc::new(RwLock::new(ArrangerModel {
|
||||
jack: jack.clone(),
|
||||
clock: clock.clone(),
|
||||
name: Arc::new(RwLock::new(String::new())),
|
||||
phrases: phrases.clone(), // FIXME
|
||||
tracks: vec![],
|
||||
scenes: vec![],
|
||||
})),
|
||||
sequencer: Arc::new(RwLock::new(SequencerModel {
|
||||
transport: transport.clone(),
|
||||
phrases: phrases.clone(),
|
||||
player: Arc::new(RwLock::new(MIDIPlayer::new(jack, &clock, "preview")?)),
|
||||
})),
|
||||
transport: transport.clone(),
|
||||
phrases: phrases.clone(),
|
||||
}));
|
||||
|
|
@ -51,28 +44,28 @@ impl ArrangerApp {
|
|||
Ok(Self::new(
|
||||
&model,
|
||||
ArrangerView::from(&model),
|
||||
ArrangerAudio(arrangement.clone()),
|
||||
ArrangerAudio(model.clone()),
|
||||
None,
|
||||
None
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrangerModel {
|
||||
pub arrangement: Arc<RwLock<Arrangement>>,
|
||||
pub struct Arranger {
|
||||
pub arrangement: Arc<RwLock<ArrangerModel>>,
|
||||
pub sequencer: Arc<RwLock<SequencerModel>>,
|
||||
pub transport: Arc<RwLock<tek_api::Transport>>,
|
||||
pub phrases: Arc<RwLock<PhrasePool>>,
|
||||
}
|
||||
|
||||
impl<E: Engine> From<&Arc<RwLock<ArrangerModel>>> for ArrangerView<E> {
|
||||
fn from (model: &Arc<RwLock<ArrangerModel>>) -> Self {
|
||||
impl<E: Engine> From<&Arc<RwLock<Arranger>>> for ArrangerView<E> {
|
||||
fn from (model: &Arc<RwLock<Arranger>>) -> Self {
|
||||
let mut view = Self {
|
||||
model: model.clone(),
|
||||
sequencer: SequencerView::from(&model.read().unwrap().sequencer),
|
||||
split: 20,
|
||||
selected: ArrangementEditorFocus::Clip(0, 0),
|
||||
mode: ArrangementEditorMode::Vertical(2),
|
||||
selected: ArrangerFocus::Clip(0, 0),
|
||||
mode: ArrangerMode::Vertical(2),
|
||||
color: Color::Rgb(28, 35, 25).into(),
|
||||
size: Measure::new(),
|
||||
focused: false,
|
||||
|
|
@ -85,15 +78,15 @@ impl<E: Engine> From<&Arc<RwLock<ArrangerModel>>> for ArrangerView<E> {
|
|||
|
||||
/// Root level object for standalone `tek_arranger`
|
||||
pub struct ArrangerView<E: Engine> {
|
||||
pub model: Arc<RwLock<ArrangerModel>>,
|
||||
pub model: Arc<RwLock<Arranger>>,
|
||||
/// Sequencer component
|
||||
pub sequencer: SequencerView<E>,
|
||||
/// Height of arrangement
|
||||
pub split: u16,
|
||||
/// Currently selected element.
|
||||
pub selected: ArrangementEditorFocus,
|
||||
pub selected: ArrangerFocus,
|
||||
/// Display mode of arranger
|
||||
pub mode: ArrangementEditorMode,
|
||||
pub mode: ArrangerMode,
|
||||
/// Background color of arrangement
|
||||
pub color: ItemColor,
|
||||
/// Whether the arranger is currently focused
|
||||
|
|
@ -106,7 +99,7 @@ pub struct ArrangerView<E: Engine> {
|
|||
|
||||
/// Display mode of arranger
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum ArrangementEditorMode {
|
||||
pub enum ArrangerMode {
|
||||
/// Tracks are rows
|
||||
Horizontal,
|
||||
/// Tracks are columns
|
||||
|
|
@ -114,7 +107,7 @@ pub enum ArrangementEditorMode {
|
|||
}
|
||||
|
||||
/// Arranger display mode can be cycled
|
||||
impl ArrangementEditorMode {
|
||||
impl ArrangerMode {
|
||||
/// Cycle arranger display mode
|
||||
pub fn to_next (&mut self) {
|
||||
*self = match self {
|
||||
|
|
@ -130,7 +123,7 @@ impl ArrangementEditorMode {
|
|||
impl<E: Engine> Audio for ArrangerView<E> {
|
||||
#[inline] fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
||||
// FIXME: one of these per playing track
|
||||
if let ArrangementEditorFocus::Clip(t, s) = self.selected {
|
||||
if let ArrangerFocus::Clip(t, s) = self.selected {
|
||||
let phrase = self.model.scenes.get(s).map(|scene|scene.clips.get(t));
|
||||
if let Some(Some(Some(phrase))) = phrase {
|
||||
if let Some(track) = self.model.tracks.get(t) {
|
||||
|
|
@ -164,9 +157,9 @@ impl Content for ArrangerView<Tui> {
|
|||
lay!(
|
||||
Layers::new(move |add|{
|
||||
match self.mode {
|
||||
ArrangementEditorMode::Horizontal =>
|
||||
ArrangerMode::Horizontal =>
|
||||
add(&arranger_content_horizontal(self))?,
|
||||
ArrangementEditorMode::Vertical(factor) =>
|
||||
ArrangerMode::Vertical(factor) =>
|
||||
add(&arranger_content_vertical(self, factor))?
|
||||
};
|
||||
add(&self.size)
|
||||
|
|
@ -176,7 +169,7 @@ impl Content for ArrangerView<Tui> {
|
|||
.bg(TuiTheme::border_bg())
|
||||
.fg(TuiTheme::border_fg(self.focused)))),
|
||||
widget(&self.size),
|
||||
widget(&format!("[{}] Arrangement", if self.entered {
|
||||
widget(&format!("[{}] Arranger", if self.entered {
|
||||
"■"
|
||||
} else {
|
||||
" "
|
||||
|
|
@ -206,7 +199,7 @@ impl<E: Engine> ArrangerView<E> {
|
|||
pub fn activate (&mut self) {
|
||||
let arrangement = self.model.read().unwrap().arrangement.read().unwrap();
|
||||
match self.selected {
|
||||
ArrangementEditorFocus::Scene(s) => {
|
||||
ArrangerFocus::Scene(s) => {
|
||||
for (t, track) in self.model.tracks.iter_mut().enumerate() {
|
||||
let player = &mut track.player;
|
||||
let clip = self.model.scenes[s].clips[t].as_ref();
|
||||
|
|
@ -220,7 +213,7 @@ impl<E: Engine> ArrangerView<E> {
|
|||
//self.transport.toggle_play()
|
||||
//}
|
||||
},
|
||||
ArrangementEditorFocus::Clip(t, s) => {
|
||||
ArrangerFocus::Clip(t, s) => {
|
||||
let clip = self.model.scenes[s].clips[t].as_ref();
|
||||
self.model.tracks[t].player.enqueue_next(clip);
|
||||
},
|
||||
|
|
@ -238,8 +231,8 @@ impl<E: Engine> ArrangerView<E> {
|
|||
let arrangement = self.model.read().unwrap().arrangement.read().unwrap();
|
||||
let selected = self.selected;
|
||||
(self.model.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected {
|
||||
ArrangementEditorFocus::Scene(s) => s == self.model.scenes.len() - 1,
|
||||
ArrangementEditorFocus::Clip(_, s) => s == self.model.scenes.len() - 1,
|
||||
ArrangerFocus::Scene(s) => s == self.model.scenes.len() - 1,
|
||||
ArrangerFocus::Clip(_, s) => s == self.model.scenes.len() - 1,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
|
@ -253,26 +246,26 @@ impl<E: Engine> ArrangerView<E> {
|
|||
pub fn randomize_color (&mut self) {
|
||||
let arrangement = self.model.read().unwrap().arrangement.read().unwrap();
|
||||
match self.selected {
|
||||
ArrangementEditorFocus::Mix => {
|
||||
ArrangerFocus::Mix => {
|
||||
self.color = ItemColor::random_dark()
|
||||
},
|
||||
ArrangementEditorFocus::Track(t) => {
|
||||
ArrangerFocus::Track(t) => {
|
||||
self.model.tracks[t].color = ItemColor::random()
|
||||
},
|
||||
ArrangementEditorFocus::Scene(s) => {
|
||||
ArrangerFocus::Scene(s) => {
|
||||
self.model.scenes[s].color = ItemColor::random()
|
||||
},
|
||||
ArrangementEditorFocus::Clip(t, s) => {
|
||||
ArrangerFocus::Clip(t, s) => {
|
||||
if let Some(phrase) = &self.model.scenes[s].clips[t] {
|
||||
phrase.write().unwrap().color = ItemColorTriplet::random();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn selected_scene (&self) -> Option<&ArrangementScene> {
|
||||
pub fn selected_scene (&self) -> Option<&ArrangerScene> {
|
||||
self.selected.scene().map(|s|self.model.scenes.get(s)).flatten()
|
||||
}
|
||||
pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangementScene> {
|
||||
pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> {
|
||||
self.selected.scene().map(|s|self.model.scenes.get_mut(s)).flatten()
|
||||
}
|
||||
pub fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use crate::*;
|
|||
/// Status bar for arranger ap
|
||||
pub enum ArrangerStatusBar {
|
||||
Transport,
|
||||
ArrangementMix,
|
||||
ArrangementTrack,
|
||||
ArrangementScene,
|
||||
ArrangementClip,
|
||||
ArrangerMix,
|
||||
ArrangerTrack,
|
||||
ArrangerScene,
|
||||
ArrangerClip,
|
||||
PhrasePool,
|
||||
PhraseView,
|
||||
PhraseEdit,
|
||||
|
|
@ -21,15 +21,15 @@ impl StatusBar<Tui> for ArrangerStatusBar {
|
|||
impl ArrangerStatusBar {
|
||||
fn update <E: Engine> (&mut self, state: &ArrangerView<E>) {
|
||||
*self = match state.focused() {
|
||||
ArrangerViewFocus::Transport => ArrangerStatusBar::Transport,
|
||||
ArrangerViewFocus::Arrangement => match state.arrangement.selected {
|
||||
ArrangementEditorFocus::Mix => ArrangerStatusBar::ArrangementMix,
|
||||
ArrangementEditorFocus::Track(_) => ArrangerStatusBar::ArrangementTrack,
|
||||
ArrangementEditorFocus::Scene(_) => ArrangerStatusBar::ArrangementScene,
|
||||
ArrangementEditorFocus::Clip(_, _) => ArrangerStatusBar::ArrangementClip,
|
||||
ArrangerViewFocus::Transport => ArrangerStatusBar::Transport,
|
||||
ArrangerViewFocus::Arranger => match state.selected {
|
||||
ArrangerFocus::Mix => ArrangerStatusBar::ArrangerMix,
|
||||
ArrangerFocus::Track(_) => ArrangerStatusBar::ArrangerTrack,
|
||||
ArrangerFocus::Scene(_) => ArrangerStatusBar::ArrangerScene,
|
||||
ArrangerFocus::Clip(_, _) => ArrangerStatusBar::ArrangerClip,
|
||||
},
|
||||
ArrangerViewFocus::PhrasePool => ArrangerStatusBar::PhrasePool,
|
||||
ArrangerViewFocus::PhraseEditor => match state.sequencer.editor.entered {
|
||||
ArrangerViewFocus::PhrasePool => ArrangerStatusBar::PhrasePool,
|
||||
ArrangerViewFocus::PhraseEditor => match state.sequencer.editor.entered {
|
||||
true => ArrangerStatusBar::PhraseEdit,
|
||||
false => ArrangerStatusBar::PhraseView,
|
||||
},
|
||||
|
|
@ -42,10 +42,10 @@ impl Content for ArrangerStatusBar {
|
|||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
let label = match self {
|
||||
Self::Transport => "TRANSPORT",
|
||||
Self::ArrangementMix => "PROJECT",
|
||||
Self::ArrangementTrack => "TRACK",
|
||||
Self::ArrangementScene => "SCENE",
|
||||
Self::ArrangementClip => "CLIP",
|
||||
Self::ArrangerMix => "PROJECT",
|
||||
Self::ArrangerTrack => "TRACK",
|
||||
Self::ArrangerScene => "SCENE",
|
||||
Self::ArrangerClip => "CLIP",
|
||||
Self::PhrasePool => "SEQ LIST",
|
||||
Self::PhraseView => "VIEW SEQ",
|
||||
Self::PhraseEdit => "EDIT SEQ",
|
||||
|
|
@ -55,14 +55,14 @@ impl Content for ArrangerStatusBar {
|
|||
let mode_fg = TuiTheme::mode_fg();
|
||||
let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg);
|
||||
let commands = match self {
|
||||
Self::ArrangementMix => Self::command(&[
|
||||
Self::ArrangerMix => Self::command(&[
|
||||
["", "c", "olor"],
|
||||
["", "<>", "resize"],
|
||||
["", "+-", "zoom"],
|
||||
["", "n", "ame/number"],
|
||||
["", "Enter", " stop all"],
|
||||
]),
|
||||
Self::ArrangementClip => Self::command(&[
|
||||
Self::ArrangerClip => Self::command(&[
|
||||
["", "g", "et"],
|
||||
["", "s", "et"],
|
||||
["", "a", "dd"],
|
||||
|
|
@ -74,7 +74,7 @@ impl Content for ArrangerStatusBar {
|
|||
["", ",.", "select"],
|
||||
["", "Enter", " launch"],
|
||||
]),
|
||||
Self::ArrangementTrack => Self::command(&[
|
||||
Self::ArrangerTrack => Self::command(&[
|
||||
["re", "n", "ame"],
|
||||
["", ",.", "resize"],
|
||||
["", "<>", "move"],
|
||||
|
|
@ -85,7 +85,7 @@ impl Content for ArrangerStatusBar {
|
|||
["", "Del", "ete"],
|
||||
["", "Enter", " stop"],
|
||||
]),
|
||||
Self::ArrangementScene => Self::command(&[
|
||||
Self::ArrangerScene => Self::command(&[
|
||||
["re", "n", "ame"],
|
||||
["", "Del", "ete"],
|
||||
["", "Enter", " launch"],
|
||||
|
|
@ -121,20 +121,20 @@ impl Content for ArrangerStatusBar {
|
|||
}
|
||||
|
||||
//pub fn arranger_menu_bar () -> MenuBar {
|
||||
//use ArrangementEditorCommand as Cmd;
|
||||
//use ArrangementCommand as Edit;
|
||||
//use ArrangementEditorFocus as Focus;
|
||||
//use ArrangementTrackCommand as Track;
|
||||
//use ArrangementClipCommand as Clip;
|
||||
//use ArrangementSceneCommand as Scene;
|
||||
//use ArrangerCommand as Cmd;
|
||||
//use ArrangerCommand as Edit;
|
||||
//use ArrangerFocus as Focus;
|
||||
//use ArrangerTrackCommand as Track;
|
||||
//use ArrangerClipCommand as Clip;
|
||||
//use ArrangerSceneCommand as Scene;
|
||||
//use TransportCommand as Transport;
|
||||
//MenuBar::new()
|
||||
//.add({
|
||||
//use ArrangementCommand::*;
|
||||
//use ArrangerCommand::*;
|
||||
//Menu::new("File")
|
||||
//.cmd("n", "New project", ArrangerViewCommand::Arrangement(New))
|
||||
//.cmd("l", "Load project", ArrangerViewCommand::Arrangement(Load))
|
||||
//.cmd("s", "Save project", ArrangerViewCommand::Arrangement(Save))
|
||||
//.cmd("n", "New project", ArrangerViewCommand::Arranger(New))
|
||||
//.cmd("l", "Load project", ArrangerViewCommand::Arranger(Load))
|
||||
//.cmd("s", "Save project", ArrangerViewCommand::Arranger(Save))
|
||||
//})
|
||||
//.add({
|
||||
//Menu::new("Transport")
|
||||
|
|
@ -144,24 +144,24 @@ impl Content for ArrangerStatusBar {
|
|||
//.cmd("S", "Stop and rewind", TransportCommand::Transport(Stop(Some(0))))
|
||||
//})
|
||||
//.add({
|
||||
//use ArrangementCommand::*;
|
||||
//use ArrangerCommand::*;
|
||||
//Menu::new("Track")
|
||||
//.cmd("a", "Append new", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("i", "Insert new", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("n", "Rename", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("d", "Delete", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd(">", "Move up", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("<", "Move down", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("a", "Append new", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("i", "Insert new", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("n", "Rename", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("d", "Delete", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd(">", "Move up", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("<", "Move down", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//})
|
||||
//.add({
|
||||
//use ArrangementCommand::*;
|
||||
//use ArrangerCommand::*;
|
||||
//Menu::new("Scene")
|
||||
//.cmd("a", "Append new", ArrangerViewCommand::Arrangement(AddScene))
|
||||
//.cmd("i", "Insert new", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("n", "Rename", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("d", "Delete", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd(">", "Move up", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("<", "Move down", ArrangerViewCommand::Arrangement(AddTrack))
|
||||
//.cmd("a", "Append new", ArrangerViewCommand::Arranger(AddScene))
|
||||
//.cmd("i", "Insert new", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("n", "Rename", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("d", "Delete", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd(">", "Move up", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//.cmd("<", "Move down", ArrangerViewCommand::Arranger(AddTrack))
|
||||
//})
|
||||
//.add({
|
||||
//use PhraseRenameCommand as Rename;
|
||||
|
|
|
|||
|
|
@ -3,20 +3,15 @@ use crate::*;
|
|||
#[derive(Clone)]
|
||||
pub enum ArrangerViewCommand {
|
||||
Focus(FocusCommand),
|
||||
Arrangement(ArrangementEditorCommand),
|
||||
Edit(ArrangerCommand),
|
||||
Select(ArrangerFocus),
|
||||
Zoom(usize),
|
||||
Transport(TransportViewCommand),
|
||||
Phrases(PhrasePoolViewCommand),
|
||||
Editor(PhraseEditorCommand),
|
||||
EditPhrase(Option<Arc<RwLock<Phrase>>>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ArrangementEditorCommand {
|
||||
Edit(ArrangementCommand),
|
||||
Select(ArrangementEditorFocus),
|
||||
Zoom(usize),
|
||||
}
|
||||
|
||||
/// Handle top-level events in standalone arranger.
|
||||
impl Handle<Tui> for ArrangerView<Tui> {
|
||||
fn handle (&mut self, i: &TuiInput) -> Perhaps<bool> {
|
||||
|
|
@ -57,13 +52,120 @@ impl InputToCommand<Tui, ArrangerView<Tui>> for ArrangerViewCommand {
|
|||
PhrasePoolViewCommand::input_to_command(&view.sequencer.phrases, input)?
|
||||
)
|
||||
},
|
||||
ArrangerViewFocus::Arrangement => match input.event() {
|
||||
key!(KeyCode::Char('e')) => EditPhrase(
|
||||
view.selected_phrase()
|
||||
),
|
||||
_ => Arrangement(
|
||||
ArrangementEditorCommand::input_to_command(&view, &input)?
|
||||
)
|
||||
ArrangerViewFocus::Arranger => {
|
||||
use ArrangerFocus as Focus;
|
||||
use ArrangerCommand as Model;
|
||||
use ArrangerTrackCommand as Track;
|
||||
use ArrangerClipCommand as Clip;
|
||||
use ArrangerSceneCommand as Scene;
|
||||
match input.event() {
|
||||
key!(KeyCode::Char('e')) => EditPhrase(
|
||||
view.selected_phrase()
|
||||
),
|
||||
_ => match input.event() {
|
||||
// FIXME: boundary conditions
|
||||
|
||||
key!(KeyCode::Up) => match view.selected {
|
||||
ArrangerFocus::Mix => return None,
|
||||
ArrangerFocus::Track(t) => return None,
|
||||
ArrangerFocus::Scene(s) => Select(Focus::Scene(s - 1)),
|
||||
ArrangerFocus::Clip(t, s) => Select(Focus::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Down) => match view.selected {
|
||||
ArrangerFocus::Mix => Select(Focus::Scene(0)),
|
||||
ArrangerFocus::Track(t) => Select(Focus::Clip(t, 0)),
|
||||
ArrangerFocus::Scene(s) => Select(Focus::Scene(s + 1)),
|
||||
ArrangerFocus::Clip(t, s) => Select(Focus::Clip(t, s + 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Left) => match view.selected {
|
||||
ArrangerFocus::Mix => return None,
|
||||
ArrangerFocus::Track(t) => Select(Focus::Track(t - 1)),
|
||||
ArrangerFocus::Scene(s) => return None,
|
||||
ArrangerFocus::Clip(t, s) => Select(Focus::Clip(t - 1, s)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Right) => match view.selected {
|
||||
ArrangerFocus::Mix => return None,
|
||||
ArrangerFocus::Track(t) => Select(Focus::Track(t + 1)),
|
||||
ArrangerFocus::Scene(s) => Select(Focus::Clip(0, s)),
|
||||
ArrangerFocus::Clip(t, s) => Select(Focus::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('+')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('=')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('_')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('-')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('`')) => { todo!("toggle view mode") },
|
||||
|
||||
key!(KeyCode::Char(',')) => match view.selected {
|
||||
ArrangerFocus::Mix => Zoom(0),
|
||||
ArrangerFocus::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('.')) => match view.selected {
|
||||
ArrangerFocus::Mix => Zoom(0),
|
||||
ArrangerFocus::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('<')) => match view.selected {
|
||||
ArrangerFocus::Mix => Zoom(0),
|
||||
ArrangerFocus::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('>')) => match view.selected {
|
||||
ArrangerFocus::Mix => Zoom(0),
|
||||
ArrangerFocus::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Enter) => match view.selected {
|
||||
ArrangerFocus::Mix => return None,
|
||||
ArrangerFocus::Track(t) => return None,
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Play(s))),
|
||||
ArrangerFocus::Clip(t, s) => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Delete) => match view.selected {
|
||||
ArrangerFocus::Mix => Edit(Model::Clear),
|
||||
ArrangerFocus::Track(t) => Edit(Model::Track(Track::Delete(t))),
|
||||
ArrangerFocus::Scene(s) => Edit(Model::Scene(Scene::Delete(s))),
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('c')) => Edit(Model::Clip(Clip::RandomColor)),
|
||||
|
||||
key!(KeyCode::Char('s')) => match view.selected {
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('g')) => match view.selected {
|
||||
ArrangerFocus::Clip(t, s) => Edit(Model::Clip(Clip::Get(t, s))),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(Ctrl-KeyCode::Char('a')) => Edit(Model::Scene(Scene::Add)),
|
||||
|
||||
key!(Ctrl-KeyCode::Char('t')) => Edit(Model::Track(Track::Add)),
|
||||
|
||||
key!(KeyCode::Char('l')) => Edit(Model::Clip(Clip::SetLoop(false))),
|
||||
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -81,8 +183,15 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangerViewCommand {
|
|||
delegate(cmd, Self::Editor, &mut view.sequencer.editor),
|
||||
Self::Transport(cmd) =>
|
||||
delegate(cmd, Self::Transport, &mut view.sequencer.transport),
|
||||
Self::Arrangement(cmd) =>
|
||||
delegate(cmd, Self::Arrangement, &mut view),
|
||||
Self::Zoom(zoom) => {
|
||||
todo!();
|
||||
},
|
||||
Self::Select(selected) => {
|
||||
view.selected = selected;
|
||||
},
|
||||
Self::Edit(command) => {
|
||||
return Ok(command.execute(&mut view.model)?.map(Self::Edit))
|
||||
},
|
||||
Self::EditPhrase(phrase) => {
|
||||
view.sequencer.editor.phrase = phrase.clone();
|
||||
view.focus(ArrangerViewFocus::PhraseEditor);
|
||||
|
|
@ -96,139 +205,8 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangerViewCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, ArrangerView<Tui>> for ArrangementEditorCommand {
|
||||
fn input_to_command (state: &ArrangerView<Tui>, input: &TuiInput) -> Option<Self> {
|
||||
use ArrangementEditorCommand as Cmd;
|
||||
use ArrangementCommand as Edit;
|
||||
use ArrangementEditorFocus as Focus;
|
||||
use ArrangementTrackCommand as Track;
|
||||
use ArrangementClipCommand as Clip;
|
||||
use ArrangementSceneCommand as Scene;
|
||||
Some(match input.event() {
|
||||
// FIXME: boundary conditions
|
||||
|
||||
key!(KeyCode::Up) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => return None,
|
||||
ArrangementEditorFocus::Track(t) => return None,
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Scene(s - 1)),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Down) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Select(Focus::Scene(0)),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Clip(t, 0)),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Scene(s + 1)),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s + 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Left) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => return None,
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Track(t - 1)),
|
||||
ArrangementEditorFocus::Scene(s) => return None,
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t - 1, s)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Right) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => return None,
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Track(t + 1)),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Clip(0, s)),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('+')) => Cmd::Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('=')) => Cmd::Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('_')) => Cmd::Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('-')) => Cmd::Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('`')) => { todo!("toggle view mode") },
|
||||
|
||||
key!(KeyCode::Char(',')) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('.')) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('<')) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('>')) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Enter) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => return None,
|
||||
ArrangementEditorFocus::Track(t) => return None,
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Play(s))),
|
||||
ArrangementEditorFocus::Clip(t, s) => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Delete) => match state.selected {
|
||||
ArrangementEditorFocus::Mix => Cmd::Edit(Edit::Clear),
|
||||
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Delete(t))),
|
||||
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Delete(s))),
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('c')) => Cmd::Edit(Edit::Clip(Clip::RandomColor)),
|
||||
|
||||
key!(KeyCode::Char('s')) => match state.selected {
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('g')) => match state.selected {
|
||||
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Get(t, s))),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(Ctrl-KeyCode::Char('a')) => Cmd::Edit(Edit::Scene(Scene::Add)),
|
||||
|
||||
key!(Ctrl-KeyCode::Char('t')) => Cmd::Edit(Edit::Track(Track::Add)),
|
||||
|
||||
key!(KeyCode::Char('l')) => Cmd::Edit(Edit::Clip(Clip::SetLoop(false))),
|
||||
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
|
||||
fn execute (self, view: &mut ArrangerView<E>) -> Perhaps<Self> {
|
||||
match self {
|
||||
Self::Zoom(zoom) => {
|
||||
todo!();
|
||||
},
|
||||
Self::Select(selected) => {
|
||||
view.selected = selected;
|
||||
},
|
||||
Self::Edit(command) => {
|
||||
return Ok(command.execute(&mut view.model)?.map(Self::Edit))
|
||||
},
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn phrase_next (&mut self) {
|
||||
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
|
||||
//if let ArrangerFocus::Clip(track, scene) = self.selected {
|
||||
//if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] {
|
||||
//let phrases = self.model.phrases.read().unwrap();
|
||||
//let index = phrases.index_of(&*phrase.read().unwrap());
|
||||
|
|
@ -241,7 +219,7 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
|
|||
//}
|
||||
//}
|
||||
//pub fn phrase_prev (&mut self) {
|
||||
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
|
||||
//if let ArrangerFocus::Clip(track, scene) = self.selected {
|
||||
//if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] {
|
||||
//let phrases = self.model.phrases.read().unwrap();
|
||||
//let index = phrases.index_of(&*phrase.read().unwrap());
|
||||
|
|
@ -255,7 +233,7 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
|
|||
//}
|
||||
|
||||
//pub fn phrase_get (&mut self) {
|
||||
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
|
||||
//if let ArrangerFocus::Clip(track, scene) = self.selected {
|
||||
//if let Some(phrase) = &self.model.scenes[scene].clips[track] {
|
||||
//let mut phrases = self.model.phrases.write().unwrap();
|
||||
//if let Some(index) = &*phrases.index_of(&*phrase.read().unwrap()) {
|
||||
|
|
@ -277,7 +255,7 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
|
|||
//}
|
||||
|
||||
//pub fn next_color (&self) -> ItemColor {
|
||||
//if let ArrangementEditorFocus::Clip(track, scene) = self.arrangement.selected {
|
||||
//if let ArrangerFocus::Clip(track, scene) = self.arrangement.selected {
|
||||
//let track_color = self.arrangement.model.tracks[track].color;
|
||||
//let scene_color = self.arrangement.model.scenes[scene].color;
|
||||
//track_color.mix(scene_color, 0.5).mix(ItemColor::random(), 0.25)
|
||||
|
|
@ -295,14 +273,14 @@ impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
|
|||
//.map(|scene|scene.clips[track_index] = None));
|
||||
//}
|
||||
//pub fn phrase_put (&mut self) {
|
||||
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
|
||||
//if let ArrangerFocus::Clip(track, scene) = self.selected {
|
||||
//self.model.scenes[scene].clips[track] = self.selected_phrase().clone();
|
||||
//}
|
||||
//}
|
||||
//pub fn selected_scene (&self) -> Option<&ArrangementScene> {
|
||||
//pub fn selected_scene (&self) -> Option<&ArrangerScene> {
|
||||
//self.selected.scene().map(|s|self.model.scenes.get(s)).flatten()
|
||||
//}
|
||||
//pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangementScene> {
|
||||
//pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> {
|
||||
//self.selected.scene().map(|s|self.model.scenes.get_mut(s)).flatten()
|
||||
//}
|
||||
//pub fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub enum ArrangerViewFocus {
|
|||
/// The transport (toolbar) is focused
|
||||
Transport,
|
||||
/// The arrangement (grid) is focused
|
||||
Arrangement,
|
||||
Arranger,
|
||||
/// The phrase list (pool) is focused
|
||||
PhrasePool,
|
||||
/// The phrase editor (sequencer) is focused
|
||||
|
|
@ -26,7 +26,7 @@ impl<E: Engine> FocusGrid for ArrangerView<E> {
|
|||
let focused = self.focused();
|
||||
if !self.entered {
|
||||
self.entered =
|
||||
focused == ArrangerViewFocus::Arrangement;
|
||||
focused == ArrangerViewFocus::Arranger;
|
||||
self.sequencer.editor.entered =
|
||||
focused == ArrangerViewFocus::PhraseEditor;
|
||||
self.sequencer.phrases.entered =
|
||||
|
|
@ -51,14 +51,14 @@ impl<E: Engine> FocusGrid for ArrangerView<E> {
|
|||
use ArrangerViewFocus::*;
|
||||
&[
|
||||
&[Transport, Transport],
|
||||
&[Arrangement, Arrangement],
|
||||
&[Arranger, Arranger],
|
||||
&[PhrasePool, PhraseEditor],
|
||||
]
|
||||
}
|
||||
fn update_focus (&mut self) {
|
||||
let focused = self.focused();
|
||||
self.focused =
|
||||
focused == ArrangerViewFocus::Arrangement;
|
||||
focused == ArrangerViewFocus::Arranger;
|
||||
self.sequencer.transport.focused =
|
||||
focused == ArrangerViewFocus::Transport;
|
||||
self.sequencer.phrases.focused =
|
||||
|
|
@ -70,7 +70,7 @@ impl<E: Engine> FocusGrid for ArrangerView<E> {
|
|||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
/// Represents the current user selection in the arranger
|
||||
pub enum ArrangementEditorFocus {
|
||||
pub enum ArrangerFocus {
|
||||
/// The whole mix is selected
|
||||
Mix,
|
||||
/// A track is selected.
|
||||
|
|
@ -82,11 +82,11 @@ pub enum ArrangementEditorFocus {
|
|||
}
|
||||
|
||||
/// Focus identification methods
|
||||
impl ArrangementEditorFocus {
|
||||
impl ArrangerFocus {
|
||||
pub fn description <E: Engine> (
|
||||
&self,
|
||||
tracks: &Vec<ArrangementTrack>,
|
||||
scenes: &Vec<ArrangementScene>,
|
||||
tracks: &Vec<ArrangerTrack>,
|
||||
scenes: &Vec<ArrangerScene>,
|
||||
) -> String {
|
||||
format!("Selected: {}", match self {
|
||||
Self::Mix => format!("Everything"),
|
||||
|
|
@ -120,7 +120,7 @@ impl ArrangementEditorFocus {
|
|||
match self { Self::Clip(_, _) => true, _ => false }
|
||||
}
|
||||
pub fn track (&self) -> Option<usize> {
|
||||
use ArrangementEditorFocus::*;
|
||||
use ArrangerFocus::*;
|
||||
match self {
|
||||
Clip(t, _) => Some(*t),
|
||||
Track(t) => Some(*t),
|
||||
|
|
@ -128,7 +128,7 @@ impl ArrangementEditorFocus {
|
|||
}
|
||||
}
|
||||
pub fn scene (&self) -> Option<usize> {
|
||||
use ArrangementEditorFocus::*;
|
||||
use ArrangerFocus::*;
|
||||
match self {
|
||||
Clip(_, s) => Some(*s),
|
||||
Scene(s) => Some(*s),
|
||||
|
|
@ -137,7 +137,7 @@ impl ArrangementEditorFocus {
|
|||
}
|
||||
}
|
||||
//pub fn track_next (&mut self, last_track: usize) {
|
||||
//use ArrangementEditorFocus::*;
|
||||
//use ArrangerFocus::*;
|
||||
//*self = match self {
|
||||
//Mix => Track(0),
|
||||
//Track(t) => Track(last_track.min(*t + 1)),
|
||||
|
|
@ -146,7 +146,7 @@ impl ArrangementEditorFocus {
|
|||
//}
|
||||
//}
|
||||
//pub fn track_prev (&mut self) {
|
||||
//use ArrangementEditorFocus::*;
|
||||
//use ArrangerFocus::*;
|
||||
//*self = match self {
|
||||
//Mix => Mix,
|
||||
//Scene(s) => Scene(*s),
|
||||
|
|
@ -155,7 +155,7 @@ impl ArrangementEditorFocus {
|
|||
//}
|
||||
//}
|
||||
//pub fn scene_next (&mut self, last_scene: usize) {
|
||||
//use ArrangementEditorFocus::*;
|
||||
//use ArrangerFocus::*;
|
||||
//*self = match self {
|
||||
//Mix => Scene(0),
|
||||
//Track(t) => Clip(*t, 0),
|
||||
|
|
@ -164,7 +164,7 @@ impl ArrangementEditorFocus {
|
|||
//}
|
||||
//}
|
||||
//pub fn scene_prev (&mut self) {
|
||||
//use ArrangementEditorFocus::*;
|
||||
//use ArrangerFocus::*;
|
||||
//*self = match self {
|
||||
//Mix => Mix,
|
||||
//Track(t) => Track(*t),
|
||||
|
|
|
|||
|
|
@ -158,13 +158,13 @@ pub fn arranger_content_horizontal (
|
|||
}),
|
||||
// scenes
|
||||
CustomWidget::new(|_|{todo!()}, |to: &mut TuiOutput|{
|
||||
let selected = &view.selected;
|
||||
let scenes = &view.model.scenes;
|
||||
let area = to.area();
|
||||
let [x, y, _, height] = to.area();
|
||||
let mut x2 = 0;
|
||||
let [x, y, _, height] = area;
|
||||
Ok(for (scene_index, scene) in scenes.iter().enumerate() {
|
||||
let active_scene = selected.scene() == Some(scene_index);
|
||||
Ok(for (scene_index, scene) in view.model.read().unwrap()
|
||||
.arrangement.read().unwrap().
|
||||
scenes.iter().enumerate()
|
||||
{
|
||||
let active_scene = view.selected.scene() == Some(scene_index);
|
||||
let sep = Some(if active_scene {
|
||||
Style::default().yellow().not_dim()
|
||||
} else {
|
||||
|
|
@ -177,7 +177,7 @@ pub fn arranger_content_horizontal (
|
|||
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 = selected.track() == Some(i);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
fn track_widths (tracks: &[ArrangementTrack]) -> Vec<(usize, usize)> {
|
||||
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||
let mut widths = vec![];
|
||||
let mut total = 0;
|
||||
for track in tracks.iter() {
|
||||
|
|
@ -16,16 +16,17 @@ pub fn arranger_content_vertical (
|
|||
view: &ArrangerView<Tui>,
|
||||
factor: usize
|
||||
) -> impl Widget<Engine = Tui> + use<'_> {
|
||||
let tracks = view.model.tracks.as_slice();
|
||||
let scenes = view.model.scenes.as_slice();
|
||||
let model = view.model.read().unwrap();
|
||||
let clock = model.transport.read().unwrap().clock;
|
||||
let tracks = model.arrangement.read().unwrap().tracks.as_slice();
|
||||
let scenes = model.arrangement.read().unwrap().scenes.as_slice();
|
||||
let cols = track_widths(tracks);
|
||||
let rows = ArrangementScene::ppqs(scenes, factor);
|
||||
let rows = ArrangerScene::ppqs(scenes, factor);
|
||||
let bg = view.color;
|
||||
let clip_bg = TuiTheme::border_bg();
|
||||
let sep_fg = TuiTheme::separator_fg(false);
|
||||
let header_h = 3u16;//5u16;
|
||||
let scenes_w = 3 + ArrangementScene::longest_name(scenes) as u16; // x of 1st track
|
||||
let clock = &view.sequencer.transport.model.clock;
|
||||
let scenes_w = 3 + ArrangerScene::longest_name(scenes) as u16; // x of 1st track
|
||||
let arrangement = Layers::new(move |add|{
|
||||
let rows: &[(usize, usize)] = rows.as_ref();
|
||||
let cols: &[(usize, usize)] = cols.as_ref();
|
||||
|
|
@ -98,12 +99,12 @@ pub fn arranger_content_vertical (
|
|||
.push_x(scenes_w)
|
||||
});
|
||||
// scene titles
|
||||
let scene_name = |scene: &ArrangementScene, playing: bool, height|row!(
|
||||
let scene_name = |scene: &ArrangerScene, playing: bool, height|row!(
|
||||
if playing { "▶ " } else { " " },
|
||||
TuiStyle::bold(scene.name.read().unwrap().as_str(), true),
|
||||
).fixed_xy(scenes_w, height);
|
||||
// scene clips
|
||||
let scene_clip = |scene: &ArrangementScene, track: usize, w: u16, h: u16|Layers::new(move |add|{
|
||||
let scene_clip = |scene: &ArrangerScene, track: usize, w: u16, h: u16|Layers::new(move |add|{
|
||||
let mut bg = clip_bg;
|
||||
match (tracks.get(track), scene.clips.get(track)) {
|
||||
(Some(track), Some(Some(phrase))) => {
|
||||
|
|
@ -164,16 +165,16 @@ pub fn arranger_content_vertical (
|
|||
let mut scene_area: Option<[u16;4]> = None;
|
||||
let mut clip_area: Option<[u16;4]> = None;
|
||||
let area = match selected {
|
||||
ArrangementEditorFocus::Mix => area,
|
||||
ArrangementEditorFocus::Track(t) => {
|
||||
ArrangerFocus::Mix => area,
|
||||
ArrangerFocus::Track(t) => {
|
||||
track_area = Some(get_track_area(t));
|
||||
area
|
||||
},
|
||||
ArrangementEditorFocus::Scene(s) => {
|
||||
ArrangerFocus::Scene(s) => {
|
||||
scene_area = Some(get_scene_area(s));
|
||||
area
|
||||
},
|
||||
ArrangementEditorFocus::Clip(t, s) => {
|
||||
ArrangerFocus::Clip(t, s) => {
|
||||
track_area = Some(get_track_area(t));
|
||||
scene_area = Some(get_scene_area(s));
|
||||
clip_area = Some(get_clip_area(t, s));
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ pub type SequencerApp = AppContainer<
|
|||
SequencerStatusBar
|
||||
>;
|
||||
|
||||
impl SequencerApp {
|
||||
pub fn run <'a> (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerApp {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
let clock = Arc::new(Clock::from(Instant::default()));
|
||||
|
||||
let transport = Arc::new(RwLock::new(tek_api::Transport {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ pub type TransportApp = AppContainer<
|
|||
TransportStatusBar
|
||||
>;
|
||||
|
||||
impl TransportApp {
|
||||
pub fn run <'a> (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
impl TryFrom<&Arc<RwLock<JackClient>>> for TransportApp {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
let model = Arc::new(RwLock::new(Transport {
|
||||
metronome: false,
|
||||
transport: jack.read().unwrap().transport(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue