HexoDSP/src/lib.rs

321 lines
11 KiB
Rust
Raw Normal View History

// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
2021-08-04 01:58:43 +00:00
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
2022-07-19 04:07:14 +00:00
/*!# HexoDSP - Comprehensive DSP graph and synthesis library for developing a modular synthesizer in Rust, such as HexoSynth.
2021-05-18 03:39:21 +00:00
This project contains the complete DSP backend of the modular
synthesizer [HexoSynth](https://github.com/WeirdConstructor/HexoSynth).
It's aimed to provide a toolkit for everyone who wants to develop
a synthesizer in Rust. You can use it to quickly define a DSP graph
that you can change at runtime. It comes with a large collection
of already developed DSP modules/nodes, such as oscillators, filters,
amplifiers, envelopes and sequencers.
The DSP graph API also provides multiple kinds of feedback to track what the
signals in the DSP threads look like. From monitoring the inputs and outputs of
single nodes to get the current output value of all nodes.
Here a short list of features:
* Runtime changeable DSP graph
* Serialization and loading of the DSP graph and the parameters
2021-05-18 03:39:21 +00:00
* Full monitoring and feedback introspection into the running DSP graph
* Provides a wide variety of modules
* Extensible framework for quickly developing new nodes at compile time
* A comprehensive automated test suite
2022-07-19 04:07:14 +00:00
And following DSP nodes:
| Category | Name | Function |
|-|-|-|
| IO Util | Out | Audio output (to DAW or Jack) |
| Osc | Sampl | Sample player |
| Osc | Sin | Sine oscillator |
| Osc | BOsc | Basic bandlimited waveform oscillator (waveforms: Sin, Tri, Saw, Pulse/Square) |
| Osc | VOsc | Vector phase shaping oscillator |
| Osc | Noise | Noise oscillator |
| Signal | Amp | Amplifier/Attenuator |
| Signal | SFilter | Simple collection of filters, useable for synthesis |
| Signal | Delay | Single tap signal delay |
| Signal | PVerb | Reverb node, based on Dattorros plate reverb algorithm |
| Signal | AllP | All-Pass filter based on internal delay line feedback |
| Signal | Comb | Comb filter |
| N-\>M | Mix3 | 3 channel mixer |
| N-\>M | Mux9 | 9 channel to 1 output multiplexer/switch |
| Ctrl | SMap | Simple control signal mapper |
| Ctrl | Map | Control signal mapper |
| Ctrl | CQnt | Control signal pitch quantizer |
| Ctrl | Quant | Pitch signal quantizer |
| Mod | TSeq | Tracker/pattern sequencer |
| Mod | Ad | Attack-Decay envelope |
| Mod | TsLFO | Tri/Saw waveform low frequency oscillator (LFO) |
| Mod | RndWk | Random walker, a Sample & Hold noise generator |
| IO Util | FbWr / FbRd | Utility modules for feedback in patches |
2021-05-18 03:39:21 +00:00
## API Examples
### Documentation
The development documentation with all private fields and functions can be
found separately hosted:
[HexoDSP API Developer Documentation](http://m8geil.de/hexodsp_doc/hexodsp/).
2021-05-18 03:39:21 +00:00
### Raw `NodeConfigurator` API
This API is the most low level API provided by HexoDSP.
It shows how to create nodes and connect them.
The execution of the nodes in the audio thread is
controlled by a `NodeProg`, which defines the order
the nodes are executed in.
This only showcases the non-realtime generation of audio
samples. For a real time application of this library please
refer to the examples that come with this library.
```rust
use hexodsp::*;
let (mut node_conf, mut node_exec) = new_node_engine();
node_conf.create_node(NodeId::Sin(0));
node_conf.create_node(NodeId::Amp(0));
let mut prog = node_conf.rebuild_node_ports();
node_conf.add_prog_node(&mut prog, &NodeId::Sin(0));
node_conf.add_prog_node(&mut prog, &NodeId::Amp(0));
node_conf.set_prog_node_exec_connection(
&mut prog,
(NodeId::Amp(0), NodeId::Amp(0).inp("inp").unwrap()),
(NodeId::Sin(0), NodeId::Sin(0).out("sig").unwrap()));
node_conf.upload_prog(prog, true);
let (out_l, out_r) = node_exec.test_run(0.1, false);
```
### Hexagonal Matrix API
This is a short overview of the API provided by the
hexagonal Matrix API, which is the primary API used
inside [HexoSynth](https://github.com/WeirdConstructor/HexoSynth).
This only showcases the non-realtime generation of audio
samples. For a real time application of this library please
refer to the examples that come with this library.
```rust
use hexodsp::*;
let (node_conf, mut node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
let sin = NodeId::Sin(0);
let amp = NodeId::Amp(0);
matrix.place(0, 0, Cell::empty(sin)
.out(None, None, sin.out("sig")));
matrix.place(0, 1, Cell::empty(amp)
.input(amp.inp("inp"), None, None));
matrix.sync().unwrap();
let gain_p = amp.inp_param("gain").unwrap();
matrix.set_param(gain_p, SAtom::param(0.25));
let (out_l, out_r) = node_exec.test_run(0.11, true);
// out_l and out_r contain two channels of audio
// samples now.
```
## State of Development
As of 2021-05-18: The architecture and it's functionality have been mostly
feature complete by now. The only part that is still lacking is the collection
of modules/nodes, this is the area of current development. Adding lots of
nodes.
Make sure to follow [Weird Constructors Mastodon
account](https://mastodon.online/@weirdconstructor) or the releases of this
project to be notified of updates.
### Road Map / TODO List
I have a pretty detailed TODO list in my private notebook.
## Running the Jack Example:
To run the example:
2021-05-19 01:58:33 +00:00
```text
2022-07-05 18:00:17 +00:00
cargo run --release --example jack_demo_node_api
2021-05-19 01:58:33 +00:00
```
2021-05-18 03:39:21 +00:00
You might need following dependencies (Ubuntu Linux):
2021-05-19 01:58:33 +00:00
```text
2021-05-18 03:39:21 +00:00
sudo apt install libjack0 libjack-jackd2-dev qjackctl
2021-05-19 01:58:33 +00:00
```
2021-05-18 03:39:21 +00:00
These might work on Debian too:
2021-05-19 01:58:33 +00:00
```text
2021-05-18 03:39:21 +00:00
sudo apt install libjack0 libjack-dev
2021-05-19 01:58:33 +00:00
```
2021-05-18 03:39:21 +00:00
## Running the Automated Testsuite:
There exists an automate test suite for the DSP and backend code:
2021-05-19 01:58:33 +00:00
```text
2021-05-18 03:39:21 +00:00
cargo test
2021-05-19 01:58:33 +00:00
```
2021-05-18 03:39:21 +00:00
## Known Bugs
* The ones you encounter and create as issues on GitHub.
## Contributions
I currently have a quite precise vision of what I want to achieve and my goal
is to make music with this project eventually.
The projects is still young, and I currently don't have that much time to
devote for project coordination. So please don't be offended if your issue rots
in the GitHub issue tracker, or your pull requests is left dangling around
for ages.
I might merge pull requests if I find the time and think that the contributions
are in line with my vision.
Please bear in mind, that I can only accept contributions under the License
2022-07-05 18:03:32 +00:00
of this project (GPLv3 or later).
2021-05-18 03:39:21 +00:00
## Contact the Author
You can reach me via Discord or Mastodon. I'm joined most public Rust Discord
servers, especially the "Rust Audio" Discord server. I am also sometimes on
freenode.net, for instance in the `#lad` channel (nick `weirdctr`).
## Support Development
You can support me (and the development of this project) via Liberapay:
<a href="https://liberapay.com/WeirdConstructor/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
## License
This project is licensed under the GNU Affero General Public License Version 3 or
later.
2022-07-05 18:03:32 +00:00
### Why GPL?
2021-05-18 03:39:21 +00:00
The obivious reason is that this project copied and translated code from many
other free software / open source synthesis projects. The sources
will show the origin and license of the individual parts.
#### My Reasons
Picking a license for my code bothered me for a long time. I read many
discussions about this topic. Read the license explanations. And discussed
this matter with other developers.
First about _why I write code for free_ at all, the reasons are:
- It's my passion to write computer programs. In my free time I can
write the code I want, when I want and the way I want. I can freely
allocate my time and freely choose the projects I want to work on.
- To help a friend or member of my family.
- To solve a problem I have.
- To learn something new.
Those are the reasons why I write code for free. Now the reasons
_why I publish the code_, when I could as well keep it to myself:
- So that it may bring value to users and the free software community.
- Show my work as an artist.
- To get into contact with other developers.
- To exchange knowledge and help other developers.
- And it's a nice change to put some more polish on my private projects.
2022-07-05 18:03:32 +00:00
Most of those reasons don't yet justify GPL. The main point of the GPL, as far
as I understand: The GPL makes sure the software stays free software until
2021-05-18 03:39:21 +00:00
eternity. That the _end user_ of the software always stays in control. That the users
have the means to adapt the software to new platforms or use cases.
Even if the original authors don't maintain the software anymore.
It ultimately prevents _"vendor lock in"_. I really dislike vendor lock in,
especially as developer. Especially as developer I want and need to stay
in control of the computers and software I use.
Another point is, that my work (and the work of any other developer) has a
value. If I give away my work without _any_ strings attached, I effectively
work for free. This compromises the price I (and potentially other developers)
can demand for the skill, workforce and time.
2022-07-05 18:03:32 +00:00
This makes two reasons for me to choose the GPL:
2021-05-18 03:39:21 +00:00
1. I do not want to support vendor lock in scenarios for free.
I want to prevent those when I have a choice, when I invest my private
time to bring value to the end users.
2. I don't want to low ball my own (and other developer's) wage and prices
by giving away the work I spent my scarce private time on with no strings
attached. I do not want companies to be able to use it in closed source
projects to drive a vendor lock in scenario.
We can discuss relicensing of my code or project if you are interested in using
it in a closed source project. Bear in mind, that I can only relicense the
parts of the project I wrote. If the project contains GPL code from other
projects and authors, I can't relicense it.
*/
2022-07-17 09:58:28 +00:00
pub mod cell_dir;
2022-07-03 05:18:43 +00:00
#[allow(unused_macros, non_snake_case)]
pub mod dsp;
2022-07-17 09:58:28 +00:00
pub mod log;
pub mod matrix;
pub mod matrix_repr;
2022-07-17 09:58:28 +00:00
pub mod monitor;
pub mod nodes;
pub mod sample_lib;
mod util;
pub use cell_dir::CellDir;
2022-07-17 09:58:28 +00:00
pub use dsp::{NodeId, NodeInfo, ParamId, SAtom};
pub use log::log;
pub use matrix::{Cell, Matrix};
pub use matrix_repr::load_patch_from_file;
pub use matrix_repr::save_patch_to_file;
2022-07-17 09:58:28 +00:00
pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor};
pub use sample_lib::{SampleLibrary, SampleLoadError};
pub struct Context<'a, 'b, 'c, 'd> {
2022-07-17 09:58:28 +00:00
pub nframes: usize,
pub output: &'a mut [&'b mut [f32]],
pub input: &'c [&'d [f32]],
}
impl<'a, 'b, 'c, 'd> nodes::NodeAudioContext for Context<'a, 'b, 'c, 'd> {
#[inline]
2022-07-17 09:58:28 +00:00
fn nframes(&self) -> usize {
self.nframes
}
#[inline]
fn output(&mut self, channel: usize, frame: usize, v: f32) {
self.output[channel][frame] = v;
}
#[inline]
fn input(&mut self, channel: usize, frame: usize) -> f32 {
self.input[channel][frame]
}
}
2021-05-18 01:59:00 +00:00
pub fn test() -> bool {
true
}