audio: Implement save/restore of jack port configuration
This commit is contained in:
+87
-7
@@ -1,5 +1,6 @@
|
||||
use jack::{AudioIn, AudioOut, ClientOptions};
|
||||
use jack::{AudioIn, AudioOut, ClientOptions, NotificationHandler};
|
||||
use oximedia_metering::vu_meter::VuMeter;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -38,10 +39,70 @@ pub struct TtsOutStream {
|
||||
pub sample_rate: u32
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
struct AudioConfig {
|
||||
mic_in_connections: Vec<String>,
|
||||
tts_out_connections: Vec<String>
|
||||
}
|
||||
|
||||
impl AudioConfig {
|
||||
pub fn load() -> Self {
|
||||
if let Ok(contents) = std::fs::read_to_string("audio.json") {
|
||||
serde_json::from_str(contents.as_str()).unwrap()
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Notify {
|
||||
config: AudioConfig,
|
||||
mic_port: jack::Port<jack::Unowned>,
|
||||
tts_port: jack::Port<jack::Unowned>,
|
||||
log: mpsc::Sender<String>
|
||||
}
|
||||
|
||||
impl NotificationHandler for Notify {
|
||||
fn ports_connected(
|
||||
&mut self,
|
||||
client: &jack::Client,
|
||||
port_id_a: jack::PortId,
|
||||
port_id_b: jack::PortId,
|
||||
are_connected: bool,
|
||||
) {
|
||||
let port_a = client.port_by_id(port_id_a).unwrap();
|
||||
let port_b = client.port_by_id(port_id_b).unwrap();
|
||||
|
||||
let (stream_name, other_port, target_cfg) = if port_b == self.mic_port {
|
||||
("Microphone input", port_a, &mut self.config.mic_in_connections)
|
||||
} else if port_a == self.tts_port {
|
||||
("TTS output", port_b, &mut self.config.tts_out_connections)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Ok(port_name) = other_port.name() {
|
||||
if are_connected {
|
||||
self.log.blocking_send(format!("{} connected to {}", stream_name, port_name)).unwrap();
|
||||
target_cfg.push(port_name);
|
||||
} else {
|
||||
self.log.blocking_send(format!("{} disconnected from {}", stream_name, port_name)).unwrap();
|
||||
target_cfg.retain(|x| { x != &port_name} );
|
||||
}
|
||||
|
||||
let save_data = serde_json::to_string_pretty(&self.config).unwrap();
|
||||
std::fs::write("audio.json", save_data).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_audio_input(messages: &mpsc::Sender<String>) -> (AudioInputControl, MicStream, TtsOutStream) {
|
||||
|
||||
let (exit_tx, exit_rx) = oneshot::channel();
|
||||
|
||||
let config = AudioConfig::load();
|
||||
|
||||
let (mic_audio_sink, mic_audio_src) = mpsc::channel(32);
|
||||
let (tts_audio_sink, mut tts_audio_src) = mpsc::channel(32);
|
||||
let (volume_sink, volume_src) = watch::channel(0.);
|
||||
@@ -51,12 +112,32 @@ pub async fn start_audio_input(messages: &mpsc::Sender<String>) -> (AudioInputCo
|
||||
let mut tts_port = client.register_port("tts-out", AudioOut::default()).unwrap();
|
||||
let rate = client.sample_rate();
|
||||
|
||||
if let Ok(_) = client.connect_ports_by_name("mixxx-mic-1:capture_MONO", mic_port.name().unwrap().as_str()) {
|
||||
messages.send("Connected to audio.".into()).await.unwrap();
|
||||
} else {
|
||||
messages.send("Failed to reconnect to audio.".into()).await.unwrap();
|
||||
let mic_name = mic_port.name().unwrap();
|
||||
let tts_name = tts_port.name().unwrap();
|
||||
|
||||
for port in &config.mic_in_connections {
|
||||
if let Ok(_) = client.connect_ports_by_name(&port, &mic_name) {
|
||||
messages.send(format!("Connected mic to {}", port)).await.unwrap();
|
||||
} else {
|
||||
messages.send(format!("Failed to reconnect mic to {}.", port)).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for port in &config.tts_out_connections {
|
||||
if let Ok(_) = client.connect_ports_by_name(&tts_name, &port) {
|
||||
messages.send(format!("Connected TTS output to {}", port)).await.unwrap();
|
||||
} else {
|
||||
messages.send(format!("Failed to reconnect TTS output to {}.", port)).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let notifier = Notify {
|
||||
config,
|
||||
mic_port: mic_port.clone_unowned(),
|
||||
tts_port: tts_port.clone_unowned(),
|
||||
log: messages.clone()
|
||||
};
|
||||
|
||||
let mut meter = VuMeter::new(rate.into(), 1, None);
|
||||
let mut tts_output_buf = vec![];
|
||||
tts_output_buf.reserve(1024);
|
||||
@@ -78,7 +159,6 @@ pub async fn start_audio_input(messages: &mpsc::Sender<String>) -> (AudioInputCo
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if let Ok(mut next_outbuf) = tts_audio_src.try_recv() {
|
||||
tts_output_buf.append(&mut next_outbuf);
|
||||
}
|
||||
@@ -100,7 +180,7 @@ pub async fn start_audio_input(messages: &mpsc::Sender<String>) -> (AudioInputCo
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
let async_client = client.activate_async((), handler).unwrap();
|
||||
let async_client = client.activate_async(notifier, handler).unwrap();
|
||||
|
||||
exit_rx.await.unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user