Some WIP stuff
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -833,6 +833,13 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jack-playback"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jack",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jack-sine"
|
name = "jack-sine"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["engine", "examples/jack-sine", "lib", "tui"]
|
members = ["engine", "examples/jack-playback", "examples/jack-sine", "lib", "tui"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ struct DummyAudioBackend {}
|
|||||||
|
|
||||||
impl DummyAudioBackend {
|
impl DummyAudioBackend {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { }
|
Self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use log::{info, warn};
|
|
||||||
use std::error::Error;
|
|
||||||
use crate::audio_engine::AudioBackend;
|
use crate::audio_engine::AudioBackend;
|
||||||
use crossbeam_channel::bounded;
|
use crossbeam_channel::bounded;
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
#[cfg(feature = "jack")]
|
#[cfg(feature = "jack")]
|
||||||
use jack;
|
use jack;
|
||||||
@@ -36,83 +36,39 @@ pub(crate) struct JackStatus {
|
|||||||
#[cfg(feature = "jack")]
|
#[cfg(feature = "jack")]
|
||||||
impl AudioBackend for JackAudioBackend {
|
impl AudioBackend for JackAudioBackend {
|
||||||
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
info!("starting the jack client");
|
|
||||||
let (client, status) =
|
|
||||||
jack::Client::new("termix_engine", jack::ClientOptions::default())?;
|
|
||||||
//self.status.client = Some(client);
|
|
||||||
self.status.status = Some(status);
|
|
||||||
|
|
||||||
let out_port = client.register_port("termix_output", jack::AudioOut::default())?;
|
|
||||||
|
|
||||||
let (tx, rx) = bounded(1_000_000);
|
|
||||||
struct State {
|
|
||||||
out_port: jack::Port<jack::AudioOut>,
|
|
||||||
rx: crossbeam_channel::Receiver<f64>,
|
|
||||||
frequency: f64,
|
|
||||||
frame_t: f64,
|
|
||||||
time: f64,
|
|
||||||
}
|
|
||||||
let process = jack::contrib::ClosureProcessHandler::with_state(
|
|
||||||
State {
|
|
||||||
out_port,
|
|
||||||
rx,
|
|
||||||
frequency: 220.0,
|
|
||||||
frame_t: 1.0 / client.sample_rate() as f64,
|
|
||||||
time: 0.0,
|
|
||||||
},
|
|
||||||
|state, _, ps| -> jack::Control {
|
|
||||||
// Get output buffer
|
|
||||||
let out = state.out_port.as_mut_slice(ps);
|
|
||||||
|
|
||||||
// Check frequency requests
|
|
||||||
while let Ok(f) = state.rx.try_recv() {
|
|
||||||
state.time = 0.0;
|
|
||||||
state.frequency = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write output
|
|
||||||
for v in out.iter_mut() {
|
|
||||||
let x = state.frequency * state.time * 2.0 * std::f64::consts::PI;
|
|
||||||
let y = x.sin();
|
|
||||||
*v = y as f32;
|
|
||||||
state.time += state.frame_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue as normal
|
|
||||||
jack::Control::Continue
|
|
||||||
},
|
|
||||||
move |_, _, _| jack::Control::Continue,
|
|
||||||
);
|
|
||||||
let active_client = client.activate_async((), process).unwrap();
|
|
||||||
active_client
|
|
||||||
.as_client()
|
|
||||||
.connect_ports_by_name("termix_engine:termix_output", "system:playback_1")
|
|
||||||
.unwrap();
|
|
||||||
active_client
|
|
||||||
.as_client()
|
|
||||||
.connect_ports_by_name("termix_engine:termix_output", "system:playback_2")
|
|
||||||
.unwrap();
|
|
||||||
/* TODO:
|
|
||||||
* - Create a port for the main output
|
|
||||||
* - Define the jack options
|
|
||||||
* - buffer size
|
|
||||||
* - sample rate
|
|
||||||
* - Define the callback function
|
|
||||||
* - Activate the client
|
|
||||||
*/
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn init_client(&self) {
|
#[cfg(feature = "jack")]
|
||||||
todo!()
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use jack::ClientOptions;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn start_jack_client() {
|
||||||
|
let (client, _status) =
|
||||||
|
jack::Client::new("list_clients_example", ClientOptions::NO_START_SERVER)
|
||||||
|
.expect("Failed to create JACK client");
|
||||||
|
|
||||||
|
// Get all ports, then extract the unique client names
|
||||||
|
let ports = client.ports(None, None, jack::PortFlags::empty());
|
||||||
|
|
||||||
|
let mut clients: Vec<String> = ports
|
||||||
|
.iter()
|
||||||
|
.filter_map(|port| port.split(':').next().map(|s| s.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
clients.sort();
|
||||||
|
clients.dedup();
|
||||||
|
|
||||||
|
println!("JACK clients:");
|
||||||
|
for c in clients {
|
||||||
|
assert_eq!(c, "test");
|
||||||
|
println!(" - {}", c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discover(&self) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_client(&self) -> Result<(), Box<dyn Error>> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,16 +82,4 @@ impl AudioBackend for JackAudioBackend {
|
|||||||
warn!("jack support is not enabled");
|
warn!("jack support is not enabled");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_client(&self) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discover(&self) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_client(&self) -> Result<(), Box<dyn Error>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
pub(crate) mod jack_ab;
|
|
||||||
pub(crate) mod coreaudio_ab;
|
pub(crate) mod coreaudio_ab;
|
||||||
pub(crate) mod dummy_ab;
|
pub(crate) mod dummy_ab;
|
||||||
|
pub(crate) mod jack_ab;
|
||||||
|
|
||||||
pub(crate) trait AudioBackend {
|
pub(crate) trait AudioBackend {
|
||||||
// Start a audio backend client
|
// Start a audio backend client
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use lib::termix::{self, audio_backend::{Backend, BackendList, DesiredAudioBacked, FILE_DESCRIPTOR_SET, SupportedAudioBackends, audio_backend_rpc_server::{AudioBackendRpc, AudioBackendRpcServer}}};
|
use lib::termix::{
|
||||||
|
self,
|
||||||
|
audio_backend::{
|
||||||
|
AudioBackendDescription, Backend, BackendList, DesiredAudioBacked, FILE_DESCRIPTOR_SET,
|
||||||
|
SupportedAudioBackends,
|
||||||
|
audio_backend_rpc_server::{AudioBackendRpc, AudioBackendRpcServer},
|
||||||
|
},
|
||||||
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use tonic::{Response, transport::Server};
|
use tonic::{Response, transport::Server};
|
||||||
use tonic_reflection::server;
|
use tonic_reflection::server;
|
||||||
|
|
||||||
use crate::{audio_engine::{AudioBackend, jack_ab::{self, JackAudioBackend}}, control_pane::ControlPane};
|
use crate::{
|
||||||
|
audio_engine::{
|
||||||
|
AudioBackend,
|
||||||
|
jack_ab::{self, JackAudioBackend},
|
||||||
|
},
|
||||||
|
control_pane::ControlPane,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct Grpc {
|
pub(crate) struct Grpc {
|
||||||
pub(crate) port: i32,
|
pub(crate) port: i32,
|
||||||
@@ -39,6 +52,13 @@ pub struct TermixAudioBackend {}
|
|||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl AudioBackendRpc for TermixAudioBackend {
|
impl AudioBackendRpc for TermixAudioBackend {
|
||||||
|
async fn stop_client(
|
||||||
|
&self,
|
||||||
|
_: tonic::Request<()>,
|
||||||
|
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_client(
|
async fn start_client(
|
||||||
&self,
|
&self,
|
||||||
request: tonic::Request<DesiredAudioBacked>,
|
request: tonic::Request<DesiredAudioBacked>,
|
||||||
@@ -47,21 +67,23 @@ impl AudioBackendRpc for TermixAudioBackend {
|
|||||||
match request.get_ref().backend() {
|
match request.get_ref().backend() {
|
||||||
SupportedAudioBackends::AbUnspecified => {
|
SupportedAudioBackends::AbUnspecified => {
|
||||||
unimplemented!("unsupported backend");
|
unimplemented!("unsupported backend");
|
||||||
},
|
}
|
||||||
SupportedAudioBackends::AbJack => {
|
SupportedAudioBackends::AbJack => {
|
||||||
info!("trying to use JACK as the backend");
|
info!("trying to use JACK as the backend");
|
||||||
let mut ab = jack_ab::JackAudioBackend::new();
|
let mut ab = jack_ab::JackAudioBackend::new();
|
||||||
ab.start_client();
|
ab.start_client();
|
||||||
},
|
}
|
||||||
|
SupportedAudioBackends::AbCoreaudio => todo!(),
|
||||||
};
|
};
|
||||||
Ok(Response::new(()))
|
Ok(Response::new(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_client(
|
async fn describe_backend(
|
||||||
&self,
|
&self,
|
||||||
_: tonic::Request<()>,
|
request: tonic::Request<DesiredAudioBacked>,
|
||||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
) -> Result<tonic::Response<AudioBackendDescription>, tonic::Status> {
|
||||||
todo!()
|
info!("Describing the audio backend");
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
async fn init_connection(
|
async fn init_connection(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
7
examples/jack-playback/Cargo.toml
Normal file
7
examples/jack-playback/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "jack-playback"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jack = "0.13.3"
|
||||||
5
examples/jack-playback/src/main.rs
Normal file
5
examples/jack-playback/src/main.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fn main() {
|
||||||
|
// 1. Create client
|
||||||
|
let (client, _status) =
|
||||||
|
jack::Client::new("rust_jack_simple", jack::ClientOptions::default()).unwrap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user