Skip to main content

rust_audio_api/nodes/
oscillator.rs

1use crate::types::AudioUnit;
2use dasp::signal::{self, Signal};
3use ringbuf::storage::Heap;
4use ringbuf::traits::{Consumer, Observer, Producer, Split};
5use ringbuf::wrap::caching::Caching;
6use ringbuf::{HeapRb, SharedRb};
7use std::sync::Arc;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::thread;
10use std::time::Duration;
11
12/// An audio source that generates periodic waveforms.
13///
14/// Currently, it generates a sine wave at the specified frequency.
15/// It runs a background thread to generate samples and uses a ring buffer
16/// to communicate with the audio processing thread.
17pub struct OscillatorNode {
18    consumer: Caching<Arc<SharedRb<Heap<[f32; 2]>>>, false, true>,
19    gain: f32,
20    _running: Arc<AtomicBool>,
21}
22
23impl OscillatorNode {
24    /// Creates a new `OscillatorNode` with the given sample rate and frequency.
25    ///
26    /// # Parameters
27    /// - `sample_rate`: The target sample rate (e.g., 44100.0).
28    /// - `frequency`: The frequency of the sine wave in Hz (e.g., 440.0).
29    pub fn new(sample_rate: f64, frequency: f64) -> Self {
30        // Set ringbuf capacity for approx 0.5s buffer (e.g., 48000 Hz => 24000)
31        let capacity = (sample_rate * 0.5) as usize;
32        let ringbuf = HeapRb::<[f32; 2]>::new(capacity);
33        let (mut producer, consumer) = ringbuf.split();
34
35        let running = Arc::new(AtomicBool::new(true));
36        let running_clone = running.clone();
37
38        thread::spawn(move || {
39            let mut sig = signal::rate(sample_rate).const_hz(frequency).sine();
40            while running_clone.load(Ordering::Relaxed) {
41                // If buffer is full, sleep briefly to let Audio Thread consume (prevents high CPU usage)
42                if producer.is_full() {
43                    thread::sleep(Duration::from_millis(5));
44                    continue;
45                }
46
47                let sample = sig.next() as f32;
48                let frame = [sample, sample];
49                let _ = producer.try_push(frame); // Ignore if full (sleep handles backpressure)
50            }
51        });
52
53        Self {
54            consumer,
55            gain: 1.0,
56            _running: running,
57        }
58    }
59
60    /// Sets the output gain (volume) for this oscillator.
61    pub fn set_gain(&mut self, gain: f32) {
62        self.gain = gain;
63    }
64
65    /// Oscillator is an active node (Source); it is unaffected by input.
66    #[inline(always)]
67    pub fn process(&mut self, _input: Option<&AudioUnit>, output: &mut AudioUnit) {
68        dasp::slice::map_in_place(&mut output[..], |_| {
69            if let Some(sample) = self.consumer.try_pop() {
70                [sample[0] * self.gain, sample[1] * self.gain]
71            } else {
72                [0.0, 0.0]
73            }
74        });
75    }
76}
77
78impl Drop for OscillatorNode {
79    fn drop(&mut self) {
80        self._running.store(false, Ordering::Relaxed);
81    }
82}