You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
4.8KB

  1. /* Audio Library for Teensy, Ladder Filter
  2. * Copyright (c) 2021, Richard van Hoesel
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice, development funding notice, and this permission
  12. * notice shall be included in all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. */
  22. //-----------------------------------------------------------
  23. // Huovilainen New Moog (HNM) model as per CMJ jun 2006
  24. // Implemented as Teensy Audio Library compatible object
  25. // Richard van Hoesel, Feb. 9 2021
  26. // v.1.02 now includes both cutoff and resonance "CV" modulation inputs
  27. // please retain this header if you use this code.
  28. //-----------------------------------------------------------
  29. // https://forum.pjrc.com/threads/60488?p=269755&viewfull=1#post269755
  30. // https://forum.pjrc.com/threads/60488?p=269609&viewfull=1#post269609
  31. #include <Arduino.h>
  32. #include "filter_ladder.h"
  33. #include <math.h>
  34. #include <stdint.h>
  35. #define MOOG_PI ((float)3.14159265358979323846264338327950288)
  36. #define MAX_RESONANCE ((float)1.07)
  37. #define MAX_FREQUENCY ((float)(AUDIO_SAMPLE_RATE_EXACT * 0.249f))
  38. float AudioFilterLadder::LPF(float s, int i)
  39. {
  40. float ft = s * (1.0f/1.3f) + (0.3f/1.3f) * z0[i] - z1[i];
  41. ft = ft * alpha + z1[i];
  42. z1[i] = ft;
  43. z0[i] = s;
  44. return ft;
  45. }
  46. void AudioFilterLadder::resonance(float res)
  47. {
  48. // maps resonance = 0->1 to K = 0 -> 4
  49. if (res > MAX_RESONANCE) {
  50. res = MAX_RESONANCE;
  51. } else if (res < 0.0f) {
  52. res = 0.0f;
  53. }
  54. K = 4.0f * res;
  55. }
  56. void AudioFilterLadder::frequency(float c)
  57. {
  58. Fbase = c;
  59. compute_coeffs(c);
  60. }
  61. void AudioFilterLadder::octaveControl(float octaves)
  62. {
  63. if (octaves > 7.0f) {
  64. octaves = 7.0f;
  65. } else if (octaves < 0.0f) {
  66. octaves = 0.0f;
  67. }
  68. octaveScale = octaves / 32768.0f;
  69. }
  70. void AudioFilterLadder::compute_coeffs(float c)
  71. {
  72. if (c > MAX_FREQUENCY) {
  73. c = MAX_FREQUENCY;
  74. } else if (c < 1.0f) {
  75. c = 1.0f;
  76. }
  77. float wc = c * (float)(2.0f * MOOG_PI / AUDIO_SAMPLE_RATE_EXACT);
  78. float wc2 = wc * wc;
  79. alpha = 0.9892f * wc - 0.4324f * wc2 + 0.1381f * wc * wc2 - 0.0202f * wc2 * wc2;
  80. }
  81. bool AudioFilterLadder::resonating()
  82. {
  83. for (int i=0; i < 4; i++) {
  84. if (fabsf(z0[i]) > 0.0001f) return true;
  85. if (fabsf(z1[i]) > 0.0001f) return true;
  86. }
  87. return false;
  88. }
  89. static inline float fast_exp2f(float x)
  90. {
  91. float i;
  92. float f = modff(x, &i);
  93. f *= 0.693147f / 256.0f;
  94. f += 1.0f;
  95. f *= f;
  96. f *= f;
  97. f *= f;
  98. f *= f;
  99. f *= f;
  100. f *= f;
  101. f *= f;
  102. f *= f;
  103. f = ldexpf(f, i);
  104. return f;
  105. }
  106. static inline float fast_tanh(float x)
  107. {
  108. float x2 = x * x;
  109. return x * (27.0f + x2) / (27.0f + 9.0f * x2);
  110. }
  111. void AudioFilterLadder::update(void)
  112. {
  113. audio_block_t *blocka, *blockb, *blockc;
  114. float Ktot;
  115. bool FCmodActive = true;
  116. bool QmodActive = true;
  117. blocka = receiveWritable(0);
  118. blockb = receiveReadOnly(1);
  119. blockc = receiveReadOnly(2);
  120. if (!blocka) {
  121. if (resonating()) {
  122. // When no data arrives but the filter is still
  123. // resonating, we must continue computing the filter
  124. // with zero input to sustain the resonance
  125. blocka = allocate();
  126. }
  127. if (!blocka) {
  128. if (blockb) release(blockb);
  129. if (blockc) release(blockc);
  130. return;
  131. }
  132. for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
  133. blocka->data[i] = 0;
  134. }
  135. }
  136. if (!blockb) {
  137. FCmodActive = false;
  138. }
  139. if (!blockc) {
  140. QmodActive = false;
  141. Ktot = K;
  142. }
  143. for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
  144. float input = blocka->data[i] * (1.0f/32768.0f);
  145. if (FCmodActive) {
  146. float FCmod = blockb->data[i] * octaveScale;
  147. float ftot = Fbase * fast_exp2f(FCmod);
  148. if (ftot > MAX_FREQUENCY) ftot = MAX_FREQUENCY;
  149. if (FCmod != 0) compute_coeffs(ftot);
  150. }
  151. if (QmodActive) {
  152. float Qmod = blockc->data[i] * (1.0f/32768.0f);
  153. Ktot = K + (MAX_RESONANCE * 4.0f) * Qmod;
  154. if (Ktot < 0.0f) Ktot = 0.0f;
  155. }
  156. float u = input - (z1[3] - 0.5f * input) * Ktot;
  157. u = fast_tanh(u);
  158. float stage1 = LPF(u, 0);
  159. float stage2 = LPF(stage1, 1);
  160. float stage3 = LPF(stage2, 2);
  161. float stage4 = LPF(stage3, 3);
  162. blocka->data[i] = stage4 * 32767.0f;
  163. }
  164. transmit(blocka);
  165. release(blocka);
  166. if (blockb) release(blockb);
  167. if (blockc) release(blockc);
  168. }