Compare commits
12 Commits
804c2e221c
...
multitrack
| Author | SHA1 | Date | |
|---|---|---|---|
|
50ebac998c
|
|||
|
|
af97801d31 | ||
|
dcb6d7b0ea
|
|||
|
2385d3901f
|
|||
|
85aac6c8aa
|
|||
|
61c5799ffc
|
|||
|
8a6227d382
|
|||
|
86566221b1
|
|||
|
2052178ebf
|
|||
|
|
56aa1e1bbf | ||
|
c595cab609
|
|||
|
7fb2e6aade
|
2
.codespellrc
Normal file
2
.codespellrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[codespell]
|
||||||
|
ignore-words-list = ratatui
|
||||||
@@ -4,6 +4,7 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
|
- id: check-toml
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
|
|||||||
1841
Cargo.lock
generated
1841
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["engine", "examples/jack-sine", "lib"]
|
members = ["engine", "examples/jack-sine", "lib", "tui"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
# termix
|
# termix
|
||||||
[](https://ci.badhouseplants.net/repos/19)
|
[](https://ci.badhouseplants.net/repos/19)
|
||||||
|
|
||||||
|
|
||||||
|
# Requirenments
|
||||||
|
|
||||||
|
On all systems:
|
||||||
|
- protoc
|
||||||
|
|||||||
@@ -4,5 +4,18 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jack = "0.13.3"
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
|
coreaudio-rs = { version = "0.13.0", optional = true }
|
||||||
|
env_logger = "0.11.8"
|
||||||
|
jack = {version = "0.13.3", optional = true }
|
||||||
lib = { path = "../lib/" }
|
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"]
|
||||||
|
|||||||
0
engine/src/audio_engine/coreaudio_ab.rs
Normal file
0
engine/src/audio_engine/coreaudio_ab.rs
Normal file
46
engine/src/audio_engine/jack_ab.rs
Normal file
46
engine/src/audio_engine/jack_ab.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use crate::audio_engine::AudioBackend;
|
||||||
|
|
||||||
|
pub(crate) struct JackAudioBackend {
|
||||||
|
pub(crate) feature_jack: bool,
|
||||||
|
pub(crate) running: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JackAudioBackend {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
|
||||||
|
let feature_jack = cfg!(feature = "jack");
|
||||||
|
// TODO: It should be retrieved from the system
|
||||||
|
let running = true;
|
||||||
|
Self { feature_jack, running }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "jack")]
|
||||||
|
impl AudioBackend for JackAudioBackend {
|
||||||
|
fn start_client(&self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_client(&self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discover(&self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "jack"))]
|
||||||
|
impl AudioBackend for JackAudioBackend {
|
||||||
|
fn start_client(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_client(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discover(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
12
engine/src/audio_engine/mod.rs
Normal file
12
engine/src/audio_engine/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
pub(crate) mod jack_ab;
|
||||||
|
|
||||||
|
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(&self);
|
||||||
|
// Initialization of the client should happen
|
||||||
|
// when a project is opened.
|
||||||
|
fn init_client(&self);
|
||||||
|
fn discover(&self);
|
||||||
|
}
|
||||||
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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
74
engine/src/control_pane/grpc/mod.rs
Normal file
74
engine/src/control_pane/grpc/mod.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use lib::termix::audio_backend::{
|
||||||
|
self, Backend, BackendList, FILE_DESCRIPTOR_SET,
|
||||||
|
audio_backend_server::{AudioBackend, AudioBackendServer},
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
use tonic::{Response, transport::Server};
|
||||||
|
use tonic_reflection::server;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::{audio_engine::jack_ab::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(AudioBackendServer::new(audio_backend))
|
||||||
|
.serve(addr)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TermixAudioBackend {}
|
||||||
|
|
||||||
|
#[tonic::async_trait]
|
||||||
|
impl AudioBackend for TermixAudioBackend {
|
||||||
|
async fn start_client(
|
||||||
|
&self,
|
||||||
|
requesa: tonic::Request<()>,
|
||||||
|
) -> std::result::Result<tonic::Response<()>, tonic::Status> {
|
||||||
|
info!("starting the audio backend client");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_connection(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<()>,
|
||||||
|
) -> std::result::Result<tonic::Response<()>, tonic::Status> {
|
||||||
|
info!("initializing the connection to the audio backend");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_available_backends(
|
||||||
|
&self,
|
||||||
|
request: 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>>;
|
||||||
|
}
|
||||||
@@ -1,33 +1,29 @@
|
|||||||
use lib::{self, metadata::Metadata, track::Track};
|
pub(crate) mod audio_engine;
|
||||||
|
pub(crate) mod control_pane;
|
||||||
|
|
||||||
fn main() {
|
use crate::control_pane::ControlPane;
|
||||||
let mut current_project = lib::project::Project {
|
use clap::Parser;
|
||||||
name: "test".to_string(),
|
|
||||||
tracks: None,
|
|
||||||
regions: None,
|
|
||||||
current_sample: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let track = Track {
|
/// Simple program to greet a person
|
||||||
metadata: Metadata::new("test".to_string()),
|
#[derive(Parser, Debug)]
|
||||||
track_type: lib::track::TrackType::Audio,
|
#[command(version, about, long_about = None)]
|
||||||
active: true,
|
struct Args {
|
||||||
};
|
#[arg(long, default_value_t = 50051)]
|
||||||
|
grpc_port: i32,
|
||||||
current_project.tracks = Some(vec![track]);
|
#[arg(long, default_value_t = true)]
|
||||||
|
grpc_enable_reflections: bool,
|
||||||
/*
|
}
|
||||||
* Engine should run and wait for commands,
|
|
||||||
* but currently I need to implement the multitrack
|
#[tokio::main]
|
||||||
* audio player somehow
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
*
|
env_logger::init();
|
||||||
*
|
let args = Args::parse();
|
||||||
* for track in tracks {
|
|
||||||
* if track.is_region_playing(now: time) {
|
let grpc_control_pane = control_pane::grpc::Grpc {
|
||||||
* let current_region = track.get_current_region(now: time)
|
port: args.grpc_port,
|
||||||
* let audio = current_region.get_data(now: time)
|
enable_reflections: args.grpc_enable_reflections,
|
||||||
* }
|
};
|
||||||
* }
|
grpc_control_pane.start_server().await?;
|
||||||
*/
|
|
||||||
println!("Hello, world!");
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
Set this on mac ❯ export DYLD_FALLBACK_LIBRARY_PATH="$(brew --prefix jack)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}"
|
Set this on mac ❯ export DYLD_FALLBACK_LIBRARY_PATH="$(brew --prefix jack)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}"
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,13 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
prost = "0.14.1"
|
||||||
|
tokio = { version = "1.48.0", features = ["rt-multi-thread"] }
|
||||||
|
tonic = "0.14.2"
|
||||||
|
tonic-prost = "0.14.2"
|
||||||
uuid = { version = "1.18.1", features = ["v4"] }
|
uuid = { version = "1.18.1", features = ["v4"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
prost-build = "0.14.1"
|
||||||
|
prost-types = "0.14.1"
|
||||||
|
tonic-prost-build = "0.14.2"
|
||||||
|
|||||||
17
lib/build.rs
Normal file
17
lib/build.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use std::{env, io::Result, path::PathBuf};
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
//let proto_dir = "proto";
|
||||||
|
//let paths = fs::read_dir(proto_dir).unwrap();
|
||||||
|
//for path in paths {
|
||||||
|
// prost_build::compile_protos(&[path.unwrap().path()], &[proto_dir])?;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
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));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
22
lib/proto/audio_backend.proto
Normal file
22
lib/proto/audio_backend.proto
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package termix.audio_backend;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
service AudioBackend {
|
||||||
|
rpc StartClient(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
rpc InitConnection(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
rpc GetAvailableBackends(google.protobuf.Empty) returns (BackendList);
|
||||||
|
}
|
||||||
|
|
||||||
|
message BackendList {
|
||||||
|
repeated Backend backends = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Backend {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Shirt {
|
||||||
|
google.protobuf.Empty dummy = 1;
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
pub mod metadata;
|
pub mod termix {
|
||||||
|
pub mod audio_backend {
|
||||||
pub mod project;
|
pub const FILE_DESCRIPTOR_SET: &[u8] =
|
||||||
pub mod region;
|
tonic::include_file_descriptor_set!("audio_backend_descriptor");
|
||||||
pub mod track;
|
tonic::include_proto!("termix.audio_backend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
10
tui/Cargo.toml
Normal file
10
tui/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
|
color-eyre = "0.6.5"
|
||||||
|
crossterm = "0.29.0"
|
||||||
|
ratatui = "0.29.0"
|
||||||
31
tui/src/main.rs
Normal file
31
tui/src/main.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use color_eyre::Result;
|
||||||
|
use crossterm::event::{self, Event};
|
||||||
|
use ratatui::{DefaultTerminal, Frame};
|
||||||
|
|
||||||
|
/// Simple program to greet a person
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let _ = Args::parse();
|
||||||
|
color_eyre::install()?;
|
||||||
|
let terminal = ratatui::init();
|
||||||
|
let result = run(terminal);
|
||||||
|
ratatui::restore();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
terminal.draw(render)?;
|
||||||
|
if matches!(event::read()?, Event::Key(_)) {
|
||||||
|
break Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(frame: &mut Frame) {
|
||||||
|
frame.render_widget("hello world", frame.area());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user