summaryrefslogtreecommitdiff
path: root/klangfarbrs/src/lib.rs
blob: cdeb52b13a30a66949a48cb1275c22339fd17c30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// use gdnative::api::Resource;
use gdnative::prelude::*;

#[derive(NativeClass)]
#[inherit(Node)]
pub struct Synth {
    frequency: f32
}

#[methods]
impl Synth {
    fn new(_owner: &Node) -> Self {
        Synth { frequency: 220.0 }
    }

    #[export]
    fn set_freq(
        &mut self,
        _owner: &Node,
        freq: f32
    ) {
        self.frequency = freq;
    }

    #[export]
    fn _ready(&self, _owner: &Node) {
        match test::test() {
            Ok(()) => godot_print!("POOOP"),
            Err(error) => godot_print!("SHITE! {:?}", error),
        };
    }
}

pub mod test {
    use cpal;
    use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
    use dasp::{Sample};
    use dasp_signal::{self as signal, Signal};
    use std::sync::mpsc;

    pub fn test() -> Result<(), anyhow::Error> {
        let host = cpal::default_host();
        let device = host
            .default_output_device()
            .expect("failed to find a default output device");
        let config = device.default_output_config()?;

        match config.sample_format() {
            cpal::SampleFormat::F32 => run::<f32>(&device, &config.into())?,
            cpal::SampleFormat::I16 => run::<i16>(&device, &config.into())?,
            cpal::SampleFormat::U16 => run::<u16>(&device, &config.into())?,
        }

        Ok(())
    }

    fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
    where
        T: cpal::Sample,
    {
        // Create a signal chain to play back 1 second of each oscillator at A4.
        let hz = signal::rate(config.sample_rate.0 as f64).const_hz(440.0);
        let one_sec = config.sample_rate.0 as usize;
        let mut synth = hz
            .clone()
            .sine()
            .take(one_sec)
            .chain(hz.clone().saw().take(one_sec))
            .chain(hz.clone().square().take(one_sec))
            .chain(hz.clone().noise_simplex().take(one_sec))
            .chain(signal::noise(0).take(one_sec))
            .map(|s| s.to_sample::<f32>() * 0.2);

        // A channel for indicating when playback has completed.
        let (complete_tx, complete_rx) = mpsc::sync_channel(1);

        // Create and run the stream.
        let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
        let channels = config.channels as usize;
        let stream = device.build_output_stream(
            config,
            move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
                write_data(data, channels, &complete_tx, &mut synth)
            },
            err_fn,
        )?;
        stream.play()?;

        // Wait for playback to complete.
        complete_rx.recv().unwrap();
        stream.pause()?;

        Ok(())
    }

    fn write_data<T>(
        output: &mut [T],
        channels: usize,
        complete_tx: &mpsc::SyncSender<()>,
        signal: &mut dyn Iterator<Item = f32>,
    ) where
        T: cpal::Sample,
    {
        for frame in output.chunks_mut(channels) {
            let sample = match signal.next() {
                None => {
                    complete_tx.try_send(()).ok();
                    0.0
                }
                Some(sample) => sample,
            };
            let value: T = cpal::Sample::from::<f32>(&sample);
            for sample in frame.iter_mut() {
                *sample = value;
            }
        }
    }

}

// Function that registers all exposed classes to Godot
fn init(handle: InitHandle) {
    // Register the `Synth` type we declared.
    handle.add_class::<Synth>();
}

// Macro that creates the entry-points of the dynamic library.
godot_init!(init);