mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: remove unused deps
This commit is contained in:
parent
ceaaeb1fc7
commit
af2e237b94
3 changed files with 518 additions and 576 deletions
301
.old/tek.rs.old
301
.old/tek.rs.old
|
|
@ -1520,3 +1520,304 @@
|
||||||
//self.perf.update(t0, scope);
|
//self.perf.update(t0, scope);
|
||||||
//return Control::Continue
|
//return Control::Continue
|
||||||
//});
|
//});
|
||||||
|
//fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
|
||||||
|
//self.devices.get(i).map(|d|d.state.write().unwrap())
|
||||||
|
//}
|
||||||
|
//pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
|
||||||
|
//self.get_device_mut(self.device)
|
||||||
|
//}
|
||||||
|
///// Add a device to the end of the chain.
|
||||||
|
//pub fn append_device (&mut self, device: JackDevice<E>) -> Usually<&mut JackDevice<E>> {
|
||||||
|
//self.devices.push(device);
|
||||||
|
//let index = self.devices.len() - 1;
|
||||||
|
//Ok(&mut self.devices[index])
|
||||||
|
//}
|
||||||
|
//pub fn add_device (&mut self, device: JackDevice<E>) {
|
||||||
|
//self.devices.push(device);
|
||||||
|
//}
|
||||||
|
//pub fn connect_first_device (&self) -> Usually<()> {
|
||||||
|
//if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) {
|
||||||
|
//device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?;
|
||||||
|
//}
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
//pub fn connect_last_device (&self, app: &Track) -> Usually<()> {
|
||||||
|
//Ok(match self.devices.get(self.devices.len().saturating_sub(1)) {
|
||||||
|
//Some(device) => {
|
||||||
|
//app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?;
|
||||||
|
//app.audio_out(1).map(|right|device.connect_audio_out(1, &right)).transpose()?;
|
||||||
|
//()
|
||||||
|
//},
|
||||||
|
//None => ()
|
||||||
|
//})
|
||||||
|
//}
|
||||||
|
use crate::*;
|
||||||
|
impl MixerTrack {
|
||||||
|
pub fn new (name: &str) -> Usually<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
name: name.to_string().into(),
|
||||||
|
audio_ins: vec![],
|
||||||
|
audio_outs: vec![],
|
||||||
|
devices: vec![],
|
||||||
|
//ports: JackPorts::default(),
|
||||||
|
//devices: vec![],
|
||||||
|
//device: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TrackView<'a> {
|
||||||
|
pub chain: Option<&'a MixerTrack>,
|
||||||
|
pub direction: Direction,
|
||||||
|
pub focused: bool,
|
||||||
|
pub entered: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Content<TuiOut> for TrackView<'a> {
|
||||||
|
fn render (&self, to: &mut TuiOut) {
|
||||||
|
todo!();
|
||||||
|
//let mut area = to.area();
|
||||||
|
//if let Some(chain) = self.chain {
|
||||||
|
//match self.direction {
|
||||||
|
//Direction::Down => area.width = area.width.min(40),
|
||||||
|
//Direction::Right => area.width = area.width.min(10),
|
||||||
|
//_ => { unimplemented!() },
|
||||||
|
//}
|
||||||
|
//to.fill_bg(to.area(), Nord::bg_lo(self.focused, self.entered));
|
||||||
|
//let mut split = Stack::new(self.direction);
|
||||||
|
//for device in chain.devices.as_slice().iter() {
|
||||||
|
//split = split.add_ref(device);
|
||||||
|
//}
|
||||||
|
//let (area, areas) = split.render_areas(to)?;
|
||||||
|
//if self.focused && self.entered && areas.len() > 0 {
|
||||||
|
//Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?;
|
||||||
|
//}
|
||||||
|
//Ok(Some(area))
|
||||||
|
//} else {
|
||||||
|
//let [x, y, width, height] = area;
|
||||||
|
//let label = "No chain selected";
|
||||||
|
//let x = x + (width - label.len() as u16) / 2;
|
||||||
|
//let y = y + height / 2;
|
||||||
|
//to.blit(&label, x, y, Some(Style::default().dim().bold()))?;
|
||||||
|
//Ok(Some(area))
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl Content<TuiOut> for Mixer<Tui> {
|
||||||
|
//fn content (&self) -> impl Content<TuiOut> {
|
||||||
|
//Stack::right(|add| {
|
||||||
|
//for channel in self.tracks.iter() {
|
||||||
|
//add(channel)?;
|
||||||
|
//}
|
||||||
|
//Ok(())
|
||||||
|
//})
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//impl Content<TuiOut> for Track<Tui> {
|
||||||
|
//fn content (&self) -> impl Content<TuiOut> {
|
||||||
|
//TrackView {
|
||||||
|
//chain: Some(&self),
|
||||||
|
//direction: tek_core::Direction::Right,
|
||||||
|
//focused: true,
|
||||||
|
//entered: true,
|
||||||
|
////pub channels: u8,
|
||||||
|
////pub input_ports: Vec<Port<AudioIn>>,
|
||||||
|
////pub pre_gain_meter: f64,
|
||||||
|
////pub gain: f64,
|
||||||
|
////pub insert_ports: Vec<Port<AudioOut>>,
|
||||||
|
////pub return_ports: Vec<Port<AudioIn>>,
|
||||||
|
////pub post_gain_meter: f64,
|
||||||
|
////pub post_insert_meter: f64,
|
||||||
|
////pub level: f64,
|
||||||
|
////pub pan: f64,
|
||||||
|
////pub output_ports: Vec<Port<AudioOut>>,
|
||||||
|
////pub post_fader_meter: f64,
|
||||||
|
////pub route: String,
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
handle!(TuiIn: |self: Mixer, engine|{
|
||||||
|
if let crossterm::event::Event::Key(event) = engine.event() {
|
||||||
|
|
||||||
|
match event.code {
|
||||||
|
//KeyCode::Char('c') => {
|
||||||
|
//if event.modifiers == KeyModifiers::CONTROL {
|
||||||
|
//self.exit();
|
||||||
|
//}
|
||||||
|
//},
|
||||||
|
KeyCode::Down => {
|
||||||
|
self.selected_track = (self.selected_track + 1) % self.tracks.len();
|
||||||
|
println!("{}", self.selected_track);
|
||||||
|
return Ok(Some(true))
|
||||||
|
},
|
||||||
|
KeyCode::Up => {
|
||||||
|
if self.selected_track == 0 {
|
||||||
|
self.selected_track = self.tracks.len() - 1;
|
||||||
|
} else {
|
||||||
|
self.selected_track -= 1;
|
||||||
|
}
|
||||||
|
println!("{}", self.selected_track);
|
||||||
|
return Ok(Some(true))
|
||||||
|
},
|
||||||
|
KeyCode::Left => {
|
||||||
|
if self.selected_column == 0 {
|
||||||
|
self.selected_column = 6
|
||||||
|
} else {
|
||||||
|
self.selected_column -= 1;
|
||||||
|
}
|
||||||
|
return Ok(Some(true))
|
||||||
|
},
|
||||||
|
KeyCode::Right => {
|
||||||
|
if self.selected_column == 6 {
|
||||||
|
self.selected_column = 0
|
||||||
|
} else {
|
||||||
|
self.selected_column += 1;
|
||||||
|
}
|
||||||
|
return Ok(Some(true))
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
println!("\n{event:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
});
|
||||||
|
|
||||||
|
handle!(TuiIn: |self:MixerTrack,from|{
|
||||||
|
match from.event() {
|
||||||
|
//, NONE, "chain_cursor_up", "move cursor up", || {
|
||||||
|
kpat!(KeyCode::Up) => {
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
// , NONE, "chain_cursor_down", "move cursor down", || {
|
||||||
|
kpat!(KeyCode::Down) => {
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
// Left, NONE, "chain_cursor_left", "move cursor left", || {
|
||||||
|
kpat!(KeyCode::Left) => {
|
||||||
|
//if let Some(track) = app.arranger.track_mut() {
|
||||||
|
//track.device = track.device.saturating_sub(1);
|
||||||
|
//return Ok(true)
|
||||||
|
//}
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
// , NONE, "chain_cursor_right", "move cursor right", || {
|
||||||
|
kpat!(KeyCode::Right) => {
|
||||||
|
//if let Some(track) = app.arranger.track_mut() {
|
||||||
|
//track.device = (track.device + 1).min(track.devices.len().saturating_sub(1));
|
||||||
|
//return Ok(true)
|
||||||
|
//}
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
// , NONE, "chain_mode_switch", "switch the display mode", || {
|
||||||
|
kpat!(KeyCode::Char('`')) => {
|
||||||
|
//app.chain_mode = !app.chain_mode;
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
_ => Ok(None)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pub enum MixerTrackCommand {}
|
||||||
|
|
||||||
|
//impl MixerTrackDevice for LV2Plugin {}
|
||||||
|
|
||||||
|
pub trait MixerTrackDevice: Debug + Send + Sync {
|
||||||
|
fn boxed (self) -> Box<dyn MixerTrackDevice> where Self: Sized + 'static {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MixerTrackDevice for Sampler {}
|
||||||
|
|
||||||
|
impl MixerTrackDevice for Plugin {}
|
||||||
|
|
||||||
|
const SYM_NAME: &str = ":name";
|
||||||
|
const SYM_GAIN: &str = ":gain";
|
||||||
|
const SYM_SAMPLER: &str = "sampler";
|
||||||
|
const SYM_LV2: &str = "lv2";
|
||||||
|
|
||||||
|
from_edn!("mixer/track" => |jack: &Arc<RwLock<JackConnection>>, args| -> MixerTrack {
|
||||||
|
let mut _gain = 0.0f64;
|
||||||
|
let mut track = MixerTrack {
|
||||||
|
name: "".into(),
|
||||||
|
audio_ins: vec![],
|
||||||
|
audio_outs: vec![],
|
||||||
|
devices: vec![],
|
||||||
|
};
|
||||||
|
edn!(edn in args {
|
||||||
|
Edn::Map(map) => {
|
||||||
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) {
|
||||||
|
track.name = n.to_string();
|
||||||
|
}
|
||||||
|
if let Some(Edn::Double(g)) = map.get(&Edn::Key(SYM_GAIN)) {
|
||||||
|
_gain = f64::from(*g);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Edn::List(args) => match args.first() {
|
||||||
|
// Add a sampler device to the track
|
||||||
|
Some(Edn::Symbol(SYM_SAMPLER)) => {
|
||||||
|
track.devices.push(
|
||||||
|
Box::new(Sampler::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
|
||||||
|
);
|
||||||
|
panic!(
|
||||||
|
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"",
|
||||||
|
&track.name,
|
||||||
|
args.first().unwrap()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// Add a LV2 plugin to the track.
|
||||||
|
Some(Edn::Symbol(SYM_LV2)) => {
|
||||||
|
track.devices.push(
|
||||||
|
Box::new(Plugin::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
|
||||||
|
);
|
||||||
|
panic!(
|
||||||
|
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"",
|
||||||
|
&track.name,
|
||||||
|
args.first().unwrap()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
None =>
|
||||||
|
panic!("empty list track {}", &track.name),
|
||||||
|
_ =>
|
||||||
|
panic!("unexpected in track {}: {:?}", &track.name, args.first().unwrap())
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
Ok(track)
|
||||||
|
});
|
||||||
|
|
||||||
|
//impl ArrangerScene {
|
||||||
|
|
||||||
|
////TODO
|
||||||
|
////pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually<Self> {
|
||||||
|
////let mut name = None;
|
||||||
|
////let mut clips = vec![];
|
||||||
|
////edn!(edn in args {
|
||||||
|
////Edn::Map(map) => {
|
||||||
|
////let key = map.get(&Edn::Key(":name"));
|
||||||
|
////if let Some(Edn::Str(n)) = key {
|
||||||
|
////name = Some(*n);
|
||||||
|
////} else {
|
||||||
|
////panic!("unexpected key in scene '{name:?}': {key:?}")
|
||||||
|
////}
|
||||||
|
////},
|
||||||
|
////Edn::Symbol("_") => {
|
||||||
|
////clips.push(None);
|
||||||
|
////},
|
||||||
|
////Edn::Int(i) => {
|
||||||
|
////clips.push(Some(*i as usize));
|
||||||
|
////},
|
||||||
|
////_ => panic!("unexpected in scene '{name:?}': {edn:?}")
|
||||||
|
////});
|
||||||
|
////Ok(ArrangerScene {
|
||||||
|
////name: Arc::new(name.unwrap_or("").to_string().into()),
|
||||||
|
////color: ItemColor::random(),
|
||||||
|
////clips,
|
||||||
|
////})
|
||||||
|
////}
|
||||||
|
//}
|
||||||
|
|
|
||||||
447
tek/src/lib.rs
447
tek/src/lib.rs
|
|
@ -9,19 +9,13 @@
|
||||||
pub type Usually<T> = std::result::Result<T, Box<dyn Error>>;
|
pub type Usually<T> = std::result::Result<T, Box<dyn Error>>;
|
||||||
/// Standard optional result type.
|
/// Standard optional result type.
|
||||||
pub type Perhaps<T> = std::result::Result<Option<T>, Box<dyn Error>>;
|
pub type Perhaps<T> = std::result::Result<Option<T>, Box<dyn Error>>;
|
||||||
pub mod mixer; pub use self::mixer::*;
|
|
||||||
pub use ::tek_tui::{
|
pub use ::tek_tui::{
|
||||||
*,
|
*, tek_edn::*, tek_input::*, tek_output::*,
|
||||||
tek_edn::*,
|
crossterm, crossterm::event::{
|
||||||
tek_input::*,
|
|
||||||
tek_output::*,
|
|
||||||
crossterm,
|
|
||||||
crossterm::event::{
|
|
||||||
Event, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers,
|
Event, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers,
|
||||||
KeyCode::{self, *},
|
KeyCode::{self, *},
|
||||||
},
|
},
|
||||||
ratatui,
|
ratatui, ratatui::{
|
||||||
ratatui::{
|
|
||||||
prelude::{Color, Style, Stylize, Buffer, Modifier},
|
prelude::{Color, Style, Stylize, Buffer, Modifier},
|
||||||
buffer::Cell,
|
buffer::Cell,
|
||||||
}
|
}
|
||||||
|
|
@ -31,25 +25,9 @@ pub use ::tek_jack::{self, *, jack::{*, contrib::*}};
|
||||||
pub use ::tek_midi::{self, *, midly::{*, num::*, live::*}};
|
pub use ::tek_midi::{self, *, midly::{*, num::*, live::*}};
|
||||||
pub use ::tek_sampler::{self, *};
|
pub use ::tek_sampler::{self, *};
|
||||||
pub use ::tek_plugin::{self, *};
|
pub use ::tek_plugin::{self, *};
|
||||||
use EdnItem::*;
|
|
||||||
pub(crate) use ClockCommand::{Play, Pause};
|
|
||||||
pub(crate) use KeyCode::{Tab, Char};
|
|
||||||
pub(crate) use SamplerCommand as SmplCmd;
|
|
||||||
pub(crate) use MidiEditCommand as EditCmd;
|
|
||||||
pub(crate) use PoolClipCommand as PoolCmd;
|
|
||||||
pub(crate) use std::cmp::{Ord, Eq, PartialEq};
|
|
||||||
pub(crate) use std::collections::BTreeMap;
|
|
||||||
pub(crate) use std::error::Error;
|
pub(crate) use std::error::Error;
|
||||||
pub(crate) use std::fmt::{Debug, Display, Formatter};
|
pub(crate) use std::sync::atomic::{AtomicBool, Ordering::{self, *}};
|
||||||
pub(crate) use std::io::{Stdout, stdout};
|
pub(crate) use std::sync::{Arc, RwLock};
|
||||||
pub(crate) use std::marker::PhantomData;
|
|
||||||
pub(crate) use std::ops::{Add, Sub, Mul, Div, Rem};
|
|
||||||
pub(crate) use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::{self, *}};
|
|
||||||
pub(crate) use std::sync::{Arc, Mutex, RwLock};
|
|
||||||
pub(crate) use std::thread::{spawn, JoinHandle};
|
|
||||||
pub(crate) use std::time::Duration;
|
|
||||||
pub(crate) use std::path::PathBuf;
|
|
||||||
pub(crate) use std::ffi::OsString;
|
|
||||||
#[derive(Default)] pub struct App {
|
#[derive(Default)] pub struct App {
|
||||||
pub jack: Arc<RwLock<JackConnection>>,
|
pub jack: Arc<RwLock<JackConnection>>,
|
||||||
pub edn: String,
|
pub edn: String,
|
||||||
|
|
@ -138,15 +116,75 @@ render!(TuiOut: (self: Meter<'a>) => col!(
|
||||||
render!(TuiOut: (self: Meters<'a>) => col!(
|
render!(TuiOut: (self: Meters<'a>) => col!(
|
||||||
format!("L/{:>+9.3}", self.0[0]),
|
format!("L/{:>+9.3}", self.0[0]),
|
||||||
format!("R/{:>+9.3}", self.0[1])));
|
format!("R/{:>+9.3}", self.0[1])));
|
||||||
|
/// Represents the current user selection in the arranger
|
||||||
|
#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection {
|
||||||
|
/// The whole mix is selected
|
||||||
|
#[default] Mix,
|
||||||
|
/// A track is selected.
|
||||||
|
Track(usize),
|
||||||
|
/// A scene is selected.
|
||||||
|
Scene(usize),
|
||||||
|
/// A clip (track × scene) is selected.
|
||||||
|
Clip(usize, usize),
|
||||||
|
}
|
||||||
|
/// Focus identification methods
|
||||||
|
impl Selection {
|
||||||
|
pub fn is_mix (&self) -> bool { matches!(self, Self::Mix) }
|
||||||
|
pub fn is_track (&self) -> bool { matches!(self, Self::Track(_)) }
|
||||||
|
pub fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) }
|
||||||
|
pub fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) }
|
||||||
|
pub fn track (&self) -> Option<usize> {
|
||||||
|
use Selection::*;
|
||||||
|
match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None }
|
||||||
|
}
|
||||||
|
pub fn scene (&self) -> Option<usize> {
|
||||||
|
use Selection::*;
|
||||||
|
match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None }
|
||||||
|
}
|
||||||
|
pub fn description (
|
||||||
|
&self,
|
||||||
|
tracks: &[Track],
|
||||||
|
scenes: &[Scene],
|
||||||
|
) -> Arc<str> {
|
||||||
|
format!("Selected: {}", match self {
|
||||||
|
Self::Mix => "Everything".to_string(),
|
||||||
|
Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name))
|
||||||
|
.unwrap_or_else(||"T??".into()),
|
||||||
|
Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name))
|
||||||
|
.unwrap_or_else(||"S??".into()),
|
||||||
|
Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) {
|
||||||
|
(Some(_), Some(scene)) => match scene.clip(*t) {
|
||||||
|
Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name),
|
||||||
|
None => format!("T{t} S{s}: Empty")
|
||||||
|
},
|
||||||
|
_ => format!("T{t} S{s}: Empty"),
|
||||||
|
}
|
||||||
|
}).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl HasSelection for App {
|
||||||
|
fn selected (&self) -> &Selection { &self.selected }
|
||||||
|
fn selected_mut (&mut self) -> &mut Selection { &mut self.selected }
|
||||||
|
}
|
||||||
|
pub trait HasSelection {
|
||||||
|
fn selected (&self) -> &Selection;
|
||||||
|
fn selected_mut (&mut self) -> &mut Selection;
|
||||||
|
}
|
||||||
#[derive(Debug, Default)] struct Track {
|
#[derive(Debug, Default)] struct Track {
|
||||||
/// Name of track
|
/// Name of track
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
/// Preferred width of track column
|
/// Preferred width of track column
|
||||||
width: usize,
|
width: usize,
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
color: ItemPalette,
|
color: ItemPalette,
|
||||||
/// MIDI player state
|
/// MIDI player state
|
||||||
player: MidiPlayer,
|
player: MidiPlayer,
|
||||||
|
/// Inputs of 1st device
|
||||||
|
audio_ins: Vec<JackPort<AudioIn>>,
|
||||||
|
/// Outputs of last device
|
||||||
|
audio_outs: Vec<JackPort<AudioOut>>,
|
||||||
|
/// Device chain
|
||||||
|
devices: Vec<Device>,
|
||||||
}
|
}
|
||||||
impl Track {
|
impl Track {
|
||||||
const MIN_WIDTH: usize = 9;
|
const MIN_WIDTH: usize = 9;
|
||||||
|
|
@ -162,6 +200,65 @@ impl Track {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl HasTracks for App {
|
||||||
|
fn tracks (&self) -> &Vec<Track> { &self.tracks }
|
||||||
|
fn tracks_mut (&mut self) -> &mut Vec<Track> { &mut self.tracks }
|
||||||
|
}
|
||||||
|
pub trait HasTracks: HasSelection + HasClock + HasJack {
|
||||||
|
fn tracks (&self) -> &Vec<Track>;
|
||||||
|
fn tracks_mut (&mut self) -> &mut Vec<Track>;
|
||||||
|
fn tracks_sizes (
|
||||||
|
&self,
|
||||||
|
editing: bool,
|
||||||
|
bigger: usize
|
||||||
|
) -> impl Iterator<Item=(usize,&Track,usize,usize)> {
|
||||||
|
let mut x = 0;
|
||||||
|
let active = match self.selected() {
|
||||||
|
Selection::Track(t) if editing => Some(t),
|
||||||
|
Selection::Clip(t, _) if editing => Some(t),
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||||
|
let width = if Some(&index) == active { bigger } else { track.width.max(8) };
|
||||||
|
let data = (index, track, x, x + width);
|
||||||
|
x += width;
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn track_next_name (&self) -> Arc<str> {
|
||||||
|
format!("Trk{:02}", self.tracks().len() + 1).into()
|
||||||
|
}
|
||||||
|
fn track (&self) -> Option<&Track> {
|
||||||
|
self.selected().track().and_then(|s|self.tracks().get(s))
|
||||||
|
}
|
||||||
|
fn track_mut (&mut self) -> Option<&mut Track> {
|
||||||
|
self.selected().track().and_then(|s|self.tracks_mut().get_mut(s))
|
||||||
|
}
|
||||||
|
fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||||
|
(||Tui::bg(TuiTheme::g(32), Bsp::s(
|
||||||
|
help_tag("add ", "t", "rack"),
|
||||||
|
help_tag("", "a", "dd scene"),
|
||||||
|
)).boxed()).into()
|
||||||
|
}
|
||||||
|
fn track_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||||
|
let iter = ||self.tracks_with_sizes();
|
||||||
|
(move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| {
|
||||||
|
let name = Push::x(1, &track.name);
|
||||||
|
let color = track.color;
|
||||||
|
let fg = color.lightest.rgb;
|
||||||
|
let bg = color.base.rgb;
|
||||||
|
let active = self.selected().track() == Some(i);
|
||||||
|
let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) };
|
||||||
|
let border = Style::default().fg(bfg).bg(bg);
|
||||||
|
Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16,
|
||||||
|
Outer(border).enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name)))))
|
||||||
|
))
|
||||||
|
})).boxed()).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trait Device {}
|
||||||
|
impl Device for Sampler {}
|
||||||
|
impl Device for Plugin {}
|
||||||
#[derive(Debug, Default)] struct Scene {
|
#[derive(Debug, Default)] struct Scene {
|
||||||
/// Name of scene
|
/// Name of scene
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
|
|
@ -202,6 +299,87 @@ impl Scene {
|
||||||
match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None }
|
match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)] pub enum SceneCommand {
|
||||||
|
Add,
|
||||||
|
Del(usize),
|
||||||
|
Swap(usize, usize),
|
||||||
|
SetSize(usize),
|
||||||
|
SetZoom(usize),
|
||||||
|
SetColor(usize, ItemPalette),
|
||||||
|
Enqueue(usize),
|
||||||
|
}
|
||||||
|
edn_command!(SceneCommand: |state: App| {
|
||||||
|
("add" [] Self::Add)
|
||||||
|
("del" [a: usize] Self::Del(0))
|
||||||
|
("zoom" [a: usize] Self::SetZoom(a.unwrap()))
|
||||||
|
("color" [a: usize] Self::SetColor(a.unwrap(), ItemPalette::random()))
|
||||||
|
("enqueue" [a: usize] Self::Enqueue(a.unwrap()))
|
||||||
|
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
||||||
|
});
|
||||||
|
command!(|self: SceneCommand, state: App|match self { _ => todo!("scene command") });
|
||||||
|
impl HasScenes for App {
|
||||||
|
fn scenes (&self) -> &Vec<Scene> { &self.scenes }
|
||||||
|
fn scenes_mut (&mut self) -> &mut Vec<Scene> { &mut self.scenes }
|
||||||
|
}
|
||||||
|
pub trait HasScenes: HasSelection {
|
||||||
|
fn scenes (&self) -> &Vec<Scene>;
|
||||||
|
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
||||||
|
fn scenes_sizes(
|
||||||
|
&self,
|
||||||
|
editing: bool,
|
||||||
|
scene_height: usize,
|
||||||
|
scene_larger: usize,
|
||||||
|
) -> impl Iterator<Item = (usize, &Scene, usize, usize)> {
|
||||||
|
let mut y = 0;
|
||||||
|
let (selected_track, selected_scene) = match self.selected() {
|
||||||
|
Selection::Clip(t, s) => (Some(t), Some(s)),
|
||||||
|
_ => (None, None)
|
||||||
|
};
|
||||||
|
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
||||||
|
let active = editing && selected_track.is_some() && selected_scene == Some(&s);
|
||||||
|
let height = if active { scene_larger } else { scene_height };
|
||||||
|
let data = (s, scene, y, y + height);
|
||||||
|
y += height;
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn scene_default_name (&self) -> Arc<str> {
|
||||||
|
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
||||||
|
}
|
||||||
|
fn scene (&self) -> Option<&Scene> {
|
||||||
|
self.selected().scene().and_then(|s|self.scenes().get(s))
|
||||||
|
}
|
||||||
|
fn scene_mut (&mut self) -> Option<&mut Scene> {
|
||||||
|
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
|
||||||
|
}
|
||||||
|
fn scene_del (&mut self, index: usize) {
|
||||||
|
todo!("delete scene");
|
||||||
|
}
|
||||||
|
fn scene_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||||
|
(||{
|
||||||
|
let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0))));
|
||||||
|
let selected = self.selected().scene();
|
||||||
|
Fill::y(Align::c(Map::new(||self.scenes_with_sizes(2), move|(_, scene, y1, y2), i| {
|
||||||
|
let h = (y2 - y1) as u16;
|
||||||
|
let name = format!("🭬{}", &scene.name);
|
||||||
|
let color = scene.color;
|
||||||
|
let active = selected == Some(i);
|
||||||
|
let mid = if active { color.light } else { color.base };
|
||||||
|
let top = Some(last_color.read().unwrap().base.rgb);
|
||||||
|
let cell = phat_sel_3(
|
||||||
|
active,
|
||||||
|
Tui::bold(true, name.clone()),
|
||||||
|
Tui::bold(true, name),
|
||||||
|
top,
|
||||||
|
mid.rgb,
|
||||||
|
Color::Rgb(0, 0, 0)
|
||||||
|
);
|
||||||
|
*last_color.write().unwrap() = color;
|
||||||
|
map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell))
|
||||||
|
}))).boxed()
|
||||||
|
}).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl App {
|
impl App {
|
||||||
pub fn sequencer (
|
pub fn sequencer (
|
||||||
jack: &Arc<RwLock<JackConnection>>, pool: MidiPool, editor: MidiEditor,
|
jack: &Arc<RwLock<JackConnection>>, pool: MidiPool, editor: MidiEditor,
|
||||||
|
|
@ -405,7 +583,7 @@ command!(|self: AppCommand, state: App|match self {
|
||||||
match cmd {
|
match cmd {
|
||||||
// autoselect: automatically load selected clip in editor
|
// autoselect: automatically load selected clip in editor
|
||||||
// autocolor: update color in all places simultaneously
|
// autocolor: update color in all places simultaneously
|
||||||
PoolCommand::Select(_) | PoolCommand::Clip(PoolCmd::SetColor(_, _)) =>
|
PoolCommand::Select(_) | PoolCommand::Clip(PoolClipCommand::SetColor(_, _)) =>
|
||||||
editor.set_clip(pool.clip().as_ref()),
|
editor.set_clip(pool.clip().as_ref()),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -458,24 +636,6 @@ edn_command!(ClipCommand: |state: App| {
|
||||||
,b: usize] Self::SetColor(a.unwrap(), b.unwrap(), ItemPalette::random()))
|
,b: usize] Self::SetColor(a.unwrap(), b.unwrap(), ItemPalette::random()))
|
||||||
});
|
});
|
||||||
command!(|self: ClipCommand, state: App|match self { _ => todo!("clip command") });
|
command!(|self: ClipCommand, state: App|match self { _ => todo!("clip command") });
|
||||||
#[derive(Clone, Debug)] pub enum SceneCommand {
|
|
||||||
Add,
|
|
||||||
Del(usize),
|
|
||||||
Swap(usize, usize),
|
|
||||||
SetSize(usize),
|
|
||||||
SetZoom(usize),
|
|
||||||
SetColor(usize, ItemPalette),
|
|
||||||
Enqueue(usize),
|
|
||||||
}
|
|
||||||
edn_command!(SceneCommand: |state: App| {
|
|
||||||
("add" [] Self::Add)
|
|
||||||
("del" [a: usize] Self::Del(0))
|
|
||||||
("zoom" [a: usize] Self::SetZoom(a.unwrap()))
|
|
||||||
("color" [a: usize] Self::SetColor(a.unwrap(), ItemPalette::random()))
|
|
||||||
("enqueue" [a: usize] Self::Enqueue(a.unwrap()))
|
|
||||||
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
|
||||||
});
|
|
||||||
command!(|self: SceneCommand, state: App|match self { _ => todo!("scene command") });
|
|
||||||
#[derive(Clone, Debug)] pub enum TrackCommand {
|
#[derive(Clone, Debug)] pub enum TrackCommand {
|
||||||
Add,
|
Add,
|
||||||
Del(usize),
|
Del(usize),
|
||||||
|
|
@ -692,100 +852,8 @@ fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content<TuiOut> {
|
||||||
fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
|
fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
|
||||||
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
|
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
|
||||||
}
|
}
|
||||||
impl HasSelection for App {
|
impl Arrangement for App {}
|
||||||
fn selected (&self) -> &Selection { &self.selected }
|
pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack {
|
||||||
fn selected_mut (&mut self) -> &mut Selection { &mut self.selected }
|
|
||||||
}
|
|
||||||
pub trait HasSelection {
|
|
||||||
fn selected (&self) -> &Selection;
|
|
||||||
fn selected_mut (&mut self) -> &mut Selection;
|
|
||||||
}
|
|
||||||
/// Represents the current user selection in the arranger
|
|
||||||
#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection {
|
|
||||||
/// The whole mix is selected
|
|
||||||
#[default] Mix,
|
|
||||||
/// A track is selected.
|
|
||||||
Track(usize),
|
|
||||||
/// A scene is selected.
|
|
||||||
Scene(usize),
|
|
||||||
/// A clip (track × scene) is selected.
|
|
||||||
Clip(usize, usize),
|
|
||||||
}
|
|
||||||
/// Focus identification methods
|
|
||||||
impl Selection {
|
|
||||||
pub fn is_mix (&self) -> bool { matches!(self, Self::Mix) }
|
|
||||||
pub fn is_track (&self) -> bool { matches!(self, Self::Track(_)) }
|
|
||||||
pub fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) }
|
|
||||||
pub fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) }
|
|
||||||
pub fn track (&self) -> Option<usize> {
|
|
||||||
use Selection::*;
|
|
||||||
match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None }
|
|
||||||
}
|
|
||||||
pub fn scene (&self) -> Option<usize> {
|
|
||||||
use Selection::*;
|
|
||||||
match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None }
|
|
||||||
}
|
|
||||||
pub fn description (
|
|
||||||
&self,
|
|
||||||
tracks: &[Track],
|
|
||||||
scenes: &[Scene],
|
|
||||||
) -> Arc<str> {
|
|
||||||
format!("Selected: {}", match self {
|
|
||||||
Self::Mix => "Everything".to_string(),
|
|
||||||
Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name))
|
|
||||||
.unwrap_or_else(||"T??".into()),
|
|
||||||
Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name))
|
|
||||||
.unwrap_or_else(||"S??".into()),
|
|
||||||
Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) {
|
|
||||||
(Some(_), Some(scene)) => match scene.clip(*t) {
|
|
||||||
Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name),
|
|
||||||
None => format!("T{t} S{s}: Empty")
|
|
||||||
},
|
|
||||||
_ => format!("T{t} S{s}: Empty"),
|
|
||||||
}
|
|
||||||
}).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HasTracks for App {
|
|
||||||
fn tracks (&self) -> &Vec<Track> { &self.tracks }
|
|
||||||
fn tracks_mut (&mut self) -> &mut Vec<Track> { &mut self.tracks }
|
|
||||||
}
|
|
||||||
pub trait HasTracks: HasSelection + HasScenes + HasClock + HasJack {
|
|
||||||
fn tracks (&self) -> &Vec<Track>;
|
|
||||||
fn tracks_mut (&mut self) -> &mut Vec<Track>;
|
|
||||||
fn tracks_sizes (
|
|
||||||
&self,
|
|
||||||
editing: bool,
|
|
||||||
bigger: usize
|
|
||||||
) -> impl Iterator<Item=(usize,&Track,usize,usize)> {
|
|
||||||
let mut x = 0;
|
|
||||||
let active = match self.selected() {
|
|
||||||
Selection::Track(t) if editing => Some(t),
|
|
||||||
Selection::Clip(t, _) if editing => Some(t),
|
|
||||||
_ => None
|
|
||||||
};
|
|
||||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
|
||||||
let width = if Some(&index) == active { bigger } else { track.width.max(8) };
|
|
||||||
let data = (index, track, x, x + width);
|
|
||||||
x += width;
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn track_next_name (&self) -> Arc<str> {
|
|
||||||
format!("Trk{:02}", self.tracks().len() + 1).into()
|
|
||||||
}
|
|
||||||
fn track (&self) -> Option<&Track> {
|
|
||||||
self.selected().track().and_then(|s|self.tracks().get(s))
|
|
||||||
}
|
|
||||||
fn track_mut (&mut self) -> Option<&mut Track> {
|
|
||||||
self.selected().track().and_then(|s|self.tracks_mut().get_mut(s))
|
|
||||||
}
|
|
||||||
fn track_del (&mut self, index: usize) {
|
|
||||||
self.tracks_mut().remove(index);
|
|
||||||
for scene in self.scenes_mut().iter_mut() {
|
|
||||||
scene.clips.remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<&mut Track>
|
-> Usually<&mut Track>
|
||||||
{
|
{
|
||||||
|
|
@ -827,65 +895,11 @@ pub trait HasTracks: HasSelection + HasScenes + HasClock + HasJack {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
fn track_del (&mut self, index: usize) {
|
||||||
(||Tui::bg(TuiTheme::g(32), Bsp::s(
|
self.tracks_mut().remove(index);
|
||||||
help_tag("add ", "t", "rack"),
|
for scene in self.scenes_mut().iter_mut() {
|
||||||
help_tag("", "a", "dd scene"),
|
scene.clips.remove(index);
|
||||||
)).boxed()).into()
|
}
|
||||||
}
|
|
||||||
fn track_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
|
||||||
let iter = ||self.tracks_with_sizes();
|
|
||||||
(move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| {
|
|
||||||
let name = Push::x(1, &track.name);
|
|
||||||
let color = track.color;
|
|
||||||
let fg = color.lightest.rgb;
|
|
||||||
let bg = color.base.rgb;
|
|
||||||
let active = self.selected.track() == Some(i);
|
|
||||||
let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) };
|
|
||||||
let border = Style::default().fg(bfg).bg(bg);
|
|
||||||
Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16,
|
|
||||||
Outer(border).enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name)))))
|
|
||||||
))
|
|
||||||
})).boxed()).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HasScenes for App {
|
|
||||||
fn scenes (&self) -> &Vec<Scene> { &self.scenes }
|
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<Scene> { &mut self.scenes }
|
|
||||||
}
|
|
||||||
pub trait HasScenes: HasSelection {
|
|
||||||
fn scenes (&self) -> &Vec<Scene>;
|
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
|
||||||
fn scenes_sizes(
|
|
||||||
&self,
|
|
||||||
editing: bool,
|
|
||||||
scene_height: usize,
|
|
||||||
scene_larger: usize,
|
|
||||||
) -> impl Iterator<Item = (usize, &Scene, usize, usize)> {
|
|
||||||
let mut y = 0;
|
|
||||||
let (selected_track, selected_scene) = match self.selected() {
|
|
||||||
Selection::Clip(t, s) => (Some(t), Some(s)),
|
|
||||||
_ => (None, None)
|
|
||||||
};
|
|
||||||
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
|
||||||
let active = editing && selected_track.is_some() && selected_scene == Some(&s);
|
|
||||||
let height = if active { scene_larger } else { scene_height };
|
|
||||||
let data = (s, scene, y, y + height);
|
|
||||||
y += height;
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn scene_default_name (&self) -> Arc<str> {
|
|
||||||
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
|
||||||
}
|
|
||||||
fn scene (&self) -> Option<&Scene> {
|
|
||||||
self.selected().scene().and_then(|s|self.scenes().get(s))
|
|
||||||
}
|
|
||||||
fn scene_mut (&mut self) -> Option<&mut Scene> {
|
|
||||||
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
|
|
||||||
}
|
|
||||||
fn scene_del (&mut self, index: usize) {
|
|
||||||
todo!("delete scene");
|
|
||||||
}
|
}
|
||||||
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<&mut Scene>
|
-> Usually<&mut Scene>
|
||||||
|
|
@ -909,30 +923,6 @@ pub trait HasScenes: HasSelection {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn scene_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
|
||||||
(||{
|
|
||||||
let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0))));
|
|
||||||
let selected = self.selected.scene();
|
|
||||||
Fill::y(Align::c(Map::new(||self.scenes_with_sizes(2), move|(_, scene, y1, y2), i| {
|
|
||||||
let h = (y2 - y1) as u16;
|
|
||||||
let name = format!("🭬{}", &scene.name);
|
|
||||||
let color = scene.color;
|
|
||||||
let active = selected == Some(i);
|
|
||||||
let mid = if active { color.light } else { color.base };
|
|
||||||
let top = Some(last_color.read().unwrap().base.rgb);
|
|
||||||
let cell = phat_sel_3(
|
|
||||||
active,
|
|
||||||
Tui::bold(true, name.clone()),
|
|
||||||
Tui::bold(true, name),
|
|
||||||
top,
|
|
||||||
mid.rgb,
|
|
||||||
Color::Rgb(0, 0, 0)
|
|
||||||
);
|
|
||||||
*last_color.write().unwrap() = color;
|
|
||||||
map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell))
|
|
||||||
}))).boxed()
|
|
||||||
}).into()
|
|
||||||
}
|
|
||||||
fn scene_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
fn scene_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||||
let editing = self.is_editing();
|
let editing = self.is_editing();
|
||||||
let tracks = move||self.tracks_with_sizes();
|
let tracks = move||self.tracks_with_sizes();
|
||||||
|
|
@ -979,9 +969,6 @@ pub trait HasScenes: HasSelection {
|
||||||
Fixed::x(w, map_east(x1 as u16, w, column))
|
Fixed::x(w, map_east(x1 as u16, w, column))
|
||||||
}))).boxed()).into()
|
}))).boxed()).into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl Arrangement for App {}
|
|
||||||
pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack {
|
|
||||||
fn activate (&mut self) -> Usually<()> {
|
fn activate (&mut self) -> Usually<()> {
|
||||||
let selected = self.selected().clone();
|
let selected = self.selected().clone();
|
||||||
match selected {
|
match selected {
|
||||||
|
|
|
||||||
346
tek/src/mixer.rs
346
tek/src/mixer.rs
|
|
@ -1,346 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Mixer {
|
|
||||||
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
|
||||||
pub jack: Arc<RwLock<JackConnection>>,
|
|
||||||
pub name: Arc<str>,
|
|
||||||
pub tracks: Vec<MixerTrack>,
|
|
||||||
pub selected_track: usize,
|
|
||||||
pub selected_column: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A mixer track.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MixerTrack {
|
|
||||||
pub name: Arc<str>,
|
|
||||||
/// Inputs of 1st device
|
|
||||||
pub audio_ins: Vec<Port<AudioIn>>,
|
|
||||||
/// Outputs of last device
|
|
||||||
pub audio_outs: Vec<Port<AudioOut>>,
|
|
||||||
/// Device chain
|
|
||||||
pub devices: Vec<Box<dyn MixerTrackDevice>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
audio!(|self: Mixer, _client, _scope|Control::Continue);
|
|
||||||
|
|
||||||
impl Mixer {
|
|
||||||
pub fn new (jack: &Arc<RwLock<JackConnection>>, name: &str) -> Usually<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
jack: jack.clone(),
|
|
||||||
name: name.into(),
|
|
||||||
selected_column: 0,
|
|
||||||
selected_track: 1,
|
|
||||||
tracks: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn track_add (&mut self, name: &str, channels: usize) -> Usually<&mut Self> {
|
|
||||||
let track = MixerTrack::new(name)?;
|
|
||||||
self.tracks.push(track);
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
pub fn track (&self) -> Option<&MixerTrack> {
|
|
||||||
self.tracks.get(self.selected_track)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MixerTrack {
|
|
||||||
pub fn new (name: &str) -> Usually<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
name: name.to_string().into(),
|
|
||||||
audio_ins: vec![],
|
|
||||||
audio_outs: vec![],
|
|
||||||
devices: vec![],
|
|
||||||
//ports: JackPorts::default(),
|
|
||||||
//devices: vec![],
|
|
||||||
//device: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
//fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
|
|
||||||
//self.devices.get(i).map(|d|d.state.write().unwrap())
|
|
||||||
//}
|
|
||||||
//pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
|
|
||||||
//self.get_device_mut(self.device)
|
|
||||||
//}
|
|
||||||
///// Add a device to the end of the chain.
|
|
||||||
//pub fn append_device (&mut self, device: JackDevice<E>) -> Usually<&mut JackDevice<E>> {
|
|
||||||
//self.devices.push(device);
|
|
||||||
//let index = self.devices.len() - 1;
|
|
||||||
//Ok(&mut self.devices[index])
|
|
||||||
//}
|
|
||||||
//pub fn add_device (&mut self, device: JackDevice<E>) {
|
|
||||||
//self.devices.push(device);
|
|
||||||
//}
|
|
||||||
//pub fn connect_first_device (&self) -> Usually<()> {
|
|
||||||
//if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) {
|
|
||||||
//device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
//pub fn connect_last_device (&self, app: &Track) -> Usually<()> {
|
|
||||||
//Ok(match self.devices.get(self.devices.len().saturating_sub(1)) {
|
|
||||||
//Some(device) => {
|
|
||||||
//app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?;
|
|
||||||
//app.audio_out(1).map(|right|device.connect_audio_out(1, &right)).transpose()?;
|
|
||||||
//()
|
|
||||||
//},
|
|
||||||
//None => ()
|
|
||||||
//})
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TrackView<'a> {
|
|
||||||
pub chain: Option<&'a MixerTrack>,
|
|
||||||
pub direction: Direction,
|
|
||||||
pub focused: bool,
|
|
||||||
pub entered: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Content<TuiOut> for TrackView<'a> {
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
todo!();
|
|
||||||
//let mut area = to.area();
|
|
||||||
//if let Some(chain) = self.chain {
|
|
||||||
//match self.direction {
|
|
||||||
//Direction::Down => area.width = area.width.min(40),
|
|
||||||
//Direction::Right => area.width = area.width.min(10),
|
|
||||||
//_ => { unimplemented!() },
|
|
||||||
//}
|
|
||||||
//to.fill_bg(to.area(), Nord::bg_lo(self.focused, self.entered));
|
|
||||||
//let mut split = Stack::new(self.direction);
|
|
||||||
//for device in chain.devices.as_slice().iter() {
|
|
||||||
//split = split.add_ref(device);
|
|
||||||
//}
|
|
||||||
//let (area, areas) = split.render_areas(to)?;
|
|
||||||
//if self.focused && self.entered && areas.len() > 0 {
|
|
||||||
//Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?;
|
|
||||||
//}
|
|
||||||
//Ok(Some(area))
|
|
||||||
//} else {
|
|
||||||
//let [x, y, width, height] = area;
|
|
||||||
//let label = "No chain selected";
|
|
||||||
//let x = x + (width - label.len() as u16) / 2;
|
|
||||||
//let y = y + height / 2;
|
|
||||||
//to.blit(&label, x, y, Some(Style::default().dim().bold()))?;
|
|
||||||
//Ok(Some(area))
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl Content<TuiOut> for Mixer<Tui> {
|
|
||||||
//fn content (&self) -> impl Content<TuiOut> {
|
|
||||||
//Stack::right(|add| {
|
|
||||||
//for channel in self.tracks.iter() {
|
|
||||||
//add(channel)?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//impl Content<TuiOut> for Track<Tui> {
|
|
||||||
//fn content (&self) -> impl Content<TuiOut> {
|
|
||||||
//TrackView {
|
|
||||||
//chain: Some(&self),
|
|
||||||
//direction: tek_core::Direction::Right,
|
|
||||||
//focused: true,
|
|
||||||
//entered: true,
|
|
||||||
////pub channels: u8,
|
|
||||||
////pub input_ports: Vec<Port<AudioIn>>,
|
|
||||||
////pub pre_gain_meter: f64,
|
|
||||||
////pub gain: f64,
|
|
||||||
////pub insert_ports: Vec<Port<AudioOut>>,
|
|
||||||
////pub return_ports: Vec<Port<AudioIn>>,
|
|
||||||
////pub post_gain_meter: f64,
|
|
||||||
////pub post_insert_meter: f64,
|
|
||||||
////pub level: f64,
|
|
||||||
////pub pan: f64,
|
|
||||||
////pub output_ports: Vec<Port<AudioOut>>,
|
|
||||||
////pub post_fader_meter: f64,
|
|
||||||
////pub route: String,
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
handle!(TuiIn: |self: Mixer, engine|{
|
|
||||||
if let crossterm::event::Event::Key(event) = engine.event() {
|
|
||||||
|
|
||||||
match event.code {
|
|
||||||
//KeyCode::Char('c') => {
|
|
||||||
//if event.modifiers == KeyModifiers::CONTROL {
|
|
||||||
//self.exit();
|
|
||||||
//}
|
|
||||||
//},
|
|
||||||
KeyCode::Down => {
|
|
||||||
self.selected_track = (self.selected_track + 1) % self.tracks.len();
|
|
||||||
println!("{}", self.selected_track);
|
|
||||||
return Ok(Some(true))
|
|
||||||
},
|
|
||||||
KeyCode::Up => {
|
|
||||||
if self.selected_track == 0 {
|
|
||||||
self.selected_track = self.tracks.len() - 1;
|
|
||||||
} else {
|
|
||||||
self.selected_track -= 1;
|
|
||||||
}
|
|
||||||
println!("{}", self.selected_track);
|
|
||||||
return Ok(Some(true))
|
|
||||||
},
|
|
||||||
KeyCode::Left => {
|
|
||||||
if self.selected_column == 0 {
|
|
||||||
self.selected_column = 6
|
|
||||||
} else {
|
|
||||||
self.selected_column -= 1;
|
|
||||||
}
|
|
||||||
return Ok(Some(true))
|
|
||||||
},
|
|
||||||
KeyCode::Right => {
|
|
||||||
if self.selected_column == 6 {
|
|
||||||
self.selected_column = 0
|
|
||||||
} else {
|
|
||||||
self.selected_column += 1;
|
|
||||||
}
|
|
||||||
return Ok(Some(true))
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("\n{event:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
});
|
|
||||||
|
|
||||||
handle!(TuiIn: |self:MixerTrack,from|{
|
|
||||||
match from.event() {
|
|
||||||
//, NONE, "chain_cursor_up", "move cursor up", || {
|
|
||||||
kpat!(KeyCode::Up) => {
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
// , NONE, "chain_cursor_down", "move cursor down", || {
|
|
||||||
kpat!(KeyCode::Down) => {
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
// Left, NONE, "chain_cursor_left", "move cursor left", || {
|
|
||||||
kpat!(KeyCode::Left) => {
|
|
||||||
//if let Some(track) = app.arranger.track_mut() {
|
|
||||||
//track.device = track.device.saturating_sub(1);
|
|
||||||
//return Ok(true)
|
|
||||||
//}
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
// , NONE, "chain_cursor_right", "move cursor right", || {
|
|
||||||
kpat!(KeyCode::Right) => {
|
|
||||||
//if let Some(track) = app.arranger.track_mut() {
|
|
||||||
//track.device = (track.device + 1).min(track.devices.len().saturating_sub(1));
|
|
||||||
//return Ok(true)
|
|
||||||
//}
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
// , NONE, "chain_mode_switch", "switch the display mode", || {
|
|
||||||
kpat!(KeyCode::Char('`')) => {
|
|
||||||
//app.chain_mode = !app.chain_mode;
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
_ => Ok(None)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pub enum MixerTrackCommand {}
|
|
||||||
|
|
||||||
//impl MixerTrackDevice for LV2Plugin {}
|
|
||||||
|
|
||||||
pub trait MixerTrackDevice: Debug + Send + Sync {
|
|
||||||
fn boxed (self) -> Box<dyn MixerTrackDevice> where Self: Sized + 'static {
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MixerTrackDevice for Sampler {}
|
|
||||||
|
|
||||||
impl MixerTrackDevice for Plugin {}
|
|
||||||
|
|
||||||
const SYM_NAME: &str = ":name";
|
|
||||||
const SYM_GAIN: &str = ":gain";
|
|
||||||
const SYM_SAMPLER: &str = "sampler";
|
|
||||||
const SYM_LV2: &str = "lv2";
|
|
||||||
|
|
||||||
from_edn!("mixer/track" => |jack: &Arc<RwLock<JackConnection>>, args| -> MixerTrack {
|
|
||||||
let mut _gain = 0.0f64;
|
|
||||||
let mut track = MixerTrack {
|
|
||||||
name: "".into(),
|
|
||||||
audio_ins: vec![],
|
|
||||||
audio_outs: vec![],
|
|
||||||
devices: vec![],
|
|
||||||
};
|
|
||||||
edn!(edn in args {
|
|
||||||
Edn::Map(map) => {
|
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) {
|
|
||||||
track.name = n.to_string();
|
|
||||||
}
|
|
||||||
if let Some(Edn::Double(g)) = map.get(&Edn::Key(SYM_GAIN)) {
|
|
||||||
_gain = f64::from(*g);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Edn::List(args) => match args.first() {
|
|
||||||
// Add a sampler device to the track
|
|
||||||
Some(Edn::Symbol(SYM_SAMPLER)) => {
|
|
||||||
track.devices.push(
|
|
||||||
Box::new(Sampler::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
|
|
||||||
);
|
|
||||||
panic!(
|
|
||||||
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"",
|
|
||||||
&track.name,
|
|
||||||
args.first().unwrap()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
// Add a LV2 plugin to the track.
|
|
||||||
Some(Edn::Symbol(SYM_LV2)) => {
|
|
||||||
track.devices.push(
|
|
||||||
Box::new(Plugin::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
|
|
||||||
);
|
|
||||||
panic!(
|
|
||||||
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"",
|
|
||||||
&track.name,
|
|
||||||
args.first().unwrap()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
None =>
|
|
||||||
panic!("empty list track {}", &track.name),
|
|
||||||
_ =>
|
|
||||||
panic!("unexpected in track {}: {:?}", &track.name, args.first().unwrap())
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
});
|
|
||||||
Ok(track)
|
|
||||||
});
|
|
||||||
|
|
||||||
//impl ArrangerScene {
|
|
||||||
|
|
||||||
////TODO
|
|
||||||
////pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually<Self> {
|
|
||||||
////let mut name = None;
|
|
||||||
////let mut clips = vec![];
|
|
||||||
////edn!(edn in args {
|
|
||||||
////Edn::Map(map) => {
|
|
||||||
////let key = map.get(&Edn::Key(":name"));
|
|
||||||
////if let Some(Edn::Str(n)) = key {
|
|
||||||
////name = Some(*n);
|
|
||||||
////} else {
|
|
||||||
////panic!("unexpected key in scene '{name:?}': {key:?}")
|
|
||||||
////}
|
|
||||||
////},
|
|
||||||
////Edn::Symbol("_") => {
|
|
||||||
////clips.push(None);
|
|
||||||
////},
|
|
||||||
////Edn::Int(i) => {
|
|
||||||
////clips.push(Some(*i as usize));
|
|
||||||
////},
|
|
||||||
////_ => panic!("unexpected in scene '{name:?}': {edn:?}")
|
|
||||||
////});
|
|
||||||
////Ok(ArrangerScene {
|
|
||||||
////name: Arc::new(name.unwrap_or("").to_string().into()),
|
|
||||||
////color: ItemColor::random(),
|
|
||||||
////clips,
|
|
||||||
////})
|
|
||||||
////}
|
|
||||||
//}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue