WIP: Preparing the codebase, nothing important
This commit is contained in:
22
engine/Cargo.toml
Normal file
22
engine/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "engine"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
coreaudio-rs = { version = "0.13.0", optional = true }
|
||||
crossbeam-channel = "0.5.15"
|
||||
env_logger = "0.11.8"
|
||||
jack = {version = "0.13.3", optional = true }
|
||||
lib = { path = "../lib/" }
|
||||
log = "0.4.28"
|
||||
prost = "0.14.1"
|
||||
tokio = { version = "1.48.0", features = ["rt-multi-thread"] }
|
||||
tonic = "0.14.2"
|
||||
tonic-prost = "0.14.2"
|
||||
tonic-reflection = "0.14.2"
|
||||
|
||||
[features]
|
||||
jack = ["dep:jack"]
|
||||
coreaudio = ["dep:coreaudio-rs"]
|
||||
26
engine/src/audio_engine/coreaudio_ab.rs
Normal file
26
engine/src/audio_engine/coreaudio_ab.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
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(())
|
||||
}
|
||||
|
||||
fn describe_backend() -> Result<super::BackendDescription, Box<dyn std::error::Error>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coreaudio"))]
|
||||
impl AudioBackend for CoreAudioBackend {
|
||||
fn start_client(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
todo!()
|
||||
}
|
||||
fn describe_backend() -> Result<super::BackendDescription, Box<dyn std::error::Error>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
19
engine/src/audio_engine/dummy_ab.rs
Normal file
19
engine/src/audio_engine/dummy_ab.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
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!()
|
||||
}
|
||||
|
||||
fn describe_backend() -> Result<super::BackendDescription, Box<dyn std::error::Error>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
75
engine/src/audio_engine/jack_ab.rs
Normal file
75
engine/src/audio_engine/jack_ab.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::audio_engine::AudioBackend;
|
||||
use crossbeam_channel::bounded;
|
||||
use log::{info, warn};
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(feature = "jack")]
|
||||
use jack;
|
||||
#[cfg(feature = "jack")]
|
||||
use jack::ClientOptions;
|
||||
|
||||
pub(crate) struct JackAudioBackend {
|
||||
pub(crate) feature_jack: bool,
|
||||
pub(crate) running: bool,
|
||||
status: JackStatus,
|
||||
}
|
||||
|
||||
impl JackAudioBackend {
|
||||
pub(crate) fn new() -> Self {
|
||||
let feature_jack = cfg!(feature = "jack");
|
||||
// TODO: It should be retrieved from the system
|
||||
let running = true;
|
||||
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")]
|
||||
impl AudioBackend for JackAudioBackend {
|
||||
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get the possible input and output ports to use them for the session initialization
|
||||
fn describe_backend() -> Result<super::BackendDescription, Box<dyn Error>> {
|
||||
use jack::{Client, PortFlags};
|
||||
use crate::audio_engine::BackendDescription;
|
||||
|
||||
let (client, _) = Client::new("list_ports", ClientOptions::empty())?;
|
||||
let ports_in = client.ports(None, None, PortFlags::IS_INPUT);
|
||||
let ports_out = client.ports(None, None, PortFlags::IS_OUTPUT);
|
||||
let output = BackendDescription {
|
||||
audio_devices_out: ports_out,
|
||||
audio_devices_in: ports_in,
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "jack"))]
|
||||
#[derive(Default)]
|
||||
pub(crate) struct JackStatus {}
|
||||
|
||||
#[cfg(not(feature = "jack"))]
|
||||
impl AudioBackend for JackAudioBackend {
|
||||
fn start_client(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
warn!("jack support is not enabled");
|
||||
Ok(())
|
||||
}
|
||||
fn describe_backend() -> Result<super::BackendDescription, Box<dyn Error>> {
|
||||
unimplemented!("jack is disabled")
|
||||
}
|
||||
}
|
||||
17
engine/src/audio_engine/mod.rs
Normal file
17
engine/src/audio_engine/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::error::Error;
|
||||
|
||||
pub(crate) mod coreaudio_ab;
|
||||
pub(crate) mod dummy_ab;
|
||||
pub(crate) mod jack_ab;
|
||||
|
||||
pub(crate) struct BackendDescription {
|
||||
pub(crate) audio_devices_out: Vec<String>,
|
||||
pub(crate) audio_devices_in: Vec<String>,
|
||||
}
|
||||
pub(crate) trait AudioBackend {
|
||||
// Start a audio backend client
|
||||
// It should be executed either on the startup,
|
||||
// or when the audio backend is switched
|
||||
fn start_client(&mut self) -> Result<(), Box<dyn Error>>;
|
||||
fn describe_backend() -> Result<BackendDescription, Box<dyn Error>>;
|
||||
}
|
||||
9
engine/src/control_pane/dummy.rs
Normal file
9
engine/src/control_pane/dummy.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use crate::control_pane::ControlPane;
|
||||
|
||||
struct Dummy {}
|
||||
|
||||
impl ControlPane for Dummy {
|
||||
async fn start_server(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
125
engine/src/control_pane/grpc/mod.rs
Normal file
125
engine/src/control_pane/grpc/mod.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::result::Result;
|
||||
|
||||
use lib::termix::{
|
||||
self,
|
||||
audio_backend::{
|
||||
AudioBackendDescription, Backend, BackendList, DesiredAudioBacked, FILE_DESCRIPTOR_SET,
|
||||
SupportedAudioBackends,
|
||||
audio_backend_rpc_server::{AudioBackendRpc, AudioBackendRpcServer},
|
||||
},
|
||||
};
|
||||
use log::info;
|
||||
use tonic::{Response, Status, transport::Server};
|
||||
use tonic_reflection::server;
|
||||
|
||||
use crate::{
|
||||
audio_engine::{
|
||||
AudioBackend,
|
||||
jack_ab::{self, JackAudioBackend},
|
||||
},
|
||||
control_pane::ControlPane,
|
||||
};
|
||||
|
||||
pub(crate) struct Grpc {
|
||||
pub(crate) port: i32,
|
||||
pub(crate) enable_reflections: bool,
|
||||
}
|
||||
|
||||
impl ControlPane for Grpc {
|
||||
async fn start_server(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!("starting the grpc server on port {}", self.port);
|
||||
// TODO: Use the port from self
|
||||
let addr = "[::1]:50051".parse()?;
|
||||
let mut server = Server::builder();
|
||||
let audio_backend_reflections = server::Builder::configure()
|
||||
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
|
||||
.build_v1()
|
||||
.unwrap();
|
||||
let audio_backend = TermixAudioBackend::default();
|
||||
|
||||
server
|
||||
.add_service(audio_backend_reflections)
|
||||
.add_service(AudioBackendRpcServer::new(audio_backend))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TermixAudioBackend {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl AudioBackendRpc for TermixAudioBackend {
|
||||
async fn stop_client(
|
||||
&self,
|
||||
_: tonic::Request<()>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn start_client(
|
||||
&self,
|
||||
request: tonic::Request<DesiredAudioBacked>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
info!("starting the audio backend client");
|
||||
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();
|
||||
}
|
||||
SupportedAudioBackends::AbCoreaudio => todo!(),
|
||||
};
|
||||
Ok(Response::new(()))
|
||||
}
|
||||
|
||||
async fn describe_backend(
|
||||
&self,
|
||||
request: tonic::Request<DesiredAudioBacked>,
|
||||
) -> Result<tonic::Response<AudioBackendDescription>, tonic::Status> {
|
||||
info!("Describing the audio backend");
|
||||
match request.get_ref().backend() {
|
||||
SupportedAudioBackends::AbUnspecified => return Err(Status::not_found("backend os not specified")),
|
||||
SupportedAudioBackends::AbCoreaudio => todo!(),
|
||||
SupportedAudioBackends::AbJack => {
|
||||
let backend_desc= match jack_ab::JackAudioBackend::describe_backend(){
|
||||
Ok(desc) => desc,
|
||||
Err(err) => return Err(Status::internal(err.to_string())),
|
||||
};
|
||||
Ok(Response::new(AudioBackendDescription{
|
||||
core_audio_description: None,
|
||||
input_devices: backend_desc.audio_devices_in,
|
||||
output_devices: backend_desc.audio_devices_out
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn init_connection(
|
||||
&self,
|
||||
request: tonic::Request<()>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
info!("initializing the connection to the audio backend");
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_available_backends(
|
||||
&self,
|
||||
_: tonic::Request<()>,
|
||||
) -> Result<Response<BackendList>, tonic::Status> {
|
||||
info!("discovering available backends");
|
||||
|
||||
let mut response = BackendList::default();
|
||||
let jack = JackAudioBackend::new();
|
||||
if jack.feature_jack {
|
||||
response.backends.push(Backend {
|
||||
name: "jack".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
}
|
||||
6
engine/src/control_pane/mod.rs
Normal file
6
engine/src/control_pane/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub(crate) mod dummy;
|
||||
pub(crate) mod grpc;
|
||||
|
||||
pub(crate) trait ControlPane {
|
||||
async fn start_server(&self) -> Result<(), Box<dyn std::error::Error>>;
|
||||
}
|
||||
29
engine/src/main.rs
Normal file
29
engine/src/main.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
pub(crate) mod audio_engine;
|
||||
pub(crate) mod control_pane;
|
||||
|
||||
use crate::control_pane::ControlPane;
|
||||
use clap::Parser;
|
||||
|
||||
/// Simple program to greet a person
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[arg(long, default_value_t = 50051)]
|
||||
grpc_port: i32,
|
||||
#[arg(long, default_value_t = true)]
|
||||
grpc_enable_reflections: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
let args = Args::parse();
|
||||
|
||||
let grpc_control_pane = control_pane::grpc::Grpc {
|
||||
port: args.grpc_port,
|
||||
enable_reflections: args.grpc_enable_reflections,
|
||||
};
|
||||
grpc_control_pane.start_server().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user