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.

278 lines
7.0KB

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