mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
289 lines
9.6 KiB
Rust
289 lines
9.6 KiB
Rust
use crate::prelude::*;
|
|
|
|
pub struct Mixer {
|
|
name: String,
|
|
jack: Jack<Notifications>,
|
|
tracks: Vec<Track>,
|
|
selected_track: usize,
|
|
selected_column: usize,
|
|
}
|
|
|
|
impl Mixer {
|
|
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
|
let (client, status) = Client::new(
|
|
name,
|
|
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(DynamicDevice::new(render, handle, |_|{}, Self {
|
|
name: name.into(),
|
|
selected_column: 0,
|
|
selected_track: 1,
|
|
tracks: vec![
|
|
Track::new(&jack.as_client(), 1, "Mono 1")?,
|
|
Track::new(&jack.as_client(), 1, "Mono 2")?,
|
|
Track::new(&jack.as_client(), 2, "Stereo 1")?,
|
|
Track::new(&jack.as_client(), 2, "Stereo 2")?,
|
|
Track::new(&jack.as_client(), 2, "Stereo 3")?,
|
|
Track::new(&jack.as_client(), 2, "Bus 1")?,
|
|
Track::new(&jack.as_client(), 2, "Bus 2")?,
|
|
Track::new(&jack.as_client(), 2, "Mix")?,
|
|
],
|
|
jack,
|
|
}))
|
|
}
|
|
}
|
|
|
|
pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
|
-> Usually<(u16, u16)>
|
|
{
|
|
if area.height < 2 {
|
|
return Ok((0, 0))
|
|
}
|
|
area.x = area.width.saturating_sub(80) / 2;
|
|
area.width = area.width.min(80);
|
|
area.height = state.tracks.len() as u16 + 2;
|
|
draw_box(buf, area);
|
|
let x = area.x + 1;
|
|
let y = area.y + 1;
|
|
let h = area.height - 2;
|
|
for (i, track) in state.tracks.iter().enumerate() {
|
|
//buf.set_string(
|
|
//x, y + index as u16,
|
|
//&track.name, Style::default().bold().not_dim()
|
|
//);
|
|
for (j, (column, field)) in [
|
|
(0, format!(" {:10} ", 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() {
|
|
buf.set_string(
|
|
x + column as u16,
|
|
y + i as u16,
|
|
field,
|
|
if state.selected_track == i && state.selected_column == j {
|
|
Style::default().white().bold().not_dim()
|
|
} else {
|
|
Style::default().not_dim()
|
|
}
|
|
);
|
|
//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()))?;
|
|
//}
|
|
//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(())
|
|
//}
|
|
}
|
|
}
|
|
Ok((20,10))
|
|
}
|
|
|
|
pub fn handle (state: &mut Mixer, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
|
if let EngineEvent::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(())
|
|
}
|
|
|
|
// 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 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 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,
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
//}
|
|
//}
|