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.

324 lines
9.2KB

  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. // WSOLA playback.
  28. #ifndef CLOUDS_DSP_WSOLA_SAMPLE_PLAYER_H_
  29. #define CLOUDS_DSP_WSOLA_SAMPLE_PLAYER_H_
  30. #include <algorithm>
  31. #include <cmath>
  32. #include <cstdio>
  33. #include <cstdlib>
  34. #include "stmlib/stmlib.h"
  35. #include "stmlib/dsp/units.h"
  36. #include "clouds/dsp/audio_buffer.h"
  37. #include "clouds/dsp/correlator.h"
  38. #include "clouds/dsp/frame.h"
  39. #include "clouds/dsp/window.h"
  40. #include "clouds/dsp/parameters.h"
  41. #include "clouds/resources.h"
  42. namespace clouds {
  43. const int32_t kMaxWSOLASize = 4096;
  44. using namespace stmlib;
  45. class WSOLASamplePlayer {
  46. public:
  47. WSOLASamplePlayer() { }
  48. ~WSOLASamplePlayer() { }
  49. void Init(
  50. Correlator* correlator,
  51. int32_t num_channels) {
  52. correlator_ = correlator;
  53. num_channels_ = num_channels;
  54. pitch_ = 0.0f;
  55. position_ = 0.0f;
  56. smoothed_pitch_ = 0.0f;
  57. windows_[0].Init();
  58. windows_[1].Init();
  59. next_pitch_ratio_ = 1.0f;
  60. correlator_loaded_ = true;
  61. search_source_ = 0;
  62. search_target_ = 0;
  63. window_size_ = kMaxWSOLASize / 2;
  64. env_phase_ = 0.0f;
  65. env_phase_increment_ = 0.5f;
  66. tap_delay_ = 0;
  67. tap_delay_counter_ = 0;
  68. synchronized_ = false;
  69. }
  70. template<Resolution resolution>
  71. void Play(
  72. const AudioBuffer<resolution>* buffer,
  73. const Parameters& parameters,
  74. float* out,
  75. size_t size) {
  76. int32_t max_delay = buffer->size() - 2 * window_size_;
  77. tap_delay_counter_ += size;
  78. if (tap_delay_counter_ > max_delay) {
  79. tap_delay_ = 0;
  80. tap_delay_counter_ = 0;
  81. synchronized_ = false;
  82. }
  83. if (parameters.trigger && !parameters.freeze) {
  84. if(tap_delay_counter_ > 128) {
  85. synchronized_ = true;
  86. tap_delay_ = tap_delay_counter_;
  87. }
  88. tap_delay_counter_ = 0;
  89. }
  90. env_phase_ += env_phase_increment_;
  91. if (env_phase_ >= 1.0f) {
  92. env_phase_ = 1.0;
  93. }
  94. position_ = parameters.position;
  95. position_ += (1.0f - env_phase_) * (1.0f - position_);
  96. pitch_ = parameters.pitch;
  97. size_factor_ = parameters.size;
  98. if (windows_[0].done() && windows_[1].done()) {
  99. windows_[1].MarkAsRegenerated();
  100. ScheduleAlignedWindow(buffer, &windows_[0]);
  101. }
  102. const float swap_channels = parameters.stereo_spread;
  103. while (size--) {
  104. // Sum the two windows.
  105. std::fill(&out[0], &out[kMaxNumChannels], 0);
  106. for (int32_t i = 0; i < 2; ++i) {
  107. windows_[i].OverlapAdd(buffer, out, num_channels_, swap_channels);
  108. }
  109. // Regenerate expired windows.
  110. for (int32_t i = 0; i < 2; ++i) {
  111. if (windows_[i].needs_regeneration()) {
  112. windows_[i].MarkAsRegenerated();
  113. ScheduleAlignedWindow(buffer, &windows_[1 - i]);
  114. windows_[1 - i].OverlapAdd(buffer, out, num_channels_, swap_channels);
  115. }
  116. }
  117. out += 2;
  118. }
  119. }
  120. template<int32_t num_channels, Resolution resolution>
  121. int32_t ReadSignBits(
  122. const AudioBuffer<resolution>* buffer,
  123. int32_t phase_increment,
  124. int32_t source,
  125. int32_t size,
  126. uint32_t* destination) {
  127. int32_t phase = 0;
  128. uint32_t bits = 0;
  129. uint32_t bit_counter = 0;
  130. int32_t num_samples = 0;
  131. if (source < 0) {
  132. source += buffer->size();
  133. }
  134. while ((phase >> 16) < size) {
  135. int32_t integral = source + (phase >> 16);
  136. uint16_t fractional = phase & 0xffff;
  137. float s = buffer[0].ReadLinear(integral, fractional);
  138. if (num_channels == 2) {
  139. s += buffer[1].ReadLinear(integral, fractional);
  140. }
  141. bits |= s > 0.0f ? 1 : 0;
  142. if ((bit_counter & 0x1f) == 0x1f) {
  143. destination[bit_counter >> 5] = bits;
  144. num_samples += 32;
  145. }
  146. ++bit_counter;
  147. bits <<= 1;
  148. phase += phase_increment;
  149. }
  150. while (bit_counter & 0x1f) {
  151. if ((bit_counter & 0x1f) == 0x1f) {
  152. destination[bit_counter >> 5] = bits;
  153. num_samples += 32;
  154. }
  155. ++bit_counter;
  156. bits <<= 1;
  157. }
  158. return num_samples;
  159. }
  160. template<Resolution resolution>
  161. void LoadCorrelator(const AudioBuffer<resolution>* buffer) {
  162. if (correlator_loaded_) {
  163. return;
  164. }
  165. float stride = window_size_ / 2048.0f;
  166. CONSTRAIN(stride, 1.0f, 2.0f);
  167. stride *= 65536.0f;
  168. int32_t increment = static_cast<int32_t>(
  169. stride * (next_pitch_ratio_ < 1.25f ? 1.25f : next_pitch_ratio_));
  170. int32_t num_samples = 0;
  171. if (num_channels_ == 1) {
  172. num_samples = ReadSignBits<1>(
  173. buffer,
  174. increment,
  175. search_source_,
  176. window_size_,
  177. correlator_->source());
  178. ReadSignBits<1>(
  179. buffer,
  180. increment,
  181. search_target_ - window_size_,
  182. window_size_ * 2,
  183. correlator_->destination());
  184. } else {
  185. num_samples = ReadSignBits<2>(
  186. buffer,
  187. increment,
  188. search_source_,
  189. window_size_,
  190. correlator_->source());
  191. ReadSignBits<2>(
  192. buffer,
  193. increment,
  194. search_target_ - window_size_,
  195. window_size_ * 2,
  196. correlator_->destination());
  197. }
  198. correlator_->StartSearch(
  199. num_samples,
  200. search_target_ - window_size_ + (window_size_ >> 1),
  201. increment);
  202. correlator_loaded_ = true;
  203. }
  204. inline bool synchronized() const { return synchronized_; }
  205. private:
  206. template<Resolution resolution>
  207. void ScheduleAlignedWindow(
  208. const AudioBuffer<resolution>* buffer,
  209. Window* window) {
  210. int32_t next_window_position = correlator_->best_match();
  211. correlator_loaded_ = false;
  212. window->Start(
  213. buffer->size(),
  214. next_window_position - (window_size_ >> 1),
  215. window_size_,
  216. static_cast<uint32_t>(next_pitch_ratio_ * 65536.0f));
  217. float pitch_error = pitch_ - smoothed_pitch_;
  218. float pitch_error_sign = pitch_error < 0.0f ? -1.0 : 1.0;
  219. pitch_error *= pitch_error_sign;
  220. if (pitch_error >= 12.0f) {
  221. pitch_error = 12.0f;
  222. }
  223. smoothed_pitch_ += pitch_error * pitch_error_sign;
  224. float pitch_ratio = SemitonesToRatio(smoothed_pitch_);
  225. float inv_pitch_ratio = SemitonesToRatio(-smoothed_pitch_);
  226. next_pitch_ratio_ = pitch_ratio;
  227. float size_factor = SemitonesToRatio((size_factor_ - 1.0f) * 60.0f);
  228. int32_t new_window_size = static_cast<int32_t>(size_factor * kMaxWSOLASize);
  229. if (abs(new_window_size - window_size_) > 64) {
  230. int32_t error = (new_window_size - window_size_) >> 5;
  231. new_window_size = window_size_ + error;
  232. window_size_ = new_window_size - (new_window_size % 4);
  233. }
  234. // The center offset of the window we want to mix in.
  235. int32_t limit = buffer->size();
  236. limit -= static_cast<int32_t>(2.0f * window_size_ * inv_pitch_ratio);
  237. limit -= 2 * window_size_;
  238. if (limit < 0) {
  239. limit = 0;
  240. }
  241. float position = limit * position_;
  242. if (synchronized_) {
  243. int index = roundf(position_ * static_cast<float>(kMultDivSteps));
  244. CONSTRAIN(index, 0, kMultDivSteps-1);
  245. do position = kMultDivs[index--] * static_cast<float>(tap_delay_);
  246. while (position > limit && index >= 0);
  247. /* to compensate partially for the size of the windows.
  248. * TODO: this is still not completely right... */
  249. position -= window_size_ * 2;
  250. if (position < 0) position = 0;
  251. }
  252. int32_t target_position = buffer->head();
  253. target_position -= static_cast<int32_t>(position);
  254. target_position -= window_size_;
  255. search_source_ = next_window_position;
  256. search_target_ = target_position;
  257. }
  258. Correlator* correlator_;
  259. Window windows_[2];
  260. int32_t window_size_;
  261. int32_t num_channels_;
  262. float pitch_;
  263. float smoothed_pitch_;
  264. float position_;
  265. float size_factor_;
  266. float next_pitch_ratio_;
  267. bool correlator_loaded_;
  268. int32_t search_source_;
  269. int32_t search_target_;
  270. float env_phase_;
  271. float env_phase_increment_;
  272. int32_t tap_delay_;
  273. int32_t tap_delay_counter_;
  274. bool synchronized_;
  275. DISALLOW_COPY_AND_ASSIGN(WSOLASamplePlayer);
  276. };
  277. } // namespace clouds
  278. #endif // CLOUDS_DSP_WSOLA_SAMPLE_PLAYER_H_