Full Code of irh/freeverb-rs for AI

main f6148740d5ed cached
78 files
77.5 KB
22.3k tokens
172 symbols
1 requests
Download .txt
Repository: irh/freeverb-rs
Branch: main
Commit: f6148740d5ed
Files: 78
Total size: 77.5 KB

Directory structure:
gitextract_ujjsr6s2/

├── .git-blame-ignore-revs
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── crates/
│   ├── audio_module/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── command.rs
│   │       ├── lib.rs
│   │       ├── module.rs
│   │       ├── parameter.rs
│   │       ├── processor.rs
│   │       ├── string_converter.rs
│   │       └── value_converter.rs
│   ├── clib/
│   │   ├── .gitignore
│   │   ├── Cargo.toml
│   │   ├── build.sh
│   │   ├── cbindgen.toml
│   │   ├── src/
│   │   │   └── lib.rs
│   │   └── test.cpp
│   ├── freeverb/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── all_pass.rs
│   │       ├── comb.rs
│   │       ├── delay_line.rs
│   │       ├── float.rs
│   │       ├── freeverb.rs
│   │       ├── lib.rs
│   │       └── tuning.rs
│   └── freeverb_module/
│       ├── Cargo.toml
│       └── src/
│           └── lib.rs
└── examples/
    ├── app_gtk/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── audio_thread.rs
    │       ├── gtk_parameter_slider.rs
    │       ├── gtk_parameter_toggle.rs
    │       └── main.rs
    ├── app_juce/
    │   ├── .gitignore
    │   ├── JuceLibraryCode/
    │   │   ├── AppConfig.h
    │   │   ├── JuceHeader.h
    │   │   ├── ReadMe.txt
    │   │   ├── include_juce_audio_basics.cpp
    │   │   ├── include_juce_audio_basics.mm
    │   │   ├── include_juce_audio_devices.cpp
    │   │   ├── include_juce_audio_devices.mm
    │   │   ├── include_juce_audio_formats.cpp
    │   │   ├── include_juce_audio_formats.mm
    │   │   ├── include_juce_audio_processors.cpp
    │   │   ├── include_juce_audio_processors.mm
    │   │   ├── include_juce_audio_utils.cpp
    │   │   ├── include_juce_audio_utils.mm
    │   │   ├── include_juce_core.cpp
    │   │   ├── include_juce_core.mm
    │   │   ├── include_juce_cryptography.cpp
    │   │   ├── include_juce_cryptography.mm
    │   │   ├── include_juce_data_structures.cpp
    │   │   ├── include_juce_data_structures.mm
    │   │   ├── include_juce_events.cpp
    │   │   ├── include_juce_events.mm
    │   │   ├── include_juce_graphics.cpp
    │   │   ├── include_juce_graphics.mm
    │   │   ├── include_juce_gui_basics.cpp
    │   │   ├── include_juce_gui_basics.mm
    │   │   ├── include_juce_gui_extra.cpp
    │   │   ├── include_juce_gui_extra.mm
    │   │   ├── include_juce_opengl.cpp
    │   │   ├── include_juce_opengl.mm
    │   │   ├── include_juce_video.cpp
    │   │   └── include_juce_video.mm
    │   ├── Source/
    │   │   ├── Main.cpp
    │   │   ├── MainComponent.cpp
    │   │   └── MainComponent.h
    │   └── freeverb.jucer
    └── wasm/
        ├── .gitignore
        ├── Cargo.toml
        ├── README.md
        ├── package.json
        ├── public/
        │   ├── freeverb-processor.js
        │   ├── index.html
        │   └── index.js
        └── src/
            └── lib.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .git-blame-ignore-revs
================================================
# .git-blame-ignore-revs

# Rename /src -> /crates
990a498e2d963d18ce8cfa675674fa8c4173295e



