Bladeren bron

Finish AudioFilterStateVariable (signal control of corner frequency)

dds
PaulStoffregen 10 jaren geleden
bovenliggende
commit
bc6e23011f
4 gewijzigde bestanden met toevoegingen van 165 en 19 verwijderingen
  1. +82
    -19
      filter_variable.cpp
  2. +15
    -0
      filter_variable.h
  3. +67
    -0
      gui/list.html
  4. +1
    -0
      keywords.txt

+ 82
- 19
filter_variable.cpp Bestand weergeven

#include "filter_variable.h" #include "filter_variable.h"
#include "utility/dspinst.h" #include "utility/dspinst.h"



// State Variable Filter (Chamberlin) with 2X oversampling // State Variable Filter (Chamberlin) with 2X oversampling
// http://www.musicdsp.org/showArchiveComment.php?ArchiveID=92 // http://www.musicdsp.org/showArchiveComment.php?ArchiveID=92


// The fast 32x32 with rshift32 discards 2 bits, which probably
// never matter.
//#define MULT(a, b) (int32_t)(((int64_t)(a) * (b)) >> 30) //#define MULT(a, b) (int32_t)(((int64_t)(a) * (b)) >> 30)
#define MULT(a, b) (multiply_32x32_rshift32_rounded(a, b) << 2) #define MULT(a, b) (multiply_32x32_rshift32_rounded(a, b) << 2)


static bool first = true;
// It's very unlikely anyone could hear any difference, but if you
// care deeply about numerical precision in seldom-used cases,
// uncomment this to improve the control signal accuracy
//#define IMPROVE_HIGH_FREQUENCY_ACCURACY

// This increases the exponential approximation accuracy from
// about 0.341% error to only 0.012% error, which probably makes
// no audible difference.
//#define IMPROVE_EXPONENTIAL_ACCURACY


void AudioFilterStateVariable::update_fixed(const int16_t *in, void AudioFilterStateVariable::update_fixed(const int16_t *in,
int16_t *lp, int16_t *bp, int16_t *hp) int16_t *lp, int16_t *bp, int16_t *hp)
inputprev = state_inputprev; inputprev = state_inputprev;
lowpass = state_lowpass; lowpass = state_lowpass;
bandpass = state_bandpass; bandpass = state_bandpass;

do { do {
input = (*in++) << 12; input = (*in++) << 12;
lowpass = lowpass + MULT(fmult, bandpass); lowpass = lowpass + MULT(fmult, bandpass);
*lp++ = lowpasstmp; *lp++ = lowpasstmp;
*bp++ = bandpasstmp; *bp++ = bandpasstmp;
*hp++ = highpasstmp; *hp++ = highpasstmp;
#if 0
if (first) {
Serial.print(input >> 12);
Serial.print(", ");
Serial.print(lowpasstmp);
Serial.print(", ");
Serial.print(bandpasstmp);
Serial.print(", ");
Serial.print(highpasstmp);
Serial.println();
}
#endif
} while (in < end); } while (in < end);

state_inputprev = inputprev; state_inputprev = inputprev;
state_lowpass = lowpass; state_lowpass = lowpass;
state_bandpass = bandpass; state_bandpass = bandpass;
first = false;
} }



