Skip to content

Commit

Permalink
Merge pull request #66 from klangner/audio_sink
Browse files Browse the repository at this point in the history
Audio sink
  • Loading branch information
klangner authored Jan 30, 2024
2 parents deff34a + 187f227 commit 8d76efc
Show file tree
Hide file tree
Showing 29 changed files with 213 additions and 214 deletions.
44 changes: 24 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "dsp"
version = "0.10.2"
version = "0.11.0"
authors = ["Krzysztof Langner <[email protected]>"]
description = "Digital Signal Processing"
keywords = ["dsp"]
license = "MIT OR Apache-2.0"
license = "Apache-2.0"
repository = "https://github.com/klangner/dsp"
homepage = "https://github.com/klangner/dsp"
documentation = "https://docs.rs/dsp"
edition = "2018"
edition = "2021"

[features]
default = ["audio"]
Expand All @@ -17,31 +17,35 @@ random = ["rand", "rand_distr"]

[dependencies]
anyhow = "1.0"
arraydeque = "^0.4"
byteorder = "1"
itertools = "^0.10"
num-complex = "^0.4"
rustfft = "^6"
cpal = { version = "0.13.4", optional = true }
audrey = { version = "^0.3", optional = true }
rand = { version = "^0.8", optional = true }
rand_distr = {version = "^0.4", optional = true }
arraydeque = "0.5"
byteorder = "1.5"
itertools = "0.12"
num-complex = "0.4"
rustfft = "6.2"
cpal = { version = "0.15", optional = true }
audrey = { version = "0.3", optional = true }
rand = { version = "0.8", optional = true }
rand_distr = {version = "0.4", optional = true }

[dev-dependencies]
assert_approx_eq = "^1.1"
gnuplot = "^0.0.37"
clap = "^2"
lewton = "^0.10"
criterion = "^0.3.5"
matfile = "0.3"
transpose = "^0.2"
pitch_calc = "^0.12"
assert_approx_eq = "1.1"
gnuplot = "0.0.41"
clap = { version = "4", features = ["derive"] }
lewton = "0.10"
criterion = "0.5"
matfile = "0.4"
transpose = "0.2"
pitch_calc = "0.12"
crossbeam-deque = "0.8"

[[bench]]
name = "gen_bench"
harness = false

[[example]]
name = "play"
required-features = ["audio"]

[[example]]
name = "tuner"
required-features = ["audio"]
Expand Down
25 changes: 0 additions & 25 deletions LICENSE-MIT

This file was deleted.

