/* * AudioSynthWaveformSine_F32 * * Created: Chip Audette (OpenAudio) Feb 2017 * Modeled on: AudioSynthWaveformSine from Teensy Audio Library * * Purpose: Create sine wave of given amplitude and frequency * * License: MIT License. Use at your own risk. * */ /* Revised 7 Feb 2022 to use a larger 512 point table and direct floating * point. The level of harmonics depends on the exact frequency, but seems * to be around -110 dB below the sine wave output. This is more than * adequate for most applications. For some testing, a pure sine wave, * limited only by the 24 bit mantissa, is useful. For this, the function * pureSpectrum(true) will run two stages of biquad filtering putting the * harmonics below -135 dBc. This filter tracks the frequency() entry, and * is available above a few hundred Hz, depending on the sample rate. --Bob * * Update time is about 9 microsends for 128 update() with T4.x. This goes * up to 16 microseconds if "pureSpectrum" is used. */ #ifndef synth_sine2_f32_h_ #define synth_sine2_f32_h_ #include "Arduino.h" #include "AudioStream_F32.h" #include "arm_math.h" class AudioSynthWaveformSine_F32 : public AudioStream_F32 { //GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node //GUI: shortName:sine //this line used for automatic generation of GUI node public: AudioSynthWaveformSine_F32() : AudioStream_F32(0, NULL), magnitude(0.5f) { initSine(); } //uses default AUDIO_SAMPLE_RATE from AudioStream.h AudioSynthWaveformSine_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL), magnitude(0.5f) { setSampleRate_Hz(settings.sample_rate_Hz); initSine(); } void initSine(void) { for(int ii=0; ii<10; ii++) // Coeff for BiQuad BPF coeff32[ii] = 0.0; coeff32[0] = 1.0; // b0 = 1 for pass through coeff32[5] = 1.0; // {numStages, pState, pCoeffs}; arm_biquad_cascade_df1_init_f32( &bq_inst, 2, state32, coeff32 ); } void frequency(float32_t _freq) { // Frequency in Hz freq = _freq; if (freq < 0.0f) freq = 0.0f; if (freq > sample_rate_Hz/2.0f) freq = sample_rate_Hz/2.0f; phaseIncrement = 512.0f * freq / sample_rate_Hz; // Find coeff for 2 stages of BPF to remove harmoncs // Always compute these in case pureSpectrum is enabled later. if(freq > 0.003f*sample_rate_Hz) { float32_t q = 20.0f; float32_t w0 = freq * (2.0f * 3.141592654f / sample_rate_Hz); float32_t alpha = sin(w0) / (q * 2.0); float32_t scale = 1.0f / (1.0f + alpha); /* b0 */ coeff32[0] = alpha * scale; /* b1 */ coeff32[1] = 0; /* b2 */ coeff32[2] = (-alpha) * scale; /* a1 */ coeff32[3] = -(-2.0 * cos(w0)) * scale; /* a2 */ coeff32[4] = -(1.0 - alpha) * scale; /* b0 */ coeff32[5] = coeff32[0]; /* b1 */ coeff32[6] = coeff32[1]; /* b2 */ coeff32[7] = coeff32[2]; /* a1 */ coeff32[8] = coeff32[3]; /* a2 */ coeff32[9] = coeff32[4]; arm_biquad_cascade_df1_init_f32( &bq_inst, 2, coeff32, state32 ); } else { for(int ii=0; ii<10; ii++) // Coeff for BiQuad BPF coeff32[ii] = 0.0; coeff32[0] = 1.0; // b0 = 1 for pass through coeff32[5] = 1.0; arm_biquad_cascade_df1_init_f32( &bq_inst, 2, coeff32, state32 ); enabled = false; } } /* Externally, phase comes in the range (.0, 360.0). * Internally, the full circle is represented as (0.0, 512.0). This is * convenient for finding the entry to the sine table. */ void phase(float32_t _angle) { angle = 1.42222222f*_angle; // Change (0,360) to (0, 512) while (angle < 0.0f) angle += 512.0f; while (angle > 512.0f) angle -= 512.0; } // The amplitude, a, is the peak, as in zero-to-peak. This produces outputs // ranging from -a to +a. void amplitude(float32_t a) { if (a < 0.0f) a = 0.0f; magnitude = a; } void setSampleRate_Hz(const float &fs_Hz) { phaseIncrement *= sample_rate_Hz / fs_Hz; //change the phase increment for the new frequency sample_rate_Hz = fs_Hz; } void begin(void) { enabled = true; } void end(void) { enabled = false; } void pureSpectrum(bool _setPure) { doPureSpectrum = _setPure; } virtual void update(void); private: float32_t freq = 1000.0f; float32_t angle = 0.0f; // Phase angle float32_t phaseS = 0.0f; float32_t phaseIncrement = 0.0f; float32_t magnitude = 0.0f; float32_t sample_rate_Hz = AUDIO_SAMPLE_RATE; bool doPureSpectrum = false; // Adds bandpass filter (not normally needed) bool enabled = true; float32_t coeff32[10]; // 2 biquad stages for filtering output float32_t state32[8]; arm_biquad_casd_df1_inst_f32 bq_inst; // ARM DSP Math library filter instance. }; #endif