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.

237 lines
7.2KB

  1. // Mutable Instruments Streams emulation for VCV Rack
  2. // Copyright (C) 2020 Tyler Coy
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. #pragma once
  17. #include <cmath>
  18. #include <rack.hpp>
  19. #include "cv_scaler.hpp"
  20. #include "ui.hpp"
  21. #include <streams/processor.h>
  22. namespace streams
  23. {
  24. using namespace rack;
  25. class DigitalEngine
  26. {
  27. public:
  28. static constexpr int kSampleRate = 31089;
  29. template <int block_size>
  30. struct ChannelFrame
  31. {
  32. // Parameters
  33. float shape_knob;
  34. float mod_knob;
  35. float level_mod_knob;
  36. float response_knob;
  37. bool function_button;
  38. // Inputs
  39. float excite_in[block_size];
  40. float signal_in[block_size];
  41. float level_adc_in[block_size];
  42. // Outputs
  43. float dac_out[block_size];
  44. float pwm_out[block_size];
  45. // Lights
  46. float led_green[4];
  47. float led_red[4];
  48. };
  49. template <int block_size>
  50. struct Frame
  51. {
  52. ChannelFrame<block_size> ch1;
  53. ChannelFrame<block_size> ch2;
  54. bool metering_button;
  55. };
  56. DigitalEngine()
  57. {
  58. Reset();
  59. }
  60. void Reset(void)
  61. {
  62. adc_.Init();
  63. cv_scaler_.Init(&adc_);
  64. processor_[0].Init(0);
  65. processor_[1].Init(1);
  66. ui_.Init(&adc_, &cv_scaler_, &processor_[0]);
  67. pwm_value_[0] = 0;
  68. pwm_value_[1] = 0;
  69. for (int i = 0; i < 4; i++)
  70. {
  71. led_lpf_[i].reset();
  72. led_lpf_[i].setLambda(kLambdaLEDs);
  73. }
  74. }
  75. const UiSettings& ui_settings(void)
  76. {
  77. return ui_.settings();
  78. }
  79. void ApplySettings(const UiSettings& settings)
  80. {
  81. ui_.ApplySettings(settings);
  82. }
  83. void SyncUI(const DigitalEngine& other)
  84. {
  85. ui_.SyncUI(other.ui_);
  86. for (int i = 0; i < 4; i++)
  87. {
  88. led_lpf_[i].reset();
  89. }
  90. }
  91. void Randomize(void)
  92. {
  93. UiSettings settings;
  94. settings.alternate[0] = random::u32() & 1;
  95. settings.alternate[1] = random::u32() & 1;
  96. int modulus0 = (settings.alternate[0]) ?
  97. 1 + PROCESSOR_FUNCTION_FILTER_CONTROLLER :
  98. 1 + PROCESSOR_FUNCTION_COMPRESSOR;
  99. int modulus1 = (settings.alternate[1]) ?
  100. 1 + PROCESSOR_FUNCTION_FILTER_CONTROLLER :
  101. 1 + PROCESSOR_FUNCTION_COMPRESSOR;
  102. settings.function[0] = random::u32() % modulus0;
  103. settings.function[1] = random::u32() % modulus1;
  104. settings.monitor_mode = ui_.settings().monitor_mode;
  105. settings.linked = false;
  106. ApplySettings(settings);
  107. }
  108. template <int block_size>
  109. void Process(Frame<block_size>& frame)
  110. {
  111. ProcessUI(frame);
  112. for (int i = 0; i < block_size; i++)
  113. {
  114. float ch1_signal_adc = clamp(frame.ch1.signal_in[i], 0.f, kVdda);
  115. float ch2_signal_adc = clamp(frame.ch2.signal_in[i], 0.f, kVdda);
  116. float ch1_excite_adc = clamp(frame.ch1.excite_in[i], 0.f, kVdda);
  117. float ch2_excite_adc = clamp(frame.ch2.excite_in[i], 0.f, kVdda);
  118. float ch1_level_adc = clamp(frame.ch1.level_adc_in[i], 0.f, kVdda);
  119. float ch2_level_adc = clamp(frame.ch2.level_adc_in[i], 0.f, kVdda);
  120. adc_.cvs_[0] = std::round(0xFFFF * ch1_excite_adc / kVdda);
  121. adc_.cvs_[1] = std::round(0xFFFF * ch1_signal_adc / kVdda);
  122. adc_.cvs_[2] = std::round(0xFFFF * ch1_level_adc / kVdda);
  123. adc_.cvs_[3] = std::round(0xFFFF * ch2_excite_adc / kVdda);
  124. adc_.cvs_[4] = std::round(0xFFFF * ch2_signal_adc / kVdda);
  125. adc_.cvs_[5] = std::round(0xFFFF * ch2_level_adc / kVdda);
  126. uint16_t gain[2] = {0, 0};
  127. uint16_t frequency[2] = {kPWMPeriod, kPWMPeriod};
  128. for (int i = 0; i < 2; i++)
  129. {
  130. processor_[i].Process(
  131. cv_scaler_.audio_sample(i),
  132. cv_scaler_.excite_sample(i),
  133. &gain[i],
  134. &frequency[i]);
  135. // Filter PWM value
  136. frequency[i] = kPWMPeriod - frequency[i];
  137. pwm_value_[i] += (frequency[i] - pwm_value_[i]) >> 4;
  138. }
  139. frame.ch1.dac_out[i] =
  140. kVoltsPerLSB * cv_scaler_.ScaleGain(0, gain[0]);
  141. frame.ch2.dac_out[i] =
  142. kVoltsPerLSB * cv_scaler_.ScaleGain(1, gain[1]);
  143. frame.ch1.pwm_out[i] = (kVdda / kPWMPeriod) * pwm_value_[0];
  144. frame.ch2.pwm_out[i] = (kVdda / kPWMPeriod) * pwm_value_[1];
  145. }
  146. }
  147. protected:
  148. using float_4 = simd::float_4;
  149. AdcEmulator adc_;
  150. CvScaler cv_scaler_;
  151. Processor processor_[2];
  152. Ui ui_;
  153. uint16_t pwm_value_[2];
  154. dsp::TExponentialFilter<float_4> led_lpf_[4];
  155. template <int block_size>
  156. void ProcessUI(Frame<block_size>& frame)
  157. {
  158. float timestep = block_size * 1.f / kSampleRate;
  159. adc_.pots_[0] = std::round(0xFFFF * frame.ch1.shape_knob);
  160. adc_.pots_[1] = std::round(0xFFFF * frame.ch1.mod_knob);
  161. adc_.pots_[2] = std::round(0xFFFF * frame.ch2.shape_knob);
  162. adc_.pots_[3] = std::round(0xFFFF * frame.ch2.mod_knob);
  163. ui_.switches().SetPin(SWITCH_MODE_1, frame.ch1.function_button);
  164. ui_.switches().SetPin(SWITCH_MODE_2, frame.ch2.function_button);
  165. ui_.switches().SetPin(SWITCH_MONITOR, frame.metering_button);
  166. ui_.Poll(timestep * 1e6);
  167. ui_.DoEvents();
  168. for (int i = 0; i < 4; i++)
  169. {
  170. auto led = float_4(
  171. ui_.leds().intensity_green(i),
  172. ui_.leds().intensity_red(i),
  173. ui_.leds().intensity_green(i + 4),
  174. ui_.leds().intensity_red(i + 4));
  175. led = led_lpf_[i].process(timestep, led);
  176. frame.ch1.led_green[i] = led[0];
  177. frame.ch1.led_red[i] = led[1];
  178. frame.ch2.led_green[i] = led[2];
  179. frame.ch2.led_red[i] = led[3];
  180. }
  181. }
  182. static constexpr int kUiPollRate = 4000;
  183. static constexpr float kVdda = 3.3f;
  184. static constexpr int kPWMPeriod = 65535;
  185. static constexpr float kDacVref = 2.5f;
  186. static constexpr float kVoltsPerLSB = kDacVref / 65536.f;
  187. // The VU meter flickers when monitoring LEVEL or OUT when there is an
  188. // audio signal at the LEVEL input. Due to human persistence of vision,
  189. // the only noticable effect on the hardware module is a slight dimming of
  190. // the LEDs. However, the flickering is very apparent on the software module
  191. // due to the low UI refresh rate. We solve this by applying a lowpass
  192. // filter to the LED brightness. This lambda value is simply hand-tuned
  193. // to match hardware.
  194. static constexpr float kLambdaLEDs = 1.5e-3 * kSampleRate;
  195. };
  196. }