wip: remove unused deps

This commit is contained in:
🪞👃🪞 2025-01-13 18:41:17 +01:00
parent ceaaeb1fc7
commit af2e237b94
3 changed files with 518 additions and 576 deletions

View file

@ -1520,3 +1520,304 @@
//self.perf.update(t0, scope);
//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,
////})
////}
//}

View file

@ -9,19 +9,13 @@
pub type Usually<T> = std::result::Result<T, Box<dyn Error>>;
/// Standard optional result type.
pub type Perhaps<T> = std::result::Result<Option<T>, Box<dyn Error>>;
pub mod mixer; pub use self::mixer::*;
pub use ::tek_tui::{
*,
tek_edn::*,
tek_input::*,
tek_output::*,
crossterm,
crossterm::event::{
*, tek_edn::*, tek_input::*, tek_output::*,
crossterm, crossterm::event::{
Event, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers,
KeyCode::{self, *},
},
ratatui,
ratatui::{
ratatui, ratatui::{
prelude::{Color, Style, Stylize, Buffer, Modifier},
buffer::Cell,
}
@ -31,25 +25,9 @@ pub use ::tek_jack::{self, *, jack::{*, contrib::*}};
pub use ::tek_midi::{self, *, midly::{*, num::*, live::*}};
pub use ::tek_sampler::{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::fmt::{Debug, Display, Formatter};
pub(crate) use std::io::{Stdout, stdout};
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;
pub(crate) use std::sync::atomic::{AtomicBool, Ordering::{self, *}};
pub(crate) use std::sync::{Arc, RwLock};
#[derive(Default)] pub struct App {
pub jack: Arc<RwLock<JackConnection>>,
pub edn: String,
@ -138,15 +116,75 @@ render!(TuiOut: (self: Meter<'a>) => col!(
render!(TuiOut: (self: Meters<'a>) => col!(
format!("L/{:>+9.3}", self.0[0]),
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 {
/// Name of track
name: Arc<str>,
name: Arc<str>,
/// Preferred width of track column
width: usize,
width: usize,
/// Identifying color of track
color: ItemPalette,
color: ItemPalette,
/// 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 {
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 {
/// Name of scene
name: Arc<str>,
@ -202,6 +299,87 @@ impl Scene {
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 {
pub fn sequencer (
jack: &Arc<RwLock<JackConnection>>, pool: MidiPool, editor: MidiEditor,
@ -405,7 +583,7 @@ command!(|self: AppCommand, state: App|match self {
match cmd {
// autoselect: automatically load selected clip in editor
// 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()),
_ => {}
}
@ -458,24 +636,6 @@ edn_command!(ClipCommand: |state: App| {
,b: usize] Self::SetColor(a.unwrap(), b.unwrap(), ItemPalette::random()))
});
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 {
Add,
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> {
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
}
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;
}
/// 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);
}
}
impl Arrangement for App {}
pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack {
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut Track>
{
@ -827,65 +895,11 @@ pub trait HasTracks: HasSelection + HasScenes + HasClock + HasJack {
}
Ok(())
}
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()
}
}
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 track_del (&mut self, index: usize) {
self.tracks_mut().remove(index);
for scene in self.scenes_mut().iter_mut() {
scene.clips.remove(index);
}
}
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut Scene>
@ -909,30 +923,6 @@ pub trait HasScenes: HasSelection {
}
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> {
let editing = self.is_editing();
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))
}))).boxed()).into()
}
}
impl Arrangement for App {}
pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack {
fn activate (&mut self) -> Usually<()> {
let selected = self.selected().clone();
match selected {

View file

@ -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,
////})
////}
//}