Added pureSpectrum filtering option.

pull/13/head
boblark 3 years ago
parent 59e11fd79f
commit 8353f29b10
  1. 68
      synth_sine_f32.cpp
  2. 149
      synth_sine_f32.h

@ -7,52 +7,36 @@
* Purpose: Create sine wave of given amplitude and frequency
*
* License: MIT License. Use at your own risk.
*
*
* Revised per synth_sine_f32.h. 7 Feb 2022 Bob.
*/
#include "synth_sine_f32.h"
#include "utility/dspinst.h"
// 513 values of the sine wave in a float array:
#include "sinTable512_f32.h"
// data_waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
}
void AudioSynthWaveformSine_F32::update(void) {
audio_block_f32_t *block;
uint32_t i, ph, inc, index, scale;
int32_t val1, val2;
static uint32_t block_length = 0;
void AudioSynthWaveformSine_F32::update(void) {
audio_block_f32_t *blockS;
uint16_t index, i;
float32_t a, b;
if (enabled) {
if (magnitude) {
block = allocate_f32();
if (block) {
block_length = (uint32_t)block->length;
ph = phase_accumulator;
inc = phase_increment;
for (i=0; i < block_length; i++) {
index = ph >> 24;
val1 = AudioWaveformSine[index];
val2 = AudioWaveformSine[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
#if (defined(KINETISK) || defined(__IMXRT1062__) )
block->data[i] = (float) multiply_32x32_rshift32(val1 + val2, magnitude);
#elif defined(KINETISL)
block->data[i] = (float) ((((val1 + val2) >> 16) * magnitude) >> 16);
#endif
ph += inc;
block->data[i] = block->data[i] / 32767.0f; // scale to float
}
phase_accumulator = ph;
blockS = AudioStream_F32::allocate_f32(); // Output blocks
if (!blockS) return;
AudioStream_F32::transmit(block);
AudioStream_F32::release(block);
return;
}
}// end if (magnitude)
phase_accumulator += phase_increment * block_length; // continue sine wave while magnitude==0
} // end if (enabled)
for (i=0; i < blockS->length; i++) {
phaseS += phaseIncrement;
if (phaseS > 512.0f) phaseS -= 512.0f;
index = (uint16_t) phaseS;
float32_t deltaPhase = phaseS - (float32_t)index;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index];
b = sinTable512_f32[index+1];
blockS->data[i] = magnitude*(a+(b-a)*deltaPhase); // Linear interpolation
}
// For higher frequencies, an optional bandpass filter the output
// This does a pass through for lower frequencies
if(doPureSpectrum)
arm_biquad_cascade_df1_f32(&bq_inst, blockS->data, blockS->data, 128);
AudioStream_F32::transmit(blockS);
AudioStream_F32::release (blockS);
}

@ -1,5 +1,5 @@
/*
* AdioSynthWaveformSine_F32
* AudioSynthWaveformSine_F32
*
* Created: Chip Audette (OpenAudio) Feb 2017
* Modeled on: AudioSynthWaveformSine from Teensy Audio Library
@ -9,55 +9,128 @@
* 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_sine_f32_h_
#define synth_sine_f32_h_
#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(16384) { } //uses default AUDIO_SAMPLE_RATE from AudioStream.h
AudioSynthWaveformSine_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL), magnitude(16384) {
setSampleRate_Hz(settings.sample_rate_Hz);
}
void frequency(float freq) {
if (freq < 0.0) freq = 0.0;
else if (freq > sample_rate_Hz/2.f) freq = sample_rate_Hz/2.f;
phase_increment = freq * (4294967296.0 / sample_rate_Hz);
}
void phase(float angle) {
if (angle < 0.0f) angle = 0.0f;
else if (angle > 360.0f) {
angle = angle - 360.0f;
if (angle >= 360.0f) return;
}
phase_accumulator = angle * (4294967296.0f / 360.0f);
}
void amplitude(float n) {
if (n < 0) n = 0;
else if (n > 1.0f) n = 1.0f;
magnitude = n * 65536.0f;
}
void setSampleRate_Hz(const float &fs_Hz) {
phase_increment *= 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; }
virtual void update(void);
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.
* Corrected 1-day at phase_r() 24 Feb 22
*/
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:
uint32_t phase_accumulator = 0;
uint32_t phase_increment = 0;
int32_t magnitude = 0;
float sample_rate_Hz = AUDIO_SAMPLE_RATE;
volatile uint8_t enabled = 1;
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

Loading…
Cancel
Save