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.

212 lines
6.0KB

  1. // Copyright 2014 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. // Single grain synthesis.
  28. #ifndef CLOUDS_DSP_GRAIN_H_
  29. #define CLOUDS_DSP_GRAIN_H_
  30. #include "stmlib/stmlib.h"
  31. #include "stmlib/dsp/dsp.h"
  32. #include "clouds/dsp/audio_buffer.h"
  33. #include "clouds/resources.h"
  34. namespace clouds {
  35. const float slope_response[4] = { 1.3f, 1.0f, 1.0f, 1.0f };
  36. const float bias_response[4] = { 1.0f, 2.0f - 1.0f/500.0f, 1.0f/500.0f, 1.0f };
  37. enum GrainQuality {
  38. GRAIN_QUALITY_LOW,
  39. GRAIN_QUALITY_MEDIUM,
  40. GRAIN_QUALITY_HIGH
  41. };
  42. class Grain {
  43. public:
  44. Grain() { }
  45. ~Grain() { }
  46. inline float InterpolatePlateau(const float* table, float index, float size) {
  47. index *= size;
  48. MAKE_INTEGRAL_FRACTIONAL(index)
  49. float a = table[index_integral];
  50. float b = table[index_integral + 1];
  51. if (index_fractional < 1.0f/1.1f)
  52. return a + (b - a) * index_fractional * 1.1f;
  53. else
  54. return b;
  55. }
  56. void Init() {
  57. active_ = false;
  58. envelope_phase_ = 2.0f;
  59. }
  60. void Start(
  61. int32_t pre_delay,
  62. int32_t buffer_size,
  63. int32_t start,
  64. int32_t width,
  65. bool reverse,
  66. int32_t phase_increment,
  67. float window_shape,
  68. float gain_l,
  69. float gain_r,
  70. GrainQuality recommended_quality) {
  71. pre_delay_ = pre_delay;
  72. reverse_ = reverse;
  73. first_sample_ = (start + buffer_size) % buffer_size;
  74. if (reverse) {
  75. phase_increment_ = -phase_increment;
  76. phase_ = width * phase_increment;
  77. } else {
  78. phase_increment_ = phase_increment;
  79. phase_ = 0;
  80. }
  81. envelope_phase_ = 0.0f;
  82. envelope_phase_increment_ = 2.0f / static_cast<float>(width);
  83. envelope_slope_ = InterpolatePlateau(slope_response, window_shape, 3);
  84. envelope_slope_ *= envelope_slope_ * envelope_slope_;
  85. envelope_slope_ *= envelope_slope_ * envelope_slope_;
  86. envelope_slope_ *= envelope_slope_ * envelope_slope_;
  87. envelope_bias_ = InterpolatePlateau(bias_response, window_shape, 3);
  88. active_ = true;
  89. gain_l_ = gain_l;
  90. gain_r_ = gain_r;
  91. recommended_quality_ = recommended_quality;
  92. }
  93. inline void RenderEnvelope(float* destination, size_t size) {
  94. const float increment = envelope_phase_increment_;
  95. const float slope = envelope_slope_;
  96. const float bias = envelope_bias_;
  97. float phase = envelope_phase_;
  98. while (size--) {
  99. float gain = phase <= bias ?
  100. phase * slope / bias :
  101. (2.0f - phase) * slope / (2.0f - bias);
  102. if (gain > 1.0f) gain = 1.0f;
  103. phase += increment;
  104. if (phase >= 2.0f) {
  105. *destination = -1.0f;
  106. break;
  107. }
  108. *destination++ = gain;
  109. }
  110. envelope_phase_ = phase;
  111. }
  112. template<int32_t num_channels, GrainQuality quality, Resolution resolution>
  113. inline void OverlapAdd(
  114. const AudioBuffer<resolution>* buffer,
  115. float* destination,
  116. float* envelope,
  117. size_t size) {
  118. if (!active_) {
  119. return;
  120. }
  121. // Rendering is done on 32-sample long blocks. The pre-delay allows grains
  122. // to start at arbitrary samples within a block, rather than at block
  123. // boundaries.
  124. while (pre_delay_ && size) {
  125. destination += 2;
  126. --size;
  127. --pre_delay_;
  128. }
  129. // Pre-render the envelope in one pass.
  130. RenderEnvelope(envelope, size);
  131. const int32_t phase_increment = phase_increment_;
  132. const int32_t first_sample = first_sample_;
  133. const float gain_l = gain_l_;
  134. const float gain_r = gain_r_;
  135. int32_t phase = phase_;
  136. while (size--) {
  137. int32_t sample_index = first_sample + (phase >> 16);
  138. float gain = *envelope++;
  139. if (gain == -1.0f) {
  140. active_ = false;
  141. break;
  142. }
  143. float l = buffer[0].template Read<InterpolationMethod(quality)>(
  144. sample_index, phase & 65535) * gain;
  145. if (num_channels == 1) {
  146. *destination++ += l * gain_l;
  147. *destination++ += l * gain_r;
  148. } else if (num_channels == 2) {
  149. float r = buffer[1].template Read<InterpolationMethod(quality)>(
  150. sample_index, phase & 65535) * gain;
  151. *destination++ += l * gain_l + r * (1.0f - gain_r);
  152. *destination++ += r * gain_r + l * (1.0f - gain_l);
  153. }
  154. phase += phase_increment;
  155. }
  156. phase_ = phase;
  157. }
  158. inline bool active() { return active_; }
  159. inline GrainQuality recommended_quality() const {
  160. return recommended_quality_;
  161. }
  162. private:
  163. int32_t first_sample_;
  164. int32_t phase_;
  165. int32_t phase_increment_;
  166. int32_t pre_delay_;
  167. float envelope_slope_;
  168. float envelope_bias_; /* asymetry of envelope: -1..1 */
  169. float envelope_phase_;
  170. float envelope_phase_increment_;
  171. float gain_l_;
  172. float gain_r_;
  173. bool active_;
  174. bool reverse_;
  175. GrainQuality recommended_quality_;
  176. DISALLOW_COPY_AND_ASSIGN(Grain);
  177. };
  178. } // namespace clouds
  179. #endif // CLOUDS_DSP_GRAIN_H_