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