A lot of pointless stuff
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -469,6 +469,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"coreaudio-rs",
|
"coreaudio-rs",
|
||||||
|
"crossbeam-channel",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"jack",
|
"jack",
|
||||||
"lib",
|
"lib",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.53", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
coreaudio-rs = { version = "0.13.0", optional = true }
|
coreaudio-rs = { version = "0.13.0", optional = true }
|
||||||
|
crossbeam-channel = "0.5.15"
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
jack = {version = "0.13.3", optional = true }
|
jack = {version = "0.13.3", optional = true }
|
||||||
lib = { path = "../lib/" }
|
lib = { path = "../lib/" }
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::audio_engine::AudioBackend;
|
||||||
|
|
||||||
|
pub(crate) struct CoreAudioBackend {}
|
||||||
|
|
||||||
|
impl CoreAudioBackend {}
|
||||||
|
|
||||||
|
#[cfg(feature = "coreaudio")]
|
||||||
|
impl AudioBackend for CoreAudioBackend {
|
||||||
|
fn start_client(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coreaudio"))]
|
||||||
|
impl AudioBackend for CoreAudioBackend {
|
||||||
|
fn start_client(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
15
engine/src/audio_engine/dummy_ab.rs
Normal file
15
engine/src/audio_engine/dummy_ab.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::audio_engine::AudioBackend;
|
||||||
|
|
||||||
|
struct DummyAudioBackend {}
|
||||||
|
|
||||||
|
impl DummyAudioBackend {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioBackend for DummyAudioBackend {
|
||||||
|
fn start_client(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,106 @@
|
|||||||
|
use log::{info, warn};
|
||||||
|
use std::error::Error;
|
||||||
use crate::audio_engine::AudioBackend;
|
use crate::audio_engine::AudioBackend;
|
||||||
|
use crossbeam_channel::bounded;
|
||||||
|
|
||||||
|
#[cfg(feature = "jack")]
|
||||||
|
use jack;
|
||||||
|
|
||||||
pub(crate) struct JackAudioBackend {
|
pub(crate) struct JackAudioBackend {
|
||||||
pub(crate) feature_jack: bool,
|
pub(crate) feature_jack: bool,
|
||||||
pub(crate) running: bool,
|
pub(crate) running: bool,
|
||||||
|
status: JackStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JackAudioBackend {
|
impl JackAudioBackend {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
|
|
||||||
let feature_jack = cfg!(feature = "jack");
|
let feature_jack = cfg!(feature = "jack");
|
||||||
// TODO: It should be retrieved from the system
|
// TODO: It should be retrieved from the system
|
||||||
let running = true;
|
let running = true;
|
||||||
Self { feature_jack, running }
|
let status = JackStatus::default();
|
||||||
|
Self {
|
||||||
|
feature_jack,
|
||||||
|
running,
|
||||||
|
status,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "jack")]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct JackStatus {
|
||||||
|
client: Option<jack::Client>,
|
||||||
|
status: Option<jack::ClientStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "jack")]
|
#[cfg(feature = "jack")]
|
||||||
impl AudioBackend for JackAudioBackend {
|
impl AudioBackend for JackAudioBackend {
|
||||||
fn start_client(&self) {
|
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
todo!()
|
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_client(&self) {
|
fn init_client(&self) {
|
||||||
@@ -28,12 +110,21 @@ impl AudioBackend for JackAudioBackend {
|
|||||||
fn discover(&self) {
|
fn discover(&self) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_client(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "jack"))]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct JackStatus {}
|
||||||
|
|
||||||
#[cfg(not(feature = "jack"))]
|
#[cfg(not(feature = "jack"))]
|
||||||
impl AudioBackend for JackAudioBackend {
|
impl AudioBackend for JackAudioBackend {
|
||||||
fn start_client(&self) {
|
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
unimplemented!()
|
warn!("jack support is not enabled");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_client(&self) {
|
fn init_client(&self) {
|
||||||
@@ -43,4 +134,8 @@ impl AudioBackend for JackAudioBackend {
|
|||||||
fn discover(&self) {
|
fn discover(&self) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_client(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
pub(crate) mod jack_ab;
|
pub(crate) mod jack_ab;
|
||||||
|
pub(crate) mod coreaudio_ab;
|
||||||
|
pub(crate) mod dummy_ab;
|
||||||
|
|
||||||
pub(crate) trait AudioBackend {
|
pub(crate) trait AudioBackend {
|
||||||
// Start a audio backend client
|
// Start a audio backend client
|
||||||
// It should be executed either on the startup,
|
// It should be executed either on the startup,
|
||||||
// or when the audio backend is switched
|
// or when the audio backend is switched
|
||||||
fn start_client(&self);
|
fn start_client(&mut self) -> Result<(), Box<dyn Error>>;
|
||||||
// Initialization of the client should happen
|
|
||||||
// when a project is opened.
|
|
||||||
fn init_client(&self);
|
|
||||||
fn discover(&self);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
use lib::termix::audio_backend::{
|
use std::result::Result;
|
||||||
self, Backend, BackendList, FILE_DESCRIPTOR_SET,
|
|
||||||
audio_backend_server::{AudioBackend, AudioBackendServer},
|
use lib::termix::{self, audio_backend::{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::jack_ab::JackAudioBackend, control_pane::ControlPane};
|
|
||||||
|
|
||||||
pub(crate) struct Grpc {
|
pub(crate) struct Grpc {
|
||||||
pub(crate) port: i32,
|
pub(crate) port: i32,
|
||||||
@@ -28,7 +26,7 @@ impl ControlPane for Grpc {
|
|||||||
|
|
||||||
server
|
server
|
||||||
.add_service(audio_backend_reflections)
|
.add_service(audio_backend_reflections)
|
||||||
.add_service(AudioBackendServer::new(audio_backend))
|
.add_service(AudioBackendRpcServer::new(audio_backend))
|
||||||
.serve(addr)
|
.serve(addr)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -40,34 +38,51 @@ impl ControlPane for Grpc {
|
|||||||
pub struct TermixAudioBackend {}
|
pub struct TermixAudioBackend {}
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl AudioBackend for TermixAudioBackend {
|
impl AudioBackendRpc for TermixAudioBackend {
|
||||||
async fn start_client(
|
async fn start_client(
|
||||||
&self,
|
&self,
|
||||||
requesa: tonic::Request<()>,
|
request: tonic::Request<DesiredAudioBacked>,
|
||||||
) -> std::result::Result<tonic::Response<()>, tonic::Status> {
|
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||||
info!("starting the audio backend client");
|
info!("starting the audio backend client");
|
||||||
todo!()
|
match request.get_ref().backend() {
|
||||||
|
SupportedAudioBackends::AbUnspecified => {
|
||||||
|
unimplemented!("unsupported backend");
|
||||||
|
},
|
||||||
|
SupportedAudioBackends::AbJack => {
|
||||||
|
info!("trying to use JACK as the backend");
|
||||||
|
let mut ab = jack_ab::JackAudioBackend::new();
|
||||||
|
ab.start_client();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(Response::new(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn stop_client(
|
||||||
|
&self,
|
||||||
|
_: tonic::Request<()>,
|
||||||
|
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn init_connection(
|
async fn init_connection(
|
||||||
&self,
|
&self,
|
||||||
request: tonic::Request<()>,
|
request: tonic::Request<()>,
|
||||||
) -> std::result::Result<tonic::Response<()>, tonic::Status> {
|
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||||
info!("initializing the connection to the audio backend");
|
info!("initializing the connection to the audio backend");
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_available_backends(
|
async fn get_available_backends(
|
||||||
&self,
|
&self,
|
||||||
request: tonic::Request<()>,
|
_: tonic::Request<()>,
|
||||||
) -> Result<Response<BackendList>, tonic::Status> {
|
) -> Result<Response<BackendList>, tonic::Status> {
|
||||||
info!("discovering available backends");
|
info!("discovering available backends");
|
||||||
|
|
||||||
let mut response = BackendList::default();
|
let mut response = BackendList::default();
|
||||||
let jack = JackAudioBackend::new();
|
let jack = JackAudioBackend::new();
|
||||||
if jack.feature_jack {
|
if jack.feature_jack {
|
||||||
response.backends.push(Backend {
|
response.backends.push(Backend {
|
||||||
name: "jack".to_string(),
|
name: "jack".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(Response::new(response))
|
Ok(Response::new(response))
|
||||||
}
|
}
|
||||||
|
|||||||
18
lib/build.rs
18
lib/build.rs
@@ -1,17 +1,29 @@
|
|||||||
use std::{env, io::Result, path::PathBuf};
|
use std::{env, fs, io::Result, path::PathBuf};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
|
||||||
//let proto_dir = "proto";
|
//let proto_dir = "proto";
|
||||||
//let paths = fs::read_dir(proto_dir).unwrap();
|
//let paths = fs::read_dir(proto_dir).unwrap();
|
||||||
//for path in paths {
|
//for path in paths {
|
||||||
// prost_build::compile_protos(&[path.unwrap().path()], &[proto_dir])?;
|
// let path_str = path.unwrap().path().to_str().unwrap().to_string();
|
||||||
|
// let descriptor = format!("{}_descriptor.bin", path_str);
|
||||||
|
// tonic_prost_build::configure()
|
||||||
|
// .file_descriptor_set_path(out_dir.join("audio_backend_descriptor.bin"))
|
||||||
|
// .compile_protos(&["proto/audio_backend.proto"], &["proto"])
|
||||||
|
// .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
//}
|
//}
|
||||||
//
|
|
||||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
tonic_prost_build::configure()
|
tonic_prost_build::configure()
|
||||||
.file_descriptor_set_path(out_dir.join("audio_backend_descriptor.bin"))
|
.file_descriptor_set_path(out_dir.join("audio_backend_descriptor.bin"))
|
||||||
.compile_protos(&["proto/audio_backend.proto"], &["proto"])
|
.compile_protos(&["proto/audio_backend.proto"], &["proto"])
|
||||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
|
||||||
|
tonic_prost_build::configure()
|
||||||
|
.file_descriptor_set_path(out_dir.join("track_descriptor.bin"))
|
||||||
|
.compile_protos(&["proto/track.proto"], &["proto"])
|
||||||
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,41 @@ package termix.audio_backend;
|
|||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
service AudioBackend {
|
service AudioBackendRPC {
|
||||||
rpc StartClient(google.protobuf.Empty) returns (google.protobuf.Empty);
|
// Stop the active audio server
|
||||||
|
rpc StopClient(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
// Start the audio server of choice
|
||||||
|
rpc StartClient(DesiredAudioBacked) returns (google.protobuf.Empty);
|
||||||
|
// Get information about the possible audio backend configuration options
|
||||||
|
rpc DescribeBackend(DesiredAudioBacked) returns (AudioBackendDescription);
|
||||||
rpc InitConnection(google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc InitConnection(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
rpc GetAvailableBackends(google.protobuf.Empty) returns (BackendList);
|
rpc GetAvailableBackends(google.protobuf.Empty) returns (BackendList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SupportedAudioBackends {
|
||||||
|
AB_UNSPECIFIED = 0;
|
||||||
|
AB_JACK = 1;
|
||||||
|
AB_COREAUDIO = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DesiredAudioBacked {
|
||||||
|
SupportedAudioBackends backend = 1;
|
||||||
|
CoreAudioOptions core_audio_opts = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AudioBackendDescription {
|
||||||
|
CoreAudioAvailableOptions core_audio_description = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CoreAudioAvailableOptions {
|
||||||
|
repeated string input_devices = 1;
|
||||||
|
repeated string output_devices = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CoreAudioOptions {
|
||||||
|
string input_device = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message BackendList {
|
message BackendList {
|
||||||
repeated Backend backends = 1;
|
repeated Backend backends = 1;
|
||||||
}
|
}
|
||||||
@@ -16,7 +45,3 @@ message BackendList {
|
|||||||
message Backend {
|
message Backend {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Shirt {
|
|
||||||
google.protobuf.Empty dummy = 1;
|
|
||||||
}
|
|
||||||
|
|||||||
19
lib/proto/track.proto
Normal file
19
lib/proto/track.proto
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package termix.track;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
service TrackOp {
|
||||||
|
rpc Create(Track) returns (google.protobuf.Empty);
|
||||||
|
rpc List(google.protobuf.Empty) returns (Tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Track {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Tracks {
|
||||||
|
repeated Track tracks = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user