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.

174 lines
4.1KB

  1. #pragma once
  2. #include "frame.hpp"
  3. #include "ringbuffer.hpp"
  4. #include "fir.hpp"
  5. #include <assert.h>
  6. #include <string.h>
  7. #include <speex/speex_resampler.h>
  8. namespace rack {
  9. template<int CHANNELS>
  10. struct SampleRateConverter {
  11. SpeexResamplerState *st = NULL;
  12. int channels = CHANNELS;
  13. int quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  14. int inRate = 44100;
  15. int outRate = 44100;
  16. SampleRateConverter() {
  17. refreshState();
  18. }
  19. ~SampleRateConverter() {
  20. if (st) {
  21. speex_resampler_destroy(st);
  22. }
  23. }
  24. /** Sets the number of channels to actually process. This can be at most CHANNELS. */
  25. void setChannels(int channels) {
  26. assert(channels <= CHANNELS);
  27. if (channels == this->channels)
  28. return;
  29. this->channels = channels;
  30. refreshState();
  31. }
  32. /** From 0 (worst, fastest) to 10 (best, slowest) */
  33. void setQuality(int quality) {
  34. if (quality == this->quality)
  35. return;
  36. this->quality = quality;
  37. refreshState();
  38. }
  39. void setRates(int inRate, int outRate) {
  40. if (inRate == this->inRate && outRate == this->outRate)
  41. return;
  42. this->inRate = inRate;
  43. this->outRate = outRate;
  44. refreshState();
  45. }
  46. void refreshState() {
  47. if (st) {
  48. speex_resampler_destroy(st);
  49. st = NULL;
  50. }
  51. if (channels > 0 && inRate != outRate) {
  52. int err;
  53. st = speex_resampler_init(channels, inRate, outRate, quality, &err);
  54. assert(st);
  55. assert(err == RESAMPLER_ERR_SUCCESS);
  56. speex_resampler_set_input_stride(st, CHANNELS);
  57. speex_resampler_set_output_stride(st, CHANNELS);
  58. }
  59. }
  60. /** `in` and `out` are interlaced with the number of channels */
  61. void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) {
  62. assert(in);
  63. assert(inFrames);
  64. assert(out);
  65. assert(outFrames);
  66. if (st) {
  67. // Resample each channel at a time
  68. spx_uint32_t inLen;
  69. spx_uint32_t outLen;
  70. for (int i = 0; i < channels; i++) {
  71. inLen = *inFrames;
  72. outLen = *outFrames;
  73. int err = speex_resampler_process_float(st, i, ((const float*) in) + i, &inLen, ((float*) out) + i, &outLen);
  74. assert(err == RESAMPLER_ERR_SUCCESS);
  75. }
  76. *inFrames = inLen;
  77. *outFrames = outLen;
  78. }
  79. else {
  80. // Simply copy the buffer without conversion
  81. int frames = std::min(*inFrames, *outFrames);
  82. memcpy(out, in, frames * sizeof(Frame<CHANNELS>));
  83. *inFrames = frames;
  84. *outFrames = frames;
  85. }
  86. }
  87. };
  88. template<int OVERSAMPLE, int QUALITY>
  89. struct Decimator {
  90. float inBuffer[OVERSAMPLE*QUALITY];
  91. float kernel[OVERSAMPLE*QUALITY];
  92. int inIndex;
  93. Decimator(float cutoff = 0.9f) {
  94. boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE);
  95. blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY);
  96. reset();
  97. }
  98. void reset() {
  99. inIndex = 0;
  100. memset(inBuffer, 0, sizeof(inBuffer));
  101. }
  102. /** `in` must be length OVERSAMPLE */
  103. float process(float *in) {
  104. // Copy input to buffer
  105. memcpy(&inBuffer[inIndex], in, OVERSAMPLE*sizeof(float));
  106. // Advance index
  107. inIndex += OVERSAMPLE;
  108. inIndex %= OVERSAMPLE*QUALITY;
  109. // Perform naive convolution
  110. float out = 0.f;
  111. for (int i = 0; i < OVERSAMPLE*QUALITY; i++) {
  112. int index = inIndex - 1 - i;
  113. index = (index + OVERSAMPLE*QUALITY) % (OVERSAMPLE*QUALITY);
  114. out += kernel[i] * inBuffer[index];
  115. }
  116. return out;
  117. }
  118. };
  119. template<int OVERSAMPLE, int QUALITY>
  120. struct Upsampler {
  121. float inBuffer[QUALITY];
  122. float kernel[OVERSAMPLE*QUALITY];
  123. int inIndex;
  124. Upsampler(float cutoff = 0.9f) {
  125. boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE);
  126. blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY);
  127. reset();
  128. }
  129. void reset() {
  130. inIndex = 0;
  131. memset(inBuffer, 0, sizeof(inBuffer));
  132. }
  133. /** `out` must be length OVERSAMPLE */
  134. void process(float in, float *out) {
  135. // Zero-stuff input buffer
  136. inBuffer[inIndex] = OVERSAMPLE * in;
  137. // Advance index
  138. inIndex++;
  139. inIndex %= QUALITY;
  140. // Naively convolve each sample
  141. // TODO replace with polyphase filter hierarchy
  142. for (int i = 0; i < OVERSAMPLE; i++) {
  143. float y = 0.f;
  144. for (int j = 0; j < QUALITY; j++) {
  145. int index = inIndex - 1 - j;
  146. index = (index + QUALITY) % QUALITY;
  147. int kernelIndex = OVERSAMPLE * j + i;
  148. y += kernel[kernelIndex] * inBuffer[index];
  149. }
  150. out[i] = y;
  151. }
  152. }
  153. };
  154. } // namespace rack