================================================
FILE: .gitignore
================================================
**/target
**/*.rs.bk
/.vscode


================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
  "crates/*",
  "examples/app_gtk",
  "examples/wasm",
]

[workspace.package]
edition = "2024"
authors = ["irh <ian.r.hobson@gmail.com>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/irh/freeverb-rs"


================================================
FILE: LICENSE
================================================
Copyright (c) 2018 Ian Hobson

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# freeverb-rs

A Rust implementation of the Freeverb algorithm.

## About Freeverb

Freeverb was originally written in C++ by "Jezar at Dreampoint", and was released into the public domain in June 2000. It is now widely used in various incarnations in multiple software packages.

- [Analysis of the Freeverb algorithm](https://ccrma.stanford.edu/~jos/pasp/Freeverb.html)
- [More information and a link to original C++ source](https://freeverb3-vst.sourceforge.io/sites.shtml)

## About freeverb-rs

This implementation of Freeverb in Rust is an almost direct conversion of the original source, created as a demonstration project for a [talk I gave about Rust at the Audio Developer Conference 2018](https://www.youtube.com/watch?v=Yom9E-67bdI). The code has been updated since then, so if you want to follow along with the talk then take a look at the `adc-2018` branch.

A difference from the original implementation is that delay line buffers are dynamically allocated so that lengths can be adjusted for sample rates other than 44.1kHz.

## Repo structure

[`crates/freeverb/`](./crates/freeverb)

This contains the core implementation of Freeverb, with a simple interface.

[`crates/clib`](./crates/clib)

A static library that provides C bindings to the Freeverb processor, used by app_juce.

[`crates/audio_module`](./crates/audio_module)

This contains a very minimal generic module+parameter library

[`crates/freeverb_module`](./crates/freeverb_module)

The `freeverb` processor wrapped up as an `AudioModule`, currently only used by `app_gtk`.

[`examples/app_gtk`](./examples/app_gtk)

A very basic audio+GUI application that runs the Freeverb processor.

You will need `gtk4` installed on your system for this to work.

[`examples/app_juce`](./examples/app_juce)

A very basic JUCE application that runs the Freeverb processor via a statically linked library.

[`examples/wasm`](./examples/wasm)

A library that provides a Wasm interface to the Freeverb processor.

Also in the folder is a small web application that runs the Wasm processor.


================================================
FILE: crates/audio_module/Cargo.toml
================================================
[package]
name = "audio_module"
publish = false
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }

[dependencies]


================================================
FILE: crates/audio_module/src/command.rs
================================================
pub enum Command {
    SetParameter(usize, f32),
}

pub trait CommandHandler {
    fn handle_command(&mut self, command: Command);
}


================================================
FILE: crates/audio_module/src/lib.rs
================================================
mod command;
mod module;
mod parameter;
mod processor;
mod string_converter;
mod value_converter;

pub use {
    command::{Command, CommandHandler},
    module::{AudioModule, ParameterProvider},
    parameter::*,
    processor::AudioProcessor,
    string_converter::*,
    value_converter::*,
};


================================================
FILE: crates/audio_module/src/module.rs
================================================
use crate::{AudioProcessor, Parameter};

pub trait AudioModule: ParameterProvider {
    type Processor: AudioProcessor;

    fn create_processor(sample_rate: usize) -> Self::Processor;
}

pub trait ParameterProvider {
    fn parameter_count() -> usize;
    fn parameter(id: usize) -> Box<dyn Parameter>;
}


================================================
FILE: crates/audio_module/src/parameter.rs
================================================
use crate::{
    string_converter::{DefaultStringConverter, StringConverter, float_string_converter},
    value_converter::{DefaultValueConverter, ValueConverter, linear_value_converter},
};

pub enum ValueType {
    Float,
    Bool,
}

pub trait Parameter {
    fn name(&self) -> String;
    fn default_user_value(&self) -> f32;

    fn value_type(&self) -> ValueType {
        ValueType::Float
    }

    fn make_value_converter(&self) -> Box<dyn ValueConverter> {
        Box::new(DefaultValueConverter {})
    }

    fn make_string_converter(&self) -> Box<dyn StringConverter> {
        Box::new(DefaultStringConverter {})
    }
}

pub struct BoolParameter {
    pub name: String,
    pub default_user_value: bool,
}

impl BoolParameter {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            default_user_value: false,
        }
    }

    pub fn default_user_value(mut self, default: bool) -> Self {
        self.default_user_value = default;
        self
    }
}

impl Parameter for BoolParameter {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn default_user_value(&self) -> f32 {
        if self.default_user_value { 1.0 } else { 0.0 }
    }

    fn value_type(&self) -> ValueType {
        ValueType::Bool
    }
}

pub struct FloatParameter {
    pub name: String,
    pub unit: String,
    pub min_user_value: f32,
    pub max_user_value: f32,
    pub default_user_value: f32,
    pub value_converter_maker: fn(&FloatParameter) -> Box<dyn ValueConverter>,
    pub string_converter_maker: fn(&FloatParameter) -> Box<dyn StringConverter>,
}

impl FloatParameter {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            unit: String::default(),
            min_user_value: 0.0,
            max_user_value: 1.0,
            default_user_value: 0.0,
            value_converter_maker: linear_value_converter,
            string_converter_maker: float_string_converter,
        }
    }

    pub fn unit(mut self, unit: &str) -> Self {
        self.unit = unit.to_string();
        self
    }

    pub fn range(mut self, min: f32, max: f32) -> Self {
        self.min_user_value = min;
        self.max_user_value = max;
        self
    }

    pub fn default_user_value(mut self, default: f32) -> Self {
        self.default_user_value = default;
        self
    }

    pub fn value_converter(
        mut self,
        converter: fn(&FloatParameter) -> Box<dyn ValueConverter>,
    ) -> Self {
        self.value_converter_maker = converter;
        self
    }

    pub fn string_converter(
        mut self,
        converter: fn(&FloatParameter) -> Box<dyn StringConverter>,
    ) -> Self {
        self.string_converter_maker = converter;
        self
    }
}

impl Parameter for FloatParameter {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn default_user_value(&self) -> f32 {
        self.default_user_value
    }

    fn make_value_converter(&self) -> Box<dyn ValueConverter> {
        (self.value_converter_maker)(self)
    }

    fn make_string_converter(&self) -> Box<dyn StringConverter> {
        (self.string_converter_maker)(self)
    }
}


================================================
FILE: crates/audio_module/src/processor.rs
================================================
use crate::CommandHandler;

pub trait AudioProcessor: CommandHandler + Send + Sync + 'static {
    fn process(&mut self, input: &[f32], output: &mut [f32], channels: u32);
}


================================================
FILE: crates/audio_module/src/string_converter.rs
================================================
use crate::FloatParameter;

pub trait StringConverter {
    fn to_string(&self, value: f32) -> String;
}

#[derive(Clone)]
pub struct DefaultStringConverter {}

impl StringConverter for DefaultStringConverter {
    fn to_string(&self, value: f32) -> String {
        format!("{:.0}", value)
    }
}

#[derive(Clone)]
pub struct BoolStringConverter {}

impl StringConverter for BoolStringConverter {
    fn to_string(&self, value: f32) -> String {
        if value == 0.0 { "off" } else { "on" }.to_string()
    }
}

#[derive(Clone)]
pub struct FloatStringConverter {
    unit: String,
}

impl FloatStringConverter {
    pub fn new(unit: String) -> Self {
        Self { unit }
    }
}

impl StringConverter for FloatStringConverter {
    fn to_string(&self, value: f32) -> String {
        format!("{:.0} {1}", value, self.unit)
    }
}

#[derive(Clone)]
pub struct PercentStringConverter {}

impl StringConverter for PercentStringConverter {
    fn to_string(&self, value: f32) -> String {
        format!("{:.0} %", value * 100.0)
    }
}

pub fn float_string_converter(parameter: &FloatParameter) -> Box<dyn StringConverter> {
    Box::new(FloatStringConverter::new(parameter.unit.clone()))
}

pub fn percent_string_converter(_: &FloatParameter) -> Box<dyn StringConverter> {
    Box::new(PercentStringConverter {})
}


================================================
FILE: crates/audio_module/src/value_converter.rs
================================================
use crate::FloatParameter;

pub trait ValueConverter {
    fn user_to_linear(&self, value: f32) -> f32;
    fn linear_to_user(&self, value: f32) -> f32;
}

pub struct DefaultValueConverter {}

impl ValueConverter for DefaultValueConverter {
    fn user_to_linear(&self, value: f32) -> f32 {
        value
    }

    fn linear_to_user(&self, value: f32) -> f32 {
        value
    }
}

pub struct LinearValueConverter {
    pub min_user_value: f32,
    pub user_value_range: f32,
}

impl LinearValueConverter {
    pub fn new(min: f32, max: f32) -> Self {
        Self {
            min_user_value: min,
            user_value_range: max - min,
        }
    }
}

impl ValueConverter for LinearValueConverter {
    fn user_to_linear(&self, value: f32) -> f32 {
        (value - self.min_user_value) / self.user_value_range
    }

    fn linear_to_user(&self, value: f32) -> f32 {
        self.min_user_value + value * self.user_value_range
    }
}

pub struct LogValueConverter {
    pub log_min_user_value: f32,
    pub log_user_value_range: f32,
}

impl LogValueConverter {
    pub fn new(min: f32, max: f32) -> Self {
        Self {
            log_min_user_value: min.log2(),
            log_user_value_range: max.log2() - min.log2(),
        }
    }
}

impl ValueConverter for LogValueConverter {
    fn user_to_linear(&self, value: f32) -> f32 {
        (value.log2() - self.log_min_user_value) / self.log_user_value_range
    }

    fn linear_to_user(&self, value: f32) -> f32 {
        (self.log_min_user_value + value * self.log_user_value_range).exp2()
    }
}

pub fn linear_value_converter(parameter: &FloatParameter) -> Box<dyn ValueConverter> {
    Box::new(LinearValueConverter::new(
        parameter.min_user_value,
        parameter.max_user_value,
    ))
}

pub fn log_value_converter(parameter: &FloatParameter) -> Box<dyn ValueConverter> {
    Box::new(LogValueConverter::new(
        parameter.min_user_value,
        parameter.max_user_value,
    ))
}


================================================
FILE: crates/clib/.gitignore
================================================
freeverb.hpp
libfreeverb_clib.a


================================================
FILE: crates/clib/Cargo.toml
================================================
[package]
name = "freeverb-clib"
publish = false
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }

[dependencies]
freeverb = { path = "../freeverb" }

[lib]
crate-type = ["staticlib", "rlib"]


================================================
FILE: crates/clib/build.sh
================================================
cargo build --release
cbindgen -d --lang c++ -o freeverb.hpp .
clang++ --std=c++1z --stdlib=libc++ -L../target/release -lfreeverb_clib test.cpp


================================================
FILE: crates/clib/cbindgen.toml
================================================
namespace = "freeverb"


================================================
FILE: crates/clib/src/lib.rs
================================================
pub use freeverb::Freeverb;

/// Create a Freeverb instance with a given sample rate
///
/// The client is responsible for freeing the instance's memory when it's no longer required,
/// see `destroy()`.
#[unsafe(no_mangle)]
pub extern "C" fn create(sample_rate: usize) -> *mut Freeverb<f64> {
    Box::into_raw(Box::new(Freeverb::<f64>::new(sample_rate)))
}

/// Destroy a Freeverb instance
///
/// # Safety
///
/// The instance must have been previously created using `create()`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn destroy(freeverb: *mut Freeverb<f64>) {
    if !freeverb.is_null() {
        unsafe {
            let _ = Box::from_raw(freeverb);
        }
    } else {
        panic!("")
    }
}

/// Process an audio buffer
///
/// # Safety
///
/// The input and output buffers must be (at least) sample_count f32s in size.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn process(
    freeverb: &mut Freeverb<f64>,
    input_l: *const f32,
    input_r: *const f32,
    output_l: *mut f32,
    output_r: *mut f32,
    sample_count: usize,
) {
    unsafe {
        for i in 0..sample_count as isize {
            let out = freeverb.tick((*input_l.offset(i) as f64, *input_r.offset(i) as f64));
            *output_l.offset(i) = out.0 as f32;
            *output_r.offset(i) = out.1 as f32;
        }
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn set_dampening(freeverb: &mut Freeverb<f64>, value: f64) {
    freeverb.set_dampening(value)
}

#[unsafe(no_mangle)]
pub extern "C" fn set_freeze(freeverb: &mut Freeverb<f64>, value: bool) {
    freeverb.set_freeze(value)
}

#[unsafe(no_mangle)]
pub extern "C" fn set_wet(freeverb: &mut Freeverb<f64>, value: f64) {
    freeverb.set_wet(value)
}

#[unsafe(no_mangle)]
pub extern "C" fn set_width(freeverb: &mut Freeverb<f64>, value: f64) {
    freeverb.set_width(value)
}

#[unsafe(no_mangle)]
pub extern "C" fn set_dry(freeverb: &mut Freeverb<f64>, value: f64) {
    freeverb.set_dry(value)
}

#[unsafe(no_mangle)]
pub extern "C" fn set_room_size(freeverb: &mut Freeverb<f64>, value: f64) {
    freeverb.set_room_size(value)
}


================================================
FILE: crates/clib/test.cpp
================================================
#include "freeverb.hpp"

#include <memory>

int main() {
  auto pFreeverb = freeverb::create(44100);
  freeverb::set_wet(pFreeverb, 1.0);
  freeverb::destroy(pFreeverb);
}


================================================
FILE: crates/freeverb/Cargo.toml
================================================
[package]
name = "freeverb"
version = "0.2.0"
edition = { workspace = true }
authors = { workspace = true }
readme = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
description = "A Rust implementation of the Freeverb algorithm"
documentation = "https://docs.rs/freeverb"
keywords = ["audio", "dsp", "effect", "reverb", "stereo"]

[dependencies]


================================================
FILE: crates/freeverb/src/all_pass.rs
================================================
use crate::{delay_line::DelayLine, float::Float};

pub struct AllPass<T> {
    delay_line: DelayLine<T>,
}

impl<T: Float> AllPass<T> {
    pub fn new(delay_length: usize) -> Self {
        Self {
            delay_line: DelayLine::new(delay_length),
        }
    }

    pub fn tick(&mut self, input: T) -> T {
        let delayed = self.delay_line.read();
        let output = -input + delayed;

        // in the original version of freeverb this is a member which is never modified
        let feedback = T::from(0.5);

        self.delay_line
            .write_and_advance(input + delayed * feedback);

        output
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn basic_ticking() {
        let mut allpass = super::AllPass::new(2);
        assert_eq!(allpass.tick(1.0), -1.0);
        assert_eq!(allpass.tick(0.0), 0.0);
        assert_eq!(allpass.tick(0.0), 1.0);
        assert_eq!(allpass.tick(0.0), 0.0);
        assert_eq!(allpass.tick(0.0), 0.5);
        assert_eq!(allpass.tick(0.0), 0.0);
        assert_eq!(allpass.tick(0.0), 0.25);
    }
}


================================================
FILE: crates/freeverb/src/comb.rs
================================================
use crate::{delay_line::DelayLine, float::Float};

pub struct Comb<T> {
    delay_line: DelayLine<T>,
    feedback: T,
    filter_state: T,
    dampening: T,
    dampening_inverse: T,
}

impl<T: Float> Comb<T> {
    pub fn new(delay_length: usize) -> Self {
        Self {
            delay_line: DelayLine::new(delay_length),
            feedback: T::from(0.5),
            filter_state: T::from(0.0),
            dampening: T::from(0.5),
            dampening_inverse: T::from(0.5),
        }
    }

    pub fn set_dampening(&mut self, value: T) {
        self.dampening = value;
        self.dampening_inverse = T::from(1.0) - value;
    }

    pub fn set_feedback(&mut self, value: T) {
        self.feedback = value;
    }

    pub fn tick(&mut self, input: T) -> T {
        let output = self.delay_line.read();

        self.filter_state = output * self.dampening_inverse + self.filter_state * self.dampening;

        self.delay_line
            .write_and_advance(input + self.filter_state * self.feedback);

        output
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn basic_ticking() {
        let mut comb = super::Comb::new(2);
        assert_eq!(comb.tick(1.0), 0.0);
        assert_eq!(comb.tick(0.0), 0.0);
        assert_eq!(comb.tick(0.0), 1.0);
        assert_eq!(comb.tick(0.0), 0.0);
        assert_eq!(comb.tick(0.0), 0.25);
        assert_eq!(comb.tick(0.0), 0.125);
        assert_eq!(comb.tick(0.0), 0.125);
        assert_eq!(comb.tick(0.0), 0.09375);
    }
}


================================================
FILE: crates/freeverb/src/delay_line.rs
================================================
use crate::float::Float;

pub struct DelayLine<T> {
    buffer: Vec<T>,
    index: usize,
}

impl<T: Float> DelayLine<T> {
    pub fn new(length: usize) -> Self {
        Self {
            buffer: vec![T::from(0.0); length],
            index: 0,
        }
    }

    pub fn read(&self) -> T {
        self.buffer[self.index]
    }

    pub fn write_and_advance(&mut self, value: T) {
        self.buffer[self.index] = value;

        if self.index == self.buffer.len() - 1 {
            self.index = 0;
        } else {
            self.index += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    macro_rules! delay_line_test {
        ($name:ident, $length:expr) => {
            #[test]
            fn $name() {
                let mut line = super::DelayLine::new($length);
                for i in 0..$length {
                    assert_eq!(line.read(), 0.0);
                    line.write_and_advance(i as f32);
                }
                for i in 0..$length {
                    assert_eq!(line.read(), i as f32);
                    line.write_and_advance(0.0);
                }
            }
        };
    }

    delay_line_test!(length_1, 1);
    delay_line_test!(length_3, 3);
    delay_line_test!(length_10, 10);
}


================================================
FILE: crates/freeverb/src/float.rs
================================================
use std::{
    fmt::Debug,
    ops::{Add, AddAssign, Div, Mul, Neg, Sub},
};

/// A trait for the floating point ops needed by the [Freeverb](crate::Freeverb) processor.
pub trait Float:
    Add<Output = Self>
    + Sub<Output = Self>
    + Mul<Output = Self>
    + Div<Output = Self>
    + Neg<Output = Self>
    + AddAssign
    + PartialEq
    + Default
    + From<f32>
    + Copy
    + Clone
    + Debug
    + Send
    + Sync
    + 'static
{
    /// Converts the value into an `f32`.
    ///
    /// `f64` doesn't implement `Into<f32>` so an explicit method is needed here.
    fn to_f32(self) -> f32;
}

impl Float for f32 {
    fn to_f32(self) -> f32 {
        self
    }
}

impl Float for f64 {
    fn to_f32(self) -> f32 {
        self as f32
    }
}


================================================
FILE: crates/freeverb/src/freeverb.rs
================================================
use crate::{all_pass::AllPass, comb::Comb, float::Float, tuning::*};

/// A processor for the Freeverb reverb algorithm.
///
/// 64-bit processing is enabled by default.
/// 32-bit processing can be optionally enabled by using `f32` as the generic `T` parameter.
pub struct Freeverb<T: Float = f64> {
    combs: [(Comb<T>, Comb<T>); 8],
    allpasses: [(AllPass<T>, AllPass<T>); 4],
    wet_gains: (T, T),
    wet: T,
    width: T,
    dry: T,
    input_gain: T,
    dampening: T,
    room_size: T,
    frozen: bool,
}

impl<T: Float> Freeverb<T> {
    /// Produces a new processor with the given sample rate.
    ///
    /// The algorithm's tuning constants were designed for a sample rate of 44.1kHz,
    /// with a note that they will 'probably be OK' for 48kHz, but would require scaling for other
    /// sample rates. In this implementation the constants are scaled when using any sample rate,
    /// including 48kHz.
    pub fn new(sr: usize) -> Self {
        let mut freeverb = Freeverb::<T> {
            combs: [
                (
                    Comb::new(adjust_length(COMB_TUNING_L1, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R1, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L2, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R2, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L3, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R3, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L4, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R4, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L5, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R5, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L6, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R6, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L7, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R7, sr)),
                ),
                (
                    Comb::new(adjust_length(COMB_TUNING_L8, sr)),
                    Comb::new(adjust_length(COMB_TUNING_R8, sr)),
                ),
            ],
            allpasses: [
                (
                    AllPass::new(adjust_length(ALLPASS_TUNING_L1, sr)),
                    AllPass::new(adjust_length(ALLPASS_TUNING_R1, sr)),
                ),
                (
                    AllPass::new(adjust_length(ALLPASS_TUNING_L2, sr)),
                    AllPass::new(adjust_length(ALLPASS_TUNING_R2, sr)),
                ),
                (
                    AllPass::new(adjust_length(ALLPASS_TUNING_L3, sr)),
                    AllPass::new(adjust_length(ALLPASS_TUNING_R3, sr)),
                ),
                (
                    AllPass::new(adjust_length(ALLPASS_TUNING_L4, sr)),
                    AllPass::new(adjust_length(ALLPASS_TUNING_R4, sr)),
                ),
            ],
            wet_gains: (T::default(), T::default()),
            wet: T::default(),
            dry: T::default(),
            input_gain: T::default(),
            width: T::default(),
            dampening: T::default(),
            room_size: T::default(),
            frozen: false,
        };

        freeverb.set_wet(T::from(1.0) / T::from(SCALE_WET));
        freeverb.set_width(T::from(1.0));
        freeverb.set_dampening(T::from(0.5));
        freeverb.set_room_size(T::from(0.5));
        freeverb.set_frozen(false);

        freeverb
    }

    /// Processes a single pair of values.
    ///
    /// The pair's values are the left/right channels of a single processing frame.
    ///
    /// To process a buffer of frames this function should be called repeatedly.
    pub fn tick(&mut self, input: (T, T)) -> (T, T) {
        let input_mixed = (input.0 + input.1) * T::from(FIXED_GAIN) * self.input_gain;

        let mut out = (T::from(0.0), T::from(0.0));

        for combs in self.combs.iter_mut() {
            out.0 += combs.0.tick(input_mixed);
            out.1 += combs.1.tick(input_mixed);
        }

        for allpasses in self.allpasses.iter_mut() {
            out.0 = allpasses.0.tick(out.0);
            out.1 = allpasses.1.tick(out.1);
        }

        (
            out.0 * self.wet_gains.0 + out.1 * self.wet_gains.1 + input.0 * self.dry,
            out.1 * self.wet_gains.0 + out.0 * self.wet_gains.1 + input.1 * self.dry,
        )
    }

    /// Sets the processors dampening value.
    ///
    /// The value should be in the range `0..=1`.
    pub fn set_dampening(&mut self, value: T) {
        self.dampening = value * T::from(SCALE_DAMPENING);
        self.update_combs();
    }

    /// Enables or disables the reverb's 'freeze' feature.
    ///
    /// This is called `set_mode` in the original implementation.
    pub fn set_freeze(&mut self, frozen: bool) {
        self.frozen = frozen;
        self.update_combs();
    }

    /// Sets the amount of the 'dry' signal to include in the processor's output.
    ///
    /// The dry signal is the unmodified input, without any of the reverb's output.
    ///
    /// The value should be in the range `0..=1`.
    pub fn set_dry(&mut self, value: T) {
        self.dry = value;
    }

    /// Sets the amount of the 'wet' signal to include in the processor's output.
    ///
    /// The wet signal is the reverb's output, without any of the unmodified input.
    ///
    /// The value should be in the range `0..=1`.
    pub fn set_wet(&mut self, value: T) {
        self.wet = value * T::from(SCALE_WET);
        self.update_wet_gains();
    }

    /// Sets the processor's stereo width.
    ///
    /// The value should be in the range `0..=1`.
    pub fn set_width(&mut self, value: T) {
        self.width = value;
        self.update_wet_gains();
    }

    /// Sets the processor's 'room size'.
    ///
    /// The value should be in the range `0..=1`.
    pub fn set_room_size(&mut self, value: T) {
        self.room_size = value * T::from(SCALE_ROOM) + T::from(OFFSET_ROOM);
        self.update_combs();
    }

    fn update_wet_gains(&mut self) {
        self.wet_gains = (
            self.wet * (self.width / T::from(2.0) + T::from(0.5)),
            self.wet * ((T::from(1.0) - self.width) / T::from(2.0)),
        )
    }

    fn set_frozen(&mut self, frozen: bool) {
        self.frozen = frozen;
        self.input_gain = if frozen { T::from(0.0) } else { T::from(1.0) };
        self.update_combs();
    }

    fn update_combs(&mut self) {
        let (feedback, dampening) = if self.frozen {
            (T::from(1.0), T::from(0.0))
        } else {
            (self.room_size, self.dampening)
        };

        for combs in self.combs.iter_mut() {
            combs.0.set_feedback(feedback);
            combs.1.set_feedback(feedback);

            combs.0.set_dampening(dampening);
            combs.1.set_dampening(dampening);
        }
    }
}

fn adjust_length(length: usize, sr: usize) -> usize {
    (length as f64 * sr as f64 / 44100.0) as usize
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn check_adjust_length() {
        assert_eq!(adjust_length(1000, 44100), 1000);
        assert_eq!(adjust_length(100, 22050), 50);
        assert_eq!(adjust_length(2000, 88200), 4000);
        assert_eq!(adjust_length(4000, 96000), 8707);
    }

    #[test]
    fn check_output() {
        // Validate the processor's output against reference values take from the original
        // implementation.
        let mut freeverb = Freeverb::<f32>::new(44100);

        // Pass in the delta function.
        let delta = (1.0, 1.0);
        let silence = (0.0, 0.0);
        assert_eq!(freeverb.tick(delta), silence);

        // Process 3000 frames in total.
        for _ in 0..2999 {
            freeverb.tick(silence);
        }

        // Check that the output is within a small difference of the reference
        check_almost_equal(freeverb.tick(silence), (-0.0000064512, -0.014999998));
        check_almost_equal(freeverb.tick(silence), (0.0074987095, 0.002340001));
        check_almost_equal(freeverb.tick(silence), (0.0018747419, -0.0049695));
        check_almost_equal(freeverb.tick(silence), (-0.0000000516, -0.0008064));
        check_almost_equal(freeverb.tick(silence), (-0.03070501, 0.01296372));
        check_almost_equal(freeverb.tick(silence), (-0.024516001, -0.000032256));
        check_almost_equal(freeverb.tick(silence), (-0.0004032004, -0.03024645));
        check_almost_equal(freeverb.tick(silence), (-0.0000806401, -0.0060492903));
    }

    #[track_caller]
    fn check_almost_equal(output: (f32, f32), expected: (f32, f32)) {
        let difference = ((output.0 - expected.0).abs(), (output.1 - expected.1).abs());
        let allowed_difference = 1.0e-10;

        if difference.0 > allowed_difference || difference.1 > allowed_difference {
            panic!(
                "Value is not almost equal to the expected output:
  output: {output:?}
  expected: {expected:?}
  allowed difference: {allowed_difference}
  difference: {difference:?}
                ",
            )
        }
    }
}


================================================
FILE: crates/freeverb/src/lib.rs
================================================
//! A Rust implementation of the Freeverb reverb algorithm.
//!
//! Freeverb was originally written in C++ by "Jezar at Dreampoint", and was released into the public
//! domain in June 2000. It is now widely used in various incarnations in multiple software packages.
//!
//! - The orignal C++ source code can be found [here](https://freeverb3-vst.sourceforge.io).
//! - For an analysis of the algorithm see
//!   [here](https://ccrma.stanford.edu/~jos/pasp/Freeverb.html).

mod all_pass;
mod comb;
mod delay_line;
mod float;
mod freeverb;
mod tuning;

pub use self::{float::Float, freeverb::Freeverb};


================================================
FILE: crates/freeverb/src/tuning.rs
================================================
pub const FIXED_GAIN: f32 = 0.015;

pub const SCALE_WET: f32 = 3.0;
pub const SCALE_DAMPENING: f32 = 0.4;

pub const SCALE_ROOM: f32 = 0.28;
pub const OFFSET_ROOM: f32 = 0.7;

pub const STEREO_SPREAD: usize = 23;

pub const COMB_TUNING_L1: usize = 1116;
pub const COMB_TUNING_R1: usize = COMB_TUNING_L1 + STEREO_SPREAD;
pub const COMB_TUNING_L2: usize = 1188;
pub const COMB_TUNING_R2: usize = COMB_TUNING_L2 + STEREO_SPREAD;
pub const COMB_TUNING_L3: usize = 1277;
pub const COMB_TUNING_R3: usize = COMB_TUNING_L3 + STEREO_SPREAD;
pub const COMB_TUNING_L4: usize = 1356;
pub const COMB_TUNING_R4: usize = COMB_TUNING_L4 + STEREO_SPREAD;
pub const COMB_TUNING_L5: usize = 1422;
pub const COMB_TUNING_R5: usize = COMB_TUNING_L5 + STEREO_SPREAD;
pub const COMB_TUNING_L6: usize = 1491;
pub const COMB_TUNING_R6: usize = COMB_TUNING_L6 + STEREO_SPREAD;
pub const COMB_TUNING_L7: usize = 1557;
pub const COMB_TUNING_R7: usize = COMB_TUNING_L7 + STEREO_SPREAD;
pub const COMB_TUNING_L8: usize = 1617;
pub const COMB_TUNING_R8: usize = COMB_TUNING_L8 + STEREO_SPREAD;

pub const ALLPASS_TUNING_L1: usize = 556;
pub const ALLPASS_TUNING_R1: usize = ALLPASS_TUNING_L1 + STEREO_SPREAD;
pub const ALLPASS_TUNING_L2: usize = 441;
pub const ALLPASS_TUNING_R2: usize = ALLPASS_TUNING_L2 + STEREO_SPREAD;
pub const ALLPASS_TUNING_L3: usize = 341;
pub const ALLPASS_TUNING_R3: usize = ALLPASS_TUNING_L3 + STEREO_SPREAD;
pub const ALLPASS_TUNING_L4: usize = 225;
pub const ALLPASS_TUNING_R4: usize = ALLPASS_TUNING_L4 + STEREO_SPREAD;


================================================
FILE: crates/freeverb_module/Cargo.toml
================================================
[package]
name = "freeverb_module"
publish = false
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }

[dependencies]
audio_module = { path = "../audio_module" }
freeverb = { path = "../freeverb" }
num-derive = "0.4.2"
num-traits = "0.2"


================================================
FILE: crates/freeverb_module/src/lib.rs
================================================
#[macro_use]
extern crate num_derive;

use {
    audio_module::{
        AudioModule, AudioProcessor, BoolParameter, Command, CommandHandler, FloatParameter,
        Parameter, ParameterProvider, percent_string_converter,
    },
    freeverb::{Float, Freeverb},
    num_traits::FromPrimitive,
};

#[derive(FromPrimitive)]
pub enum Parameters {
    Dampening,
    Width,
    RoomSize,
    Freeze,
    Dry,
    Wet,
}

pub struct FreeverbProcessor<T: Float = f64> {
    freeverb: Freeverb<T>,
}

impl FreeverbProcessor {
    fn new(sample_rate: usize) -> Self {
        Self {
            freeverb: Freeverb::new(sample_rate),
        }
    }
}

impl<T: Float> CommandHandler for FreeverbProcessor<T> {
    fn handle_command(&mut self, command: Command) {
        match command {
            Command::SetParameter(id, value) => match Parameters::from_usize(id).unwrap() {
                Parameters::Dampening => {
                    self.freeverb.set_dampening(value.into());
                }
                Parameters::Width => {
                    self.freeverb.set_width(value.into());
                }
                Parameters::RoomSize => {
                    self.freeverb.set_room_size(value.into());
                }
                Parameters::Freeze => {
                    self.freeverb.set_freeze(value != 0.0);
                }
                Parameters::Dry => {
                    self.freeverb.set_dry(value.into());
                }
                Parameters::Wet => {
                    self.freeverb.set_wet(value.into());
                }
            },
        }
    }
}

impl<T: Float> AudioProcessor for FreeverbProcessor<T> {
    fn process(&mut self, input: &[f32], output: &mut [f32], channels: u32) {
        debug_assert_eq!(channels, 2);
        debug_assert_eq!(input.len(), output.len());

        for i in (0..input.len()).step_by(2) {
            let result = self.freeverb.tick((input[i].into(), input[i + 1].into()));

            output[i] = result.0.to_f32();
            output[i + 1] = result.1.to_f32();
        }
    }
}

pub struct FreeverbModule {}

impl AudioModule for FreeverbModule {
    type Processor = FreeverbProcessor;

    fn create_processor(sample_rate: usize) -> Self::Processor {
        FreeverbProcessor::new(sample_rate)
    }
}

impl ParameterProvider for FreeverbModule {
    fn parameter_count() -> usize {
        (0..usize::MAX)
            .take_while(|&x| Parameters::from_usize(x).is_some())
            .count()
    }

    fn parameter(id: usize) -> Box<dyn Parameter> {
        match Parameters::from_usize(id).unwrap() {
            Parameters::Dampening => Box::new(
                FloatParameter::new("Dampening")
                    .string_converter(percent_string_converter)
                    .default_user_value(0.5),
            ),
            Parameters::Width => Box::new(
                FloatParameter::new("Width")
                    .string_converter(percent_string_converter)
                    .default_user_value(0.5),
            ),
            Parameters::RoomSize => Box::new(
                FloatParameter::new("Room Size")
                    .string_converter(percent_string_converter)
                    .default_user_value(0.5),
            ),
            Parameters::Freeze => Box::new(BoolParameter::new("Freeze")),
            Parameters::Dry => Box::new(
                FloatParameter::new("Dry")
                    .string_converter(percent_string_converter)
                    .default_user_value(0.0),
            ),
            Parameters::Wet => Box::new(
                FloatParameter::new("Wet")
                    .string_converter(percent_string_converter)
                    .default_user_value(1.0),
            ),
        }
    }
}


================================================
FILE: examples/app_gtk/Cargo.toml
================================================
[package]
name = "freeverb-gtk"
publish = false
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }

[dependencies]
audio_module = { path = "../../crates/audio_module" }
freeverb_module = { path = "../../crates/freeverb_module" }

audio_thread_priority = "0.34.0"
cpal = "0.16.0"
crossbeam-channel = "0.5.15"
gtk = { version = "0.10.1", package = "gtk4", features = ["v4_12"] }
ringbuf = "0.4.8"


================================================
FILE: examples/app_gtk/src/audio_thread.rs
================================================
use {
    audio_module::{AudioModule, AudioProcessor, Command, CommandHandler},
    cpal::traits::{DeviceTrait, HostTrait, StreamTrait},
    ringbuf::{
        HeapRb,
        traits::{Consumer, Producer, Split},
    },
};

#[allow(unused)]
pub struct AudioStreams {
    pub output: cpal::Stream,
    pub input: cpal::Stream,
}

pub fn start_audio<Module: AudioModule>(
    command_receiver: crossbeam_channel::Receiver<Command>,
    sample_rate: usize,
) -> Result<AudioStreams, ()> {
    let mut processor = Module::create_processor(sample_rate);

    const CHANNELS: usize = 2;
    const FRAMES_PER_BUFFER: usize = 128;
    const SAMPLES_PER_BUFFER: usize = FRAMES_PER_BUFFER * CHANNELS;

    let host = cpal::default_host();

    let input_device = host
        .default_input_device()
        .expect("failed to find a default input device");

    let output_device = host
        .default_output_device()
        .expect("failed to find a default output device");

    let stream_config = cpal::StreamConfig {
        channels: CHANNELS as u16,
        sample_rate: cpal::SampleRate(sample_rate as u32),
        buffer_size: cpal::BufferSize::Fixed(FRAMES_PER_BUFFER as u32),
    };

    let mut process_buffer = [0.0f32; SAMPLES_PER_BUFFER];
    let ring_buffer = HeapRb::new(SAMPLES_PER_BUFFER * 2);
    let (mut to_output, mut from_input) = ring_buffer.split();

    let input = input_device
        .build_input_stream(
            &stream_config,
            move |data: &[f32], _info: &cpal::InputCallbackInfo| {
                debug_assert!(data.len() == SAMPLES_PER_BUFFER);

                while let Ok(command) = command_receiver.try_recv() {
                    processor.handle_command(command);
                }

                processor.process(data, &mut process_buffer, CHANNELS as u32);

                to_output.push_slice(&process_buffer);
            },
            move |err| eprintln!("Error on audio input stream: {}", err),
            None,
        )
        .expect("Failed to create input audio stream");

    let output = output_device
        .build_output_stream(
            &stream_config,
            move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| {
                // println!("output buffer");
                debug_assert!(data.len() == SAMPLES_PER_BUFFER);

                let consumed = from_input.pop_slice(data);

                if consumed < SAMPLES_PER_BUFFER {
                    println!("output underflowed");
                }
            },
            move |err| eprintln!("Error on audio output stream: {}", err),
            None,
        )
        .expect("Failed to create input audio stream");

    if let Err(error) = input.play() {
        eprintln!("Error while starting input audio stream: {}", error);
        return Err(());
    }

    if let Err(error) = output.play() {
        eprintln!("Error while starting output audio stream: {}", error);
        return Err(());
    }

    println!("Started audio i/o");
    Ok(AudioStreams { input, output })
}


================================================
FILE: examples/app_gtk/src/gtk_parameter_slider.rs
================================================
use {
    audio_module::{Command, Parameter},
    gtk::{Label, Orientation, PositionType, Scale, prelude::*},
};

pub fn make_slider(
    parameter: Box<dyn Parameter>,
    id: usize,
    command_sender: crossbeam_channel::Sender<Command>,
) -> gtk::Box {
    let value_converter = parameter.make_value_converter();
    let string_converter = parameter.make_string_converter();

    let label = Label::new(Some(parameter.name().as_str()));

    let adjustment = gtk::Adjustment::builder()
        .lower(0.0)
        .upper(1.0)
        .value(value_converter.user_to_linear(parameter.default_user_value()) as f64)
        .step_increment(0.01)
        .page_increment(0.1)
        .build();

    let scale = Scale::builder()
        .adjustment(&adjustment)
        .inverted(true)
        .value_pos(PositionType::Bottom)
        .orientation(Orientation::Vertical)
        .draw_value(true)
        .digits(2)
        .vexpand(true)
        .hexpand(true)
        .build();

    scale.set_increments(0.01, 0.001);
    scale.set_format_value_func(move |_, x| {
        string_converter.to_string(value_converter.linear_to_user(x as f32))
    });

    scale.connect_value_changed(move |scale| {
        command_sender
            .send(Command::SetParameter(id, scale.value() as f32))
            .unwrap();
    });

    let container = gtk::Box::builder()
        .orientation(Orientation::Vertical)
        .spacing(2)
        .build();

    container.append(&label);
    container.append(&scale);

    container
}


================================================
FILE: examples/app_gtk/src/gtk_parameter_toggle.rs
================================================
use {
    audio_module::{Command, Parameter},
    gtk::{Align, Orientation, ToggleButton, prelude::*},
};

pub fn make_toggle(
    parameter: Box<dyn Parameter>,
    id: usize,
    command_sender: crossbeam_channel::Sender<Command>,
) -> gtk::Box {
    let button = ToggleButton::with_label(parameter.name().as_str());
    button.set_active(parameter.default_user_value() != 0.0);
    button.connect_toggled(move |button| {
        command_sender
            .send(Command::SetParameter(
                id,
                if button.is_active() { 1.0f32 } else { 0.0f32 },
            ))
            .unwrap();
    });

    let container = gtk::Box::builder()
        .orientation(Orientation::Vertical)
        .spacing(2)
        .baseline_position(gtk::BaselinePosition::Center)
        // .vexpand(true)
        .valign(Align::Center)
        .build();
    container.append(&button);
    container
}


================================================
FILE: examples/app_gtk/src/main.rs
================================================
use {
    audio_module::{AudioModule, ValueType},
    freeverb_module::FreeverbModule,
    gtk::{Application, ApplicationWindow, Orientation, prelude::*},
};

mod audio_thread;
mod gtk_parameter_slider;
mod gtk_parameter_toggle;

fn main() {
    run_main::<FreeverbModule>();
}

fn run_main<Module: AudioModule>() {
    if gtk::init().is_err() {
        println!("Error initializing GTK");
        return;
    }

    let (command_sender, command_receiver) = crossbeam_channel::bounded(1024);

    let sample_rate = 44100;
    let _audio_streams = audio_thread::start_audio::<Module>(command_receiver, sample_rate)
        .expect("Failed to start audio");

    let app = Application::builder()
        .application_id("org.example.freeverb-rs")
        .build();

    app.connect_activate(move |app| {
        let window = ApplicationWindow::builder()
            .application(app)
            .default_width(350)
            .default_height(300)
            .resizable(false)
            .title("freeverb-rs")
            .build();

        let widgets = gtk::Box::builder()
            .orientation(Orientation::Horizontal)
            .spacing(4)
            .height_request(200)
            .vexpand(false)
            .build();

        for id in 0..Module::parameter_count() {
            let parameter = Module::parameter(id);
            let widget = match parameter.value_type() {
                ValueType::Float => {
                    gtk_parameter_slider::make_slider(parameter, id, command_sender.clone())
                }
                ValueType::Bool => {
                    gtk_parameter_toggle::make_toggle(parameter, id, command_sender.clone())
                }
            };
            widgets.append(&widget);
        }

        window.set_child(Some(&widgets));

        window.present();
    });

    app.run();
}


================================================
FILE: examples/app_juce/.gitignore
================================================
Builds


================================================
FILE: examples/app_juce/JuceLibraryCode/AppConfig.h
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

    There's a section below where you can add your own custom code safely, and the
    Projucer will preserve the contents of that block, but the best way to change
    any of these definitions is by using the Projucer's project settings.

    Any commented-out settings will assume their default values.

*/

