From b4fd91ea13b91d2d2641e7a0caaeb025b5f33d25 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Tue, 23 Jun 2020 22:22:45 +0200 Subject: [PATCH] Added a pulse audio sink --- Cargo.toml | 2 + examples/sine-output/main.rs | 15 ++++++++ src/error.rs | 4 ++ src/lib.rs | 15 ++++---- src/sinks.rs | 74 ++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 examples/sine-output/main.rs create mode 100644 src/sinks.rs diff --git a/Cargo.toml b/Cargo.toml index 0b8605f..3ce9c15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,5 @@ edition = "2018" [dependencies] num = "*" +libpulse-binding = "2.16.0" +libpulse-simple-binding = "*" diff --git a/examples/sine-output/main.rs b/examples/sine-output/main.rs new file mode 100644 index 0000000..bb91b8f --- /dev/null +++ b/examples/sine-output/main.rs @@ -0,0 +1,15 @@ +extern crate rusty_dsp; + +use std::io; + +use rusty_dsp::multiply::MultiplyConst; +use rusty_dsp::sinks::SoundCardSink; +use rusty_dsp::sources::SinSource; + +fn main() { + let source = SinSource::new(440.0, 48000.0); + let mult = MultiplyConst::from(source, 0.3); + let mut sink = SoundCardSink::from(mult, 48000); + + sink.start().unwrap(); +} diff --git a/src/error.rs b/src/error.rs index 6b7c476..dfafe17 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,3 +5,7 @@ pub enum DspError { WouldBlock, Error(String), } + +pub fn new_error(msg: &str) -> DspError { + DspError::Error(msg.to_string()) +} diff --git a/src/lib.rs b/src/lib.rs index dca9409..c20381e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ -mod amdemod; -mod error; -mod fillers; -mod firfilter; -mod multiply; -mod resamplers; -mod sources; +pub mod amdemod; +pub mod error; +pub mod fillers; +pub mod firfilter; +pub mod multiply; +pub mod resamplers; +pub mod sinks; +pub mod sources; diff --git a/src/sinks.rs b/src/sinks.rs new file mode 100644 index 0000000..3e0c0df --- /dev/null +++ b/src/sinks.rs @@ -0,0 +1,74 @@ +extern crate libpulse_binding as pulse; +extern crate libpulse_simple_binding as psimple; + +use psimple::Simple; +use pulse::sample; +use pulse::stream::Direction; + +use super::error::{new_error, DspError}; + +pub struct SoundCardSink<'a> { + iterator: Box> + 'a>, + sampling_rate: u32, +} + +impl<'a> SoundCardSink<'a> { + pub fn from(iterator1: I, sampling_rate: u32) -> SoundCardSink<'a> + where + I: Iterator> + 'a, + { + SoundCardSink { + iterator: Box::new(iterator1), + sampling_rate: sampling_rate, + } + } + + pub fn start(&mut self) -> Result<(), DspError> { + let spec = sample::Spec { + format: sample::SAMPLE_FLOAT32NE, + channels: 1, + rate: self.sampling_rate, + }; + if !spec.is_valid() { + return Err(new_error("Sample spec is invalid.")); + } + + let stream = Simple::new( + None, // Use the default server + "Rusty DSP", // Our application’s name + Direction::Playback, // We want a playback stream + None, // Use the default device + "Sound Card Sink", // Description of our stream + &spec, // Our sample format + None, // Use default channel map + None, // Use default buffering attributes + ) + .map_err(|e| DspError::Error(e.to_string().unwrap()))?; + + loop { + let mut chunk = Vec::new(); + + while chunk.len() < 64 { + let mut sample = Some(Err(DspError::WouldBlock)); + while sample == Some(Err(DspError::WouldBlock)) { + sample = self.iterator.next(); + } + + let value = match sample { + Some(Ok(f)) => f, + None => return Ok(()), + Some(Err(DspError::WouldBlock)) => { + return Err(new_error("Unexpected WouldBlock.")) + } + Some(Err(e)) => return Err(e), + }; + + chunk.extend_from_slice(value.to_ne_bytes().as_ref()); + } + + stream + .write(chunk.as_slice()) + .map_err(|e| DspError::Error(e.to_string().unwrap()))?; + } + } +}