mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +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);
|
||||
//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>>;
|
||||
/// 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 {
|
||||
|
|
|
|||
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