#pragma once

//==============================================================================
// [BEGIN_USER_CODE_SECTION]

// (You can add your own code in this section, and the Projucer will not overwrite it)

// [END_USER_CODE_SECTION]

/*
  ==============================================================================

   In accordance with the terms of the JUCE 5 End-Use License Agreement, the
   JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
   ineffective unless you have a JUCE Indie or Pro license, or are using JUCE
   under the GPL v3 license.

   End User License Agreement: www.juce.com/juce-5-licence

  ==============================================================================
*/

// BEGIN SECTION A

#ifndef JUCE_DISPLAY_SPLASH_SCREEN
 #define JUCE_DISPLAY_SPLASH_SCREEN 1
#endif

#ifndef JUCE_REPORT_APP_USAGE
 #define JUCE_REPORT_APP_USAGE 1
#endif

// END SECTION A

#define JUCE_USE_DARK_SPLASH_SCREEN 1

//==============================================================================
#define JUCE_MODULE_AVAILABLE_juce_audio_basics          1
#define JUCE_MODULE_AVAILABLE_juce_audio_devices         1
#define JUCE_MODULE_AVAILABLE_juce_audio_formats         1
#define JUCE_MODULE_AVAILABLE_juce_audio_processors      1
#define JUCE_MODULE_AVAILABLE_juce_audio_utils           1
#define JUCE_MODULE_AVAILABLE_juce_core                  1
#define JUCE_MODULE_AVAILABLE_juce_cryptography          1
#define JUCE_MODULE_AVAILABLE_juce_data_structures       1
#define JUCE_MODULE_AVAILABLE_juce_events                1
#define JUCE_MODULE_AVAILABLE_juce_graphics              1
#define JUCE_MODULE_AVAILABLE_juce_gui_basics            1
#define JUCE_MODULE_AVAILABLE_juce_gui_extra             1
#define JUCE_MODULE_AVAILABLE_juce_opengl                1
#define JUCE_MODULE_AVAILABLE_juce_video                 1