void AudioFilterStateVariable::update_variable(const int16_t *in, void AudioFilterStateVariable::update_variable(const int16_t *in,
const int16_t *ctl, int16_t *lp, int16_t *bp, int16_t *hp) const int16_t *ctl, int16_t *lp, int16_t *bp, int16_t *hp)
{ {
// TODO: implement control signal modulation of corner frequency
update_fixed(in, lp, bp, hp);
const int16_t *end = in + AUDIO_BLOCK_SAMPLES;
int32_t input, inputprev, control;
int32_t lowpass, bandpass, highpass;
int32_t lowpasstmp, bandpasstmp, highpasstmp;
int32_t fcenter, fmult, damp, octavemult;
int32_t n;

fcenter = setting_fcenter;
octavemult = setting_octavemult;
damp = setting_damp;
inputprev = state_inputprev;
lowpass = state_lowpass;
bandpass = state_bandpass;
do {
// compute fmult using control input, fcenter and octavemult
control = *ctl++; // signal is always 15 fractional bits
control *= octavemult; // octavemult range: 0 to 28671 (12 frac bits)
n = control & 0x7FFFFFF; // 27 fractional control bits
#ifdef IMPROVE_EXPONENTIAL_ACCURACY
int32_t x = n << 3;
n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713);
int32_t sq = multiply_32x32_rshift32_rounded(x, x);
n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615);
n = n + (multiply_32x32_rshift32_rounded(sq,
multiply_32x32_rshift32_rounded(x, 1358044250)) << 1);
n = n << 1;
#else
n = (n + 134217728) << 3;
n = multiply_32x32_rshift32_rounded(n, n);
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
n = n + 715827882;
#endif
n = n >> (6 - (control >> 27)); // 4 integer control bits
fmult = multiply_32x32_rshift32_rounded(fcenter, n);
if (fmult > 5378279) fmult = 5378279;
fmult = fmult << 8;
// fmult is within 0.4% accuracy for all but the top 2 octaves
// of the audio band. This math improves accuracy above 5 kHz.
// Without this, the filter still works fine for processing
// high frequencies, but the filter's corner frequency response
// can end up about 6% higher than requested.
#ifdef IMPROVE_HIGH_FREQUENCY_ACCURACY
fmult = (multiply_32x32_rshift32_rounded(fmult, 2145892402) +
multiply_32x32_rshift32_rounded(
multiply_32x32_rshift32_rounded(fmult, fmult),
multiply_32x32_rshift32_rounded(fmult, -1383276101))) << 1;
#endif
// now do the state variable filter as normal, using fmult
input = (*in++) << 12;
lowpass = lowpass + MULT(fmult, bandpass);
highpass = ((input + inputprev)>>1) - lowpass - MULT(damp, bandpass);
inputprev = input;
bandpass = bandpass + MULT(fmult, highpass);
lowpasstmp = lowpass;
bandpasstmp = bandpass;
highpasstmp = highpass;
lowpass = lowpass + MULT(fmult, bandpass);
highpass = input - lowpass - MULT(damp, bandpass);
bandpass = bandpass + MULT(fmult, highpass);
lowpasstmp = signed_saturate_rshift(lowpass+lowpasstmp, 16, 13);
bandpasstmp = signed_saturate_rshift(bandpass+bandpasstmp, 16, 13);
highpasstmp = signed_saturate_rshift(highpass+highpasstmp, 16, 13);
*lp++ = lowpasstmp;
*bp++ = bandpasstmp;
*hp++ = highpasstmp;
} while (in < end);
state_inputprev = inputprev;
state_lowpass = lowpass;
state_bandpass = bandpass;
} }



