Skip to content

Commit

Permalink
Merge pull request #111 from HEnquist/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
HEnquist authored May 11, 2021
2 parents 7cdc687 + 605eca8 commit 5ad6da5
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 50 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 0.5.1
New features:
- Add JACK support.
- Add `GetSupportedDeviceTypes` websocket command.

Bugfixes:
- Handle wav files with extended fmt chunk.
- Don't allow starting with zero channels.

## 0.5.0
New features:
- Add RMS and Peak measurement for each channel at input and output.
Expand Down
39 changes: 20 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "camilladsp"
version = "0.5.0"
version = "0.5.1"
authors = ["Henrik Enquist <[email protected]>"]
description = "A flexible tool for processing audio"

Expand All @@ -9,9 +9,10 @@ default = ["alsa-backend", "pulse-backend", "websocket"]
alsa-backend = ["alsa", "nix"]
pulse-backend = ["libpulse-simple-binding", "libpulse-binding"]
cpal-backend = ["cpal"]
jack-backend = ["cpal-backend", "cpal/jack"]
32bit = []
websocket = ["tungstenite"]
secure-websocket = ["websocket", "native-tls", "tungstenite/tls"]
secure-websocket = ["websocket", "native-tls", "tungstenite/native-tls"]
FFTW = ["fftw"]
neon = ["rubato/neon"]
debug = []
Expand All @@ -26,36 +27,36 @@ path = "src/bin.rs"


[target.'cfg(target_os="linux")'.dependencies]
alsa = { version = "0.4.3", optional = true }
nix = { version = "0.15", optional = true }
alsa = { version = "0.5.0", optional = true }
nix = { version = "0.20", optional = true }

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
serde_json = "1.0"
serde_with = "1.5"
realfft = "1.0.0"
fftw = { version = "0.6.2", optional = true }
num-complex = "0.3"
num-integer = "0.1.43"
serde_with = "1.8"
realfft = "2.0.1"
fftw = { version = "0.7.0", optional = true }
num-complex = "0.4"
num-integer = "0.1.44"
num-traits = "0.2"
signal-hook = "0.3.6"
rand = "0.7.3"
rand_distr = "0.3.0"
signal-hook = "0.3.8"
rand = "0.8.3"
rand_distr = "0.4.0"
clap = "2.33.0"
lazy_static = "1.4.0"
slog = { version = "2.7.0", features = ["release_max_level_trace", "max_level_trace"] }
slog-term = "2.6.0"
slog-async = "2.5.0"
slog-scope = "4.3.0"
slog-term = "2.8.0"
slog-async = "2.6.0"
slog-scope = "4.4.0"
chrono = "0.4"
tungstenite = { version = "0.11.1", optional = true, default-features = false }
native-tls = { version = "0.2.4", optional = true }
tungstenite = { version = "0.13.0", optional = true, default-features = false }
native-tls = { version = "0.2.7", optional = true }
libpulse-binding = { version = "2.0", optional = true }
libpulse-simple-binding = { version = "2.0", optional = true }
rubato = "0.7.0"
rubato = "0.8.0"
#rubato = { git = "https://github.com/HEnquist/rubato", branch = "simd" }
cpal = { version = "0.13.1", optional = true }
cpal = { version = "0.13.3", optional = true }

