mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
626 lines
24 KiB
Rust
626 lines
24 KiB
Rust
use crate::*;
|
|
|
|
#[derive(Default, Debug)] pub struct Arrangement {
|
|
/// Project name.
|
|
pub name: Arc<str>,
|
|
/// Base color.
|
|
pub color: ItemTheme,
|
|
/// JACK client handle.
|
|
pub jack: Jack<'static>,
|
|
/// FIXME a render of the project arrangement, redrawn on update.
|
|
/// TODO rename to "render_cache" or smth
|
|
pub arranger: Arc<RwLock<Buffer>>,
|
|
/// Display size
|
|
pub size: Measure<TuiOut>,
|
|
/// Display size of clips area
|
|
pub size_inner: Measure<TuiOut>,
|
|
/// Source of time
|
|
#[cfg(feature = "clock")] pub clock: Clock,
|
|
/// Allows one MIDI clip to be edited
|
|
#[cfg(feature = "editor")] pub editor: Option<MidiEditor>,
|
|
/// List of global midi inputs
|
|
#[cfg(feature = "port")] pub midi_ins: Vec<MidiInput>,
|
|
/// List of global midi outputs
|
|
#[cfg(feature = "port")] pub midi_outs: Vec<MidiOutput>,
|
|
/// List of global audio inputs
|
|
#[cfg(feature = "port")] pub audio_ins: Vec<AudioInput>,
|
|
/// List of global audio outputs
|
|
#[cfg(feature = "port")] pub audio_outs: Vec<AudioOutput>,
|
|
/// Selected UI element
|
|
#[cfg(feature = "select")] pub selection: Selection,
|
|
/// Last track number (to avoid duplicate port names)
|
|
#[cfg(feature = "track")] pub track_last: usize,
|
|
/// List of tracks
|
|
#[cfg(feature = "track")] pub tracks: Vec<Track>,
|
|
/// Scroll offset of tracks
|
|
#[cfg(feature = "track")] pub track_scroll: usize,
|
|
/// List of scenes
|
|
#[cfg(feature = "scene")] pub scenes: Vec<Scene>,
|
|
/// Scroll offset of scenes
|
|
#[cfg(feature = "scene")] pub scene_scroll: usize,
|
|
}
|
|
impl HasJack<'static> for Arrangement {
|
|
fn jack (&self) -> &Jack<'static> {
|
|
&self.jack
|
|
}
|
|
}
|
|
has!(Jack<'static>: |self: Arrangement|self.jack);
|
|
has!(Measure<TuiOut>: |self: Arrangement|self.size);
|
|
#[cfg(feature = "editor")] has!(Option<MidiEditor>: |self: Arrangement|self.editor);
|
|
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
|
|
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
|
|
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
|
|
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
|
|
#[cfg(all(feature = "select", feature = "track"))] has!(Vec<Track>: |self: Arrangement|self.tracks);
|
|
#[cfg(all(feature = "select", feature = "track"))] maybe_has!(Track: |self: Arrangement|
|
|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get(self).get(index)).flatten() };
|
|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get_mut(self).get_mut(index)).flatten() });
|
|
#[cfg(all(feature = "select", feature = "scene"))] has!(Vec<Scene>: |self: Arrangement|self.scenes);
|
|
#[cfg(all(feature = "select", feature = "scene"))] maybe_has!(Scene: |self: Arrangement|
|
|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get(self).get(index)).flatten() };
|
|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get_mut(self).get_mut(index)).flatten() });
|
|
|
|
#[cfg(feature = "select")]
|
|
impl Arrangement {
|
|
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
|
|
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<Scene> { todo!() }
|
|
#[cfg(feature = "track")] fn selected_track (&self) -> Option<Track> { todo!() }
|
|
#[cfg(feature = "port")] fn selected_midi_in (&self) -> Option<MidiInput> { todo!() }
|
|
#[cfg(feature = "port")] fn selected_midi_out (&self) -> Option<MidiOutput> { todo!() }
|
|
fn selected_device (&self) -> Option<Device> { todo!() }
|
|
fn unselect (&self) -> Selection {
|
|
Selection::Nothing
|
|
}
|
|
}
|
|
|
|
impl Arrangement {
|
|
/// Width of display
|
|
pub fn w (&self) -> u16 {
|
|
self.size.w() as u16
|
|
}
|
|
/// Width allocated for sidebar.
|
|
pub fn w_sidebar (&self, is_editing: bool) -> u16 {
|
|
self.w() / if is_editing { 16 } else { 8 } as u16
|
|
}
|
|
/// Width available to display tracks.
|
|
pub fn w_tracks_area (&self, is_editing: bool) -> u16 {
|
|
self.w().saturating_sub(self.w_sidebar(is_editing))
|
|
}
|
|
/// Height of display
|
|
pub fn h (&self) -> u16 {
|
|
self.size.h() as u16
|
|
}
|
|
/// Height taken by visible device slots.
|
|
pub fn h_devices (&self) -> u16 {
|
|
2
|
|
//1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "track")]
|
|
impl TracksView for Arrangement {}
|
|
|
|
#[cfg(feature = "track")]
|
|
impl Arrangement {
|
|
/// Get the active track
|
|
pub fn get_track (&self) -> Option<&Track> {
|
|
let index = self.selection().track()?;
|
|
Has::<Vec<Track>>::get(self).get(index)
|
|
}
|
|
/// Get a mutable reference to the active track
|
|
pub fn get_track_mut (&mut self) -> Option<&mut Track> {
|
|
let index = self.selection().track()?;
|
|
Has::<Vec<Track>>::get_mut(self).get_mut(index)
|
|
}
|
|
/// Add multiple tracks
|
|
pub fn tracks_add (
|
|
&mut self,
|
|
count: usize, width: Option<usize>,
|
|
mins: &[Connect], mouts: &[Connect],
|
|
) -> Usually<()> {
|
|
let track_color_1 = ItemColor::random();
|
|
let track_color_2 = ItemColor::random();
|
|
for i in 0..count {
|
|
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
|
|
let track = self.track_add(None, Some(color), mins, mouts)?.1;
|
|
if let Some(width) = width {
|
|
track.width = width;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
/// Add a track
|
|
pub fn track_add (
|
|
&mut self,
|
|
name: Option<&str>, color: Option<ItemTheme>,
|
|
mins: &[Connect], mouts: &[Connect],
|
|
) -> Usually<(usize, &mut Track)> {
|
|
let name: Arc<str> = name.map_or_else(
|
|
||format!("trk{:02}", self.track_last).into(),
|
|
|x|x.to_string().into()
|
|
);
|
|
self.track_last += 1;
|
|
let track = Track {
|
|
width: (name.len() + 2).max(12),
|
|
color: color.unwrap_or_else(ItemTheme::random),
|
|
sequencer: Sequencer::new(
|
|
&format!("{name}"),
|
|
self.jack(),
|
|
Some(self.clock()),
|
|
None,
|
|
mins,
|
|
mouts
|
|
)?,
|
|
name,
|
|
..Default::default()
|
|
};
|
|
self.tracks_mut().push(track);
|
|
let len = self.tracks().len();
|
|
let index = len - 1;
|
|
for scene in self.scenes_mut().iter_mut() {
|
|
while scene.clips.len() < len {
|
|
scene.clips.push(None);
|
|
}
|
|
}
|
|
Ok((index, &mut self.tracks_mut()[index]))
|
|
}
|
|
|
|
pub fn view_inputs (&self, _theme: ItemTheme) -> impl Content<TuiOut> + '_ {
|
|
Bsp::s(
|
|
Fixed::Y(1, self.view_inputs_header()),
|
|
Thunk::new(|to: &mut TuiOut|{
|
|
for (index, port) in self.midi_ins().iter().enumerate() {
|
|
to.place(&Push::X(index as u16 * 10, Fixed::Y(1, self.view_inputs_row(port))))
|
|
}
|
|
})
|
|
)
|
|
}
|
|
|
|
fn view_inputs_header (&self) -> impl Content<TuiOut> + '_ {
|
|
Bsp::e(Fixed::X(20, Align::w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false))),
|
|
Bsp::w(Fixed::X(4, button_2("I", "+", false)), Thunk::new(move|to: &mut TuiOut|for (_index, track, x1, _x2) in self.tracks_with_sizes() {
|
|
#[cfg(feature = "track")]
|
|
to.place(&Push::X(x1 as u16, Tui::bg(track.color.dark.rgb, Align::w(Fixed::X(track.width as u16, row!(
|
|
Either::new(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "),
|
|
Either::new(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "),
|
|
Either::new(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "),
|
|
))))))
|
|
})))
|
|
}
|
|
|
|
fn view_inputs_row (&self, port: &MidiInput) -> impl Content<TuiOut> {
|
|
Bsp::e(Fixed::X(20, Align::w(Bsp::e(" ● ", Tui::bold(true, Tui::fg(Rgb(255,255,255), port.port_name()))))),
|
|
Bsp::w(Fixed::X(4, ()), Thunk::new(move|to: &mut TuiOut|for (_index, track, _x1, _x2) in self.tracks_with_sizes() {
|
|
#[cfg(feature = "track")]
|
|
to.place(&Tui::bg(track.color.darker.rgb, Align::w(Fixed::X(track.width as u16, row!(
|
|
Either::new(track.sequencer.monitoring, Tui::fg(Green, " ● "), " · "),
|
|
Either::new(track.sequencer.recording, Tui::fg(Red, " ● "), " · "),
|
|
Either::new(track.sequencer.overdub, Tui::fg(Yellow, " ● "), " · "),
|
|
)))))
|
|
})))
|
|
}
|
|
|
|
pub fn view_outputs (&self, theme: ItemTheme) -> impl Content<TuiOut> {
|
|
let mut h = 1;
|
|
for output in self.midi_outs().iter() {
|
|
h += 1 + output.connections.len();
|
|
}
|
|
let h = h as u16;
|
|
let list = Bsp::s(
|
|
Fixed::Y(1, Fill::X(Align::w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))),
|
|
Fixed::Y(h - 1, Fill::XY(Align::nw(Thunk::new(|to: &mut TuiOut|{
|
|
for (_index, port) in self.midi_outs().iter().enumerate() {
|
|
to.place(&Fixed::Y(1,Fill::X(Bsp::e(
|
|
Align::w(Bsp::e(" ● ", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.port_name())))),
|
|
Fill::X(Align::e(format!("{}/{} ",
|
|
port.port().get_connections().len(),
|
|
port.connections.len())))))));
|
|
for (index, conn) in port.connections.iter().enumerate() {
|
|
to.place(&Fixed::Y(1, Fill::X(Align::w(format!(" c{index:02}{}", conn.info())))));
|
|
}
|
|
}
|
|
})))));
|
|
Fixed::Y(h, view_track_row_section(theme, list, button_2("O", "+", false),
|
|
Tui::bg(theme.darker.rgb, Align::w(Fill::X(
|
|
Thunk::new(|to: &mut TuiOut|{
|
|
for (index, track, _x1, _x2) in self.tracks_with_sizes() {
|
|
to.place(&Fixed::X(track_width(index, track),
|
|
Thunk::new(|to: &mut TuiOut|{
|
|
to.place(&Fixed::Y(1, Align::w(Bsp::e(
|
|
Either::new(true, Tui::fg(Green, "play "), "play "),
|
|
Either::new(false, Tui::fg(Yellow, "solo "), "solo "),
|
|
))));
|
|
for (_index, port) in self.midi_outs().iter().enumerate() {
|
|
to.place(&Fixed::Y(1, Align::w(Bsp::e(
|
|
Either::new(true, Tui::fg(Green, " ● "), " · "),
|
|
Either::new(false, Tui::fg(Yellow, " ● "), " · "),
|
|
))));
|
|
for (_index, _conn) in port.connections.iter().enumerate() {
|
|
to.place(&Fixed::Y(1, Fill::X("")));
|
|
}
|
|
}})))}}))))))
|
|
}
|
|
|
|
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Content<TuiOut> {
|
|
let mut h = 2u16;
|
|
for track in self.tracks().iter() {
|
|
h = h.max(track.devices.len() as u16 * 2);
|
|
}
|
|
view_track_row_section(theme,
|
|
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
|
|
button_2("D", "+", false),
|
|
Thunk::new(move|to: &mut TuiOut|for (index, track, _x1, _x2) in self.tracks_with_sizes() {
|
|
to.place(&Fixed::XY(track_width(index, track), h + 1,
|
|
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h,
|
|
|_, _index|Fixed::XY(track.width as u16, 2,
|
|
Tui::fg_bg(
|
|
ItemTheme::G[32].lightest.rgb,
|
|
ItemTheme::G[32].dark.rgb,
|
|
Align::nw(format!(" · {}", "--")))))))));
|
|
}))
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(feature = "track")]
|
|
pub fn view_track_row_section (
|
|
_theme: ItemTheme,
|
|
button: impl Content<TuiOut>,
|
|
button_add: impl Content<TuiOut>,
|
|
content: impl Content<TuiOut>,
|
|
) -> impl Content<TuiOut> {
|
|
Bsp::w(Fill::Y(Fixed::X(4, Align::nw(button_add))),
|
|
Bsp::e(Fixed::X(20, Fill::Y(Align::nw(button))), Fill::XY(Align::c(content))))
|
|
}
|
|
|
|
#[cfg(feature = "scene")]
|
|
impl Arrangement {
|
|
/// Get the active scene
|
|
pub fn get_scene (&self) -> Option<&Scene> {
|
|
let index = self.selection().scene()?;
|
|
Has::<Vec<Scene>>::get(self).get(index)
|
|
}
|
|
/// Get a mutable reference to the active scene
|
|
pub fn get_scene_mut (&mut self) -> Option<&mut Scene> {
|
|
let index = self.selection().scene()?;
|
|
Has::<Vec<Scene>>::get_mut(self).get_mut(index)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "scene")]
|
|
impl ScenesView for Arrangement {
|
|
fn h_scenes (&self) -> u16 {
|
|
(self.height() as u16).saturating_sub(20)
|
|
}
|
|
fn w_side (&self) -> u16 {
|
|
(self.width() as u16 * 2 / 10).max(20)
|
|
}
|
|
fn w_mid (&self) -> u16 {
|
|
(self.width() as u16).saturating_sub(2 * self.w_side()).max(40)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "clip")]
|
|
impl Arrangement {
|
|
/// Get the active clip
|
|
pub fn get_clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
|
self.get_scene()?.clips.get(self.selection().track()?)?.clone()
|
|
}
|
|
/// Put a clip in a slot
|
|
pub fn clip_put (
|
|
&mut self, track: usize, scene: usize, clip: Option<Arc<RwLock<MidiClip>>>
|
|
) -> Option<Arc<RwLock<MidiClip>>> {
|
|
let old = self.scenes[scene].clips[track].clone();
|
|
self.scenes[scene].clips[track] = clip;
|
|
old
|
|
}
|
|
/// Change the color of a clip, returning the previous one
|
|
pub fn clip_set_color (&self, track: usize, scene: usize, color: ItemTheme)
|
|
-> Option<ItemTheme>
|
|
{
|
|
self.scenes[scene].clips[track].as_ref().map(|clip|{
|
|
let mut clip = clip.write().unwrap();
|
|
let old = clip.color.clone();
|
|
clip.color = color.clone();
|
|
panic!("{color:?} {old:?}");
|
|
old
|
|
})
|
|
}
|
|
/// Toggle looping for the active clip
|
|
pub fn toggle_loop (&mut self) {
|
|
if let Some(clip) = self.get_clip() {
|
|
clip.write().unwrap().toggle_loop()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "sampler")]
|
|
impl Arrangement {
|
|
/// Get the first sampler of the active track
|
|
pub fn sampler (&self) -> Option<&Sampler> {
|
|
self.get_track()?.sampler(0)
|
|
}
|
|
/// Get the first sampler of the active track
|
|
pub fn sampler_mut (&mut self) -> Option<&mut Sampler> {
|
|
self.get_track_mut()?.sampler_mut(0)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn wrap (bg: Color, fg: Color, content: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
|
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("▐")));
|
|
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("▌")));
|
|
Bsp::e(left, Bsp::w(right, Tui::fg_bg(fg, bg, content)))
|
|
}
|
|
|
|
pub trait HasClipsSize {
|
|
fn clips_size (&self) -> &Measure<TuiOut>;
|
|
}
|
|
|
|
impl HasClipsSize for Arrangement {
|
|
fn clips_size (&self) -> &Measure<TuiOut> { &self.size_inner }
|
|
}
|
|
|
|
pub trait HasWidth {
|
|
const MIN_WIDTH: usize;
|
|
/// Increment track width.
|
|
fn width_inc (&mut self);
|
|
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
|
|
fn width_dec (&mut self);
|
|
}
|
|
|
|
//def_command!(ArrangementCommand: |arranger: Arrangement| {
|
|
|
|
//Home => { arranger.editor = None; Ok(None) },
|
|
|
|
//Edit => {
|
|
//let selection = arranger.selection().clone();
|
|
//arranger.editor = if arranger.editor.is_some() {
|
|
//None
|
|
//} else {
|
|
//match selection {
|
|
//Selection::TrackClip { track, scene } => {
|
|
//let clip = &mut arranger.scenes_mut()[scene].clips[track];
|
|
//if clip.is_none() {
|
|
////app.clip_auto_create();
|
|
//*clip = Some(Arc::new(RwLock::new(MidiClip::new(
|
|
//&format!("t{track:02}s{scene:02}"),
|
|
//false, 384, None, Some(ItemTheme::random())
|
|
//))));
|
|
//}
|
|
//clip.as_ref().map(|c|c.into())
|
|
//}
|
|
//_ => {
|
|
//None
|
|
//}
|
|
//}
|
|
//};
|
|
//if let Some(editor) = arranger.editor.as_mut() {
|
|
//if let Some(clip) = editor.clip() {
|
|
//let length = clip.read().unwrap().length.max(1);
|
|
//let width = arranger.size_inner.w().saturating_sub(20).max(1);
|
|
//editor.set_time_zoom(length / width);
|
|
//editor.redraw();
|
|
//}
|
|
//}
|
|
//Ok(None)
|
|
//},
|
|
|
|
////// Set the selection
|
|
//Select { selection: Selection } => { *arranger.selection_mut() = *selection; Ok(None) },
|
|
|
|
////// Launch the selected clip or scene
|
|
//Launch => {
|
|
//match *arranger.selection() {
|
|
//Selection::Track(t) => {
|
|
//arranger.tracks[t].sequencer.enqueue_next(None)
|
|
//},
|
|
//Selection::TrackClip { track, scene } => {
|
|
//arranger.tracks[track].sequencer.enqueue_next(arranger.scenes[scene].clips[track].as_ref())
|
|
//},
|
|
//Selection::Scene(s) => {
|
|
//for t in 0..arranger.tracks.len() {
|
|
//arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())
|
|
//}
|
|
//},
|
|
//_ => {}
|
|
//};
|
|
//Ok(None)
|
|
//},
|
|
|
|
////// Set the color of the selected entity
|
|
//SetColor { palette: Option<ItemTheme> } => {
|
|
//let mut palette = palette.unwrap_or_else(||ItemTheme::random());
|
|
//let selection = *arranger.selection();
|
|
//Ok(Some(Self::SetColor { palette: Some(match selection {
|
|
//Selection::Mix => {
|
|
//std::mem::swap(&mut palette, &mut arranger.color);
|
|
//palette
|
|
//},
|
|
//Selection::Scene(s) => {
|
|
//std::mem::swap(&mut palette, &mut arranger.scenes[s].color);
|
|
//palette
|
|
//}
|
|
//Selection::Track(t) => {
|
|
//std::mem::swap(&mut palette, &mut arranger.tracks[t].color);
|
|
//palette
|
|
//}
|
|
//Selection::TrackClip { track, scene } => {
|
|
//if let Some(ref clip) = arranger.scenes[scene].clips[track] {
|
|
//let mut clip = clip.write().unwrap();
|
|
//std::mem::swap(&mut palette, &mut clip.color);
|
|
//palette
|
|
//} else {
|
|
//return Ok(None)
|
|
//}
|
|
//},
|
|
//_ => todo!()
|
|
//}) }))
|
|
//},
|
|
|
|
//Track { track: TrackCommand } => { todo!("delegate") },
|
|
|
|
//TrackAdd => {
|
|
//let index = arranger.track_add(None, None, &[], &[])?.0;
|
|
//*arranger.selection_mut() = match arranger.selection() {
|
|
//Selection::Track(_) => Selection::Track(index),
|
|
//Selection::TrackClip { track: _, scene } => Selection::TrackClip {
|
|
//track: index, scene: *scene
|
|
//},
|
|
//_ => *arranger.selection()
|
|
//};
|
|
//Ok(Some(Self::TrackDelete { index }))
|
|
//},
|
|
|
|
//TrackSwap { index: usize, other: usize } => {
|
|
//let index = *index;
|
|
//let other = *other;
|
|
//Ok(Some(Self::TrackSwap { index, other }))
|
|
//},
|
|
|
|
//TrackDelete { index: usize } => {
|
|
//let index = *index;
|
|
//let exists = arranger.tracks().get(index).is_some();
|
|
//if exists {
|
|
//let track = arranger.tracks_mut().remove(index);
|
|
//let Track { sequencer: Sequencer { midi_ins, midi_outs, .. }, .. } = track;
|
|
//for port in midi_ins.into_iter() {
|
|
//port.close()?;
|
|
//}
|
|
//for port in midi_outs.into_iter() {
|
|
//port.close()?;
|
|
//}
|
|
//for scene in arranger.scenes_mut().iter_mut() {
|
|
//scene.clips.remove(index);
|
|
//}
|
|
//}
|
|
//Ok(None)
|
|
////TODO:Ok(Some(Self::TrackAdd ( index, track: Some(deleted_track) })
|
|
//},
|
|
|
|
//MidiIn { input: MidiInputCommand } => {
|
|
//todo!("delegate"); Ok(None)
|
|
//},
|
|
|
|
//MidiInAdd => {
|
|
//arranger.midi_in_add()?;
|
|
//Ok(None)
|
|
//},
|
|
|
|
//MidiOut { output: MidiOutputCommand } => {
|
|
//todo!("delegate");
|
|
//Ok(None)
|
|
//},
|
|
|
|
//MidiOutAdd => {
|
|
//arranger.midi_out_add()?;
|
|
//Ok(None)
|
|
//},
|
|
|
|
//Device { command: DeviceCommand } => {
|
|
//todo!("delegate");
|
|
//Ok(None)
|
|
//},
|
|
|
|
//DeviceAdd { index: usize } => {
|
|
//todo!("delegate");
|
|
//Ok(None)
|
|
//},
|
|
|
|
//Scene { scene: SceneCommand } => {
|
|
//todo!("delegate");
|
|
//Ok(None)
|
|
//},
|
|
|
|
//OutputAdd => {
|
|
//arranger.midi_outs.push(MidiOutput::new(
|
|
//arranger.jack(),
|
|
//&format!("/M{}", arranger.midi_outs.len() + 1),
|
|
//&[]
|
|
//)?);
|
|
//Ok(None)
|
|
//},
|
|
|
|
//InputAdd => {
|
|
//arranger.midi_ins.push(MidiInput::new(
|
|
//arranger.jack(),
|
|
//&format!("M{}/", arranger.midi_ins.len() + 1),
|
|
//&[]
|
|
//)?);
|
|
//Ok(None)
|
|
//},
|
|
|
|
//SceneAdd => {
|
|
//let index = arranger.scene_add(None, None)?.0;
|
|
//*arranger.selection_mut() = match arranger.selection() {
|
|
//Selection::Scene(_) => Selection::Scene(index),
|
|
//Selection::TrackClip { track, scene } => Selection::TrackClip {
|
|
//track: *track,
|
|
//scene: index
|
|
//},
|
|
//_ => *arranger.selection()
|
|
//};
|
|
//Ok(None) // TODO
|
|
//},
|
|
|
|
//SceneSwap { index: usize, other: usize } => {
|
|
//let index = *index;
|
|
//let other = *other;
|
|
//Ok(Some(Self::SceneSwap { index, other }))
|
|
//},
|
|
|
|
//SceneDelete { index: usize } => {
|
|
//let index = *index;
|
|
//let scenes = arranger.scenes_mut();
|
|
//Ok(if scenes.get(index).is_some() {
|
|
//let _scene = scenes.remove(index);
|
|
//None
|
|
//} else {
|
|
//None
|
|
//})
|
|
//},
|
|
|
|
//SceneLaunch { index: usize } => {
|
|
//let index = *index;
|
|
//for track in 0..arranger.tracks.len() {
|
|
//let clip = arranger.scenes[index].clips[track].as_ref();
|
|
//arranger.tracks[track].sequencer.enqueue_next(clip);
|
|
//}
|
|
//Ok(None)
|
|
//},
|
|
|
|
//Clip { scene: ClipCommand } => {
|
|
//todo!("delegate")
|
|
//},
|
|
|
|
//ClipGet { a: usize, b: usize } => {
|
|
////(Get [a: usize, b: usize] cmd_todo!("\n\rtodo: clip: get: {a} {b}"))
|
|
////("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap())))
|
|
//todo!()
|
|
//},
|
|
|
|
//ClipPut { a: usize, b: usize } => {
|
|
////(Put [t: usize, s: usize, c: MaybeClip]
|
|
////Some(Self::Put(t, s, arranger.clip_put(t, s, c))))
|
|
////("put" [a: usize, b: usize, c: MaybeClip] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap())))
|
|
//todo!()
|
|
//},
|
|
|
|
//ClipDel { a: usize, b: usize } => {
|
|
////("delete" [a: usize, b: usize] Some(Self::Put(a.unwrap(), b.unwrap(), None))))
|
|
//todo!()
|
|
//},
|
|
|
|
//ClipEnqueue { a: usize, b: usize } => {
|
|
////(Enqueue [t: usize, s: usize]
|
|
////cmd!(arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())))
|
|
////("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap())))
|
|
//todo!()
|
|
//},
|
|
|
|
//ClipSwap { a: usize, b: usize }=> {
|
|
////(Edit [clip: MaybeClip] cmd_todo!("\n\rtodo: clip: edit: {clip:?}"))
|
|
////("edit" [a: MaybeClip] Some(Self::Edit(a.unwrap())))
|
|
//todo!()
|
|
//},
|
|
|
|
//});
|