// Copyright 2017 Emilie Gillet. // // Author: Emilie Gillet (emilie.o.gillet@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to enable, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // Convert the phase counters generated by RampGenerator into actual variable // slope waveforms (or variants, like EOA/EOR pulses). #ifndef TIDES_RAMP_SHAPER_H_ #define TIDES_RAMP_SHAPER_H_ #include "stmlib/dsp/dsp.h" #include "stmlib/dsp/parameter_interpolator.h" #include "stmlib/dsp/polyblep.h" #include "tides2/ramp_generator.h" namespace tides { class RampShaper { public: RampShaper() { } ~RampShaper() { } void Init() { next_sample_ = 0.0f; previous_phase_shift_ = 0.0f; going_up_ = true; } inline float BandLimitedPulse(float phase, float frequency, float pw) { CONSTRAIN(pw, frequency * 2.0f, 1.0f - 2.0f * frequency); float this_sample = next_sample_; float next_sample = 0.0f; float wrap_point = pw; if (phase < pw * 0.5f) { wrap_point = 0.0f; } else if (phase > 0.5f + pw * 0.5f) { wrap_point = 1.0f; } if (going_up_ ^ (phase < pw)) { float t = (phase - wrap_point) / frequency; float discontinuity = 1.0f; if (wrap_point != pw) { discontinuity = -discontinuity; } if (frequency < 0.0f) { discontinuity = -discontinuity; } this_sample += stmlib::ThisBlepSample(t) * discontinuity; next_sample += stmlib::NextBlepSample(t) * discontinuity; going_up_ = phase < pw; } next_sample += phase < pw ? 0.0f : 1.0f; next_sample_ = next_sample; return this_sample; } template inline float Slope( float phase, float phase_shift, float frequency, float pw) { if (ramp_mode == RAMP_MODE_AD) { return SkewedRamp(phase, 0.0f, frequency, pw); } else if (ramp_mode == RAMP_MODE_AR) { return phase; } else { if (range == RANGE_CONTROL) { return SkewedRamp(phase, phase_shift, frequency, pw); } else { return BandLimitedSlope(phase, phase_shift, frequency, pw); } } } template inline float EOA(float phase, float frequency, float pw) { if (ramp_mode == RAMP_MODE_LOOPING) { return BandLimitedPulse(phase, frequency, pw); } else if (ramp_mode == RAMP_MODE_AR) { return phase >= 0.5f ? 1.0f : 0.0f; } else { return phase >= pw ? 1.0f : 0.0f; } } template inline float EOR(float phase, float frequency, float pw) { if (ramp_mode == RAMP_MODE_LOOPING) { return 1.0f - BandLimitedPulse( phase, frequency, std::min(0.5f, 96.0f * frequency)); } else { return phase >= 1.0f; } } private: inline float BandLimitedSlope( float phase, float phase_shift, float frequency, float pw) { if (phase_shift) { phase += phase_shift; frequency += phase_shift - previous_phase_shift_; previous_phase_shift_ = phase_shift; if (phase >= 1.0f) { phase -= 1.0f; } else if (phase < 0.0f) { phase += 1.0f; } } CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency)); float this_sample = next_sample_; float next_sample = 0.0f; float wrap_point = pw; if (phase < pw * 0.5f) { wrap_point = 0.0f; } else if (phase > 0.5f + pw * 0.5f) { wrap_point = 1.0f; } const float slope_up = 1.0f / pw; const float slope_down = 1.0f / (1.0f - pw); if (going_up_ ^ (phase < pw)) { float t = (phase - wrap_point) / frequency; float discontinuity = -(slope_up + slope_down) * frequency; if (wrap_point != pw) { discontinuity = -discontinuity; } if (frequency < 0.0f) { discontinuity = -discontinuity; } this_sample += stmlib::ThisIntegratedBlepSample(t) * discontinuity; next_sample += stmlib::NextIntegratedBlepSample(t) * discontinuity; going_up_ = phase < pw; } next_sample += going_up_ ? phase * slope_up : 1.0f - (phase - pw) * slope_down; next_sample_ = next_sample; return this_sample; } inline float SkewedRamp( float phase, float phase_shift, float frequency, float pw) { if (phase_shift) { phase += phase_shift; frequency += phase_shift - previous_phase_shift_; previous_phase_shift_ = phase_shift; if (phase >= 1.0f) { phase -= 1.0f; } else if (phase < 0.0f) { phase += 1.0f; } } CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency)); const float slope_up = 0.5f / pw; const float slope_down = 0.5f / (1.0f - pw); return phase < pw ? phase * slope_up : (phase - pw) * slope_down + 0.5f; } float next_sample_; float previous_phase_shift_; bool going_up_; DISALLOW_COPY_AND_ASSIGN(RampShaper); }; class RampWaveshaper { public: RampWaveshaper() { } ~RampWaveshaper() { } void Init() { previous_input_ = 0.0f; previous_output_ = 0.0f; breakpoint_ = 0.0f; } template inline float Shape( float input, const int16_t* shape, float shape_fractional) { float ws_index = 1024.0f * input; MAKE_INTEGRAL_FRACTIONAL(ws_index) ws_index_integral &= 1023; float x0 = static_cast(shape[ws_index_integral]) / 32768.0f; float x1 = static_cast(shape[ws_index_integral + 1]) / 32768.0f; float y0 = static_cast(shape[ws_index_integral + 1025]) / 32768.0f; float y1 = static_cast(shape[ws_index_integral + 1026]) / 32768.0f; float x = x0 + (x1 - x0) * ws_index_fractional; float y = y0 + (y1 - y0) * ws_index_fractional; float output = x + (y - x) * shape_fractional; if (ramp_mode != RAMP_MODE_AR) { return output; } else { if (previous_input_ <= 0.5f && input > 0.5f) { breakpoint_ = previous_output_; } else if (previous_input_ > 0.5f && input < 0.5f) { breakpoint_ = previous_output_; } else if (input == 1.0f) { breakpoint_ = 1.0f; } else if (input == 0.5f) { breakpoint_ = 0.0f; } if (input <= 0.5f) { output = breakpoint_ + (1.0f - breakpoint_) * output; } else { output = breakpoint_ * output; } previous_input_ = input; previous_output_ = output; return output; } } private: float previous_input_; float previous_output_; float breakpoint_; DISALLOW_COPY_AND_ASSIGN(RampWaveshaper); }; } // namespace tides #endif // TIDES_RAMP_SHAPER_H_