24 changes: 5 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
[![Rust](https://github.com/klangner/dsp.rs/actions/workflows/rust.yml/badge.svg)](https://github.com/klangner/dsp.rs/actions/workflows/rust.yml)
[![Crates.io](https://img.shields.io/crates/v/dsp.svg)](https://crates.io/crates/dsp) [![Crates.io](https://img.shields.io/crates/l/dsp.svg)](https://github.com/klangner/dsp/blob/master/LICENSE-MIT) [![docs.rs](https://docs.rs/dsp/badge.svg)](https://docs.rs/dsp/)

**dsp is an early-stage open-source project**. It means that API can change at any time.
If you think that this library can help you, then let me know. We can discuss future direction and try to stabilize the API.
This library is focused in working with block of data not on real time processing.

If you are looking for realtime processing then theree is alreeady great library for it
https://github.com/FutureSDR/FutureSDR

The folder [examples](https://github.com/klangner/dsp/tree/master/examples) contains demo programs which shows how to use this library.

Expand Down Expand Up @@ -46,25 +48,9 @@ Implemented generators:
* [x] Find peak frequency


## Runtime
There is minimal support for runtime.
Runtime is designed as a graph of nodes.
Node is a single computation which takes buffer as an input and generate data
to provided output buffer. It means that node do not allocate memory for data.
There are 3 types of node:
* SourceNode - Generate new data
* ProcessingNode - Processing data between buffers
* SinkNode - Takes data from buffer and consumes it.


# License

Licensed under either of

* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)

at your option.
Licensed under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)


**Contributions**
Expand Down
4 changes: 2 additions & 2 deletions benches/gen_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ extern crate criterion;

use criterion::Criterion;

use dsp::runtime::node::SourceNode;
use dsp::node::generator::*;
use dsp::node::SourceNode;
use dsp::core::generator::*;


fn criterion_benchmark(c: &mut Criterion) {
Expand Down
45 changes: 13 additions & 32 deletions examples/file_sink.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,25 @@
#[macro_use]
extern crate clap;

use clap::{Arg, App};
use dsp::runtime::node::{SourceNode, SinkNode};
use dsp::node::generator::Sine;
use dsp::node::file::FileSink;
use clap::Parser;
use dsp::node::{SourceNode, SinkNode};
use dsp::core::generator::Sine;
use dsp::core::file::FileSink;


const SIGNAL_LENGTH: usize = 512;


// Application params
struct Params {
#[derive(Parser, Debug)]
struct Args {
/// Gain to apply to the seify source
#[clap(short, long, default_value_t = 30)]
sample_rate: usize,
freq: f32
}

/// Parse command line arguments
fn parse_params() -> Params {
let args = App::new("Save signal to file")
.arg(Arg::with_name("freq")
.short("f")
.long("frequency")
.help("Frequency in Hz")
.takes_value(true))
.arg(Arg::with_name("sample-rate")
.short("s")
.long("sample-rate")
.help("Number of samples per period")
.takes_value(true))
.get_matches();
let sample_rate = value_t!(args, "sample-rate", usize).unwrap_or(512);
let freq = value_t!(args, "freq", f32).unwrap_or(4.0);
Params { sample_rate: sample_rate, freq: freq }
/// Center frequency
#[clap(short, long, default_value_t = 100_000_000.0)]
freq: f32,
}


fn main() {
let params = parse_params();
let mut generator = Sine::new(params.freq, params.sample_rate);
let args = Args::parse();
let mut generator = Sine::new(args.freq, args.sample_rate);
let mut file_sink = FileSink::new("target/example.data");
let mut buffer = vec![0.0; SIGNAL_LENGTH];

Expand Down
4 changes: 2 additions & 2 deletions examples/file_source.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use gnuplot::{Figure, Color, AxesCommon};
use dsp::runtime::node::SourceNode;
use dsp::node::file::FileSource;
use dsp::node::SourceNode;
use dsp::core::file::FileSource;


const SAMPLE_RATE: usize = 512;
Expand Down
4 changes: 2 additions & 2 deletions examples/heart_rate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fs::File;
use gnuplot::{Figure, Color, AxesCommon};
use dsp::runtime::node::ProcessNode;
use dsp::node::correlation::*;
use dsp::node::ProcessNode;
use dsp::core::correlation::*;


const SAMPLE_RATE: usize = 100;
Expand Down
39 changes: 39 additions & 0 deletions examples/play.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

use dsp::core::generator::Sine;
use dsp::node::SourceNode;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};


const SAMPLE_RATE: u32 = 44100;
const BUFFER_SIZE: u32 = 1024;


fn main() {
let mut generator = Sine::new(440., SAMPLE_RATE as usize);

// Init output device
let host = cpal::default_host();

let device = host
.default_output_device()
.ok_or_else(|| anyhow::Error::msg("Default output device is not available")).unwrap();

let config = cpal::StreamConfig {
channels: 1,
sample_rate: cpal::SampleRate(SAMPLE_RATE),
buffer_size: cpal::BufferSize::Fixed(BUFFER_SIZE),
};

let stream = device.build_output_stream(
&config,
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
generator.write_buffer(data).unwrap();
},
move |err| {panic!("cpal stream error {:?}", err);},
None
).unwrap();

stream.play().unwrap();

std::thread::sleep(std::time::Duration::from_millis(1_000));
}
34 changes: 10 additions & 24 deletions examples/plot_freq.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,25 @@
#[macro_use]
extern crate clap;

use gnuplot::{Figure, Color, AxesCommon};
use clap::{Arg, App};
use clap::Parser;
use dsp::num_complex::Complex32;
use dsp::runtime::node::{ProcessNode, SourceNode};
use dsp::node::{generator::Sine, fft::*, complex::*};
use dsp::node::{ProcessNode, SourceNode};
use dsp::core::{generator::Sine, fft::*, complex::*};


const FRAME_SIZE: usize = 8_192;
const SAMPLE_RATE: usize = 48_000;
const MAX_FREQ: f32 = 2_000.;


// Application params
struct Params {
freq: f32
}

/// Parse command line arguments
fn parse_params() -> Params {
let args = App::new("Plot signal")
.arg(Arg::with_name("freq")
.short("f")
.long("frequency")
.help("Frequency in Hz")
.takes_value(true))
.get_matches();
let freq = value_t!(args, "freq", f32).unwrap_or(440.0);
Params { freq: freq }
#[derive(Parser, Debug)]
struct Args {
/// Center frequency
#[clap(short, long, default_value_t = 100_000_000.0)]
freq: f32,
}

fn main() {
let params = parse_params();
let mut generator = Sine::new(params.freq, SAMPLE_RATE);
let args = Args::parse();
let mut generator = Sine::new(args.freq, SAMPLE_RATE);
let mut r2c = RealToComplex::new();
let mut fft = ForwardFFT::new(FRAME_SIZE, WindowType::Hamming);
let mut buffer1 = vec![0.0; FRAME_SIZE];
Expand Down
40 changes: 11 additions & 29 deletions examples/spectrum.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#[macro_use]
extern crate clap;

use gnuplot::*;
use clap::{Arg, App};
use clap::Parser;
use dsp::num_complex::Complex32;
use dsp::runtime::node::*;
use dsp::node::{generator::*, fft::*, complex::*};
Expand All @@ -11,33 +8,18 @@ use dsp::node::{generator::*, fft::*, complex::*};
const SIGNAL_LENGTH: usize = 10*256;


// Application params
#[derive(Debug)]
struct Params {
#[derive(Parser, Debug)]
struct Args {
/// Gain to apply to the seify source
#[clap(short, long)]
gen_name: String,
freq: f32
}

/// Parse command line arguments
fn parse_params() -> Params {
let args = App::new("Plot signal")
.arg(Arg::with_name("gen")
.short("g")
.long("generator-name")
.help("Select generator type")
.takes_value(true))
.arg(Arg::with_name("freq")
.short("f")
.long("frequency")
.help("Frequency in Hz")
.takes_value(true))
.get_matches();
let gen_name = args.value_of("gen").unwrap_or("sine");
let freq = value_t!(args, "freq", f32).unwrap_or(4.0);
Params { gen_name: gen_name.to_string(),
freq: freq }
/// Center frequency
#[clap(short, long, default_value_t = 100_000_000.0)]
freq: f32,
}

/// Parse command line arguments
/// Create signal
fn create_generator(gen_name: &str, freq: f32, sample_rate:usize) -> Box<dyn SourceNode<f32>> {
match gen_name.as_ref() {
Expand All @@ -51,10 +33,10 @@ fn create_generator(gen_name: &str, freq: f32, sample_rate:usize) -> Box<dyn Sou


fn main() {
let params = parse_params();
let args = Args::parse();
let num_spectrums = 10;
let window_size = SIGNAL_LENGTH / num_spectrums;
let mut generator = create_generator(&params.gen_name, params.freq, window_size);
let mut generator = create_generator(&args.gen_name, args.freq, window_size);
let mut r2c = RealToComplex::new();
let mut c2r = ComplexToReal::new();
let mut fft = ForwardFFT::new(window_size, WindowType::Hamming);
Expand Down
Loading

0 comments on commit 8d76efc

Please sign in to comment.