1 // Copyright 2014 Emilie Gillet. 2 // 3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // Sample rate reducer. 28 29 #ifndef PLAITS_DSP_FX_SAMPLE_RATE_REDUCER_H_ 30 #define PLAITS_DSP_FX_SAMPLE_RATE_REDUCER_H_ 31 32 #include <algorithm> 33 34 #include "stmlib/dsp/polyblep.h" 35 36 namespace plaits { 37 38 class SampleRateReducer { 39 public: SampleRateReducer()40 SampleRateReducer() { } ~SampleRateReducer()41 ~SampleRateReducer() { } 42 Init()43 void Init() { 44 phase_ = 0.0f; 45 sample_ = 0.0f; 46 next_sample_ = 0.0f; 47 previous_sample_ = 0.0f; 48 } 49 50 template<bool optimized_handling_of_special_cases> Process(float frequency,float * in_out,size_t size)51 void Process(float frequency, float* in_out, size_t size) { 52 if (optimized_handling_of_special_cases) { 53 // Use fast specialized implementations for target rates close to the 54 // original rates. Caveats: 55 // - The size argument must be a multiple of 4. 56 // - There will be a transition glitch between the "optimized" and the 57 // "common case" code, so don't use this when frequency is modulated! 58 // - The optimized code is not a truly variable reclocking, instead, 59 // this is a crossfade between reclocking at SR / 2N and SR / N. 60 if (frequency >= 1.0f) { 61 return; 62 } else if (frequency >= 0.5f) { 63 ProcessHalf(2.0f - 2.0f * frequency, in_out, size); 64 return; 65 } else if (frequency >= 0.25f) { 66 ProcessQuarter(2.0f - 4.0f * frequency, in_out, size); 67 return; 68 } 69 } else { 70 CONSTRAIN(frequency, 0.0f, 1.0f); 71 } 72 float previous_sample = previous_sample_; 73 float next_sample = next_sample_; 74 float sample = sample_; 75 float phase = phase_; 76 while (size--) { 77 float this_sample = next_sample; 78 next_sample = 0.0f; 79 phase += frequency; 80 if (phase >= 1.0f) { 81 phase -= 1.0f; 82 float t = phase / frequency; 83 // t = 0: the transition occurred right at this sample. 84 // t = 1: the transition occurred at the previous sample. 85 // Use linear interpolation to recover the fractional sample. 86 float new_sample = \ 87 previous_sample + (*in_out - previous_sample) * (1.0f - t); 88 float discontinuity = new_sample - sample; 89 this_sample += discontinuity * stmlib::ThisBlepSample(t); 90 next_sample += discontinuity * stmlib::NextBlepSample(t); 91 sample = new_sample; 92 } 93 next_sample += sample; 94 previous_sample = *in_out; 95 *in_out++ = this_sample; 96 } 97 phase_ = phase; 98 next_sample_ = next_sample; 99 sample_ = sample; 100 previous_sample_ = previous_sample; 101 } 102 103 private: ProcessHalf(float amount,float * in_out,size_t size)104 void ProcessHalf(float amount, float* in_out, size_t size) { 105 // assert(size % 2 == 0); 106 while (size) { 107 in_out[1] += (in_out[0] - in_out[1]) * amount; 108 in_out += 2; 109 size -= 2; 110 } 111 sample_ = next_sample_ = previous_sample_ = in_out[-1]; 112 } 113 ProcessQuarter(float amount,float * in_out,size_t size)114 void ProcessQuarter(float amount, float* in_out, size_t size) { 115 // assert(size % 4 == 0); 116 while (size) { 117 in_out[1] = in_out[0]; 118 in_out[2] += (in_out[0] - in_out[2]) * amount; 119 in_out[3] = in_out[2]; 120 in_out += 4; 121 size -= 4; 122 } 123 sample_ = next_sample_ = previous_sample_ = in_out[-1]; 124 } 125 126 float phase_; 127 float sample_; 128 float previous_sample_; 129 float next_sample_; 130 131 DISALLOW_COPY_AND_ASSIGN(SampleRateReducer); 132 }; 133 134 } // namespace plaits 135 136 #endif // PLAITS_DSP_FX_SAMPLE_RATE_REDUCER_H_ 137