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
5.5KB

  1. // Copyright 2015 Olivier Gillet.
  2. //
  3. // Author: Olivier Gillet (ol.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. // Polyblep oscillator used for string synth synthesis.
  28. #ifndef RINGS_DSP_STRING_SYNTH_OSCILLATOR_H_
  29. #define RINGS_DSP_STRING_SYNTH_OSCILLATOR_H_
  30. #include "stmlib/stmlib.h"
  31. #include "stmlib/dsp/dsp.h"
  32. #include "stmlib/dsp/parameter_interpolator.h"
  33. #include "stmlib/dsp/units.h"
  34. namespace rings {
  35. using namespace stmlib;
  36. enum OscillatorShape {
  37. OSCILLATOR_SHAPE_BRIGHT_SQUARE,
  38. OSCILLATOR_SHAPE_SQUARE,
  39. OSCILLATOR_SHAPE_DARK_SQUARE,
  40. OSCILLATOR_SHAPE_TRIANGLE,
  41. };
  42. class StringSynthOscillator {
  43. public:
  44. StringSynthOscillator() { }
  45. ~StringSynthOscillator() { }
  46. inline void Init() {
  47. phase_ = 0.0f;
  48. phase_increment_ = 0.01f;
  49. filter_state_ = 0.0f;
  50. high_ = false;
  51. next_sample_ = 0.0f;
  52. next_sample_saw_ = 0.0f;
  53. gain_ = 0.0f;
  54. gain_saw_ = 0.0f;
  55. }
  56. template<OscillatorShape shape, bool interpolate_pitch>
  57. inline void Render(
  58. float target_increment,
  59. float target_gain,
  60. float target_gain_saw,
  61. float* out,
  62. size_t size) {
  63. // Cut harmonics above 12kHz, and low-pass harmonics above 8kHz to clear
  64. // highs
  65. if (target_increment >= 0.17f) {
  66. target_gain *= 1.0f - (target_increment - 0.17f) * 12.5f;
  67. if (target_increment >= 0.25f) {
  68. return;
  69. }
  70. }
  71. float phase = phase_;
  72. ParameterInterpolator phase_increment(
  73. &phase_increment_,
  74. target_increment,
  75. size);
  76. ParameterInterpolator gain(&gain_, target_gain, size);
  77. ParameterInterpolator gain_saw(&gain_saw_, target_gain_saw, size);
  78. float next_sample = next_sample_;
  79. float next_sample_saw = next_sample_saw_;
  80. float filter_state = filter_state_;
  81. bool high = high_;
  82. while (size--) {
  83. float this_sample = next_sample;
  84. float this_sample_saw = next_sample_saw;
  85. next_sample = 0.0f;
  86. next_sample_saw = 0.0f;
  87. float increment = interpolate_pitch
  88. ? phase_increment.Next()
  89. : target_increment;
  90. phase += increment;
  91. float sample = 0.0f;
  92. const float pw = 0.5f;
  93. if (!high && phase >= pw) {
  94. float t = (phase - pw) / increment;
  95. this_sample += ThisBlepSample(t);
  96. next_sample += NextBlepSample(t);
  97. high = true;
  98. }
  99. if (phase >= 1.0f) {
  100. phase -= 1.0f;
  101. float t = phase / increment;
  102. float a = ThisBlepSample(t);
  103. float b = NextBlepSample(t);
  104. this_sample -= a;
  105. next_sample -= b;
  106. this_sample_saw -= a;
  107. next_sample_saw -= b;
  108. high = false;
  109. }
  110. next_sample += phase < pw ? 0.0f : 1.0f;
  111. next_sample_saw += phase;
  112. if (shape == OSCILLATOR_SHAPE_TRIANGLE) {
  113. const float integrator_coefficient = increment * 0.125f;
  114. this_sample = 64.0f * (this_sample - 0.5f);
  115. filter_state += integrator_coefficient * (this_sample - filter_state);
  116. sample = filter_state;
  117. } else if (shape == OSCILLATOR_SHAPE_DARK_SQUARE) {
  118. const float integrator_coefficient = increment * 2.0f;
  119. this_sample = 4.0f * (this_sample - 0.5f);
  120. filter_state += integrator_coefficient * (this_sample - filter_state);
  121. sample = filter_state;
  122. } else if (shape == OSCILLATOR_SHAPE_BRIGHT_SQUARE) {
  123. const float integrator_coefficient = increment * 2.0f;
  124. this_sample = 2.0f * this_sample - 1.0f;
  125. filter_state += integrator_coefficient * (this_sample - filter_state);
  126. sample = (this_sample - filter_state) * 0.5f;
  127. } else {
  128. this_sample = 2.0f * this_sample - 1.0f;
  129. sample = this_sample;
  130. }
  131. this_sample_saw = 2.0f * this_sample_saw - 1.0f;
  132. *out++ += sample * gain.Next() + this_sample_saw * gain_saw.Next();
  133. }
  134. high_ = high;
  135. phase_ = phase;
  136. next_sample_ = next_sample;
  137. next_sample_saw_ = next_sample_saw;
  138. filter_state_ = filter_state;
  139. }
  140. private:
  141. static inline float ThisBlepSample(float t) {
  142. return 0.5f * t * t;
  143. }
  144. static inline float NextBlepSample(float t) {
  145. t = 1.0f - t;
  146. return -0.5f * t * t;
  147. }
  148. bool high_;
  149. float phase_;
  150. float phase_increment_;
  151. float next_sample_;
  152. float next_sample_saw_;
  153. float filter_state_;
  154. float gain_;
  155. float gain_saw_;
  156. DISALLOW_COPY_AND_ASSIGN(StringSynthOscillator);
  157. };
  158. } // namespace rings
  159. #endif // RINGS_DSP_STRING_SYNTH_OSCILLATOR_H_