void AudioFilterStateVariable::update(void) void AudioFilterStateVariable::update(void)
{ {
audio_block_t *input_block=NULL, *control_block=NULL; audio_block_t *input_block=NULL, *control_block=NULL;

+ 15
- 0
filter_variable.h Bestand weergeven

public: public:
AudioFilterStateVariable() : AudioStream(2, inputQueueArray) { AudioFilterStateVariable() : AudioStream(2, inputQueueArray) {
frequency(1000); frequency(1000);
octaveControl(1.0); // default values
resonance(0.707); resonance(0.707);
state_inputprev = 0; state_inputprev = 0;
state_lowpass = 0; state_lowpass = 0;
void frequency(float freq) { void frequency(float freq) {
if (freq < 20.0) freq = 20.0; if (freq < 20.0) freq = 20.0;
else if (freq > AUDIO_SAMPLE_RATE_EXACT/2.5) freq = AUDIO_SAMPLE_RATE_EXACT/2.5; else if (freq > AUDIO_SAMPLE_RATE_EXACT/2.5) freq = AUDIO_SAMPLE_RATE_EXACT/2.5;
setting_fcenter = (freq * (3.141592654/(AUDIO_SAMPLE_RATE_EXACT*2.0)))
* 2147483647.0;
// TODO: should we use an approximation when freq is not a const,
// so the sinf() function isn't linked?
setting_fmult = sinf(freq * (3.141592654/(AUDIO_SAMPLE_RATE_EXACT*2.0))) setting_fmult = sinf(freq * (3.141592654/(AUDIO_SAMPLE_RATE_EXACT*2.0)))
* 2147483647.0; * 2147483647.0;
} }
// TODO: allow lower Q when frequency is lower // TODO: allow lower Q when frequency is lower
setting_damp = (1.0 / q) * 1073741824.0; setting_damp = (1.0 / q) * 1073741824.0;
} }
void octaveControl(float n) {
// filter's corner frequency is Fcenter * 2^(control * N)
// where "control" ranges from -1.0 to +1.0
// and "N" allows the frequency to change from 0 to 7 octaves
if (n < 0.0) n = 0.0;
else if (n > 6.9999) n = 6.9999;
setting_octavemult = n * 4096.0;
}
virtual void update(void); virtual void update(void);
private: private:
void update_fixed(const int16_t *in, void update_fixed(const int16_t *in,
int16_t *lp, int16_t *bp, int16_t *hp); int16_t *lp, int16_t *bp, int16_t *hp);
void update_variable(const int16_t *in, const int16_t *ctl, void update_variable(const int16_t *in, const int16_t *ctl,
int16_t *lp, int16_t *bp, int16_t *hp); int16_t *lp, int16_t *bp, int16_t *hp);
int32_t setting_fcenter;
int32_t setting_fmult; int32_t setting_fmult;
int32_t setting_octavemult;
int32_t setting_damp; int32_t setting_damp;
int32_t state_inputprev; int32_t state_inputprev;
int32_t state_lowpass; int32_t state_lowpass;

+ 67
- 0
gui/list.html Bestand weergeven







<script type="text/javascript">
RED.nodes.registerType('AudioFilterStateVariable',{
shortName: "filter",
inputs:2,
outputs:3,
category: 'filter-function',
color:"#E6E0F8",
icon: "arrow-in.png"
});
</script>
<script type="text/x-red" data-help-name="AudioFilterStateVariable">
<h3>Summary</h3>
<p>A State Variable (Chamberlin) Filter with 12 dB/octave roll-off,
adjustable resonance, and optional signal control of corner
frequency.</p>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In 0</td><td>Signal to Filter</td></tr>
<tr class=odd><td align=center>In 1</td><td>Frequency Control</td></tr>
<tr class=odd><td align=center>Out 0</td><td>Low Pass Output</td></tr>
<tr class=odd><td align=center>Out 1</td><td>Band Pass Output</td></tr>
<tr class=odd><td align=center>Out 2</td><td>High Pass Output</td></tr>
</table>
<h3>Functions</h3>
<p class=func><span class=keyword>frequency</span>(freq);</p>
<p class=desc>Set the filter's corner frequency. When a signal is
connected to the control input, the filter will implement this
frequency when the signal is zero.
</p>
<p class=func><span class=keyword>resonance</span>(Q);</p>
<p class=desc>Set the filter's resonance. Q ranges from 0.7 to 5.0.
Resonance greater than 0.707 will amplify the signal near the
corner frequency. You must attenuate the signal before input
to this filter, to prevent clipping.
</p>
<p class=func><span class=keyword>octaveControl</span>(octaves);</p>
<p class=desc>Set how much (in octaves) the control signal can alter
the filter's corner freqency. Range is 0 to 7 octaves. For
example, when set to 2.5, a full scale positive signal (1.0) will
shift the filter frequency up 2.5 octaves, and a full scale negative
signal will shift it down 2.5 octaves.
</p>
<h3>Notes</h3>
<p>
When controlled by a signal, the equation for the filter
frequency is:
</p>
<p>
F = Fcenter * 2^<sup>(signal * octaves)</sup>
<br><small>If anyone knows how to do HTML equations, please
help me improve this.....</small>
</p>
<p>When operating with signal control of corner frequency, this
object uses approximately 4% of the CPU time on Teensy 3.1.
</p>
</script>
<script type="text/x-red" data-template-name="AudioFilterFIR">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>




<script type="text/javascript"> <script type="text/javascript">
RED.nodes.registerType('AudioPeak',{ RED.nodes.registerType('AudioPeak',{
shortName: "peak", shortName: "peak",

+ 1
- 0
keywords.txt Bestand weergeven

frequency KEYWORD2 frequency KEYWORD2
amplitude KEYWORD2 amplitude KEYWORD2
resonance KEYWORD2 resonance KEYWORD2
octaveControl KEYWORD2
modify KEYWORD2 modify KEYWORD2
output KEYWORD2 output KEYWORD2
trigger KEYWORD2 trigger KEYWORD2

Laden…
Annuleren
Opslaan