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.3KB

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