Przeglądaj źródła

Finish AudioFilterStateVariable (signal control of corner frequency)

dds
PaulStoffregen 10 lat temu
rodzic
commit
bc6e23011f
4 zmienionych plików z 165 dodań i 19 usunięć
  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 Wyświetl plik

@@ -27,14 +27,23 @@
#include "filter_variable.h"
#include "utility/dspinst.h"


// State Variable Filter (Chamberlin) with 2X oversampling
// 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) (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,
int16_t *lp, int16_t *bp, int16_t *hp)
@@ -50,7 +59,6 @@ void AudioFilterStateVariable::update_fixed(const int16_t *in,
inputprev = state_inputprev;
lowpass = state_lowpass;
bandpass = state_bandpass;

do {
input = (*in++) << 12;
lowpass = lowpass + MULT(fmult, bandpass);
@@ -69,33 +77,88 @@ void AudioFilterStateVariable::update_fixed(const int16_t *in,
*lp++ = lowpasstmp;
*bp++ = bandpasstmp;
*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);

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


void AudioFilterStateVariable::update_variable(const int16_t *in,
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)
{
audio_block_t *input_block=NULL, *control_block=NULL;

+ 15
- 0
filter_variable.h Wyświetl plik

@@ -34,6 +34,7 @@ class AudioFilterStateVariable: public AudioStream
public:
AudioFilterStateVariable() : AudioStream(2, inputQueueArray) {
frequency(1000);
octaveControl(1.0); // default values
resonance(0.707);
state_inputprev = 0;
state_lowpass = 0;
@@ -42,6 +43,10 @@ public:
void frequency(float freq) {
if (freq < 20.0) freq = 20.0;
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)))
* 2147483647.0;
}
@@ -51,13 +56,23 @@ public:
// TODO: allow lower Q when frequency is lower
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);
private:
void update_fixed(const int16_t *in,
int16_t *lp, int16_t *bp, int16_t *hp);
void update_variable(const int16_t *in, const int16_t *ctl,
int16_t *lp, int16_t *bp, int16_t *hp);
int32_t setting_fcenter;
int32_t setting_fmult;
int32_t setting_octavemult;
int32_t setting_damp;
int32_t state_inputprev;
int32_t state_lowpass;

+ 67
- 0
gui/list.html Wyświetl plik

@@ -1341,6 +1341,73 @@



<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">
RED.nodes.registerType('AudioPeak',{
shortName: "peak",

+ 1
- 0
keywords.txt Wyświetl plik

@@ -44,6 +44,7 @@ updateCoefs KEYWORD2
frequency KEYWORD2
amplitude KEYWORD2
resonance KEYWORD2
octaveControl KEYWORD2
modify KEYWORD2
output KEYWORD2
trigger KEYWORD2

Ładowanie…
Anuluj
Zapisz