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.

209 lines
5.7KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include <math.h>
  5. #include <set>
  6. #include <chrono>
  7. #include <thread>
  8. #include <xmmintrin.h>
  9. #include "engine.hpp"
  10. #include "util.hpp"
  11. namespace rack {
  12. float gSampleRate;
  13. static bool running = false;
  14. static std::mutex mutex;
  15. static std::thread thread;
  16. static VIPMutex vipMutex;
  17. static std::set<Module*> modules;
  18. // Merely used for keeping track of which module inputs point to which module outputs, to prevent pointer mistakes and make the rack API more rigorous
  19. static std::set<Wire*> wires;
  20. // Parameter interpolation
  21. static Module *smoothModule = NULL;
  22. static int smoothParamId;
  23. static float smoothValue;
  24. void engineInit() {
  25. gSampleRate = 44100.0;
  26. }
  27. void engineDestroy() {
  28. // Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself before the GUI was destroyed.
  29. assert(wires.empty());
  30. assert(modules.empty());
  31. }
  32. static void engineStep() {
  33. // Param interpolation
  34. if (smoothModule) {
  35. float value = smoothModule->params[smoothParamId];
  36. const float lambda = 60.0; // decay rate is 1 graphics frame
  37. const float snap = 0.0001;
  38. float delta = smoothValue - value;
  39. if (fabsf(delta) < snap) {
  40. smoothModule->params[smoothParamId] = smoothValue;
  41. smoothModule = NULL;
  42. }
  43. else {
  44. value += delta * lambda / gSampleRate;
  45. smoothModule->params[smoothParamId] = value;
  46. }
  47. }
  48. // Step all modules
  49. // std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
  50. for (Module *module : modules) {
  51. // Start clock for CPU usage
  52. // start = std::chrono::high_resolution_clock::now();
  53. // Step module by one frame
  54. module->step();
  55. // Stop clock and smooth step time value
  56. // end = std::chrono::high_resolution_clock::now();
  57. // std::chrono::duration<float> diff = end - start;
  58. // float elapsed = diff.count() * gSampleRate;
  59. // const float lambda = 1.0;
  60. // module->cpuTime += (elapsed - module->cpuTime) * lambda / gSampleRate;
  61. }
  62. // Step cables by moving their output values to inputs
  63. for (Wire *wire : wires) {
  64. wire->inputValue = wire->outputValue;
  65. wire->outputValue = 0.0;
  66. }
  67. }
  68. static void engineRun() {
  69. // Set CPU to denormals-are-zero mode
  70. // http://carlh.net/plugins/denormals.php
  71. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
  72. // Every time the engine waits and locks a mutex, it steps this many frames
  73. const int mutexSteps = 64;
  74. // Time in seconds that the engine is rushing ahead of the estimated clock time
  75. float ahead = 0.0;
  76. auto lastTime = std::chrono::high_resolution_clock::now();
  77. while (running) {
  78. vipMutex.wait();
  79. {
  80. std::lock_guard<std::mutex> lock(mutex);
  81. for (int i = 0; i < mutexSteps; i++) {
  82. engineStep();
  83. }
  84. }
  85. float stepTime = mutexSteps / gSampleRate;
  86. ahead += stepTime;
  87. auto currTime = std::chrono::high_resolution_clock::now();
  88. const float aheadFactor = 2.0;
  89. ahead -= aheadFactor * std::chrono::duration<float>(currTime - lastTime).count();
  90. lastTime = currTime;
  91. ahead = fmaxf(ahead, 0.0);
  92. // Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
  93. // The number of steps to wait before possibly sleeping
  94. const float aheadMax = 1.0; // seconds
  95. if (ahead > aheadMax) {
  96. std::this_thread::sleep_for(std::chrono::duration<float>(stepTime));
  97. }
  98. }
  99. }
  100. void engineStart() {
  101. running = true;
  102. thread = std::thread(engineRun);
  103. }
  104. void engineStop() {
  105. running = false;
  106. thread.join();
  107. }
  108. void engineAddModule(Module *module) {
  109. assert(module);
  110. VIPLock vipLock(vipMutex);
  111. std::lock_guard<std::mutex> lock(mutex);
  112. // Check that the module is not already added
  113. assert(modules.find(module) == modules.end());
  114. modules.insert(module);
  115. }
  116. void engineRemoveModule(Module *module) {
  117. assert(module);
  118. VIPLock vipLock(vipMutex);
  119. std::lock_guard<std::mutex> lock(mutex);
  120. // If a param is being smoothed on this module, remove it immediately
  121. if (module == smoothModule) {
  122. smoothModule = NULL;
  123. }
  124. // Check that all wires are disconnected
  125. for (Wire *wire : wires) {
  126. assert(wire->outputModule != module);
  127. assert(wire->inputModule != module);
  128. }
  129. auto it = modules.find(module);
  130. if (it != modules.end()) {
  131. modules.erase(it);
  132. }
  133. }
  134. void engineAddWire(Wire *wire) {
  135. assert(wire);
  136. VIPLock vipLock(vipMutex);
  137. std::lock_guard<std::mutex> lock(mutex);
  138. // Check that the wire is not already added
  139. assert(wires.find(wire) == wires.end());
  140. assert(wire->outputModule);
  141. assert(wire->inputModule);
  142. // Check that the inputs/outputs are not already used by another cable
  143. for (Wire *wire2 : wires) {
  144. assert(wire2 != wire);
  145. assert(!(wire2->outputModule == wire->outputModule && wire2->outputId == wire->outputId));
  146. assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
  147. }
  148. // Add the wire
  149. wires.insert(wire);
  150. // Connect the wire to inputModule
  151. wire->inputModule->inputs[wire->inputId] = &wire->inputValue;
  152. wire->outputModule->outputs[wire->outputId] = &wire->outputValue;
  153. }
  154. void engineRemoveWire(Wire *wire) {
  155. assert(wire);
  156. VIPLock vipLock(vipMutex);
  157. std::lock_guard<std::mutex> lock(mutex);
  158. // Disconnect wire from inputModule
  159. wire->inputModule->inputs[wire->inputId] = NULL;
  160. wire->outputModule->outputs[wire->outputId] = NULL;
  161. // Remove the wire
  162. auto it = wires.find(wire);
  163. assert(it != wires.end());
  164. wires.erase(it);
  165. }
  166. void engineSetParamSmooth(Module *module, int paramId, float value) {
  167. VIPLock vipLock(vipMutex);
  168. std::lock_guard<std::mutex> lock(mutex);
  169. // Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state
  170. if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
  171. smoothModule->params[smoothParamId] = smoothValue;
  172. }
  173. smoothModule = module;
  174. smoothParamId = paramId;
  175. smoothValue = value;
  176. }
  177. } // namespace rack