Virtual device works! but why?

This commit is contained in:
mii443
2024-02-15 21:26:09 +09:00
parent 2a8d08a48e
commit 1949fbae94
7 changed files with 326 additions and 96 deletions

149
Cargo.lock generated
View File

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
@ -100,6 +115,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bindgen"
version = "0.69.4"
@ -324,6 +354,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "glob"
version = "0.3.1"
@ -342,6 +378,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
[[package]]
name = "home"
version = "0.5.9"
@ -499,6 +541,7 @@ dependencies = [
"rustfft",
"serde",
"serde_yaml",
"tokio",
]
[[package]]
@ -513,6 +556,26 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "ndk"
version = "0.7.0"
@ -683,6 +746,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.11"
@ -725,6 +798,15 @@ dependencies = [
"syn 2.0.48",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "oboe"
version = "0.5.0"
@ -786,6 +868,12 @@ dependencies = [
"nom",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pkg-config"
version = "0.3.29"
@ -904,6 +992,12 @@ dependencies = [
"realfft",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -998,12 +1092,31 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "socket2"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "strength_reduce"
version = "0.2.4"
@ -1058,6 +1171,36 @@ dependencies = [
"syn 2.0.48",
]
[[package]]
name = "tokio"
version = "1.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
@ -1119,6 +1262,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"

View File

@ -14,3 +14,4 @@ rustfft = "6.2.0"
serde = { version = "1.0.196", features = ["derive"] }
serde_yaml = "0.9.31"
rubato = "0.14.1"
tokio = { version = "1", features = ["full"] }

View File

@ -1,7 +1,7 @@
virtual_devices:
- name: "mic"
channels: 2
sample_rate: 96000
sample_rate: 192000
routes:
input:
- name: "Mic"

View File

@ -1,4 +1,6 @@
use num::Complex;
use rubato::{FastFixedIn, PolynomialDegree, Resampler};
use rustfft::FftPlanner;
#[inline]
pub fn resampling(
@ -6,13 +8,53 @@ pub fn resampling(
target_sample_rate: u32,
data: Vec<Vec<f32>>,
) -> Vec<Vec<f32>> {
let mut resampler = FastFixedIn::<f32>::new(
current_sample_rate as f64 / target_sample_rate as f64,
2.0,
PolynomialDegree::Cubic,
data[0].len(),
data.len(),
)
.unwrap();
resampler.process(&data, None).unwrap()
let mut resampled_data = Vec::new();
let mut planner = FftPlanner::new();
for channel_data in data {
let mut complex_data: Vec<Complex<f32>> =
channel_data.iter().map(|&x| Complex::new(x, 0.0)).collect();
let fft = planner.plan_fft_forward(complex_data.len());
fft.process(&mut complex_data);
let adjustment_factor = target_sample_rate as f64 / current_sample_rate as f64;
let new_size = (complex_data.len() as f64 * adjustment_factor).round() as usize;
let mut adjusted_complex_data;
if adjustment_factor == 1.0 {
adjusted_complex_data = complex_data;
} else if adjustment_factor > 1.0 {
adjusted_complex_data = vec![Complex::new(0.0, 0.0); new_size];
let step = complex_data.len() as f64 / new_size as f64;
for (i, sample) in adjusted_complex_data.iter_mut().enumerate() {
let idx = (i as f64 * step).floor() as usize;
*sample = complex_data
.get(idx)
.cloned()
.unwrap_or(Complex::new(0.0, 0.0));
}
} else {
adjusted_complex_data = complex_data
.into_iter()
.step_by(adjustment_factor.recip().round() as usize)
.collect();
adjusted_complex_data.resize(new_size, Complex::new(0.0, 0.0));
}
let ifft = planner.plan_fft_inverse(adjusted_complex_data.len());
ifft.process(&mut adjusted_complex_data);
let len = adjusted_complex_data.len();
let resampled_channel: Vec<f32> = adjusted_complex_data
.into_iter()
.map(|x| x.re / len as f32)
.collect();
resampled_data.push(resampled_channel);
}
resampled_data
}

View File

@ -6,7 +6,7 @@ use std::{
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
Device,
Device, Stream, StreamConfig, SupportedStreamConfig,
};
use crate::{args::Run, device::virtual_device::VirtualDevice};
@ -239,7 +239,7 @@ pub fn run(run: Run) {
}
}
std::thread::sleep(std::time::Duration::from_millis(500));
let mut threads = vec![];
for output_route in &config.routes.output {
match &output_route.device {
@ -250,7 +250,6 @@ pub fn run(run: Run) {
.unwrap();
let config = device.default_output_config().unwrap();
let channels = config.channels();
let sample_rate = config.sample_rate().0;
let virtual_device = virtual_devices
@ -261,9 +260,45 @@ pub fn run(run: Run) {
.cloned()
.collect::<Vec<_>>()[0]
.clone();
let thread = std::thread::spawn({
let device = device.clone();
move || {
let stream = create_virtual_device_input_stream(
config,
device.clone(),
virtual_device,
);
stream.play().unwrap();
loop {
std::thread::sleep(std::time::Duration::from_secs(1000));
}
}
});
threads.push(thread);
}
crate::config::Device::Remote { remote } => {
unimplemented!();
}
}
}
loop {
std::thread::sleep(std::time::Duration::from_secs(1000));
}
}
fn create_virtual_device_input_stream(
config: SupportedStreamConfig,
device: Device,
virtual_device: Arc<Mutex<VirtualDevice>>,
) -> Stream {
let sample_rate = config.sample_rate().0;
let channels = config.channels();
let index = virtual_device.lock().unwrap().add_output(sample_rate);
let stream = match config.sample_format() {
match config.sample_format() {
cpal::SampleFormat::I8 => device.build_output_stream(
&config.into(),
{
@ -329,24 +364,10 @@ pub fn run(run: Run) {
None,
),
sample_format => {
eprintln!("Unsupported sample format: {:?}", sample_format);
return;
panic!("Unsupported sample format: {:?}", sample_format);
}
}
.unwrap();
stream.play().unwrap();
streams.push(stream);
}
crate::config::Device::Remote { remote } => {
unimplemented!();
}
}
}
loop {
std::thread::sleep(std::time::Duration::from_secs(1000));
}
.unwrap()
}
fn input_callback<T>(
@ -376,12 +397,23 @@ fn output_callback<T>(
{
let mut virtual_device = virtual_device.lock().unwrap();
let vd_channels = virtual_device.channels;
let audio_data = virtual_device.take_output(
let mut audio_data = virtual_device.take_output(
index,
min(channels as u8, vd_channels),
sample_rate,
data.len() / channels,
);
let mut count = 0;
while audio_data.is_none() && count < 1 {
audio_data = virtual_device.take_output(
index,
min(channels as u8, vd_channels),
sample_rate,
data.len() / channels,
);
std::thread::sleep(std::time::Duration::from_millis(10));
count += 1;
}
if audio_data.is_none() {
println!("audio_data is none");
return;

View File

@ -9,6 +9,7 @@ pub struct VirtualDevice {
output_index: HashMap<u32, Vec<usize>>,
output_buffer: HashMap<u32, Vec<Vec<f32>>>,
simple_buffer: Vec<Vec<f32>>,
}
impl VirtualDevice {
@ -19,6 +20,7 @@ impl VirtualDevice {
sample_rate,
output_index: HashMap::new(),
output_buffer: HashMap::new(),
simple_buffer: vec![vec![]; channels as usize],
}
}
@ -53,10 +55,13 @@ impl VirtualDevice {
let end = start + take_size;
for channel in 0..channels {
if end > self.output_buffer[&sample_rate][channel as usize].len() {
return None;
}
if start >= self.output_buffer[&sample_rate][channel as usize].len() {
if end >= self.output_buffer[&sample_rate][channel as usize].len() {
println!(
"End of buffer: {}, {}[{}]",
end,
self.output_buffer[&sample_rate][channel as usize].len(),
channel as usize
);
return None;
}
}

View File

@ -1,14 +1,15 @@
mod args;
mod config;
mod commands;
mod device;
mod audio;
mod commands;
mod config;
mod device;
use clap::Parser;
use crate::args::Cli;
fn main() {
#[tokio::main]
async fn main() {
let cli = Cli::parse();
match cli.command {