-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add cpal source, refactor sources
now splitting stream in channels and parsing stream format are separate but handled by the source, so that cpal source can skip format parsing. added some nicer types, and also range now is +-1 because way easier than 32k sorry this is a huge commit, ive been messing with it for a while and changed a lot across whole project, at this point i'm just committing it because it can only get worse ehe
- Loading branch information
Showing
15 changed files
with
283 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
use std::sync::mpsc; | ||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | ||
|
||
use super::{stream_to_matrix, Matrix}; | ||
|
||
pub struct DefaultAudioDeviceWithCPAL { | ||
rx: mpsc::Receiver<Matrix<f64>>, | ||
#[allow(unused)] | ||
stream: cpal::Stream, | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum AudioDeviceErrors { | ||
#[error("{0}")] | ||
Device(#[from] cpal::DevicesError), | ||
|
||
#[error("device not found")] | ||
NotFound, | ||
|
||
#[error("{0}")] | ||
BuildStream(#[from] cpal::BuildStreamError), | ||
|
||
#[error("{0}")] | ||
PlayStream(#[from] cpal::PlayStreamError), | ||
} | ||
|
||
impl DefaultAudioDeviceWithCPAL { | ||
pub fn new(device: Option<&str>, channels: u32, sample_rate: u32, buffer: u32, timeout_secs: u64) -> Result<Box<impl super::DataSource<f64>>, AudioDeviceErrors> { | ||
let host = cpal::default_host(); | ||
let device = match device { | ||
Some(name) => host | ||
.input_devices()? | ||
.find(|x| x.name().as_deref().unwrap_or("") == name) | ||
.ok_or(AudioDeviceErrors::NotFound)?, | ||
None => host | ||
.default_input_device() | ||
.ok_or(AudioDeviceErrors::NotFound)?, | ||
}; | ||
let cfg = cpal::StreamConfig { | ||
channels: channels as u16, | ||
buffer_size: cpal::BufferSize::Fixed(buffer * channels * 2), | ||
sample_rate: cpal::SampleRate(sample_rate), | ||
}; | ||
let (tx, rx) = mpsc::channel(); | ||
let stream = device.build_input_stream( | ||
&cfg, | ||
move |data:&[f32], _info| tx.send(stream_to_matrix(data.iter().cloned(), channels as usize, 1.)).unwrap_or(()), | ||
|e| eprintln!("error in input stream: {e}"), | ||
Some(std::time::Duration::from_secs(timeout_secs)), | ||
)?; | ||
stream.play()?; | ||
|
||
Ok(Box::new(DefaultAudioDeviceWithCPAL { stream, rx })) | ||
} | ||
} | ||
|
||
impl super::DataSource<f64> for DefaultAudioDeviceWithCPAL { | ||
fn recv(&mut self) -> Option<super::Matrix<f64>> { | ||
match self.rx.recv() { | ||
Ok(x) => Some(x), | ||
Err(e) => { | ||
println!("error receiving from source? {e}"); | ||
None | ||
}, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use std::{fs::File, io::Read}; | ||
|
||
use super::{format::{SampleParser, Signed16PCM}, stream_to_matrix, Matrix}; | ||
|
||
pub struct FileSource { | ||
file: File, | ||
buffer: Vec<u8>, | ||
channels: usize, | ||
sample_rate: usize, | ||
limit_rate: bool, | ||
// TODO when all data is available (eg, file) limit data flow to make it | ||
// somehow visualizable. must be optional because named pipes block | ||
// TODO support more formats | ||
} | ||
|
||
impl FileSource { | ||
#[allow(clippy::new_ret_no_self)] | ||
pub fn new(path: &str, channels: usize, sample_rate: usize, buffer: usize, limit_rate: bool) -> Result<Box<dyn super::DataSource<f64>>, std::io::Error> { | ||
Ok(Box::new( | ||
FileSource { | ||
channels, sample_rate, limit_rate, | ||
file: File::open(path)?, | ||
buffer: vec![0u8; buffer * channels], | ||
} | ||
)) | ||
} | ||
} | ||
|
||
impl super::DataSource<f64> for FileSource { | ||
fn recv(&mut self) -> Option<Matrix<f64>> { | ||
match self.file.read_exact(&mut self.buffer) { | ||
Ok(()) => Some( | ||
stream_to_matrix( | ||
self.buffer.chunks(2).map(Signed16PCM::parse), | ||
self.channels, | ||
32768.0, | ||
) | ||
), | ||
Err(_e) => None, // TODO log it | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
|
||
pub trait SampleParser<T> { | ||
fn parse(data: &[u8]) -> T; | ||
} | ||
|
||
pub struct Signed16PCM; | ||
impl SampleParser<f64> for Signed16PCM { | ||
fn parse(chunk: &[u8]) -> f64 { | ||
(chunk[0] as i16 | (chunk[1] as i16) << 8) as f64 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
pub mod format; | ||
|
||
#[cfg(feature = "pulseaudio")] | ||
pub mod pulse; | ||
|
||
pub mod file; | ||
|
||
pub mod cpal; | ||
|
||
pub type Matrix<T> = Vec<Vec<T>>; | ||
|
||
pub trait DataSource<T> { | ||
fn recv(&mut self) -> Option<Matrix<T>>; // TODO convert in Result and make generic error | ||
} | ||
|
||
/// separate a stream of alternating channels into a matrix of channel streams: | ||
/// L R L R L R L R L R | ||
/// becomes | ||
/// L L L L L | ||
/// R R R R R | ||
pub fn stream_to_matrix<I, O>(stream: impl Iterator<Item = I>, channels: usize, norm: O) -> Matrix<O> | ||
where I : Copy + Into<O>, O : Copy + std::ops::Div<Output = O> | ||
{ | ||
let mut out = vec![vec![]; channels]; | ||
let mut channel = 0; | ||
for sample in stream { | ||
out[channel].push(sample.into() / norm); | ||
channel = (channel + 1) % channels; | ||
} | ||
out | ||
} |
Oops, something went wrong.