mirror of
https://github.com/mii443/maudio-router.git
synced 2025-08-22 16:05:35 +00:00
add config parser
This commit is contained in:
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -370,6 +370,12 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
@ -490,6 +496,8 @@ dependencies = [
|
|||||||
"num",
|
"num",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rustfft",
|
"rustfft",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -908,6 +916,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@ -923,6 +937,39 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.196"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.196"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.9.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"unsafe-libyaml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1022,6 +1069,12 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unsafe-libyaml"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -11,3 +11,5 @@ cpal = { git = "https://github.com/RustAudio/cpal.git", features = ["asio"] }
|
|||||||
num = "0.4.1"
|
num = "0.4.1"
|
||||||
num-traits = "0.2.18"
|
num-traits = "0.2.18"
|
||||||
rustfft = "6.2.0"
|
rustfft = "6.2.0"
|
||||||
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
|
serde_yaml = "0.9.31"
|
||||||
|
28
src/args.rs
28
src/args.rs
@ -6,33 +6,25 @@ use clap::{Args, Parser, Subcommand};
|
|||||||
#[command(about = "A network audio router.", long_about = None)]
|
#[command(about = "A network audio router.", long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
pub command: Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Commands {
|
pub enum Commands {
|
||||||
/// Run router server
|
/// Run router
|
||||||
Server(Server),
|
Run(Run),
|
||||||
/// Run router client
|
/// Device utilities
|
||||||
Client(Client),
|
Device(Device),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
struct Server {
|
pub struct Run {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
device: String,
|
pub config: String,
|
||||||
#[arg(short, long)]
|
|
||||||
listen_address: String,
|
|
||||||
#[arg(short, long)]
|
|
||||||
port: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
struct Client {
|
pub struct Device {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
server_ip: String,
|
pub list: bool
|
||||||
#[arg(short, long)]
|
|
||||||
port: String,
|
|
||||||
#[arg(short, long)]
|
|
||||||
device: String,
|
|
||||||
}
|
}
|
11
src/commands/device.rs
Normal file
11
src/commands/device.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
|
|
||||||
|
use crate::args::Device;
|
||||||
|
|
||||||
|
pub fn device(device: Device) {
|
||||||
|
let host = cpal::default_host();
|
||||||
|
|
||||||
|
if device.list {
|
||||||
|
host.input_devices().unwrap().enumerate().for_each(|(i, device)| println!("{}: {}", i, device.name().unwrap()));
|
||||||
|
}
|
||||||
|
}
|
2
src/commands/mod.rs
Normal file
2
src/commands/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod device;
|
||||||
|
pub mod run;
|
33
src/commands/run.rs
Normal file
33
src/commands/run.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use crate::args::Run;
|
||||||
|
|
||||||
|
fn reshape_audio_data<T>(input: &[T], channels: usize) -> Vec<Vec<T>>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let mut output = vec![vec![]; channels];
|
||||||
|
for frame in input.chunks(channels) {
|
||||||
|
for (i, sample) in frame.iter().enumerate() {
|
||||||
|
output[i].push(sample.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_flat_audio_data<T>(input: &[Vec<T>]) -> Vec<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let channels = input.len();
|
||||||
|
let frames = input[0].len();
|
||||||
|
let mut output = vec![];
|
||||||
|
for i in 0..frames {
|
||||||
|
for j in 0..channels {
|
||||||
|
output.push(input[j][i].clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(args: Run) {
|
||||||
|
|
||||||
|
}
|
52
src/config.rs
Normal file
52
src/config.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct RoutesConfig {
|
||||||
|
routes: Routes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Routes {
|
||||||
|
input: Vec<Input>,
|
||||||
|
output: Vec<Output>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Input {
|
||||||
|
name: String,
|
||||||
|
virtual_device: String,
|
||||||
|
device: Device,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Output {
|
||||||
|
name: String,
|
||||||
|
input: OutputInput,
|
||||||
|
device: Device,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct OutputInput {
|
||||||
|
virtual_device: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Device {
|
||||||
|
Local { local: LocalDevice },
|
||||||
|
Remote { remote: RemoteDevice },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct LocalDevice {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct RemoteDevice {
|
||||||
|
address: String,
|
||||||
|
port: u16,
|
||||||
|
protocol: String,
|
||||||
|
buffer: usize,
|
||||||
|
channels: u8,
|
||||||
|
}
|
95
src/main.rs
95
src/main.rs
@ -1,95 +1,20 @@
|
|||||||
mod args;
|
mod args;
|
||||||
|
mod config;
|
||||||
use std::{io::Write, sync::{Arc, Mutex}};
|
mod commands;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
|
||||||
|
|
||||||
use crate::args::Cli;
|
use crate::args::Cli;
|
||||||
fn reshape_audio_data<T>(input: &[T], channels: usize) -> Vec<Vec<T>>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
let mut output = vec![vec![]; channels];
|
|
||||||
for frame in input.chunks(channels) {
|
|
||||||
for (i, sample) in frame.iter().enumerate() {
|
|
||||||
output[i].push(sample.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_flat_audio_data<T>(input: &[Vec<T>]) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
let channels = input.len();
|
|
||||||
let frames = input[0].len();
|
|
||||||
let mut output = vec![];
|
|
||||||
for i in 0..frames {
|
|
||||||
for j in 0..channels {
|
|
||||||
output.push(input[j][i].clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
println!("{:?}", cli);
|
match cli.command {
|
||||||
|
args::Commands::Run(run) => {
|
||||||
let host = cpal::default_host();
|
commands::run::run(run);
|
||||||
|
}
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
args::Commands::Device(device) => {
|
||||||
|
commands::device::device(device);
|
||||||
println!("Available input devices:");
|
}
|
||||||
host.input_devices().unwrap().enumerate().for_each(|(i, device)| println!("{}: {}", i, device.name().unwrap()));
|
}
|
||||||
|
|
||||||
print!("Select input device [0~{}]> ", host.input_devices().unwrap().count() - 1);
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
|
|
||||||
let mut input = String::new();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
let input = input.trim().parse::<usize>().unwrap();
|
|
||||||
|
|
||||||
let device = host.input_devices().unwrap().nth(input).unwrap();
|
|
||||||
|
|
||||||
let audio_data = Arc::new(Mutex::new(vec![]));
|
|
||||||
|
|
||||||
let input_stream = device.build_input_stream(
|
|
||||||
&device.default_input_config().unwrap().config(),
|
|
||||||
{
|
|
||||||
let audio_data = audio_data.clone();
|
|
||||||
move |data: &[f32], _: &_| {
|
|
||||||
let audio_data = &mut *audio_data.lock().unwrap();
|
|
||||||
*audio_data = data.iter().map(|d| d.clone()).collect::<Vec<f32>>();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |err| eprintln!("error: {}", err),
|
|
||||||
None
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let output_stream = device.build_output_stream(
|
|
||||||
&device.default_output_config().unwrap().config(),
|
|
||||||
{
|
|
||||||
let audio_data = audio_data.clone();
|
|
||||||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
|
||||||
let audio_data = audio_data.lock().unwrap().clone();
|
|
||||||
let audio_data = reshape_audio_data(&audio_data, 22);
|
|
||||||
|
|
||||||
let audio_data = to_flat_audio_data(&audio_data);
|
|
||||||
data.copy_from_slice(&audio_data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |err| eprintln!("error: {}", err),
|
|
||||||
None
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
input_stream.play().unwrap();
|
|
||||||
output_stream.play().unwrap();
|
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1000));
|
|
||||||
drop(input_stream);
|
|
||||||
drop(output_stream);
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user