Generate a sawtooth wave using Fourier series: sum((-1)^(h+1) * sin(2*pi*h*f*t) / h) Rich harmonics at 1/h amplitude but NO formant peaks (smooth spectral envelope). Tests that a spectrally rich but structurally non-vocal signal is rejected.
| 597 | /// Rich harmonics at 1/h amplitude but NO formant peaks (smooth spectral envelope). |
| 598 | /// Tests that a spectrally rich but structurally non-vocal signal is rejected. |
| 599 | inline Sample makeSawtoothWave(float freq, fl::u32 timestamp, |
| 600 | float amplitude = 16000.0f, int count = 512, |
| 601 | float sampleRate = 44100.0f) { |
| 602 | fl::vector<fl::i16> data(count, 0); |
| 603 | const float nyquist = sampleRate / 2.0f; |
| 604 | const int maxH = static_cast<int>(nyquist / freq); |
| 605 | for (int h = 1; h <= maxH; ++h) { |
| 606 | float sign = ((h % 2) == 0) ? -1.0f : 1.0f; |
| 607 | float hAmp = amplitude * sign / static_cast<float>(h); |
| 608 | float hFreq = freq * static_cast<float>(h); |
| 609 | for (int i = 0; i < count; ++i) { |
| 610 | float phase = 2.0f * FL_M_PI * hFreq * static_cast<float>(i) / sampleRate; |
| 611 | float sample = static_cast<float>(data[i]) + hAmp * fl::sinf(phase); |
| 612 | data[i] = static_cast<fl::i16>(fl::clamp(sample, -32768.0f, 32767.0f)); |
| 613 | } |
| 614 | } |
| 615 | return Sample(data, timestamp); |
| 616 | } |
| 617 | |
| 618 | /// Generate an AM-modulated sine (tremolo tone): |
| 619 | /// amp * (1 - depth + depth*sin(2*pi*modRate*t)) * sin(2*pi*freq*t) |