#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1

//==============================================================================
// juce_audio_devices flags:

#ifndef    JUCE_USE_WINRT_MIDI
 //#define JUCE_USE_WINRT_MIDI 0
#endif

#ifndef    JUCE_ASIO
 //#define JUCE_ASIO 0
#endif

#ifndef    JUCE_WASAPI
 //#define JUCE_WASAPI 1
#endif

#ifndef    JUCE_WASAPI_EXCLUSIVE
 //#define JUCE_WASAPI_EXCLUSIVE 0
#endif

#ifndef    JUCE_DIRECTSOUND
 //#define JUCE_DIRECTSOUND 1
#endif

#ifndef    JUCE_ALSA
 //#define JUCE_ALSA 1
#endif

#ifndef    JUCE_JACK
 //#define JUCE_JACK 0
#endif

#ifndef    JUCE_BELA
 //#define JUCE_BELA 0
#endif

#ifndef    JUCE_USE_ANDROID_OBOE
 //#define JUCE_USE_ANDROID_OBOE 0
#endif

#ifndef    JUCE_USE_ANDROID_OPENSLES
 //#define JUCE_USE_ANDROID_OPENSLES 0
#endif

#ifndef    JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
 //#define JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS 0
#endif

//==============================================================================
// juce_audio_formats flags:

#ifndef    JUCE_USE_FLAC
 //#define JUCE_USE_FLAC 1
#endif

#ifndef    JUCE_USE_OGGVORBIS
 //#define JUCE_USE_OGGVORBIS 1
#endif

#ifndef    JUCE_USE_MP3AUDIOFORMAT
 //#define JUCE_USE_MP3AUDIOFORMAT 0
#endif

#ifndef    JUCE_USE_LAME_AUDIO_FORMAT
 //#define JUCE_USE_LAME_AUDIO_FORMAT 0
#endif

#ifndef    JUCE_USE_WINDOWS_MEDIA_FORMAT
 //#define JUCE_USE_WINDOWS_MEDIA_FORMAT 1
#endif

//==============================================================================
// juce_audio_processors flags:

#ifndef    JUCE_PLUGINHOST_VST
 //#define JUCE_PLUGINHOST_VST 0
#endif

#ifndef    JUCE_PLUGINHOST_VST3
 //#define JUCE_PLUGINHOST_VST3 0
#endif

#ifndef    JUCE_PLUGINHOST_AU
 //#define JUCE_PLUGINHOST_AU 0
#endif

#ifndef    JUCE_PLUGINHOST_LADSPA
 //#define JUCE_PLUGINHOST_LADSPA 0
#endif

//==============================================================================
// juce_audio_utils flags:

#ifndef    JUCE_USE_CDREADER
 //#define JUCE_USE_CDREADER 0
#endif

#ifndef    JUCE_USE_CDBURNER
 //#define JUCE_USE_CDBURNER 0
#endif

//==============================================================================
// juce_core flags:

#ifndef    JUCE_FORCE_DEBUG
 //#define JUCE_FORCE_DEBUG 0
#endif

#ifndef    JUCE_LOG_ASSERTIONS
 //#define JUCE_LOG_ASSERTIONS 0
#endif

#ifndef    JUCE_CHECK_MEMORY_LEAKS
 //#define JUCE_CHECK_MEMORY_LEAKS 1
#endif

#ifndef    JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
 //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0
#endif

#ifndef    JUCE_INCLUDE_ZLIB_CODE
 //#define JUCE_INCLUDE_ZLIB_CODE 1
#endif

#ifndef    JUCE_USE_CURL
 //#define JUCE_USE_CURL 0
#endif

#ifndef    JUCE_LOAD_CURL_SYMBOLS_LAZILY
 //#define JUCE_LOAD_CURL_SYMBOLS_LAZILY 0
#endif

#ifndef    JUCE_CATCH_UNHANDLED_EXCEPTIONS
 //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1
#endif

#ifndef    JUCE_ALLOW_STATIC_NULL_VARIABLES
 //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1
#endif

#ifndef    JUCE_STRICT_REFCOUNTEDPOINTER
 #define   JUCE_STRICT_REFCOUNTEDPOINTER 1
#endif

//==============================================================================
// juce_events flags:

#ifndef    JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK
 //#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK 0
#endif

//==============================================================================
// juce_graphics flags:

#ifndef    JUCE_USE_COREIMAGE_LOADER
 //#define JUCE_USE_COREIMAGE_LOADER 1
#endif

#ifndef    JUCE_USE_DIRECTWRITE
 //#define JUCE_USE_DIRECTWRITE 1
#endif

#ifndef    JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
 //#define JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING 0
#endif

//==============================================================================
// juce_gui_basics flags:

#ifndef    JUCE_ENABLE_REPAINT_DEBUGGING
 //#define JUCE_ENABLE_REPAINT_DEBUGGING 0
#endif

#ifndef    JUCE_USE_XRANDR
 //#define JUCE_USE_XRANDR 1
#endif

#ifndef    JUCE_USE_XINERAMA
 //#define JUCE_USE_XINERAMA 1
#endif

#ifndef    JUCE_USE_XSHM
 //#define JUCE_USE_XSHM 1
#endif

#ifndef    JUCE_USE_XRENDER
 //#define JUCE_USE_XRENDER 0
#endif

#ifndef    JUCE_USE_XCURSOR
 //#define JUCE_USE_XCURSOR 1
#endif

#ifndef    JUCE_WIN_PER_MONITOR_DPI_AWARE
 //#define JUCE_WIN_PER_MONITOR_DPI_AWARE 1
#endif

//==============================================================================
// juce_gui_extra flags:

#ifndef    JUCE_WEB_BROWSER
 //#define JUCE_WEB_BROWSER 1
#endif

#ifndef    JUCE_ENABLE_LIVE_CONSTANT_EDITOR
 //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 0
#endif

//==============================================================================
// juce_video flags:

#ifndef    JUCE_USE_CAMERA
 //#define JUCE_USE_CAMERA 0
#endif

#ifndef    JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
 //#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
#endif

//==============================================================================
#ifndef    JUCE_STANDALONE_APPLICATION
 #if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
  #define  JUCE_STANDALONE_APPLICATION JucePlugin_Build_Standalone
 #else
  #define  JUCE_STANDALONE_APPLICATION 1
 #endif
#endif


================================================
FILE: examples/app_juce/JuceLibraryCode/JuceHeader.h
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

    This is the header file that your files should include in order to get all the
    JUCE library headers. You should avoid including the JUCE headers directly in
    your own source files, because that wouldn't pick up the correct configuration
    options for your app.

*/

#pragma once

#include "AppConfig.h"

#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_audio_formats/juce_audio_formats.h>
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_utils/juce_audio_utils.h>
#include <juce_core/juce_core.h>
#include <juce_cryptography/juce_cryptography.h>
#include <juce_data_structures/juce_data_structures.h>
#include <juce_events/juce_events.h>
#include <juce_graphics/juce_graphics.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_gui_extra/juce_gui_extra.h>
#include <juce_opengl/juce_opengl.h>
#include <juce_video/juce_video.h>