[build-dependencies]
version_check = "0.9"
Expand Down
68 changes: 59 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The full configuration is given in a yaml file.
### Table of Contents
**[Introduction](#introduction)**
- **[Background](#background)**
- **[How it works](#how-it-works)**
- **[System requirements](#system-requirements)**
- **[Usage example: crossover for 2-way speakers](#usage-example-crossover-for-2-way-speakers)**
- **[Dependencies](#dependencies)**
- **[Related projects](#related-projects)**
Expand All @@ -35,6 +37,7 @@ The full configuration is given in a yaml file.
- **[PulseAudio](#pulseaudio)**
- **[Wasapi](#wasapi)**
- **[CoreAudio](#coreaudio)**
- **[Jack](#jack)**

**[Configuration](#configuration)**
- **[The YAML format](#the-yaml-format)**
Expand Down Expand Up @@ -90,6 +93,37 @@ The supervisor monitors all threads by listening to their status messages. The r
### Websocket server
The websocket server lauches a separate thread to handle each connected client. All commands to change the config are send to the supervisor thread.

## System requirements
CamillaDSP runs on Linux, macOS and Windows. The exact system requirements are determined by the amount of processing the application requires, but even relatively weak CPUs like Intel Atom have much more processing power than most will need.

In general, a 64-bit CPU and OS will perform better.

A few examples, done with CamillaDSP v0.5.0:

- A Raspberry Pi 4 doing FIR filtering of 8 channels, with 262k taps per channel, at 192 kHz.
CPU usage about 55%.

- An AMD Ryzen 7 2700u (laptop) doing FIR filtering of 96 channels, with 262k taps per channel, at 192 kHz.
CPU usage just under 100%.

### Linux
Both 64 and 32 bit architechtures are supported. All platforms supported by the Rustc compiler should work.

Pre-built binaries are provided for:
- x86_64 (almost all PCs)
- armv7 (32-bit arm, for example a Raspeberry Pi 2,3,4 with a 32-bit OS)
- aarch64 (64-bit arm, for example Raspberry Pis running a 64 bit OS)

### Windows
An x86_64 CPU and the 64-bit version of Windows is recommended. Any x86_64 CPU will likely be sufficient.

Pre-built binaries are provided for 64-bit systems.

### MacOS
CamillaDSP can run on both Intel and Apple Silicon macs. Any reasonably recent version of MacOS should work.

Pre-built binaries are provided for both Intel and Apple Silicon


## Usage example: crossover for 2-way speakers
A crossover must filter all sound being played on the system. This is possible with both PulseAudio and Alsa by setting up a loopback device (Alsa) or null sink (Pulse) and setting this device as the default output device. CamillaDSP is then configured to capture from the output of this device and play the processed audio on the real sound card.
Expand Down Expand Up @@ -193,6 +227,7 @@ All the available options, or "features" are:
- `alsa-backend`: Alsa support
- `pulse-backend`: PulseAudio support
- `cpal-backend`: Wasapi and CoreAudio support
- `jack-backend`: Jack support. This also enables the cpal-backend feature if building on Windows or macOS.
- `websocket`: Websocket server for control
- `secure-websocket`: Enable secure websocket, also enables the `websocket` feature
- `FFTW`: Use FFTW instead of RustFFT
Expand All @@ -202,6 +237,11 @@ All the available options, or "features" are:
The first three (`alsa-backend`, `pulse-packend`, `websocket`) are included in the default features, meaning if you don't specify anything you will get those three.
Cargo doesn't allow disabling a single default feature, but you can disable the whole group with the `--no-default-features` flag. Then you have to manually add all the ones you want.

The `jack-backend` feature requires jack and its development files to be installed. To install:
- Fedora: ```sudo dnf install jack-audio-connection-kit jack-audio-connection-kit-devel```
- Debian/Ubuntu etc: ```sudo apt-get install jack libjack-dev```
- Arch: ```sudo pacman -S jack```

Example 1: You want `alsa-backend`, `pulse-backend`, `websocket` and `FFTW`. The first three are included by default so you only need to add `FFTW`:
```
cargo build --release --features FFTW
Expand Down Expand Up @@ -410,6 +450,14 @@ The device name is the same as the one shown in the "Audio MIDI Setup" that can

The sample format is always 32-bit float (FLOAT32LE) even if the device is configured to use another format.

## Jack
The jack server must be running.

Set `device` to "default" for both capture and playback. The sample format is fixed at 32-bit float (FLOAT32LE).

The samplerate must match the samplerate configured for the Jack server.

CamillaDSP will show up in Jack as "cpal_client_in" and "cpal_client_out".

# Configuration

Expand Down Expand Up @@ -541,13 +589,14 @@ devices:
* `Pulse`
* `Wasapi`
* `CoreAudio`
* `Jack`
* `File`
* `Stdin` (capture only)
* `Stdout` (playback only)
* `channels`: number of channels
* `device`: device name (for Alsa, Pulse, Wasapi, CoreAudio). For CoreAudio and Wasapi, "default" will give the default device.
* `filename` path the the file (for File)
* `format`: sample format.
* `format`: sample format (for all except Jack).

Currently supported sample formats are signed little-endian integers of 16, 24 and 32 bits as well as floats of 32 and 64 bits:
* S16LE - Signed 16-bit int, stored as two bytes
Expand All @@ -558,14 +607,14 @@ devices:
* FLOAT64LE - 64-bit float, stored as eight bytes

Supported formats:
| | Alsa | Pulse | Wasapi | CoreAudio | File/Stdin/Stdout |
|------------|--------------------|--------------------|--------------------|--------------------|--------------------|
| S16LE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| S24LE | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| S24LE3 | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| S32LE | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| FLOAT32LE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| FLOAT64LE | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| | Alsa | Pulse | Wasapi | CoreAudio | Jack | File/Stdin/Stdout |
|------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|
| S16LE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: |
| S24LE | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| S24LE3 | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| S32LE | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| FLOAT32LE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| FLOAT64LE | :heavy_check_mark: | :x: | :x: | :x: | :x: | :heavy_check_mark: |


Equivalent formats (for reference):
Expand Down Expand Up @@ -612,6 +661,7 @@ devices:
read_bytes: 200
```
The __Jack__ capture and playback devices do not have a `format` parameter, since they always uses the FLOAT32LE format. It seems that the `device` property should always be set to "default". This parameter may be removed in a future version.
## Resampling
Expand Down
30 changes: 30 additions & 0 deletions src/audiodevice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,20 @@ pub fn get_playback_device(conf: config::Devices) -> Box<dyn PlaybackDevice> {
adjust_period: conf.adjust_period,
enable_rate_adjust: conf.enable_rate_adjust,
}),
#[cfg(all(feature = "cpal-backend", feature = "jack-backend"))]
config::PlaybackDevice::Jack { channels, device } => {
Box::new(cpaldevice::CpalPlaybackDevice {
devname: device,
host: cpaldevice::CpalHost::Jack,
samplerate: conf.samplerate,
chunksize: conf.chunksize,
channels,
sample_format: config::SampleFormat::FLOAT32LE,
target_level: conf.target_level,
adjust_period: conf.adjust_period,
enable_rate_adjust: conf.enable_rate_adjust,
})
}
}
}

Expand Down Expand Up @@ -522,6 +536,22 @@ pub fn get_capture_device(conf: config::Devices) -> Box<dyn CaptureDevice> {
silence_threshold: conf.silence_threshold,
silence_timeout: conf.silence_timeout,
}),
#[cfg(all(feature = "cpal-backend", feature = "jack-backend"))]
config::CaptureDevice::Jack { channels, device } => {
Box::new(cpaldevice::CpalCaptureDevice {
devname: device,
host: cpaldevice::CpalHost::Jack,
samplerate: conf.samplerate,
enable_resampling: conf.enable_resampling,
resampler_conf: conf.resampler_type,
capture_samplerate,
chunksize: conf.chunksize,
channels,
sample_format: config::SampleFormat::FLOAT32LE,
silence_threshold: conf.silence_threshold,
silence_timeout: conf.silence_timeout,
})
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ fn main_process() -> i32 {
if cfg!(feature = "cpal-backend") {
features.push("cpal-backend");
}
if cfg!(feature = "jack-backend") {
features.push("jack-backend");
}
if cfg!(feature = "websocket") {
features.push("websocket");
}
Expand Down Expand Up @@ -607,10 +610,7 @@ fn main_process() -> i32 {
signal_hook::low_level::register(signal_hook::consts::SIGHUP, || debug!("Received SIGHUP"))
};

let configname = match matches.value_of("configfile") {
Some(path) => Some(path.to_string()),
None => None,
};
let configname = matches.value_of("configfile").map(|path| path.to_string());

let initial_volume = matches
.value_of("gain")
Expand Down
8 changes: 4 additions & 4 deletions src/biquadcombo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,36 +90,36 @@ impl BiquadCombo {
let qvalues = BiquadCombo::linkwitzriley_q(order);
let filters = BiquadCombo::make_highpass(samplerate, freq, qvalues);
BiquadCombo {
samplerate,
name,
filters,
samplerate,
}
}
config::BiquadComboParameters::LinkwitzRileyLowpass { order, freq } => {
let qvalues = BiquadCombo::linkwitzriley_q(order);
let filters = BiquadCombo::make_lowpass(samplerate, freq, qvalues);
BiquadCombo {
samplerate,
name,
filters,
samplerate,
}
}
config::BiquadComboParameters::ButterworthHighpass { order, freq } => {
let qvalues = BiquadCombo::butterworth_q(order);
let filters = BiquadCombo::make_highpass(samplerate, freq, qvalues);
BiquadCombo {
samplerate,
name,
filters,
samplerate,
}
}
config::BiquadComboParameters::ButterworthLowpass { order, freq } => {
let qvalues = BiquadCombo::butterworth_q(order);
let filters = BiquadCombo::make_lowpass(samplerate, freq, qvalues);
BiquadCombo {
samplerate,
name,
filters,
samplerate,
}
}
}
Expand Down
Loading

0 comments on commit 5ad6da5

Please sign in to comment.