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
    }
}