#if ! DONT_SET_USING_JUCE_NAMESPACE
 // If your code uses a lot of JUCE classes, then this will obviously save you
 // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE.
 using namespace juce;
#endif

#if ! JUCE_DONT_DECLARE_PROJECTINFO
namespace ProjectInfo
{
    const char* const  projectName    = "freeverb";
    const char* const  companyName    = "";
    const char* const  versionString  = "1.0.0";
    const int          versionNumber  = 0x10000;
}
#endif


================================================
FILE: examples/app_juce/JuceLibraryCode/ReadMe.txt
================================================

 Important Note!!
 ================

The purpose of this folder is to contain files that are auto-generated by the Projucer,
and ALL files in this folder will be mercilessly DELETED and completely re-written whenever
the Projucer saves your project.

Therefore, it's a bad idea to make any manual changes to the files in here, or to
put any of your own files in here if you don't want to lose them. (Of course you may choose
to add the folder's contents to your version-control system so that you can re-merge your own
modifications after the Projucer has saved its changes).


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_basics.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_basics/juce_audio_basics.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_basics.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_basics/juce_audio_basics.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_devices.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_devices/juce_audio_devices.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_devices.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_devices/juce_audio_devices.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_formats.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_formats/juce_audio_formats.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_formats.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_formats/juce_audio_formats.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_processors.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_processors/juce_audio_processors.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_processors.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_processors/juce_audio_processors.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_utils.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_utils/juce_audio_utils.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_audio_utils.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_audio_utils/juce_audio_utils.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_core.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_core/juce_core.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_core.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_core/juce_core.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_cryptography.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_cryptography/juce_cryptography.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_cryptography.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_cryptography/juce_cryptography.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_data_structures.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_data_structures/juce_data_structures.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_data_structures.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_data_structures/juce_data_structures.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_events.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_events/juce_events.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_events.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_events/juce_events.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_graphics.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_graphics/juce_graphics.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_graphics.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_graphics/juce_graphics.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_gui_basics.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_gui_basics/juce_gui_basics.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_gui_basics.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_gui_basics/juce_gui_basics.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_gui_extra.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_gui_extra/juce_gui_extra.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_gui_extra.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_gui_extra/juce_gui_extra.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_opengl.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_opengl/juce_opengl.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_opengl.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_opengl/juce_opengl.mm>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_video.cpp
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_video/juce_video.cpp>


================================================
FILE: examples/app_juce/JuceLibraryCode/include_juce_video.mm
================================================
/*

    IMPORTANT! This file is auto-generated each time you save your
    project - if you alter its contents, your changes may be overwritten!

*/

#include "AppConfig.h"
#include <juce_video/juce_video.mm>


================================================
FILE: examples/app_juce/Source/Main.cpp
================================================
/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic startup code for a JUCE application.

  ==============================================================================
*/

#include "../JuceLibraryCode/JuceHeader.h"
#include "MainComponent.h"

//==============================================================================
class NewProjectApplication  : public JUCEApplication
{
public:
    //==============================================================================
    NewProjectApplication() {}

    const String getApplicationName() override       { return ProjectInfo::projectName; }
    const String getApplicationVersion() override    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed() override       { return true; }

    //==============================================================================
    void initialise (const String& commandLine) override
    {
        // This method is where you should put your application's initialisation code..

        mainWindow.reset (new MainWindow (getApplicationName()));
    }

    void shutdown() override
    {
        // Add your application's shutdown code here..

        mainWindow = nullptr; // (deletes our window)
    }

    //==============================================================================
    void systemRequestedQuit() override
    {
        // This is called when the app is being asked to quit: you can ignore this
        // request and let the app carry on running, or call quit() to allow the app to close.
        quit();
    }

    void anotherInstanceStarted (const String& commandLine) override
    {
        // When another instance of the app is launched while this one is running,
        // this method is invoked, and the commandLine parameter tells you what
        // the other instance's command-line arguments were.
    }

    //==============================================================================
    /*
        This class implements the desktop window that contains an instance of
        our MainComponent class.
    */
    class MainWindow    : public DocumentWindow
    {
    public:
        MainWindow (String name)  : DocumentWindow (name,
                                                    Desktop::getInstance().getDefaultLookAndFeel()
                                                                          .findColour (ResizableWindow::backgroundColourId),
                                                    DocumentWindow::allButtons)
        {
            setUsingNativeTitleBar (true);
            setContentOwned (new MainComponent(), true);

           #if JUCE_IOS || JUCE_ANDROID
            setFullScreen (true);
           #else
            setResizable (true, true);
            centreWithSize (getWidth(), getHeight());
           #endif

            setVisible (true);
        }

        void closeButtonPressed() override
        {
            // This is called when the user tries to close this window. Here, we'll just
            // ask the app to quit when this happens, but you can change this to do
            // whatever you need.
            JUCEApplication::getInstance()->systemRequestedQuit();
        }

        /* Note: Be careful if you override any DocumentWindow methods - the base
           class uses a lot of them, so by overriding you might break its functionality.
           It's best to do all your work in your content component instead, but if
           you really have to override any DocumentWindow methods, make sure your
           subclass also calls the superclass's method.
        */

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
    };

private:
    std::unique_ptr<MainWindow> mainWindow;
};

//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (NewProjectApplication)


================================================
FILE: examples/app_juce/Source/MainComponent.cpp
================================================
#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent() : freeverb_(nullptr, &freeverb::destroy) {
  auto setupSlider = [this](auto &slider, auto &label, const auto &text,
                            const auto value) {
    addAndMakeVisible(slider);
    addAndMakeVisible(label);

    slider.setRange(0, 100);
    slider.addListener(this);
    slider.setTextValueSuffix("%");
    slider.setNumDecimalPlacesToDisplay(1);
    slider.setValue(value);

    label.setText(text, dontSendNotification);
    label.attachToComponent(&slider, true);
  };

  setupSlider(sliderDampening_, labelDampening_, "Dampening", 40.0);
  setupSlider(sliderWidth_, labelWidth_, "Width", 30.0);
  setupSlider(sliderRoomSize_, labelRoomSize_, "Room Size", 60.0);
  setupSlider(sliderDry_, labelDry_, "Dry", 50.0);
  setupSlider(sliderWet_, labelWet_, "Wet", 30.0);

  addAndMakeVisible(toggleFreeze_);
  toggleFreeze_.addListener(this);
  toggleFreeze_.setButtonText("Freeze");

  setSize(600, 300);
  setAudioChannels(2, 2);
}

MainComponent::~MainComponent() { shutdownAudio(); }

//==============================================================================
void MainComponent::prepareToPlay(int samplesPerBlockExpected,
                                  double sampleRate) {
  freeverb_.reset(freeverb::create(sampleRate));
}

void MainComponent::getNextAudioBlock(const AudioSourceChannelInfo &block) {
  const auto offset = block.startSample;
  const auto buffer = block.buffer;

  std::pair<Parameter, float> command;

  while (command_queue_.pop(command)) {
    switch (command.first) {
    case Parameter::Dampening:
      freeverb::set_dampening(freeverb_.get(), command.second);
      break;
    case Parameter::Width:
      freeverb::set_width(freeverb_.get(), command.second);
      break;
    case Parameter::RoomSize:
      freeverb::set_room_size(freeverb_.get(), command.second);
      break;
    case Parameter::Freeze:
      freeverb::set_freeze(freeverb_.get(), command.second != 0.0);
      break;
    case Parameter::Dry:
      freeverb::set_dry(freeverb_.get(), command.second);
      break;
    case Parameter::Wet:
      freeverb::set_wet(freeverb_.get(), command.second);
      break;
    }
  }

  freeverb::process(freeverb_.get(), buffer->getReadPointer(0, offset),
                    buffer->getReadPointer(1, offset),
                    buffer->getWritePointer(0, offset),
                    buffer->getWritePointer(1, offset), block.numSamples);
}

void MainComponent::releaseResources() { freeverb_.release(); }

//==============================================================================
void MainComponent::paint(Graphics &g) {
  g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
}

void MainComponent::resized() {
  const auto marginLeft = 120;
  const auto marginRight = 60;
  const auto height = 20;
  const auto verticalPadding = 5;

  auto y = verticalPadding;

  sliderDampening_.setBounds(marginLeft, y,
                             getWidth() - marginLeft - marginRight, height);
  y += height + verticalPadding;

  sliderWidth_.setBounds(marginLeft, y, getWidth() - marginLeft - marginRight,
                         height);
  y += height + verticalPadding;

  sliderRoomSize_.setBounds(marginLeft, y,
                            getWidth() - marginLeft - marginRight, height);
  y += height + verticalPadding;

  toggleFreeze_.setBounds(marginLeft, y, 200, height);
  y += height + verticalPadding;

  sliderDry_.setBounds(marginLeft, y, getWidth() - marginLeft - marginRight,
                       height);
  y += height + verticalPadding;

  sliderWet_.setBounds(marginLeft, y, getWidth() - marginLeft - marginRight,
                       height);
  y += height + verticalPadding;

}

void MainComponent::buttonClicked(Button *button) {
  if (button == &toggleFreeze_) {
    command_queue_.push(
        std::make_pair(Parameter::Freeze, button->getToggleState() ? 1.0 : 0.0));
  }
}

void MainComponent::sliderValueChanged(Slider *slider) {
  if (slider == &sliderDampening_) {
    command_queue_.push(
        std::make_pair(Parameter::Dampening, slider->getValue() / 100.0));
  } else if (slider == &sliderWidth_) {
    command_queue_.push(
        std::make_pair(Parameter::Width, slider->getValue() / 100.0));
  } else if (slider == &sliderRoomSize_) {
    command_queue_.push(
        std::make_pair(Parameter::RoomSize, slider->getValue() / 100.0));
  } else if (slider == &sliderDry_) {
    command_queue_.push(
        std::make_pair(Parameter::Dry, slider->getValue() / 100.0));
  } else if (slider == &sliderWet_) {
    command_queue_.push(
        std::make_pair(Parameter::Wet, slider->getValue() / 100.0));
  }
}


================================================
FILE: examples/app_juce/Source/MainComponent.h
================================================
#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

#include "freeverb.hpp"

#include <boost/lockfree/spsc_queue.hpp>
#include <memory>

enum class Parameter {
  Dampening,
  Width,
  RoomSize,
  Freeze,
  Dry,
  Wet,
};

class MainComponent : public AudioAppComponent,
                      public Slider::Listener,
                      public Button::Listener {
public:
  //==============================================================================
  MainComponent();
  ~MainComponent();

  //==============================================================================
  void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
  void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill) override;
  void releaseResources() override;

  //==============================================================================
  void paint(Graphics &g) override;
  void resized() override;

  //==============================================================================
  void sliderValueChanged(Slider *slider) override;

  //==============================================================================
  void buttonClicked(Button *button) override;
  void buttonStateChanged(Button *button) override {}

private:
  std::unique_ptr<freeverb::Freeverb, decltype(&freeverb::destroy)> freeverb_;
  boost::lockfree::spsc_queue<std::pair<Parameter, float>,
                              boost::lockfree::capacity<128>>
      command_queue_;

  Slider sliderDampening_;
  Slider sliderWidth_;
  Slider sliderRoomSize_;
  Slider sliderDry_;
  Slider sliderWet_;

  Label labelDampening_;
  Label labelWidth_;
  Label labelRoomSize_;
  Label labelDry_;
  Label labelWet_;

  ToggleButton toggleFreeze_;

  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
};


================================================
FILE: examples/app_juce/freeverb.jucer
================================================
<?xml version="1.0" encoding="UTF-8"?>

<JUCERPROJECT id="o4BvXu" name="freeverb" projectType="guiapp" jucerVersion="5.4.1">
  <MAINGROUP id="NWA9Zw" name="freeverb">
    <GROUP id="{58A3E6A2-A73A-19B7-EACE-F692BEF6743A}" name="Source">
      <FILE id="eLsEb6" name="MainComponent.h" compile="0" resource="0" file="Source/MainComponent.h"/>
      <FILE id="SppbIx" name="MainComponent.cpp" compile="1" resource="0"
            file="Source/MainComponent.cpp"/>
      <FILE id="pgm7Hn" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
    </GROUP>
  </MAINGROUP>
  <EXPORTFORMATS>
    <XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="freeverb_clib"
      extraCompilerFlags="-I../../../clib -I/usr/local/include" prebuildCommand="export PATH=&quot;$HOME/.cargo/bin:$PATH&quot;&#10;cd ../../../../crates/clib/&#10;./build.sh"
      extraLinkerFlags="-L../../../../target/release">
      <CONFIGURATIONS>
        <CONFIGURATION isDebug="1" name="Debug"/>
        <CONFIGURATION isDebug="0" name="Release"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_audio_basics" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_audio_devices" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_audio_formats" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_audio_processors" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_audio_utils" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_core" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_cryptography" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_data_structures" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_events" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_graphics" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_gui_basics" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_gui_extra" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_opengl" path="../../../juce/JUCE/modules"/>
        <MODULEPATH id="juce_video" path="../../../juce/JUCE/modules"/>
      </MODULEPATHS>
    </XCODE_MAC>
  </EXPORTFORMATS>
  <MODULES>
    <MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
    <MODULE id="juce_video" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
  </MODULES>
  <LIVE_SETTINGS>
    <OSX/>
  </LIVE_SETTINGS>
  <JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1"/>
</JUCERPROJECT>


================================================
FILE: examples/wasm/.gitignore
================================================
node_modules
public/freeverb_wasm.wasm


