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.

292 lines
7.8KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include <math.h>
  5. #include <vector>
  6. #include <algorithm>
  7. #include <chrono>
  8. #include <thread>
  9. #include <xmmintrin.h>
  10. #include <pmmintrin.h>
  11. #include "engine.hpp"
  12. namespace rack {
  13. bool gPaused = false;
  14. std::vector<Module*> gModules;
  15. std::vector<Wire*> gWires;
  16. bool gCpuMeters = false;
  17. static bool running = false;
  18. static float sampleRate = 44100.f;
  19. static float sampleTime;
  20. static std::mutex mutex;
  21. static std::thread thread;
  22. static VIPMutex vipMutex;
  23. // Parameter interpolation
  24. static Module *smoothModule = NULL;
  25. static int smoothParamId;
  26. static float smoothValue;
  27. float Light::getBrightness() {
  28. // LEDs are diodes, so don't allow reverse current.
  29. // For some reason, instead of the RMS, the sqrt of RMS looks better
  30. return powf(fmaxf(0.f, value), 0.25f);
  31. }
  32. void Light::setBrightnessSmooth(float brightness, float frames) {
  33. float v = (brightness > 0.f) ? brightness * brightness : 0.f;
  34. if (v < value) {
  35. // Fade out light with lambda = framerate
  36. value += (v - value) * sampleTime * frames * 60.f;
  37. }
  38. else {
  39. // Immediately illuminate light
  40. value = v;
  41. }
  42. }
  43. void Wire::step() {
  44. float value = outputModule->outputs[outputId].value;
  45. // Assume a +/-12V power supply (like Eurorack), and prevent voltages outside the power range.
  46. value = clamp(value, -12.f, 12.f);
  47. inputModule->inputs[inputId].value = value;
  48. }
  49. void engineInit() {
  50. engineSetSampleRate(44100.0);
  51. }
  52. void engineDestroy() {
  53. // Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself before the WINDOW was destroyed.
  54. assert(gWires.empty());
  55. assert(gModules.empty());
  56. }
  57. static void engineStep() {
  58. // Param interpolation
  59. if (smoothModule) {
  60. float value = smoothModule->params[smoothParamId].value;
  61. const float lambda = 60.0; // decay rate is 1 graphics frame
  62. float delta = smoothValue - value;
  63. float newValue = value + delta * lambda * sampleTime;
  64. if (value == newValue) {
  65. // Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats)
  66. smoothModule->params[smoothParamId].value = smoothValue;
  67. smoothModule = NULL;
  68. }
  69. else {
  70. smoothModule->params[smoothParamId].value = newValue;
  71. }
  72. }
  73. // Step modules
  74. for (Module *module : gModules) {
  75. std::chrono::high_resolution_clock::time_point startTime;
  76. if (gCpuMeters) {
  77. startTime = std::chrono::high_resolution_clock::now();
  78. module->step();
  79. auto stopTime = std::chrono::high_resolution_clock::now();
  80. float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * sampleRate;
  81. module->cpuTime += (cpuTime - module->cpuTime) * sampleTime / 0.5f;
  82. }
  83. else {
  84. module->step();
  85. }
  86. // Step ports
  87. for (Input &input : module->inputs) {
  88. if (input.active) {
  89. float value = input.value / 5.f;
  90. input.plugLights[0].setBrightnessSmooth(value);
  91. input.plugLights[1].setBrightnessSmooth(-value);
  92. }
  93. }
  94. for (Output &output : module->outputs) {
  95. if (output.active) {
  96. float value = output.value / 5.f;
  97. output.plugLights[0].setBrightnessSmooth(value);
  98. output.plugLights[1].setBrightnessSmooth(-value);
  99. }
  100. }
  101. }
  102. // Step cables by moving their output values to inputs
  103. for (Wire *wire : gWires) {
  104. wire->step();
  105. }
  106. }
  107. static void engineRun() {
  108. // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
  109. // https://software.intel.com/en-us/node/682949
  110. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
  111. _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
  112. // Every time the engine waits and locks a mutex, it steps this many frames
  113. const int mutexSteps = 64;
  114. // Time in seconds that the engine is rushing ahead of the estimated clock time
  115. double ahead = 0.0;
  116. auto lastTime = std::chrono::high_resolution_clock::now();
  117. while (running) {
  118. vipMutex.wait();
  119. if (!gPaused) {
  120. std::lock_guard<std::mutex> lock(mutex);
  121. for (int i = 0; i < mutexSteps; i++) {
  122. engineStep();
  123. }
  124. }
  125. double stepTime = mutexSteps * sampleTime;
  126. ahead += stepTime;
  127. auto currTime = std::chrono::high_resolution_clock::now();
  128. const double aheadFactor = 2.0;
  129. ahead -= aheadFactor * std::chrono::duration<double>(currTime - lastTime).count();
  130. lastTime = currTime;
  131. ahead = fmaxf(ahead, 0.0);
  132. // Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
  133. // The number of steps to wait before possibly sleeping
  134. const double aheadMax = 1.0; // seconds
  135. if (ahead > aheadMax) {
  136. std::this_thread::sleep_for(std::chrono::duration<double>(stepTime));
  137. }
  138. }
  139. }
  140. void engineStart() {
  141. running = true;
  142. thread = std::thread(engineRun);
  143. }
  144. void engineStop() {
  145. running = false;
  146. thread.join();
  147. }
  148. void engineAddModule(Module *module) {
  149. assert(module);
  150. VIPLock vipLock(vipMutex);
  151. std::lock_guard<std::mutex> lock(mutex);
  152. // Check that the module is not already added
  153. auto it = std::find(gModules.begin(), gModules.end(), module);
  154. assert(it == gModules.end());
  155. gModules.push_back(module);
  156. }
  157. void engineRemoveModule(Module *module) {
  158. assert(module);
  159. VIPLock vipLock(vipMutex);
  160. std::lock_guard<std::mutex> lock(mutex);
  161. // If a param is being smoothed on this module, stop smoothing it immediately
  162. if (module == smoothModule) {
  163. smoothModule = NULL;
  164. }
  165. // Check that all wires are disconnected
  166. for (Wire *wire : gWires) {
  167. assert(wire->outputModule != module);
  168. assert(wire->inputModule != module);
  169. }
  170. // Check that the module actually exists
  171. auto it = std::find(gModules.begin(), gModules.end(), module);
  172. assert(it != gModules.end());
  173. // Remove it
  174. gModules.erase(it);
  175. }
  176. static void updateActive() {
  177. // Set everything to inactive
  178. for (Module *module : gModules) {
  179. for (Input &input : module->inputs) {
  180. input.active = false;
  181. }
  182. for (Output &output : module->outputs) {
  183. output.active = false;
  184. }
  185. }
  186. // Set inputs/outputs to active
  187. for (Wire *wire : gWires) {
  188. wire->outputModule->outputs[wire->outputId].active = true;
  189. wire->inputModule->inputs[wire->inputId].active = true;
  190. }
  191. }
  192. void engineAddWire(Wire *wire) {
  193. assert(wire);
  194. VIPLock vipLock(vipMutex);
  195. std::lock_guard<std::mutex> lock(mutex);
  196. // Check wire properties
  197. assert(wire->outputModule);
  198. assert(wire->inputModule);
  199. // Check that the wire is not already added, and that the input is not already used by another cable
  200. for (Wire *wire2 : gWires) {
  201. assert(wire2 != wire);
  202. assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
  203. }
  204. // Add the wire
  205. gWires.push_back(wire);
  206. updateActive();
  207. }
  208. void engineRemoveWire(Wire *wire) {
  209. assert(wire);
  210. VIPLock vipLock(vipMutex);
  211. std::lock_guard<std::mutex> lock(mutex);
  212. // Check that the wire is already added
  213. auto it = std::find(gWires.begin(), gWires.end(), wire);
  214. assert(it != gWires.end());
  215. // Set input to 0V
  216. wire->inputModule->inputs[wire->inputId].value = 0.0;
  217. // Remove the wire
  218. gWires.erase(it);
  219. updateActive();
  220. }
  221. void engineSetParam(Module *module, int paramId, float value) {
  222. module->params[paramId].value = value;
  223. }
  224. void engineSetParamSmooth(Module *module, int paramId, float value) {
  225. VIPLock vipLock(vipMutex);
  226. std::lock_guard<std::mutex> lock(mutex);
  227. // Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state
  228. if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
  229. smoothModule->params[smoothParamId].value = smoothValue;
  230. }
  231. smoothModule = module;
  232. smoothParamId = paramId;
  233. smoothValue = value;
  234. }
  235. void engineSetSampleRate(float newSampleRate) {
  236. VIPLock vipLock(vipMutex);
  237. std::lock_guard<std::mutex> lock(mutex);
  238. sampleRate = newSampleRate;
  239. sampleTime = 1.0 / sampleRate;
  240. // onSampleRateChange
  241. for (Module *module : gModules) {
  242. module->onSampleRateChange();
  243. }
  244. }
  245. float engineGetSampleRate() {
  246. return sampleRate;
  247. }
  248. float engineGetSampleTime() {
  249. return sampleTime;
  250. }
  251. } // namespace rack