ErikNatanael / knyst

Apache License 2.0
34 stars 4 forks source link

Knyst

Knyst is a real time audio synthesis framework focusing on flexibility and performance. It's main target use case is desktop multi-threaded environments, but it can also do single threaded and/or non real time synthesis. Embedded platforms are currently not supported, but on the roadmap.

[!IMPORTANT]
Knyst is not stable. Knyst's API is still developing and can vary wildly between releases.

The name

"Knyst" is a Swedish word meaning very faint sound. It is normally almost exclusively used in the negative e.g. "Det hörs inte ett knyst" (eng. "You cannot hear a sound"), but I think it's well worth some affirmative use.

Examples

Play a sine wave

// Set up knyst
let mut backend = CpalBackend::new(CpalBackendOptions::default())?;
let _sphere = KnystSphere::start(
    &mut backend,
    SphereSettings {
        num_inputs: 1,
        num_outputs: 2,
        ..Default::default()
    },
    print_error_handler,
);
// Add a sine wave to the top level graph
let sine_osc_handle = oscillator(WavetableId::cos()).freq(200.);
// Output it to the graph output, which for the top level graph is the
// application output.
graph_output(0, sine_osc_handle);

Implement your own Gen

Using the impl_gen macro takes care of most of the boiler plate. However, if you need a variable number of inputs or outputs you have to implement the Gen trait yourself.

struct DummyGen {
    counter: Sample,
}
#[impl_gen]
impl DummyGen {
    // Having a `new` method enabled generation of a function `dummy_gen() -> Handle<DummyGenHandle>`
    pub fn new() -> Self {
      Self {
        counter: 0.,
      }
    }

    /// The process method, or any method with the `#[process]` attribute is what will be run every block
    /// when processing audio. It must not allocate or wait (unless you are doing non-realtime stuff).
    /// 
    /// Inputs are extracted from any argument with the type &[Sample]
    /// Outputs are extracted from any argument with the type &mut [Sample]
    /// 
    /// Additionally, &[Trig] is an input channel of triggers and enables the `inputname_trig()` method on the handle
    /// to conveniently schedule a trigger. `SampleRate` gives you the current sample rate and `BlockSize` gives you
    /// the block size.
    #[process]
    fn process(&mut self, counter: &[Sample], output: &mut [Sample]) -> GenState {
        for (count, out) in counter.iter().zip(output.iter_mut()) {
            self.counter += 1.;
            *out = count + self.counter;
        }
        GenState::Continue
    }
    /// The init method will be called before adding the `Gen` to a running graph. Here is where you can
    /// allocate buffers and initialise state based on sample rate and block size.
    fn init(&mut self, _sample_rate: SampleRate, _block_size: BlockSize, _node_id: NodeId) {
      self.counter = 0.;
    }
}

Features

Safety

Knyst uses a little bit of unsafe under the hood to improve performance in the most sensitive parts of the library, as do some of its dependencies. The user, however, never needs to write any unsafe code.

Roadmap

Vision for the future

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Knyst by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.