1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
//! # Knyst - audio graph and synthesis library
//!
//! Knyst is a real time audio synthesis framework focusing on flexibility and
//! performance. It's main target use case is desktop multi-threaded real time
//! environments, but it can also do single threaded and/or non real time
//! synthesis. Embedded platforms are currently not supported, but on the
//! roadmap.
//!
//! The main selling point of Knyst is that the graph can be modified while it's
//! running: nodes and connections between nodes can be added/removed. It also
//! supports shared resources such as wavetables and buffers.
//!
//! ## Status
//!
//! Knyst is in its early stages. Expect large breaking API changes between
//! versions.
//!
//! ## The name
//!
//! "Knyst" is a Swedish word meaning _very faint sound_.
//!
//! ## Architecture
//!
//! The core of Knyst is the [`Graph`] struct and the [`Gen`] trait. [`Graph`]s
//! can have nodes containing anything that implements [`Gen`]. [`Graph`]s
//! can also themselves be added as a node.
//!
//! Nodes in a running [`Graph`] can be freed or signal to the [`Graph`]
//! that they or the entire [`Graph`] should be freed. [`Connection`]s between
//! Nodes and the inputs and outputs of a [`Graph`] can also be changed
//! while the [`Graph`] is running. This way, Knyst acheives a similar
//! flexibility to SuperCollider.
//!
//! It is easy to get things wrong when using a [`Graph`] as a [`Gen`] directly
//! so that functionality is encapsulated. For the highest level [`Graph`] of
//! your program you may want to use [`RunGraph`] to get a node which
//! you can run in a real time thread or non real time to generate samples.
//! Using the [`audio_backend`]s this process is automated for you.
//!
//! ## Features
//!
//! - *unstable*: Enables unstable optimisations in some cases that currently requires nightly.
//! - *debug-warn-on-alloc*: Print a warning instead of panicing when allocating on the audio thread (debug build only).
//! - *serde-derive*: Enables some data structures to be serialized/deserialized using serde.
//! - *cpal*: Enables the cpal AudioBackend
//! - *jack*: Enables the JACK AudioBackend
//!
#![deny(rustdoc::broken_intra_doc_links)] // error if there are broken intra-doc links
#![warn(missing_docs)]
// #![warn(clippy::pedantic)]
#![cfg_attr(feature = "unstable", feature(portable_simd))]
#[allow(unused)]
use crate::audio_backend::AudioBackend;
#[allow(unused)]
use crate::gen::Gen;
#[allow(unused)]
use crate::sphere::KnystSphere;
use audio_backend::AudioBackendError;
use core::fmt::Debug;
use modal_interface::SphereError;
use resources::ResourcesError;
use std::ops::{Deref, DerefMut};
// Import these for docs
#[allow(unused_imports)]
use graph::{Connection, Graph, RunGraph};
pub use modal_interface::knyst_commands;
pub use resources::Resources;
// assert_no_alloc to make sure we are not allocating on the audio thread. The
// assertion is put in AudioBackend.
#[allow(unused_imports)]
use assert_no_alloc::*;
#[cfg(debug_assertions)] // required when disable_release is set (default)
#[global_allocator]
static A: AllocDisabler = AllocDisabler;
pub mod audio_backend;
pub mod buffer;
pub mod controller;
pub mod envelope;
pub mod gen;
pub mod graph;
pub mod handles;
pub mod inspection;
mod internal_filter;
pub mod modal_interface;
pub mod node_buffer;
pub mod offline;
pub mod prelude;
pub mod resources;
pub mod scheduling;
pub mod sphere;
pub mod time;
pub mod trig;
pub mod wavetable;
pub mod xorrng;
/// Combined error type for Knyst, containing any other error in the library.
#[derive(thiserror::Error, Debug)]
pub enum KnystError {
/// Error making a connection inside a [`Graph`]
#[error("Error adding or removing connections: {0}")]
ConnectionError(#[from] graph::connection::ConnectionError),
/// Error freeing a node from a [`Graph`]
#[error("Error freeing a node: {0}")]
FreeError(#[from] graph::FreeError),
/// Error pushing a node to a [`Graph`]
#[error("Error pushing a node: {0}]")]
PushError(#[from] graph::PushError),
/// Error scheduling a change
#[error("Error scheduling a change: {0}")]
ScheduleError(#[from] graph::ScheduleError),
/// Error from creating a RunGraph
#[error("Error with the RunGraph: {0}")]
RunGraphError(#[from] graph::run_graph::RunGraphError),
/// Error from interacting with [`Resources`]
#[error("Resources error : {0}")]
ResourcesError(#[from] ResourcesError),
/// Error from interacting with [`KnystSphere`] or any of the modal command functions
#[error("Sphere error : {0}")]
SphereError(#[from] SphereError),
/// Error from interacting with an [`AudioBackend`].
#[error("Audio backend error : {0}")]
AudioBackendError(#[from] AudioBackendError),
}
/// Convert db to amplitude
#[inline]
#[must_use]
pub fn db_to_amplitude(db: Sample) -> Sample {
(10.0 as Sample).powf(db / 20.0)
}
/// Convert amplitude to db
#[inline]
#[must_use]
pub fn amplitude_to_db(amplitude: Sample) -> Sample {
20.0 * amplitude.log10()
}
/// The current sample type used throughout Knyst
pub type Sample = f32;
/// Marker for inputs that are trigs. This makes it possible to set that value correctly through a Handle.
pub type Trig = Sample;
/// Newtype for a sample rate to identify it in function signatures. Derefs to a `Sample` for easy use on the audio thread.
#[derive(Copy, Clone, Debug)]
pub struct SampleRate(Sample);
impl SampleRate {
#[inline(always)]
#[allow(missing_docs)]
pub fn to_f64(self) -> f64 {
self.0 as f64
}
#[allow(missing_docs)]
#[inline(always)]
pub fn to_usize(self) -> usize {
self.0 as usize
}
}
impl Deref for SampleRate {
type Target = Sample;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SampleRate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<SampleRate> for f64 {
fn from(value: SampleRate) -> Self {
value.0 as f64
}
}
impl From<f32> for SampleRate {
fn from(value: f32) -> Self {
Self(value as Sample)
}
}
impl From<f64> for SampleRate {
fn from(value: f64) -> Self {
Self(value as Sample)
}
}
#[derive(Copy, Clone, Debug)]
/// BlockSize.
///
/// Can be an unorthodox block size value in the event of a partial block at the beginning of a node's existence in the graph.
pub struct BlockSize(usize);
impl From<BlockSize> for usize {
#[inline(always)]
fn from(value: BlockSize) -> Self {
value.0
}
}
impl From<usize> for BlockSize {
fn from(value: usize) -> Self {
Self(value)
}
}
impl Deref for BlockSize {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for BlockSize {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}