group devices

This commit is contained in:
🪞👃🪞 2024-06-07 18:16:23 +03:00
parent 60627ac3e5
commit 5afed6f055
8 changed files with 147 additions and 267 deletions

View file

@ -1,307 +0,0 @@
use crate::prelude::*;
// TODO:
// - Meters: propagate clipping:
// - If one stage clips, all stages after it are marked red
// - If one track clips, all tracks that feed from it are marked red?
pub const ACTIONS: [(&'static str, &'static str);2] = [
("+/-", "Adjust"),
("Ins/Del", "Add/remove track"),
];
pub struct Mixer {
exited: bool,
jack: Jack<Notifications>,
tracks: Vec<Track>,
selected_track: usize,
selected_column: usize,
}
pub struct Track {
name: String,
channels: u8,
input_ports: Vec<Port<AudioIn>>,
pre_gain_meter: f64,
gain: f64,
insert_ports: Vec<Port<AudioOut>>,
return_ports: Vec<Port<AudioIn>>,
post_gain_meter: f64,
post_insert_meter: f64,
level: f64,
pan: f64,
output_ports: Vec<Port<AudioOut>>,
post_fader_meter: f64,
route: String,
}
impl Mixer {
pub fn new () -> Result<Self, Box<dyn Error>> {
let (client, status) = Client::new(
"bloop-mixer",
ClientOptions::NO_START_SERVER
)?;
let jack = client.activate_async(
Notifications,
ClosureProcessHandler::new(Box::new(
move |_: &Client, _: &ProcessScope| -> Control {
Control::Continue
}) as Box<dyn FnMut(&Client, &ProcessScope)->Control + Send>
)
)?;
Ok(Self {
exited: false,
selected_column: 0,
selected_track: 1,
tracks: vec![
Track::new(&jack.as_client(), 1, "Kick")?,
Track::new(&jack.as_client(), 1, "Snare")?,
Track::new(&jack.as_client(), 2, "Hihats")?,
Track::new(&jack.as_client(), 2, "Sample")?,
Track::new(&jack.as_client(), 2, "Bus 1")?,
Track::new(&jack.as_client(), 2, "Bus 2")?,
Track::new(&jack.as_client(), 2, "Mix")?,
],
jack,
})
}
}
impl Track {
pub fn new (jack: &Client, channels: u8, name: &str) -> Result<Self, Box<dyn Error>> {
let mut input_ports = vec![];
let mut insert_ports = vec![];
let mut return_ports = vec![];
let mut output_ports = vec![];
for channel in 1..=channels {
input_ports.push(jack.register_port(&format!("{name} [input {channel}]"), AudioIn::default())?);
output_ports.push(jack.register_port(&format!("{name} [out {channel}]"), AudioOut::default())?);
let insert_port = jack.register_port(&format!("{name} [pre {channel}]"), AudioOut::default())?;
let return_port = jack.register_port(&format!("{name} [insert {channel}]"), AudioIn::default())?;
jack.connect_ports(&insert_port, &return_port)?;
insert_ports.push(insert_port);
return_ports.push(return_port);
}
Ok(Self {
name: name.into(),
channels,
input_ports,
pre_gain_meter: 0.0,
gain: 0.0,
post_gain_meter: 0.0,
insert_ports,
return_ports,
post_insert_meter: 0.0,
level: 0.0,
pan: 0.0,
post_fader_meter: 0.0,
route: "---".into(),
output_ports,
})
}
}
impl Exitable for Mixer {
fn exit (&mut self) {
self.exited = true
}
fn exited (&self) -> bool {
self.exited
}
}
impl WidgetRef for Mixer {
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
}
}
pub fn render (
state: &mut Mixer,
stdout: &mut Stdout,
mut offset: (u16, u16)
) -> Result<(), Box<dyn Error>> {
render_table(state, stdout, offset)?;
render_meters(state, stdout, offset)?;
Ok(())
}
fn render_table (
state: &mut Mixer,
stdout: &mut Stdout,
offset: (u16, u16)
) -> Result<(), Box<dyn Error>> {
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
stdout.queue(
move_to(0, 0)
)?.queue(
Print(" Name Gain FX1 Pan Level FX2 Route")
)?;
for (i, track) in state.tracks.iter().enumerate() {
let row = (i + 1) as u16;
for (j, (column, field)) in [
(0, format!(" {:7} ", track.name)),
(12, format!(" {:.1}dB ", track.gain)),
(22, format!(" [ ] ")),
(30, format!(" C ")),
(35, format!(" {:.1}dB ", track.level)),
(45, format!(" [ ] ")),
(51, format!(" {:7} ", track.route)),
].into_iter().enumerate() {
stdout.queue(move_to(column, row))?;
if state.selected_track == i && state.selected_column == j {
stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
} else {
stdout.queue(PrintStyledContent(field.to_string().bold()))?;
}
}
}
Ok(())
}
fn render_meters (
state: &mut Mixer,
stdout: &mut Stdout,
offset: (u16, u16)
) -> Result<(), Box<dyn Error>> {
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
for (i, track) in state.tracks.iter().enumerate() {
let row = (i + 1) as u16;
stdout
.queue(move_to(10, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(20, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(28, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(43, row))?.queue(PrintStyledContent("".green()))?;
}
Ok(())
}
impl HandleInput for Mixer {
fn handle (&mut self, event: &Event) -> Result<(), Box<dyn Error>> {
handle(self, event)
}
}
pub fn handle (state: &mut Mixer, event: &Event) -> Result<(), Box<dyn Error>> {
if let Event::Input(crossterm::event::Event::Key(event)) = event {
match event.code {
KeyCode::Char('c') => {
if event.modifiers == KeyModifiers::CONTROL {
state.exit();
}
},
KeyCode::Down => {
state.selected_track = (state.selected_track + 1) % state.tracks.len();
println!("{}", state.selected_track);
},
KeyCode::Up => {
if state.selected_track == 0 {
state.selected_track = state.tracks.len() - 1;
} else {
state.selected_track = state.selected_track - 1;
}
println!("{}", state.selected_track);
},
KeyCode::Left => {
if state.selected_column == 0 {
state.selected_column = 6
} else {
state.selected_column = state.selected_column - 1;
}
},
KeyCode::Right => {
if state.selected_column == 6 {
state.selected_column = 0
} else {
state.selected_column = state.selected_column + 1;
}
},
_ => {
println!("\n{event:?}");
}
}
}
Ok(())
}
pub struct Notifications;
impl NotificationHandler for Notifications {
fn thread_init (&self, _: &Client) {
}
fn shutdown (&mut self, status: ClientStatus, reason: &str) {
}
fn freewheel (&mut self, _: &Client, is_enabled: bool) {
}
fn sample_rate (&mut self, _: &Client, _: Frames) -> Control {
Control::Quit
}
fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) {
}
fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) {
}
fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
Control::Continue
}
fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) {
}
fn graph_reorder (&mut self, _: &Client) -> Control {
Control::Continue
}
fn xrun (&mut self, _: &Client) -> Control {
Control::Continue
}
}
//impl<W: Write> Input<TUI<W>, bool> for Mixer {
//fn handle (&mut self, engine: &mut TUI<W>) -> Result<Option<bool>> {
//Ok(None)
//}
//}
//impl<W: Write> Output<TUI<W>, [u16;2]> for Mixer {
//fn render (&self, envine: &mut TUI<W>) -> Result<Option<[u16;2]>> {
//let tracks_table = Columns::new()
//.add(titles)
//.add(input_meters)
//.add(gains)
//.add(gain_meters)
//.add(pres)
//.add(pre_meters)
//.add(levels)
//.add(pans)
//.add(pan_meters)
//.add(posts)
//.add(routes)
//Rows::new()
//.add(Columns::new()
//.add(Rows::new()
//.add("[Arrows]".bold())
//.add("Navigate"))
//.add(Rows::new()
//.add("[+/-]".bold())
//.add("Adjust"))
//.add(Rows::new()
//.add("[Ins/Del]".bold())
//.add("Add/remove track")))
//.add(tracks_table)
//.render(engine)
//}
//}