================================================
FILE: examples/wasm/Cargo.toml
================================================
[package]
name = "freeverb-wasm"
publish = false
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }

[dependencies]
freeverb-clib = { path = "../../crates/clib"}

[lib]
crate-type = ["cdylib"]


================================================
FILE: examples/wasm/README.md
================================================
# freeverb-wasm

This is an example of taking the freeverb's C API and making it available as
[WebAssembly](https://webassembly.org).

To try out the example in a browser, `cd` into this folder, and run:

```
npm install
npm run serve
```


================================================
FILE: examples/wasm/package.json
================================================
{
  "scripts": {
    "build": "cargo build --target wasm32-unknown-unknown --release && cp ../../target/wasm32-unknown-unknown/release/freeverb_wasm.wasm public",
    "build-debug": "cargo build --target wasm32-unknown-unknown && cp ../../target/wasm32-unknown-unknown/debug/freeverb_wasm.wasm public",
    "serve": "npm run build && http-server public -o"
  },
  "devDependencies": {
    "http-server": "^14.1.1"
  }
}

================================================
FILE: examples/wasm/public/freeverb-processor.js
================================================
class FreeverbProcessor extends AudioWorkletProcessor {
  static get parameterDescriptors() {
    return [
      {
        name: 'dampening',
        defaultValue: 0.5,
        minValue: 0.0,
        maxValue: 1.0,
        automationRate: 'k-rate'
      },
      {
        name: 'width',
        defaultValue: 0.5,
        minValue: 0.0,
        maxValue: 1.0,
        automationRate: 'k-rate'
      },
      {
        name: 'roomSize',
        defaultValue: 0.5,
        minValue: 0.0,
        maxValue: 1.0,
        automationRate: 'k-rate'
      },
      {
        name: 'freeze',
        defaultValue: 0,
        minValue: 0,
        maxValue: 1,
        automationRate: 'k-rate'
      },
      {
        name: 'dry',
        defaultValue: 0.5,
        minValue: 0.0,
        maxValue: 1.0,
        automationRate: 'k-rate'
      },
      {
        name: 'wet',
        defaultValue: 0.5,
        minValue: 0.0,
        maxValue: 1.0,
        automationRate: 'k-rate'
      },
    ];
  }

  constructor(options) {
    super();

    const wasmBuffer = options.processorOptions.wasmBuffer;
    const module = new WebAssembly.Module(options.processorOptions.wasmBuffer);
    this.wasm = new WebAssembly.Instance(module);

    const exports = this.wasm.exports;
    this.freeverb = exports.create(options.processorOptions.sampleRate);
    this.bufferInLPtr = exports.createBuffer(128);
    this.bufferInL = new Float32Array(exports.memory.buffer, this.bufferInLPtr, 128);
    this.bufferInRPtr = exports.createBuffer(128);
    this.bufferInR = new Float32Array(exports.memory.buffer, this.bufferInRPtr, 128);
    this.bufferOutLPtr = exports.createBuffer(128);
    this.bufferOutL = new Float32Array(exports.memory.buffer, this.bufferOutLPtr, 128);
    this.bufferOutRPtr = exports.createBuffer(128);
    this.bufferOutR = new Float32Array(exports.memory.buffer, this.bufferOutRPtr, 128);
  }

  process(inputs, outputs, parameters) {
    let freeverb = this.freeverb;
    if (!freeverb) {
      console.log("Missing freeverb");
      return false;
    }

    const input = inputs[0];
    const output = outputs[0];

    if (input.length == 0 || output.length < 2) {
      console.log("Missing io (inputs: %i, outputs: %i)", input.length, output.length);
      return false;
    }

    const exports = this.wasm.exports;
    exports.set_dampening(freeverb, parameters.dampening[0]);
    exports.set_width(freeverb, parameters.width[0]);
    exports.set_room_size(freeverb, parameters.roomSize[0]);
    exports.set_freeze(freeverb, parameters.freeze[0]);
    exports.set_dry(freeverb, parameters.dry[0]);
    exports.set_wet(freeverb, parameters.wet[0]);

    if (input.length == 1) {
      this.bufferInL.set(input[0]);
      this.bufferInR.set(input[0]);
    } else {
      this.bufferInL.set(input[0]);
      this.bufferInR.set(input[1]);
    }

    exports.process(
      freeverb, this.bufferInLPtr, this.bufferInRPtr, this.bufferOutLPtr, this.bufferOutRPtr, 128);

    output[0].set(this.bufferOutL);
    output[1].set(this.bufferOutR);

    return true;
  }
}

registerProcessor('freeverb-processor', FreeverbProcessor);


================================================
FILE: examples/wasm/public/index.html
================================================
<html>
  <body>
    Freeverb
    <br/>
    <input id="play" type="checkbox"/> Active
    <br/>
    <input id="dampening" type="range" name="dampening" min="0" max="100" value="80"/>
    <label for="dampening">Dampening</label>
    <br/>
    <input id="width" type="range" name="width" min="0" max="100" value="60"/>
    <label for="width">Width</label>
    <br/>
    <input id="room-size" type="range" name="room-size" min="0" max="100" value="30"/>
    <label for="room-size">Room Size</label>
    <br/>
    <input id="freeze" type="checkbox" name="freeze"/>
    <label for="freeze">Freeze</label>
    <br/>
    <input id="dry" type="range" name="dry" min="0" max="100" value="0"/>
    <label for="dry">Dry</label>
    <br/>
    <input id="wet" type="range" name="wet" min="0" max="100"/>
    <label for="wet">Wet</label>
    <br/>
  </body>
  <script src='index.js' type='module'></script>
</html>


================================================
FILE: examples/wasm/public/index.js
================================================
const audioContext = new AudioContext();

(async () => {
  const response = await fetch('./freeverb_wasm.wasm');
  const wasmBuffer = await response.arrayBuffer();

  await audioContext.audioWorklet.addModule('./freeverb-processor.js');

  navigator.mediaDevices.getUserMedia({ audio: true, video: false })
    .then(function(stream) {
      const mic = audioContext.createMediaStreamSource(stream);

      window.freeverb = new AudioWorkletNode(audioContext, "freeverb-processor", {
        outputChannelCount: [2],
        processorOptions: {
          wasmBuffer,
          sampleRate: audioContext.sampleRate,
        },
      });

      mic.connect(window.freeverb);
      window.freeverb.connect(audioContext.destination);
      audioContext.suspend();
    })
    .catch(function(error) {
      console.log("Failed to initialize audio:", error.name, "-", error.message);
    });
})();

let playing = false;
const play = document.getElementById('play');
play.addEventListener('click', function() {
  if (playing) {
    audioContext.suspend();
    playing = false;
    console.log("Suspending");
  } else {
    audioContext.resume();
    playing = true;
    console.log("Resuming");
  }
});

function setParameter(name, value) {
  window.freeverb.parameters.get(name).setValueAtTime(value, audioContext.currentTime);
}

const dampeningSlider = document.getElementById('dampening');
dampeningSlider.addEventListener('input', function() {
  setParameter('dampening', dampeningSlider.value / 100.0);
});

const widthSlider = document.getElementById('width');
widthSlider.addEventListener('input', function() {
  setParameter('width', widthSlider.value / 100.0);
});

const roomSizeSlider = document.getElementById('room-size');
roomSizeSlider.addEventListener('input', function() {
  setParameter('roomSize', roomSizeSlider.value / 100.0);
});

const drySlider = document.getElementById('dry');
drySlider.addEventListener('input', function() {
  setParameter('dry', drySlider.value / 100.0);
});

const wetSlider = document.getElementById('wet');
wetSlider.addEventListener('input', function() {
  setParameter('wet', wetSlider.value / 100.0);
});

const freezeButton = document.getElementById('freeze');
freezeButton.addEventListener('input', function() {
  setParameter('freeze', freezeButton.checked ? 1 : 0);
});


================================================
FILE: examples/wasm/src/lib.rs
================================================
// Re-export the C API
pub use freeverb_clib::*;

// Provide a function that allocate
#[unsafe(no_mangle)]
pub extern "C" fn createBuffer(size: usize) -> *mut f32 {
    let mut buf = Vec::<f32>::with_capacity(size);
    let ptr = buf.as_mut_ptr();
    std::mem::forget(buf);
    ptr
}
Download .txt
gitextract_ujjsr6s2/

├── .git-blame-ignore-revs
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── crates/
│   ├── audio_module/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── command.rs
│   │       ├── lib.rs
│   │       ├── module.rs
│   │       ├── parameter.rs
│   │       ├── processor.rs
│   │       ├── string_converter.rs
│   │       └── value_converter.rs
│   ├── clib/
│   │   ├── .gitignore
│   │   ├── Cargo.toml
│   │   ├── build.sh
│   │   ├── cbindgen.toml
│   │   ├── src/
│   │   │   └── lib.rs
│   │   └── test.cpp
│   ├── freeverb/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── all_pass.rs
│   │       ├── comb.rs
│   │       ├── delay_line.rs
│   │       ├── float.rs
│   │       ├── freeverb.rs
│   │       ├── lib.rs
│   │       └── tuning.rs
│   └── freeverb_module/
│       ├── Cargo.toml
│       └── src/
│           └── lib.rs
└── examples/
    ├── app_gtk/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── audio_thread.rs
    │       ├── gtk_parameter_slider.rs
    │       ├── gtk_parameter_toggle.rs
    │       └── main.rs
    ├── app_juce/
    │   ├── .gitignore
    │   ├── JuceLibraryCode/
    │   │   ├── AppConfig.h
    │   │   ├── JuceHeader.h
    │   │   ├── ReadMe.txt
    │   │   ├── include_juce_audio_basics.cpp
    │   │   ├── include_juce_audio_basics.mm
    │   │   ├── include_juce_audio_devices.cpp
    │   │   ├── include_juce_audio_devices.mm
    │   │   ├── include_juce_audio_formats.cpp
    │   │   ├── include_juce_audio_formats.mm
    │   │   ├── include_juce_audio_processors.cpp
    │   │   ├── include_juce_audio_processors.mm
    │   │   ├── include_juce_audio_utils.cpp
    │   │   ├── include_juce_audio_utils.mm
    │   │   ├── include_juce_core.cpp
    │   │   ├── include_juce_core.mm
    │   │   ├── include_juce_cryptography.cpp
    │   │   ├── include_juce_cryptography.mm
    │   │   ├── include_juce_data_structures.cpp
    │   │   ├── include_juce_data_structures.mm
    │   │   ├── include_juce_events.cpp
    │   │   ├── include_juce_events.mm
    │   │   ├── include_juce_graphics.cpp
    │   │   ├── include_juce_graphics.mm
    │   │   ├── include_juce_gui_basics.cpp
    │   │   ├── include_juce_gui_basics.mm
    │   │   ├── include_juce_gui_extra.cpp
    │   │   ├── include_juce_gui_extra.mm
    │   │   ├── include_juce_opengl.cpp
    │   │   ├── include_juce_opengl.mm
    │   │   ├── include_juce_video.cpp
    │   │   └── include_juce_video.mm
    │   ├── Source/
    │   │   ├── Main.cpp
    │   │   ├── MainComponent.cpp
    │   │   └── MainComponent.h
    │   └── freeverb.jucer
    └── wasm/
        ├── .gitignore
        ├── Cargo.toml
        ├── README.md
        ├── package.json
        ├── public/
        │   ├── freeverb-processor.js
        │   ├── index.html
        │   └── index.js
        └── src/
            └── lib.rs
Download .txt
SYMBOL INDEX (172 symbols across 25 files)

FILE: crates/audio_module/src/command.rs
  type Command (line 1) | pub enum Command {
  type CommandHandler (line 5) | pub trait CommandHandler {
    method handle_command (line 6) | fn handle_command(&mut self, command: Command);

FILE: crates/audio_module/src/module.rs
  type AudioModule (line 3) | pub trait AudioModule: ParameterProvider {
    method create_processor (line 6) | fn create_processor(sample_rate: usize) -> Self::Processor;
  type ParameterProvider (line 9) | pub trait ParameterProvider {
    method parameter_count (line 10) | fn parameter_count() -> usize;
    method parameter (line 11) | fn parameter(id: usize) -> Box<dyn Parameter>;

FILE: crates/audio_module/src/parameter.rs
  type ValueType (line 6) | pub enum ValueType {
  type Parameter (line 11) | pub trait Parameter {
    method name (line 12) | fn name(&self) -> String;
    method default_user_value (line 13) | fn default_user_value(&self) -> f32;
    method value_type (line 15) | fn value_type(&self) -> ValueType {
    method make_value_converter (line 19) | fn make_value_converter(&self) -> Box<dyn ValueConverter> {
    method make_string_converter (line 23) | fn make_string_converter(&self) -> Box<dyn StringConverter> {
    method name (line 48) | fn name(&self) -> String {
    method default_user_value (line 52) | fn default_user_value(&self) -> f32 {
    method value_type (line 56) | fn value_type(&self) -> ValueType {
    method name (line 118) | fn name(&self) -> String {
    method default_user_value (line 122) | fn default_user_value(&self) -> f32 {
    method make_value_converter (line 126) | fn make_value_converter(&self) -> Box<dyn ValueConverter> {
    method make_string_converter (line 130) | fn make_string_converter(&self) -> Box<dyn StringConverter> {
  type BoolParameter (line 28) | pub struct BoolParameter {
    method new (line 34) | pub fn new(name: &str) -> Self {
    method default_user_value (line 41) | pub fn default_user_value(mut self, default: bool) -> Self {
  type FloatParameter (line 61) | pub struct FloatParameter {
    method new (line 72) | pub fn new(name: &str) -> Self {
    method unit (line 84) | pub fn unit(mut self, unit: &str) -> Self {
    method range (line 89) | pub fn range(mut self, min: f32, max: f32) -> Self {
    method default_user_value (line 95) | pub fn default_user_value(mut self, default: f32) -> Self {
    method value_converter (line 100) | pub fn value_converter(
    method string_converter (line 108) | pub fn string_converter(

FILE: crates/audio_module/src/processor.rs
  type AudioProcessor (line 3) | pub trait AudioProcessor: CommandHandler + Send + Sync + 'static {
    method process (line 4) | fn process(&mut self, input: &[f32], output: &mut [f32], channels: u32);

FILE: crates/audio_module/src/string_converter.rs
  type StringConverter (line 3) | pub trait StringConverter {
    method to_string (line 4) | fn to_string(&self, value: f32) -> String;
    method to_string (line 11) | fn to_string(&self, value: f32) -> String {
    method to_string (line 20) | fn to_string(&self, value: f32) -> String {
    method to_string (line 37) | fn to_string(&self, value: f32) -> String {
    method to_string (line 46) | fn to_string(&self, value: f32) -> String {
  type DefaultStringConverter (line 8) | pub struct DefaultStringConverter {}
  type BoolStringConverter (line 17) | pub struct BoolStringConverter {}
  type FloatStringConverter (line 26) | pub struct FloatStringConverter {
    method new (line 31) | pub fn new(unit: String) -> Self {
  type PercentStringConverter (line 43) | pub struct PercentStringConverter {}
  function float_string_converter (line 51) | pub fn float_string_converter(parameter: &FloatParameter) -> Box<dyn Str...
  function percent_string_converter (line 55) | pub fn percent_string_converter(_: &FloatParameter) -> Box<dyn StringCon...

FILE: crates/audio_module/src/value_converter.rs
  type ValueConverter (line 3) | pub trait ValueConverter {
    method user_to_linear (line 4) | fn user_to_linear(&self, value: f32) -> f32;
    method linear_to_user (line 5) | fn linear_to_user(&self, value: f32) -> f32;
    method user_to_linear (line 11) | fn user_to_linear(&self, value: f32) -> f32 {
    method linear_to_user (line 15) | fn linear_to_user(&self, value: f32) -> f32 {
    method user_to_linear (line 35) | fn user_to_linear(&self, value: f32) -> f32 {
    method linear_to_user (line 39) | fn linear_to_user(&self, value: f32) -> f32 {
    method user_to_linear (line 59) | fn user_to_linear(&self, value: f32) -> f32 {
    method linear_to_user (line 63) | fn linear_to_user(&self, value: f32) -> f32 {
  type DefaultValueConverter (line 8) | pub struct DefaultValueConverter {}
  type LinearValueConverter (line 20) | pub struct LinearValueConverter {
    method new (line 26) | pub fn new(min: f32, max: f32) -> Self {
  type LogValueConverter (line 44) | pub struct LogValueConverter {
    method new (line 50) | pub fn new(min: f32, max: f32) -> Self {
  function linear_value_converter (line 68) | pub fn linear_value_converter(parameter: &FloatParameter) -> Box<dyn Val...
  function log_value_converter (line 75) | pub fn log_value_converter(parameter: &FloatParameter) -> Box<dyn ValueC...

FILE: crates/clib/src/lib.rs
  function create (line 8) | pub extern "C" fn create(sample_rate: usize) -> *mut Freeverb<f64> {
  function destroy (line 18) | pub unsafe extern "C" fn destroy(freeverb: *mut Freeverb<f64>) {
  function process (line 34) | pub unsafe extern "C" fn process(
  function set_dampening (line 52) | pub extern "C" fn set_dampening(freeverb: &mut Freeverb<f64>, value: f64) {
  function set_freeze (line 57) | pub extern "C" fn set_freeze(freeverb: &mut Freeverb<f64>, value: bool) {
  function set_wet (line 62) | pub extern "C" fn set_wet(freeverb: &mut Freeverb<f64>, value: f64) {
  function set_width (line 67) | pub extern "C" fn set_width(freeverb: &mut Freeverb<f64>, value: f64) {
  function set_dry (line 72) | pub extern "C" fn set_dry(freeverb: &mut Freeverb<f64>, value: f64) {
  function set_room_size (line 77) | pub extern "C" fn set_room_size(freeverb: &mut Freeverb<f64>, value: f64) {

FILE: crates/clib/test.cpp
  function main (line 5) | int main() {

FILE: crates/freeverb/src/all_pass.rs
  type AllPass (line 3) | pub struct AllPass<T> {
  function new (line 8) | pub fn new(delay_length: usize) -> Self {
  function tick (line 14) | pub fn tick(&mut self, input: T) -> T {
  function basic_ticking (line 31) | fn basic_ticking() {

FILE: crates/freeverb/src/comb.rs
  type Comb (line 3) | pub struct Comb<T> {
  function new (line 12) | pub fn new(delay_length: usize) -> Self {
  function set_dampening (line 22) | pub fn set_dampening(&mut self, value: T) {
  function set_feedback (line 27) | pub fn set_feedback(&mut self, value: T) {
  function tick (line 31) | pub fn tick(&mut self, input: T) -> T {
  function basic_ticking (line 46) | fn basic_ticking() {

FILE: crates/freeverb/src/delay_line.rs
  type DelayLine (line 3) | pub struct DelayLine<T> {
  function new (line 9) | pub fn new(length: usize) -> Self {
  function read (line 16) | pub fn read(&self) -> T {
  function write_and_advance (line 20) | pub fn write_and_advance(&mut self, value: T) {

FILE: crates/freeverb/src/float.rs
  type Float (line 7) | pub trait Float:
    method to_f32 (line 27) | fn to_f32(self) -> f32;
    method to_f32 (line 31) | fn to_f32(self) -> f32 {
    method to_f32 (line 37) | fn to_f32(self) -> f32 {

FILE: crates/freeverb/src/freeverb.rs
  type Freeverb (line 7) | pub struct Freeverb<T: Float = f64> {
  function new (line 27) | pub fn new(sr: usize) -> Self {
  function tick (line 105) | pub fn tick(&mut self, input: (T, T)) -> (T, T) {
  function set_dampening (line 129) | pub fn set_dampening(&mut self, value: T) {
  function set_freeze (line 137) | pub fn set_freeze(&mut self, frozen: bool) {
  function set_dry (line 147) | pub fn set_dry(&mut self, value: T) {
  function set_wet (line 156) | pub fn set_wet(&mut self, value: T) {
  function set_width (line 164) | pub fn set_width(&mut self, value: T) {
  function set_room_size (line 172) | pub fn set_room_size(&mut self, value: T) {
  function update_wet_gains (line 177) | fn update_wet_gains(&mut self) {
  function set_frozen (line 184) | fn set_frozen(&mut self, frozen: bool) {
  function update_combs (line 190) | fn update_combs(&mut self) {
  function adjust_length (line 207) | fn adjust_length(length: usize, sr: usize) -> usize {
  function check_adjust_length (line 216) | fn check_adjust_length() {
  function check_output (line 224) | fn check_output() {
  function check_almost_equal (line 251) | fn check_almost_equal(output: (f32, f32), expected: (f32, f32)) {

FILE: crates/freeverb/src/tuning.rs
  constant FIXED_GAIN (line 1) | pub const FIXED_GAIN: f32 = 0.015;
  constant SCALE_WET (line 3) | pub const SCALE_WET: f32 = 3.0;
  constant SCALE_DAMPENING (line 4) | pub const SCALE_DAMPENING: f32 = 0.4;
  constant SCALE_ROOM (line 6) | pub const SCALE_ROOM: f32 = 0.28;
  constant OFFSET_ROOM (line 7) | pub const OFFSET_ROOM: f32 = 0.7;
  constant STEREO_SPREAD (line 9) | pub const STEREO_SPREAD: usize = 23;
  constant COMB_TUNING_L1 (line 11) | pub const COMB_TUNING_L1: usize = 1116;
  constant COMB_TUNING_R1 (line 12) | pub const COMB_TUNING_R1: usize = COMB_TUNING_L1 + STEREO_SPREAD;
  constant COMB_TUNING_L2 (line 13) | pub const COMB_TUNING_L2: usize = 1188;
  constant COMB_TUNING_R2 (line 14) | pub const COMB_TUNING_R2: usize = COMB_TUNING_L2 + STEREO_SPREAD;
  constant COMB_TUNING_L3 (line 15) | pub const COMB_TUNING_L3: usize = 1277;
  constant COMB_TUNING_R3 (line 16) | pub const COMB_TUNING_R3: usize = COMB_TUNING_L3 + STEREO_SPREAD;
  constant COMB_TUNING_L4 (line 17) | pub const COMB_TUNING_L4: usize = 1356;
  constant COMB_TUNING_R4 (line 18) | pub const COMB_TUNING_R4: usize = COMB_TUNING_L4 + STEREO_SPREAD;
  constant COMB_TUNING_L5 (line 19) | pub const COMB_TUNING_L5: usize = 1422;
  constant COMB_TUNING_R5 (line 20) | pub const COMB_TUNING_R5: usize = COMB_TUNING_L5 + STEREO_SPREAD;
  constant COMB_TUNING_L6 (line 21) | pub const COMB_TUNING_L6: usize = 1491;
  constant COMB_TUNING_R6 (line 22) | pub const COMB_TUNING_R6: usize = COMB_TUNING_L6 + STEREO_SPREAD;
  constant COMB_TUNING_L7 (line 23) | pub const COMB_TUNING_L7: usize = 1557;
  constant COMB_TUNING_R7 (line 24) | pub const COMB_TUNING_R7: usize = COMB_TUNING_L7 + STEREO_SPREAD;
  constant COMB_TUNING_L8 (line 25) | pub const COMB_TUNING_L8: usize = 1617;
  constant COMB_TUNING_R8 (line 26) | pub const COMB_TUNING_R8: usize = COMB_TUNING_L8 + STEREO_SPREAD;
  constant ALLPASS_TUNING_L1 (line 28) | pub const ALLPASS_TUNING_L1: usize = 556;
  constant ALLPASS_TUNING_R1 (line 29) | pub const ALLPASS_TUNING_R1: usize = ALLPASS_TUNING_L1 + STEREO_SPREAD;
  constant ALLPASS_TUNING_L2 (line 30) | pub const ALLPASS_TUNING_L2: usize = 441;
  constant ALLPASS_TUNING_R2 (line 31) | pub const ALLPASS_TUNING_R2: usize = ALLPASS_TUNING_L2 + STEREO_SPREAD;
  constant ALLPASS_TUNING_L3 (line 32) | pub const ALLPASS_TUNING_L3: usize = 341;
  constant ALLPASS_TUNING_R3 (line 33) | pub const ALLPASS_TUNING_R3: usize = ALLPASS_TUNING_L3 + STEREO_SPREAD;
  constant ALLPASS_TUNING_L4 (line 34) | pub const ALLPASS_TUNING_L4: usize = 225;
  constant ALLPASS_TUNING_R4 (line 35) | pub const ALLPASS_TUNING_R4: usize = ALLPASS_TUNING_L4 + STEREO_SPREAD;

FILE: crates/freeverb_module/src/lib.rs
  type Parameters (line 14) | pub enum Parameters {
  type FreeverbProcessor (line 23) | pub struct FreeverbProcessor<T: Float = f64> {
    method new (line 28) | fn new(sample_rate: usize) -> Self {
  method handle_command (line 36) | fn handle_command(&mut self, command: Command) {
  method process (line 63) | fn process(&mut self, input: &[f32], output: &mut [f32], channels: u32) {
  type FreeverbModule (line 76) | pub struct FreeverbModule {}
  type Processor (line 79) | type Processor = FreeverbProcessor;
  method create_processor (line 81) | fn create_processor(sample_rate: usize) -> Self::Processor {
  method parameter_count (line 87) | fn parameter_count() -> usize {
  method parameter (line 93) | fn parameter(id: usize) -> Box<dyn Parameter> {

FILE: examples/app_gtk/src/audio_thread.rs
  type AudioStreams (line 11) | pub struct AudioStreams {
  function start_audio (line 16) | pub fn start_audio<Module: AudioModule>(

FILE: examples/app_gtk/src/gtk_parameter_slider.rs
  function make_slider (line 6) | pub fn make_slider(

FILE: examples/app_gtk/src/gtk_parameter_toggle.rs
  function make_toggle (line 6) | pub fn make_toggle(

FILE: examples/app_gtk/src/main.rs
  function main (line 11) | fn main() {
  function run_main (line 15) | fn run_main<Module: AudioModule>() {

FILE: examples/app_juce/JuceLibraryCode/JuceHeader.h
  function namespace (line 40) | namespace ProjectInfo

FILE: examples/app_juce/Source/Main.cpp
  function NewProjectApplication (line 19) | NewProjectApplication() {}
  function String (line 21) | const String getApplicationName() override       { return ProjectInfo::p...
  function String (line 22) | const String getApplicationVersion() override    { return ProjectInfo::v...
  function moreThanOneInstanceAllowed (line 23) | bool moreThanOneInstanceAllowed() override       { return true; }
  function initialise (line 26) | void initialise (const String& commandLine) override
  function shutdown (line 33) | void shutdown() override
  function systemRequestedQuit (line 41) | void systemRequestedQuit() override
  function anotherInstanceStarted (line 48) | void anotherInstanceStarted (const String& commandLine) override
  class MainWindow (line 60) | class MainWindow    : public DocumentWindow
    method MainWindow (line 63) | MainWindow (String name)  : DocumentWindow (name,
    method closeButtonPressed (line 81) | void closeButtonPressed() override

FILE: examples/app_juce/Source/MainComponent.h
  function Parameter (line 10) | enum class Parameter {

FILE: examples/wasm/public/freeverb-processor.js
  class FreeverbProcessor (line 1) | class FreeverbProcessor extends AudioWorkletProcessor {
    method parameterDescriptors (line 2) | static get parameterDescriptors() {
    method constructor (line 49) | constructor(options) {
    method process (line 68) | process(inputs, outputs, parameters) {

FILE: examples/wasm/public/index.js
  function setParameter (line 44) | function setParameter(name, value) {

FILE: examples/wasm/src/lib.rs
  function createBuffer (line 6) | pub extern "C" fn createBuffer(size: usize) -> *mut f32 {
Condensed preview — 78 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (88K chars).
[
  {
    "path": ".git-blame-ignore-revs",
    "chars": 93,
    "preview": "# .git-blame-ignore-revs\n\n# Rename /src -> /crates\n990a498e2d963d18ce8cfa675674fa8c4173295e\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 30,
    "preview": "**/target\n**/*.rs.bk\n/.vscode\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 264,
    "preview": "[workspace]\nresolver = \"2\"\nmembers = [\n  \"crates/*\",\n  \"examples/app_gtk\",\n  \"examples/wasm\",\n]\n\n[workspace.package]\nedi"
  },
  {
    "path": "LICENSE",
    "chars": 1054,
    "preview": "Copyright (c) 2018 Ian Hobson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis soft"
  },
  {
    "path": "README.md",
    "chars": 2055,
    "preview": "# freeverb-rs\n\nA Rust implementation of the Freeverb algorithm.\n\n## About Freeverb\n\nFreeverb was originally written in C"
  },
  {
    "path": "crates/audio_module/Cargo.toml",
    "chars": 144,
    "preview": "[package]\nname = \"audio_module\"\npublish = false\nversion = \"0.1.0\"\nauthors = { workspace = true }\nedition = { workspace ="
  },
  {
    "path": "crates/audio_module/src/command.rs",
    "chars": 133,
    "preview": "pub enum Command {\n    SetParameter(usize, f32),\n}\n\npub trait CommandHandler {\n    fn handle_command(&mut self, command:"
  },
  {
    "path": "crates/audio_module/src/lib.rs",
    "chars": 296,
    "preview": "mod command;\nmod module;\nmod parameter;\nmod processor;\nmod string_converter;\nmod value_converter;\n\npub use {\n    command"
  },
  {
    "path": "crates/audio_module/src/module.rs",
    "chars": 306,
    "preview": "use crate::{AudioProcessor, Parameter};\n\npub trait AudioModule: ParameterProvider {\n    type Processor: AudioProcessor;\n"
  },
  {
    "path": "crates/audio_module/src/parameter.rs",
    "chars": 3198,
    "preview": "use crate::{\n    string_converter::{DefaultStringConverter, StringConverter, float_string_converter},\n    value_converte"
  },
  {
    "path": "crates/audio_module/src/processor.rs",
    "chars": 174,
    "preview": "use crate::CommandHandler;\n\npub trait AudioProcessor: CommandHandler + Send + Sync + 'static {\n    fn process(&mut self,"
  },
  {
    "path": "crates/audio_module/src/string_converter.rs",
    "chars": 1321,
    "preview": "use crate::FloatParameter;\n\npub trait StringConverter {\n    fn to_string(&self, value: f32) -> String;\n}\n\n#[derive(Clone"
  },
  {
    "path": "crates/audio_module/src/value_converter.rs",
    "chars": 1974,
    "preview": "use crate::FloatParameter;\n\npub trait ValueConverter {\n    fn user_to_linear(&self, value: f32) -> f32;\n    fn linear_to"
  },
  {
    "path": "crates/clib/.gitignore",
    "chars": 32,
    "preview": "freeverb.hpp\nlibfreeverb_clib.a\n"
  },
  {
    "path": "crates/clib/Cargo.toml",
    "chars": 223,
    "preview": "[package]\nname = \"freeverb-clib\"\npublish = false\nversion = \"0.1.0\"\nauthors = { workspace = true }\nedition = { workspace "
  },
  {
    "path": "crates/clib/build.sh",
    "chars": 144,
    "preview": "cargo build --release\ncbindgen -d --lang c++ -o freeverb.hpp .\nclang++ --std=c++1z --stdlib=libc++ -L../target/release -"
  },
  {
    "path": "crates/clib/cbindgen.toml",
    "chars": 23,
    "preview": "namespace = \"freeverb\"\n"
  },
  {
    "path": "crates/clib/src/lib.rs",
    "chars": 2085,
    "preview": "pub use freeverb::Freeverb;\n\n/// Create a Freeverb instance with a given sample rate\n///\n/// The client is responsible f"
  },
  {
    "path": "crates/clib/test.cpp",
    "chars": 172,
    "preview": "#include \"freeverb.hpp\"\n\n#include <memory>\n\nint main() {\n  auto pFreeverb = freeverb::create(44100);\n  freeverb::set_wet"
  },
  {
    "path": "crates/freeverb/Cargo.toml",
    "chars": 384,
    "preview": "[package]\nname = \"freeverb\"\nversion = \"0.2.0\"\nedition = { workspace = true }\nauthors = { workspace = true }\nreadme = { w"
  },
  {
    "path": "crates/freeverb/src/all_pass.rs",
    "chars": 1063,
    "preview": "use crate::{delay_line::DelayLine, float::Float};\n\npub struct AllPass<T> {\n    delay_line: DelayLine<T>,\n}\n\nimpl<T: Floa"
  },
  {
    "path": "crates/freeverb/src/comb.rs",
    "chars": 1493,
    "preview": "use crate::{delay_line::DelayLine, float::Float};\n\npub struct Comb<T> {\n    delay_line: DelayLine<T>,\n    feedback: T,\n "
  },
  {
    "path": "crates/freeverb/src/delay_line.rs",
    "chars": 1240,
    "preview": "use crate::float::Float;\n\npub struct DelayLine<T> {\n    buffer: Vec<T>,\n    index: usize,\n}\n\nimpl<T: Float> DelayLine<T>"
  },
  {
    "path": "crates/freeverb/src/float.rs",
    "chars": 758,
    "preview": "use std::{\n    fmt::Debug,\n    ops::{Add, AddAssign, Div, Mul, Neg, Sub},\n};\n\n/// A trait for the floating point ops nee"
  },
  {
    "path": "crates/freeverb/src/freeverb.rs",
    "chars": 9319,
    "preview": "use crate::{all_pass::AllPass, comb::Comb, float::Float, tuning::*};\n\n/// A processor for the Freeverb reverb algorithm."
  },
  {
    "path": "crates/freeverb/src/lib.rs",
    "chars": 603,
    "preview": "//! A Rust implementation of the Freeverb reverb algorithm.\n//!\n//! Freeverb was originally written in C++ by \"Jezar at "
  },
  {
    "path": "crates/freeverb/src/tuning.rs",
    "chars": 1519,
    "preview": "pub const FIXED_GAIN: f32 = 0.015;\n\npub const SCALE_WET: f32 = 3.0;\npub const SCALE_DAMPENING: f32 = 0.4;\n\npub const SCA"
  },
  {
    "path": "crates/freeverb_module/Cargo.toml",
    "chars": 267,
    "preview": "[package]\nname = \"freeverb_module\"\npublish = false\nversion = \"0.1.0\"\nauthors = { workspace = true }\nedition = { workspac"
  },
  {
    "path": "crates/freeverb_module/src/lib.rs",
    "chars": 3772,
    "preview": "#[macro_use]\nextern crate num_derive;\n\nuse {\n    audio_module::{\n        AudioModule, AudioProcessor, BoolParameter, Com"
  },
  {
    "path": "examples/app_gtk/Cargo.toml",
    "chars": 424,
    "preview": "[package]\nname = \"freeverb-gtk\"\npublish = false\nversion = \"0.1.0\"\nauthors = { workspace = true }\nedition = { workspace ="
  },
  {
    "path": "examples/app_gtk/src/audio_thread.rs",
    "chars": 3037,
    "preview": "use {\n    audio_module::{AudioModule, AudioProcessor, Command, CommandHandler},\n    cpal::traits::{DeviceTrait, HostTrai"
  },
  {
    "path": "examples/app_gtk/src/gtk_parameter_slider.rs",
    "chars": 1518,
    "preview": "use {\n    audio_module::{Command, Parameter},\n    gtk::{Label, Orientation, PositionType, Scale, prelude::*},\n};\n\npub fn"
  },
  {
    "path": "examples/app_gtk/src/gtk_parameter_toggle.rs",
    "chars": 905,
    "preview": "use {\n    audio_module::{Command, Parameter},\n    gtk::{Align, Orientation, ToggleButton, prelude::*},\n};\n\npub fn make_t"
  },
  {
    "path": "examples/app_gtk/src/main.rs",
    "chars": 1845,
    "preview": "use {\n    audio_module::{AudioModule, ValueType},\n    freeverb_module::FreeverbModule,\n    gtk::{Application, Applicatio"
  },
  {
    "path": "examples/app_juce/.gitignore",
    "chars": 7,
    "preview": "Builds\n"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/AppConfig.h",
    "chars": 7439,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/JuceHeader.h",
    "chars": 1587,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/ReadMe.txt",
    "chars": 577,
    "preview": "\n Important Note!!\n ================\n\nThe purpose of this folder is to contain files that are auto-generated by the Proj"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_basics.cpp",
    "chars": 224,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_basics.mm",
    "chars": 223,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_devices.cpp",
    "chars": 226,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_devices.mm",
    "chars": 225,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_formats.cpp",
    "chars": 226,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_formats.mm",
    "chars": 225,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_processors.cpp",
    "chars": 232,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_processors.mm",
    "chars": 231,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_utils.cpp",
    "chars": 222,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_audio_utils.mm",
    "chars": 221,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_core.cpp",
    "chars": 208,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_core.mm",
    "chars": 207,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_cryptography.cpp",
    "chars": 224,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_cryptography.mm",
    "chars": 223,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_data_structures.cpp",
    "chars": 230,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_data_structures.mm",
    "chars": 229,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_events.cpp",
    "chars": 212,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_events.mm",
    "chars": 211,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_graphics.cpp",
    "chars": 216,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_graphics.mm",
    "chars": 215,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_gui_basics.cpp",
    "chars": 220,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_gui_basics.mm",
    "chars": 219,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_gui_extra.cpp",
    "chars": 218,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_gui_extra.mm",
    "chars": 217,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_opengl.cpp",
    "chars": 212,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_opengl.mm",
    "chars": 211,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_video.cpp",
    "chars": 210,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/JuceLibraryCode/include_juce_video.mm",
    "chars": 209,
    "preview": "/*\n\n    IMPORTANT! This file is auto-generated each time you save your\n    project - if you alter its contents, your cha"
  },
  {
    "path": "examples/app_juce/Source/Main.cpp",
    "chars": 4029,
    "preview": "/*\n  ==============================================================================\n\n    This file was auto-generated!\n\n"
  },
  {
    "path": "examples/app_juce/Source/MainComponent.cpp",
    "chars": 4775,
    "preview": "#include \"MainComponent.h\"\n\n//==============================================================================\nMainCompone"
  },
  {
    "path": "examples/app_juce/Source/MainComponent.h",
    "chars": 1804,
    "preview": "#pragma once\n\n#include \"../JuceLibraryCode/JuceHeader.h\"\n\n#include \"freeverb.hpp\"\n\n#include <boost/lockfree/spsc_queue.h"
  },
  {
    "path": "examples/app_juce/freeverb.jucer",
    "chars": 3554,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<JUCERPROJECT id=\"o4BvXu\" name=\"freeverb\" projectType=\"guiapp\" jucerVersion=\"5.4"
  },
  {
    "path": "examples/wasm/.gitignore",
    "chars": 39,
    "preview": "node_modules\npublic/freeverb_wasm.wasm\n"
  },
  {
    "path": "examples/wasm/Cargo.toml",
    "chars": 222,
    "preview": "[package]\nname = \"freeverb-wasm\"\npublish = false\nversion = \"0.1.0\"\nauthors = { workspace = true }\nedition = { workspace "
  },
  {
    "path": "examples/wasm/README.md",
    "chars": 239,
    "preview": "# freeverb-wasm\n\nThis is an example of taking the freeverb's C API and making it available as\n[WebAssembly](https://weba"
  },
  {
    "path": "examples/wasm/package.json",
    "chars": 419,
    "preview": "{\n  \"scripts\": {\n    \"build\": \"cargo build --target wasm32-unknown-unknown --release && cp ../../target/wasm32-unknown-u"
  },
  {
    "path": "examples/wasm/public/freeverb-processor.js",
    "chars": 3127,
    "preview": "class FreeverbProcessor extends AudioWorkletProcessor {\n  static get parameterDescriptors() {\n    return [\n      {\n     "
  },
  {
    "path": "examples/wasm/public/index.html",
    "chars": 900,
    "preview": "<html>\n  <body>\n    Freeverb\n    <br/>\n    <input id=\"play\" type=\"checkbox\"/> Active\n    <br/>\n    <input id=\"dampening\""
  },
  {
    "path": "examples/wasm/public/index.js",
    "chars": 2318,
    "preview": "const audioContext = new AudioContext();\n\n(async () => {\n  const response = await fetch('./freeverb_wasm.wasm');\n  const"
  },
  {
    "path": "examples/wasm/src/lib.rs",
    "chars": 285,
    "preview": "// Re-export the C API\npub use freeverb_clib::*;\n\n// Provide a function that allocate\n#[unsafe(no_mangle)]\npub extern \"C"
  }
]

About this extraction

This page contains the full source code of the irh/freeverb-rs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 78 files (77.5 KB), approximately 22.3k tokens, and a symbol index with 172 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!