Some WIP stuff
Some checks failed
ci/woodpecker/push/code_tests Pipeline failed
ci/woodpecker/push/pre_commit_test Pipeline failed

This commit is contained in:
2025-12-15 09:28:20 +01:00
parent 4810b19eea
commit ce5669636a
9 changed files with 88 additions and 103 deletions

7
Cargo.lock generated
View File

@@ -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"

View File

@@ -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]

View File

@@ -4,7 +4,7 @@ struct DummyAudioBackend {}
impl DummyAudioBackend { impl DummyAudioBackend {
fn new() -> Self { fn new() -> Self {
Self { } Self {}
} }
} }

View File

@@ -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!()
}
} }

View File

@@ -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

View File

@@ -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,

View File

@@ -0,0 +1,7 @@
[package]
name = "jack-playback"
version = "0.1.0"
edition = "2024"
[dependencies]
jack = "0.13.3"

View File

@@ -0,0 +1,5 @@
fn main() {
// 1. Create client
let (client, _status) =
jack::Client::new("rust_jack_simple", jack::ClientOptions::default()).unwrap();
}