Skip to main content

rill_core_dsp/generators/
basic.rs

1//! Базовые осцилляторы (Sine, Saw, Square, Triangle)
2
3use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
4use crate::generators::{Generator, ModulatableGenerator, SyncableGenerator};
5use crate::vector::prelude::*;
6use rill_core::traits::{ActionContext, ProcessResult};
7use rill_core::Transcendental;
8use std::f32::consts::PI;
9
10/// Тип волны
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum Waveform {
13    /// Чистая синусоида
14    Sine,
15    /// Пилообразная волна
16    Saw,
17    /// Квадратная волна
18    Square,
19    /// Треугольная волна
20    Triangle,
21    /// Прямоугольная волна с регулируемой скважностью
22    Pulse(f32), // с шириной импульса (0.0 - 1.0)
23}
24
25impl Waveform {
26    /// Получить название формы волны
27    pub fn name(&self) -> &'static str {
28        match self {
29            Waveform::Sine => "Sine",
30            Waveform::Saw => "Saw",
31            Waveform::Square => "Square",
32            Waveform::Triangle => "Triangle",
33            Waveform::Pulse(_) => "Pulse",
34        }
35    }
36
37    /// Получить описание формы волны
38    pub fn description(&self) -> &'static str {
39        match self {
40            Waveform::Sine => "Pure sine wave - single harmonic",
41            Waveform::Saw => "Sawtooth wave - all harmonics (1/n)",
42            Waveform::Square => "Square wave - odd harmonics (1/n)",
43            Waveform::Triangle => "Triangle wave - odd harmonics (1/n²)",
44            Waveform::Pulse(_) => "Pulse wave with variable width",
45        }
46    }
47}
48
49/// Базовый осциллятор
50///
51/// Генерирует различные формы волн с возможностью:
52/// - Изменения частоты в реальном времени
53/// - Модуляции частоты (FM)
54/// - Анти-алиасинга для пилообразной волны
55/// - Синхронизации фазы
56#[derive(Clone, Copy)]
57pub struct BasicOscillator<T: Transcendental> {
58    /// Тип волны
59    waveform: Waveform,
60    /// Частота (Hz)
61    frequency: f32,
62    /// Амплитуда (0.0 - 1.0)
63    amplitude: ScalarVector1<T>,
64    /// Текущая фаза (0..1)
65    phase: ScalarVector1<T>,
66    /// Инкремент фазы за семпл
67    phase_inc: ScalarVector1<T>,
68    /// Частота дискретизации
69    sample_rate: f32,
70    /// Количество завершённых периодов
71    periods: u32,
72    /// Модуляция частоты (FM)
73    fm_amount: ScalarVector1<T>,
74}
75
76impl<T: Transcendental> BasicOscillator<T> {
77    /// Создать новый осциллятор
78    ///
79    /// # Arguments
80    /// * `waveform` - форма волны
81    /// * `frequency` - частота в Hz
82    /// * `amplitude` - амплитуда (0.0 - 1.0)
83    pub fn new(waveform: Waveform, frequency: f32, amplitude: T) -> Self {
84        let mut osc = Self {
85            waveform,
86            frequency,
87            amplitude: ScalarVector1::splat(amplitude),
88            phase: ScalarVector1::splat(T::ZERO),
89            phase_inc: ScalarVector1::splat(T::ZERO),
90            sample_rate: 44100.0,
91            periods: 0,
92            fm_amount: ScalarVector1::splat(T::ZERO),
93        };
94        osc.update_phase_inc();
95        osc
96    }
97
98    /// Обновить инкремент фазы на основе текущей частоты
99    #[inline(always)]
100    fn update_phase_inc(&mut self) {
101        self.phase_inc = ScalarVector1::splat(T::from_f32(self.frequency / self.sample_rate));
102    }
103
104    /// Генерировать синусоиду
105    #[inline(always)]
106    fn generate_sine(&self) -> ScalarVector1<T> {
107        let phase_rad = self.phase.mul(&ScalarVector1::splat(T::from_f32(2.0 * PI)));
108        phase_rad.sin().mul(&self.amplitude)
109    }
110
111    /// Генерировать пилообразную волну (без анти-алиасинга)
112    #[inline(always)]
113    fn generate_saw_raw(&self) -> ScalarVector1<T> {
114        // 2 * phase - 1
115        self.phase
116            .mul(&ScalarVector1::splat(T::from_f32(2.0)))
117            .sub(&ScalarVector1::splat(T::from_f32(1.0)))
118            .mul(&self.amplitude)
119    }
120
121    /// Генерировать пилообразную волну с анти-алиасингом
122    #[inline(always)]
123    fn generate_saw_bandlimited(&mut self) -> ScalarVector1<T> {
124        let raw = self.generate_saw_raw();
125        // Проверка на переход через 0 (discontinuity)
126        let next_phase = self.phase.add(&self.phase_inc).extract(0);
127        let one = T::from_f32(1.0);
128
129        if next_phase >= one {
130            // Вычисляем позицию discontinuity
131            let one_vec = ScalarVector1::splat(one);
132            let t = (one_vec - self.phase) / self.phase_inc;
133            // Простая Blep коррекция
134            let blep =
135                t * ScalarVector1::splat(T::from_f32(2.0)) - ScalarVector1::splat(T::from_f32(1.0));
136            raw - blep * self.amplitude
137        } else {
138            raw
139        }
140    }
141
142    /// Генерировать квадратную волну
143    #[inline(always)]
144    fn generate_square(&self) -> ScalarVector1<T> {
145        let half = T::from_f32(0.5);
146        if self.phase.extract(0) < half {
147            self.amplitude
148        } else {
149            -self.amplitude
150        }
151    }
152
153    /// Генерировать треугольную волну
154    #[inline(always)]
155    fn generate_triangle(&self) -> ScalarVector1<T> {
156        // 4 * |phase - 0.5| - 1
157        let half = ScalarVector1::splat(T::from_f32(0.5));
158        let p = self.phase - half;
159        (p.abs() * ScalarVector1::splat(T::from_f32(4.0)) - ScalarVector1::splat(T::from_f32(1.0)))
160            * self.amplitude
161    }
162
163    /// Генерировать прямоугольную волну с переменной скважностью
164    #[inline(always)]
165    fn generate_pulse(&self, width: f32) -> ScalarVector1<T> {
166        let width_t = T::from_f32(width.clamp(0.01, 0.99));
167        if self.phase.extract(0) < width_t {
168            self.amplitude
169        } else {
170            -self.amplitude
171        }
172    }
173
174    /// Основной метод генерации семпла
175    pub(crate) fn generate(&mut self) -> ScalarVector1<T> {
176        // Применяем FM модуляцию если есть
177        let effective_inc = self.phase_inc + self.fm_amount;
178
179        // Генерируем семпл в зависимости от формы волны
180        let output_vec = match self.waveform {
181            Waveform::Sine => self.generate_sine(),
182            Waveform::Saw => self.generate_saw_bandlimited(),
183            Waveform::Square => self.generate_square(),
184            Waveform::Triangle => self.generate_triangle(),
185            Waveform::Pulse(width) => self.generate_pulse(width),
186        };
187
188        // Обновляем фазу
189        self.phase = self.phase + effective_inc;
190        let one = ScalarVector1::splat(T::from_f32(1.0));
191        if self.phase.extract(0) >= one.extract(0) {
192            self.phase = self.phase - one;
193            self.periods += 1;
194        }
195
196        output_vec
197    }
198
199    /// Сбросить фазу в 0
200    pub fn reset_phase(&mut self) {
201        self.phase = ScalarVector1::splat(T::ZERO);
202        self.periods = 0;
203    }
204
205    /// Получить текущую фазу (0..1)
206    pub fn current_phase(&self) -> T {
207        self.phase.extract(0)
208    }
209
210    /// Получить количество завершённых периодов
211    pub fn period_count(&self) -> u32 {
212        self.periods
213    }
214
215    /// Установить ширину импульса (для Pulse волны)
216    pub fn set_pulse_width(&mut self, width: f32) {
217        if let Waveform::Pulse(_) = self.waveform {
218            self.waveform = Waveform::Pulse(width.clamp(0.01, 0.99));
219        }
220    }
221}
222
223// ==================== Реализация трейта Algorithm ====================
224
225impl<T: Transcendental> Algorithm<T> for BasicOscillator<T> {
226    fn init(&mut self, sample_rate: f32) {
227        self.sample_rate = sample_rate;
228        self.update_phase_inc();
229        self.phase = ScalarVector1::splat(T::ZERO);
230        self.periods = 0;
231    }
232
233    fn reset(&mut self) {
234        self.phase = ScalarVector1::splat(T::ZERO);
235        self.periods = 0;
236        self.fm_amount = ScalarVector1::splat(T::ZERO);
237    }
238
239    fn process(
240        &mut self,
241        input: Option<&[T]>,
242        output: &mut [T],
243        _ctx: &ActionContext,
244    ) -> ProcessResult<()> {
245        let input = input.unwrap_or(&[]);
246        for out in output.iter_mut() {
247            *out = self.generate().extract(0);
248        }
249        Ok(())
250    }
251
252    fn metadata(&self) -> AlgorithmMetadata {
253        AlgorithmMetadata {
254            name: self.waveform.name(),
255            category: AlgorithmCategory::Generator,
256            description: self.waveform.description(),
257            author: "Rill",
258            version: env!("CARGO_PKG_VERSION"),
259        }
260    }
261}
262
263// ==================== Реализация трейта Generator ====================
264
265impl<T: Transcendental> Generator<T> for BasicOscillator<T> {
266    fn phase(&self) -> T {
267        self.phase.extract(0)
268    }
269
270    fn set_phase(&mut self, phase: T) {
271        let one = T::from_f32(1.0);
272        let zero = T::ZERO;
273        self.phase = ScalarVector1::splat(if phase > one {
274            one
275        } else if phase < zero {
276            zero
277        } else {
278            phase
279        });
280    }
281
282    fn frequency(&self) -> f32 {
283        self.frequency
284    }
285
286    fn set_frequency(&mut self, freq: f32) {
287        self.frequency = freq.max(0.1).min(20000.0);
288        self.update_phase_inc();
289    }
290
291    fn amplitude(&self) -> T {
292        self.amplitude.extract(0)
293    }
294
295    fn set_amplitude(&mut self, amp: T) {
296        let one = T::from_f32(1.0);
297        let zero = T::ZERO;
298        self.amplitude = ScalarVector1::splat(if amp > one {
299            one
300        } else if amp < zero {
301            zero
302        } else {
303            amp
304        });
305    }
306}
307
308// ==================== Реализация трейта SyncableGenerator ====================
309
310impl<T: Transcendental> SyncableGenerator<T> for BasicOscillator<T> {
311    fn sync(&mut self, reset: bool) {
312        if reset {
313            self.phase = ScalarVector1::splat(T::ZERO);
314        }
315    }
316
317    fn periods(&self) -> u32 {
318        self.periods
319    }
320}
321
322// ==================== Реализация трейта ModulatableGenerator ====================
323
324impl<T: Transcendental> ModulatableGenerator<T> for BasicOscillator<T> {
325    fn modulate_frequency(&mut self, amount: T) {
326        self.fm_amount = ScalarVector1::splat(amount);
327    }
328
329    fn modulation_index(&self) -> T {
330        self.fm_amount.extract(0)
331    }
332
333    fn set_modulation_index(&mut self, index: T) {
334        self.fm_amount = ScalarVector1::splat(index);
335    }
336}
337
338// ==================== Тесты ====================
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343    use float_cmp::approx_eq;
344
345    #[test]
346    fn test_sine_oscillator() {
347        let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
348        osc.init(44100.0);
349
350        // Первый семпл должен быть 0
351        let mut output = [0.0f32; 1];
352        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
353        let ctx = rill_core::traits::ActionContext::new(&tick);
354        osc.process(None, &mut output, &ctx).unwrap();
355        let sample1 = output[0];
356        assert!(approx_eq!(f32, sample1, 0.0, epsilon = 1e-6));
357
358        // Второй семпл должен быть не 0
359        osc.process(None, &mut output, &ctx).unwrap();
360        let sample2 = output[0];
361        assert!(sample2 != 0.0);
362        assert!(sample2 >= -0.5 && sample2 <= 0.5);
363    }
364
365    #[test]
366    fn test_saw_oscillator() {
367        let mut osc = BasicOscillator::<f32>::new(Waveform::Saw, 440.0, 0.5);
368        osc.init(44100.0);
369
370        let mut output = [0.0f32; 1];
371        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
372        let ctx = rill_core::traits::ActionContext::new(&tick);
373        osc.process(None, &mut output, &ctx).unwrap();
374        let sample = output[0];
375        assert!(sample >= -0.5 && sample <= 0.5);
376    }
377
378    #[test]
379    fn test_square_oscillator() {
380        let mut osc = BasicOscillator::<f32>::new(Waveform::Square, 440.0, 0.5);
381        osc.init(44100.0);
382
383        let mut output = [0.0f32; 1];
384        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
385        let ctx = rill_core::traits::ActionContext::new(&tick);
386        osc.process(None, &mut output, &ctx).unwrap();
387        let sample = output[0];
388        assert!(sample == 0.5 || sample == -0.5);
389    }
390
391    #[test]
392    fn test_triangle_oscillator() {
393        let mut osc = BasicOscillator::<f32>::new(Waveform::Triangle, 440.0, 0.5);
394        osc.init(44100.0);
395
396        let mut output = [0.0f32; 1];
397        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
398        let ctx = rill_core::traits::ActionContext::new(&tick);
399        osc.process(None, &mut output, &ctx).unwrap();
400        let sample = output[0];
401        assert!(sample >= -0.5 && sample <= 0.5);
402    }
403
404    #[test]
405    fn test_pulse_oscillator() {
406        let mut osc = BasicOscillator::<f32>::new(Waveform::Pulse(0.25), 440.0, 0.5);
407        osc.init(44100.0);
408
409        let mut output = [0.0f32; 1];
410        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
411        let ctx = rill_core::traits::ActionContext::new(&tick);
412        osc.process(None, &mut output, &ctx).unwrap();
413        let sample = output[0];
414        assert!(sample == 0.5); // При фазе 0 должен быть положительный импульс
415    }
416
417    #[test]
418    fn test_frequency_change() {
419        let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
420        osc.init(44100.0);
421
422        assert_eq!(osc.frequency(), 440.0);
423
424        osc.set_frequency(880.0);
425        assert_eq!(osc.frequency(), 880.0);
426    }
427
428    #[test]
429    fn test_amplitude_change() {
430        let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
431        osc.init(44100.0);
432
433        assert_eq!(osc.amplitude(), 0.5);
434
435        osc.set_amplitude(0.8);
436        assert_eq!(osc.amplitude(), 0.8);
437    }
438
439    #[test]
440    fn test_phase_manipulation() {
441        let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 1.0);
442        osc.init(44100.0);
443
444        osc.set_phase(0.25); // π/2
445        let mut output = [0.0f32; 1];
446        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
447        let ctx = rill_core::traits::ActionContext::new(&tick);
448        osc.process(None, &mut output, &ctx).unwrap();
449        let sample = output[0];
450        assert!(approx_eq!(f32, sample, 1.0, epsilon = 1e-4)); // sin(π/2) = 1
451    }
452
453    #[test]
454    fn test_fm_modulation() {
455        let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 1.0);
456        osc.init(44100.0);
457
458        osc.modulate_frequency(0.5);
459        assert_eq!(osc.modulation_index(), 0.5);
460
461        // Проверяем, что модуляция применяется
462        let mut output = [0.0f32; 1];
463        let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
464        let ctx = rill_core::traits::ActionContext::new(&tick);
465        osc.process(None, &mut output, &ctx).unwrap();
466        let sample = output[0];
467        assert!(sample >= -1.0 && sample <= 1.0);
468    }
469
470    #[test]
471    fn test_clone_copy() {
472        let osc1 = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
473        let osc2 = osc1; // Копирование благодаря Copy
474        let osc3 = osc1.clone(); // Явное клонирование
475
476        assert_eq!(osc1.frequency(), osc2.frequency());
477        assert_eq!(osc1.frequency(), osc3.frequency());
478    }
479}