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.

578 lines
18KB

  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. // Group of voices.
  28. #include "rings/dsp/part.h"
  29. #include "stmlib/dsp/units.h"
  30. #include "rings/resources.h"
  31. namespace rings {
  32. using namespace std;
  33. using namespace stmlib;
  34. void Part::Init(uint16_t* reverb_buffer) {
  35. active_voice_ = 0;
  36. fill(&note_[0], &note_[kMaxPolyphony], 0.0f);
  37. bypass_ = false;
  38. polyphony_ = 1;
  39. model_ = RESONATOR_MODEL_MODAL;
  40. dirty_ = true;
  41. for (int32_t i = 0; i < kMaxPolyphony; ++i) {
  42. excitation_filter_[i].Init();
  43. plucker_[i].Init();
  44. dc_blocker_[i].Init(1.0f - 10.0f / kSampleRate);
  45. }
  46. reverb_.Init(reverb_buffer);
  47. limiter_.Init();
  48. note_filter_.Init(
  49. kSampleRate / kMaxBlockSize,
  50. 0.001f, // Lag time with a sharp edge on the V/Oct input or trigger.
  51. 0.010f, // Lag time after the trigger has been received.
  52. 0.050f, // Time to transition from reactive to filtered.
  53. 0.004f); // Prevent a sharp edge to partly leak on the previous voice.
  54. }
  55. void Part::ConfigureResonators() {
  56. if (!dirty_) {
  57. return;
  58. }
  59. switch (model_) {
  60. case RESONATOR_MODEL_MODAL:
  61. {
  62. int32_t resolution = 64 / polyphony_ - 4;
  63. for (int32_t i = 0; i < polyphony_; ++i) {
  64. resonator_[i].Init();
  65. resonator_[i].set_resolution(resolution);
  66. }
  67. }
  68. break;
  69. case RESONATOR_MODEL_SYMPATHETIC_STRING:
  70. case RESONATOR_MODEL_STRING:
  71. case RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED:
  72. case RESONATOR_MODEL_STRING_AND_REVERB:
  73. {
  74. float lfo_frequencies[kNumStrings] = {
  75. 0.5f, 0.4f, 0.35f, 0.23f, 0.211f, 0.2f, 0.171f
  76. };
  77. for (int32_t i = 0; i < kNumStrings; ++i) {
  78. bool has_dispersion = model_ == RESONATOR_MODEL_STRING || \
  79. model_ == RESONATOR_MODEL_STRING_AND_REVERB;
  80. string_[i].Init(has_dispersion);
  81. float f_lfo = float(kMaxBlockSize) / float(kSampleRate);
  82. f_lfo *= lfo_frequencies[i];
  83. lfo_[i].Init<COSINE_OSCILLATOR_APPROXIMATE>(f_lfo);
  84. }
  85. for (int32_t i = 0; i < polyphony_; ++i) {
  86. plucker_[i].Init();
  87. }
  88. }
  89. break;
  90. case RESONATOR_MODEL_FM_VOICE:
  91. {
  92. for (int32_t i = 0; i < polyphony_; ++i) {
  93. fm_voice_[i].Init();
  94. }
  95. }
  96. break;
  97. default:
  98. break;
  99. }
  100. if (active_voice_ >= polyphony_) {
  101. active_voice_ = 0;
  102. }
  103. dirty_ = false;
  104. }
  105. #ifdef BRYAN_CHORDS
  106. // Chord table by Bryan Noll:
  107. float chords[kMaxPolyphony][11][8] = {
  108. {
  109. { -12.0f, -0.01f, 0.0f, 0.01f, 0.02f, 11.98f, 11.99f, 12.0f }, // OCT
  110. { -12.0f, -5.0f, 0.0f, 6.99f, 7.0f, 11.99f, 12.0f, 19.0f }, // 5
  111. { -12.0f, -5.0f, 0.0f, 5.0f, 7.0f, 11.99f, 12.0f, 17.0f }, // sus4
  112. { -12.0f, -5.0f, 0.0f, 3.0f, 7.0f, 3.01f, 12.0f, 19.0f }, // m
  113. { -12.0f, -5.0f, 0.0f, 3.0f, 7.0f, 3.01f, 10.0f, 19.0f }, // m7
  114. { -12.0f, -5.0f, 0.0f, 3.0f, 14.0f, 3.01f, 10.0f, 19.0f }, // m9
  115. { -12.0f, -5.0f, 0.0f, 3.0f, 7.0f, 3.01f, 10.0f, 17.0f }, // m11
  116. { -12.0f, -5.0f, 0.0f, 2.0f, 7.0f, 9.0f, 16.0f, 19.0f }, // 69
  117. { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f, 14.0f, 19.0f }, // M9
  118. { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f, 10.99f, 19.0f }, // M7
  119. { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.99f, 12.0f, 19.0f } // M
  120. },
  121. {
  122. { -12.0f, 0.0f, 0.01f, 12.0f }, // OCT
  123. { -12.0f, 6.99f, 7.0f, 12.0f }, // 5
  124. { -12.0f, 5.0f, 7.0f, 12.0f }, // sus4
  125. { -12.0f, 3.0f, 11.99f, 12.0f }, // m
  126. { -12.0f, 3.0f, 10.0f, 12.0f }, // m7
  127. { -12.0f, 3.0f, 10.0f, 14.0f }, // m9
  128. { -12.0f, 3.0f, 10.0f, 17.0f }, // m11
  129. { -12.0f, 2.0f, 9.0f, 16.0f }, // 69
  130. { -12.0f, 4.0f, 11.0f, 14.0f }, // M9
  131. { -12.0f, 4.0f, 7.0f, 11.0f }, // M7
  132. { -12.0f, 4.0f, 7.0f, 12.0f }, // M
  133. },
  134. {
  135. { 0.0f, -12.0f },
  136. { 0.0f, 2.0f },
  137. { 0.0f, 3.0f },
  138. { 0.0f, 4.0f },
  139. { 0.0f, 5.0f },
  140. { 0.0f, 7.0f },
  141. { 0.0f, 9.0f },
  142. { 0.0f, 10.0f },
  143. { 0.0f, 11.0f },
  144. { 0.0f, 12.0f },
  145. { -12.0f, 12.0f }
  146. },
  147. {
  148. { 0.0f, -12.0f },
  149. { 0.0f, 2.0f },
  150. { 0.0f, 3.0f },
  151. { 0.0f, 4.0f },
  152. { 0.0f, 5.0f },
  153. { 0.0f, 7.0f },
  154. { 0.0f, 9.0f },
  155. { 0.0f, 10.0f },
  156. { 0.0f, 11.0f },
  157. { 0.0f, 12.0f },
  158. { -12.0f, 12.0f }
  159. }
  160. };
  161. #else
  162. // Original chord table
  163. float chords[kMaxPolyphony][11][8] = {
  164. {
  165. { -12.0f, 0.0f, 0.01f, 0.02f, 0.03f, 11.98f, 11.99f, 12.0f },
  166. { -12.0f, 0.0f, 3.0f, 3.01f, 7.0f, 9.99f, 10.0f, 19.0f },
  167. { -12.0f, 0.0f, 3.0f, 3.01f, 7.0f, 11.99f, 12.0f, 19.0f },
  168. { -12.0f, 0.0f, 3.0f, 3.01f, 7.0f, 13.99f, 14.0f, 19.0f },
  169. { -12.0f, 0.0f, 3.0f, 3.01f, 7.0f, 16.99f, 17.0f, 19.0f },
  170. { -12.0f, 0.0f, 6.98f, 6.99f, 7.0f, 12.00f, 18.99f, 19.0f },
  171. { -12.0f, 0.0f, 3.99f, 4.0f, 7.0f, 16.99f, 17.0f, 19.0f },
  172. { -12.0f, 0.0f, 3.99f, 4.0f, 7.0f, 13.99f, 14.0f, 19.0f },
  173. { -12.0f, 0.0f, 3.99f, 4.0f, 7.0f, 11.99f, 12.0f, 19.0f },
  174. { -12.0f, 0.0f, 3.99f, 4.0f, 7.0f, 10.99f, 11.0f, 19.0f },
  175. { -12.0f, 0.0f, 4.99f, 5.0f, 7.0f, 11.99f, 12.0f, 17.0f }
  176. },
  177. {
  178. { -12.0f, 0.0f, 0.01f, 12.0f },
  179. { -12.0f, 3.0f, 7.0f, 10.0f },
  180. { -12.0f, 3.0f, 7.0f, 12.0f },
  181. { -12.0f, 3.0f, 7.0f, 14.0f },
  182. { -12.0f, 3.0f, 7.0f, 17.0f },
  183. { -12.0f, 7.0f, 12.0f, 19.0f },
  184. { -12.0f, 4.0f, 7.0f, 17.0f },
  185. { -12.0f, 4.0f, 7.0f, 14.0f },
  186. { -12.0f, 4.0f, 7.0f, 12.0f },
  187. { -12.0f, 4.0f, 7.0f, 11.0f },
  188. { -12.0f, 5.0f, 7.0f, 12.0f },
  189. },
  190. {
  191. { 0.0f, -12.0f },
  192. { 0.0f, 0.01f },
  193. { 0.0f, 2.0f },
  194. { 0.0f, 3.0f },
  195. { 0.0f, 4.0f },
  196. { 0.0f, 5.0f },
  197. { 0.0f, 7.0f },
  198. { 0.0f, 10.0f },
  199. { 0.0f, 11.0f },
  200. { 0.0f, 12.0f },
  201. { -12.0f, 12.0f }
  202. },
  203. {
  204. { 0.0f, -12.0f },
  205. { 0.0f, 0.01f },
  206. { 0.0f, 2.0f },
  207. { 0.0f, 3.0f },
  208. { 0.0f, 4.0f },
  209. { 0.0f, 5.0f },
  210. { 0.0f, 7.0f },
  211. { 0.0f, 10.0f },
  212. { 0.0f, 11.0f },
  213. { 0.0f, 12.0f },
  214. { -12.0f, 12.0f }
  215. }
  216. };
  217. #endif // BRYAN_CHORDS
  218. void Part::ComputeSympatheticStringsNotes(
  219. float tonic,
  220. float note,
  221. float parameter,
  222. float* destination,
  223. size_t num_strings) {
  224. float notes[9] = {
  225. tonic,
  226. note - 12.0f,
  227. note - 7.01955f,
  228. note,
  229. note + 7.01955f,
  230. note + 12.0f,
  231. note + 19.01955f,
  232. note + 24.0f,
  233. note + 24.0f };
  234. const float detunings[4] = {
  235. 0.013f,
  236. 0.011f,
  237. 0.007f,
  238. 0.017f
  239. };
  240. if (parameter >= 2.0f) {
  241. // Quantized chords
  242. int32_t chord_index = parameter - 2.0f;
  243. const float* chord = chords[polyphony_ - 1][chord_index];
  244. for (size_t i = 0; i < num_strings; ++i) {
  245. destination[i] = chord[i] + note;
  246. }
  247. return;
  248. }
  249. size_t num_detuned_strings = (num_strings - 1) >> 1;
  250. size_t first_detuned_string = num_strings - num_detuned_strings;
  251. for (size_t i = 0; i < first_detuned_string; ++i) {
  252. float note = 3.0f;
  253. if (i != 0) {
  254. note = parameter * 7.0f;
  255. parameter += (1.0f - parameter) * 0.2f;
  256. }
  257. MAKE_INTEGRAL_FRACTIONAL(note);
  258. note_fractional = Squash(note_fractional);
  259. float a = notes[note_integral];
  260. float b = notes[note_integral + 1];
  261. note = a + (b - a) * note_fractional;
  262. destination[i] = note;
  263. if (i + first_detuned_string < num_strings) {
  264. destination[i + first_detuned_string] = destination[i] + detunings[i & 3];
  265. }
  266. }
  267. }
  268. void Part::RenderModalVoice(
  269. int32_t voice,
  270. const PerformanceState& performance_state,
  271. const Patch& patch,
  272. float frequency,
  273. float filter_cutoff,
  274. size_t size) {
  275. // Internal exciter is a pulse, pre-filter.
  276. if (performance_state.internal_exciter &&
  277. voice == active_voice_ &&
  278. performance_state.strum) {
  279. resonator_input_[0] += 0.25f * SemitonesToRatio(
  280. filter_cutoff * filter_cutoff * 24.0f) / filter_cutoff;
  281. }
  282. // Process through filter.
  283. excitation_filter_[voice].Process<FILTER_MODE_LOW_PASS>(
  284. resonator_input_, resonator_input_, size);
  285. Resonator& r = resonator_[voice];
  286. r.set_frequency(frequency);
  287. r.set_structure(patch.structure);
  288. r.set_brightness(patch.brightness * patch.brightness);
  289. r.set_position(patch.position);
  290. r.set_damping(patch.damping);
  291. r.Process(resonator_input_, out_buffer_, aux_buffer_, size);
  292. }
  293. void Part::RenderFMVoice(
  294. int32_t voice,
  295. const PerformanceState& performance_state,
  296. const Patch& patch,
  297. float frequency,
  298. float filter_cutoff,
  299. size_t size) {
  300. FMVoice& v = fm_voice_[voice];
  301. if (performance_state.internal_exciter &&
  302. voice == active_voice_ &&
  303. performance_state.strum) {
  304. v.TriggerInternalEnvelope();
  305. }
  306. v.set_frequency(frequency);
  307. v.set_ratio(patch.structure);
  308. v.set_brightness(patch.brightness);
  309. v.set_feedback_amount(patch.position);
  310. v.set_position(/*patch.position*/ 0.0f);
  311. v.set_damping(patch.damping);
  312. v.Process(resonator_input_, out_buffer_, aux_buffer_, size);
  313. }
  314. void Part::RenderStringVoice(
  315. int32_t voice,
  316. const PerformanceState& performance_state,
  317. const Patch& patch,
  318. float frequency,
  319. float filter_cutoff,
  320. size_t size) {
  321. // Compute number of strings and frequency.
  322. int32_t num_strings = 1;
  323. float frequencies[kNumStrings];
  324. if (model_ == RESONATOR_MODEL_SYMPATHETIC_STRING ||
  325. model_ == RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED) {
  326. num_strings = 2 * kMaxPolyphony / polyphony_;
  327. float parameter = model_ == RESONATOR_MODEL_SYMPATHETIC_STRING
  328. ? patch.structure
  329. : 2.0f + performance_state.chord;
  330. ComputeSympatheticStringsNotes(
  331. performance_state.tonic + performance_state.fm,
  332. performance_state.tonic + note_[voice] + performance_state.fm,
  333. parameter,
  334. frequencies,
  335. num_strings);
  336. for (int32_t i = 0; i < num_strings; ++i) {
  337. frequencies[i] = SemitonesToRatio(frequencies[i] - 69.0f) * a3;
  338. }
  339. } else {
  340. frequencies[0] = frequency;
  341. }
  342. if (voice == active_voice_) {
  343. const float gain = 1.0f / Sqrt(static_cast<float>(num_strings) * 2.0f);
  344. for (size_t i = 0; i < size; ++i) {
  345. resonator_input_[i] *= gain;
  346. }
  347. }
  348. // Process external input.
  349. excitation_filter_[voice].Process<FILTER_MODE_LOW_PASS>(
  350. resonator_input_, resonator_input_, size);
  351. // Add noise burst.
  352. if (performance_state.internal_exciter) {
  353. if (voice == active_voice_ && performance_state.strum) {
  354. plucker_[voice].Trigger(frequency, filter_cutoff * 8.0f, patch.position);
  355. }
  356. plucker_[voice].Process(noise_burst_buffer_, size);
  357. for (size_t i = 0; i < size; ++i) {
  358. resonator_input_[i] += noise_burst_buffer_[i];
  359. }
  360. }
  361. dc_blocker_[voice].Process(resonator_input_, size);
  362. fill(&out_buffer_[0], &out_buffer_[size], 0.0f);
  363. fill(&aux_buffer_[0], &aux_buffer_[size], 0.0f);
  364. float structure = patch.structure;
  365. float dispersion = structure < 0.24f
  366. ? (structure - 0.24f) * 4.166f
  367. : (structure > 0.26f ? (structure - 0.26f) * 1.35135f : 0.0f);
  368. for (int32_t string = 0; string < num_strings; ++string) {
  369. int32_t i = voice + string * polyphony_;
  370. String& s = string_[i];
  371. float lfo_value = lfo_[i].Next();
  372. float brightness = patch.brightness;
  373. float damping = patch.damping;
  374. float position = patch.position;
  375. float glide = 1.0f;
  376. float string_index = static_cast<float>(string) / static_cast<float>(num_strings);
  377. const float* input = resonator_input_;
  378. if (model_ == RESONATOR_MODEL_STRING_AND_REVERB) {
  379. damping *= (2.0f - damping);
  380. }
  381. // When the internal exciter is used, string 0 is the main
  382. // source, the other strings are vibrating by sympathetic resonance.
  383. // When the internal exciter is not used, all strings are vibrating
  384. // by sympathetic resonance.
  385. if (string > 0 && performance_state.internal_exciter) {
  386. brightness *= (2.0f - brightness);
  387. brightness *= (2.0f - brightness);
  388. damping = 0.7f + patch.damping * 0.27f;
  389. float amount = (0.5f - fabs(0.5f - patch.position)) * 0.9f;
  390. position = patch.position + lfo_value * amount;
  391. glide = SemitonesToRatio((brightness - 1.0f) * 36.0f);
  392. input = sympathetic_resonator_input_;
  393. }
  394. s.set_dispersion(dispersion);
  395. s.set_frequency(frequencies[string], glide);
  396. s.set_brightness(brightness);
  397. s.set_position(position);
  398. s.set_damping(damping + string_index * (0.95f - damping));
  399. s.Process(input, out_buffer_, aux_buffer_, size);
  400. if (string == 0) {
  401. // Was 0.1f, Ben Wilson -> 0.2f
  402. float gain = 0.2f / static_cast<float>(num_strings);
  403. for (size_t i = 0; i < size; ++i) {
  404. float sum = out_buffer_[i] - aux_buffer_[i];
  405. sympathetic_resonator_input_[i] = gain * sum;
  406. }
  407. }
  408. }
  409. }
  410. const int32_t kPingPattern[] = {
  411. 1, 0, 2, 1, 0, 2, 1, 0
  412. };
  413. void Part::Process(
  414. const PerformanceState& performance_state,
  415. const Patch& patch,
  416. const float* in,
  417. float* out,
  418. float* aux,
  419. size_t size) {
  420. // Copy inputs to outputs when bypass mode is enabled.
  421. if (bypass_) {
  422. copy(&in[0], &in[size], &out[0]);
  423. copy(&in[0], &in[size], &aux[0]);
  424. return;
  425. }
  426. ConfigureResonators();
  427. note_filter_.Process(
  428. performance_state.note,
  429. performance_state.strum);
  430. if (performance_state.strum) {
  431. note_[active_voice_] = note_filter_.stable_note();
  432. if (polyphony_ > 1 && polyphony_ & 1) {
  433. active_voice_ = kPingPattern[step_counter_ % 8];
  434. step_counter_ = (step_counter_ + 1) % 8;
  435. } else {
  436. active_voice_ = (active_voice_ + 1) % polyphony_;
  437. }
  438. }
  439. note_[active_voice_] = note_filter_.note();
  440. fill(&out[0], &out[size], 0.0f);
  441. fill(&aux[0], &aux[size], 0.0f);
  442. for (int32_t voice = 0; voice < polyphony_; ++voice) {
  443. // Compute MIDI note value, frequency, and cutoff frequency for excitation
  444. // filter.
  445. float cutoff = patch.brightness * (2.0f - patch.brightness);
  446. float note = note_[voice] + performance_state.tonic + performance_state.fm;
  447. float frequency = SemitonesToRatio(note - 69.0f) * a3;
  448. float filter_cutoff_range = performance_state.internal_exciter
  449. ? frequency * SemitonesToRatio((cutoff - 0.5f) * 96.0f)
  450. : 0.4f * SemitonesToRatio((cutoff - 1.0f) * 108.0f);
  451. float filter_cutoff = min(voice == active_voice_
  452. ? filter_cutoff_range
  453. : (10.0f / kSampleRate), 0.499f);
  454. float filter_q = performance_state.internal_exciter ? 1.5f : 0.8f;
  455. // Process input with excitation filter. Inactive voices receive silence.
  456. excitation_filter_[voice].set_f_q<FREQUENCY_DIRTY>(filter_cutoff, filter_q);
  457. if (voice == active_voice_) {
  458. copy(&in[0], &in[size], &resonator_input_[0]);
  459. } else {
  460. fill(&resonator_input_[0], &resonator_input_[size], 0.0f);
  461. }
  462. if (model_ == RESONATOR_MODEL_MODAL) {
  463. RenderModalVoice(
  464. voice, performance_state, patch, frequency, filter_cutoff, size);
  465. } else if (model_ == RESONATOR_MODEL_FM_VOICE) {
  466. RenderFMVoice(
  467. voice, performance_state, patch, frequency, filter_cutoff, size);
  468. } else {
  469. RenderStringVoice(
  470. voice, performance_state, patch, frequency, filter_cutoff, size);
  471. }
  472. if (polyphony_ == 1) {
  473. // Send the two sets of harmonics / pickups to individual outputs.
  474. for (size_t i = 0; i < size; ++i) {
  475. out[i] += out_buffer_[i];
  476. aux[i] += aux_buffer_[i];
  477. }
  478. } else {
  479. // Dispatch odd/even voices to individual outputs.
  480. float* destination = voice & 1 ? aux : out;
  481. for (size_t i = 0; i < size; ++i) {
  482. destination[i] += out_buffer_[i] - aux_buffer_[i];
  483. }
  484. }
  485. }
  486. if (model_ == RESONATOR_MODEL_STRING_AND_REVERB) {
  487. for (size_t i = 0; i < size; ++i) {
  488. float l = out[i];
  489. float r = aux[i];
  490. out[i] = l * patch.position + (1.0f - patch.position) * r;
  491. aux[i] = r * patch.position + (1.0f - patch.position) * l;
  492. }
  493. reverb_.set_amount(0.1f + patch.damping * 0.5f);
  494. reverb_.set_diffusion(0.625f);
  495. reverb_.set_time(0.35f + 0.63f * patch.damping);
  496. reverb_.set_input_gain(0.2f);
  497. reverb_.set_lp(0.3f + patch.brightness * 0.6f);
  498. reverb_.Process(out, aux, size);
  499. for (size_t i = 0; i < size; ++i) {
  500. aux[i] = -aux[i];
  501. }
  502. }
  503. // Apply limiter to string output.
  504. limiter_.Process(out, aux, size, model_gains_[model_]);
  505. }
  506. /* static */
  507. float Part::model_gains_[] = {
  508. 1.4f, // RESONATOR_MODEL_MODAL
  509. 1.0f, // RESONATOR_MODEL_SYMPATHETIC_STRING
  510. 1.4f, // RESONATOR_MODEL_STRING
  511. 0.7f, // RESONATOR_MODEL_FM_VOICE,
  512. 1.0f, // RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED
  513. 1.4f, // RESONATOR_MODEL_STRING_AND_REVERB
  514. };
  515. } // namespace rings