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.

287 lines
10KB

  1. #include <assert.h>
  2. #include <mutex>
  3. #include <chrono>
  4. #include <thread>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include "Core.hpp"
  8. #include "audio.hpp"
  9. #include "dsp/samplerate.hpp"
  10. #include "dsp/ringbuffer.hpp"
  11. #pragma GCC diagnostic push
  12. #pragma GCC diagnostic ignored "-Wsuggest-override"
  13. #include <RtAudio.h>
  14. #pragma GCC diagnostic pop
  15. #define OUTPUTS 8
  16. #define INPUTS 8
  17. using namespace rack;
  18. struct AudioInterfaceIO : AudioIO {
  19. std::mutex engineMutex;
  20. std::condition_variable engineCv;
  21. std::mutex audioMutex;
  22. std::condition_variable audioCv;
  23. // Audio thread produces, engine thread consumes
  24. DoubleRingBuffer<Frame<INPUTS>, (1<<15)> inputBuffer;
  25. // Audio thread consumes, engine thread produces
  26. DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer;
  27. bool active = false;
  28. ~AudioInterfaceIO() {
  29. // Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
  30. setDevice(-1, 0);
  31. }
  32. void processStream(const float *input, float *output, int frames) override {
  33. // Reactivate idle stream
  34. if (!active) {
  35. active = true;
  36. inputBuffer.clear();
  37. outputBuffer.clear();
  38. }
  39. if (numInputs > 0) {
  40. // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
  41. for (int i = 0; i < frames; i++) {
  42. if (inputBuffer.full())
  43. break;
  44. Frame<INPUTS> inputFrame;
  45. memset(&inputFrame, 0, sizeof(inputFrame));
  46. memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
  47. inputBuffer.push(inputFrame);
  48. }
  49. }
  50. if (numOutputs > 0) {
  51. std::unique_lock<std::mutex> lock(audioMutex);
  52. auto cond = [&] {
  53. return (outputBuffer.size() >= (size_t) frames);
  54. };
  55. auto timeout = std::chrono::milliseconds(100);
  56. if (audioCv.wait_for(lock, timeout, cond)) {
  57. // Consume audio block
  58. for (int i = 0; i < frames; i++) {
  59. Frame<OUTPUTS> f = outputBuffer.shift();
  60. for (int j = 0; j < numOutputs; j++) {
  61. output[numOutputs*i + j] = clamp(f.samples[j], -1.f, 1.f);
  62. }
  63. }
  64. }
  65. else {
  66. // Timed out, fill output with zeros
  67. memset(output, 0, frames * numOutputs * sizeof(float));
  68. debug("Audio Interface IO underflow");
  69. }
  70. }
  71. // Notify engine when finished processing
  72. engineCv.notify_one();
  73. }
  74. void onCloseStream() override {
  75. inputBuffer.clear();
  76. outputBuffer.clear();
  77. }
  78. void onChannelsChange() override {
  79. }
  80. };
  81. struct AudioInterface : Module {
  82. enum ParamIds {
  83. NUM_PARAMS
  84. };
  85. enum InputIds {
  86. ENUMS(AUDIO_INPUT, INPUTS),
  87. NUM_INPUTS
  88. };
  89. enum OutputIds {
  90. ENUMS(AUDIO_OUTPUT, OUTPUTS),
  91. NUM_OUTPUTS
  92. };
  93. enum LightIds {
  94. ENUMS(INPUT_LIGHT, INPUTS / 2),
  95. ENUMS(OUTPUT_LIGHT, OUTPUTS / 2),
  96. NUM_LIGHTS
  97. };
  98. AudioInterfaceIO audioIO;
  99. int lastSampleRate = 0;
  100. SampleRateConverter<INPUTS> inputSrc;
  101. SampleRateConverter<OUTPUTS> outputSrc;
  102. // in rack's sample rate
  103. DoubleRingBuffer<Frame<INPUTS>, 16> inputBuffer;
  104. DoubleRingBuffer<Frame<OUTPUTS>, 16> outputBuffer;
  105. AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  106. onSampleRateChange();
  107. }
  108. void step() override;
  109. json_t *toJson() override {
  110. json_t *rootJ = json_object();
  111. json_object_set_new(rootJ, "audio", audioIO.toJson());
  112. return rootJ;
  113. }
  114. void fromJson(json_t *rootJ) override {
  115. json_t *audioJ = json_object_get(rootJ, "audio");
  116. audioIO.fromJson(audioJ);
  117. }
  118. void onSampleRateChange() override {
  119. inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
  120. outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
  121. }
  122. void onReset() override {
  123. audioIO.setDevice(-1, 0);
  124. }
  125. };
  126. void AudioInterface::step() {
  127. // Update sample rate if changed by audio driver
  128. if (audioIO.sampleRate != lastSampleRate) {
  129. onSampleRateChange();
  130. lastSampleRate = audioIO.sampleRate;
  131. }
  132. // Inputs: audio engine -> rack engine
  133. if (audioIO.active && audioIO.numInputs > 0) {
  134. // Wait until inputs are present
  135. // Give up after a timeout in case the audio device is being unresponsive.
  136. std::unique_lock<std::mutex> lock(audioIO.engineMutex);
  137. auto cond = [&] {
  138. return (!audioIO.inputBuffer.empty());
  139. };
  140. auto timeout = std::chrono::milliseconds(200);
  141. if (audioIO.engineCv.wait_for(lock, timeout, cond)) {
  142. // Convert inputs
  143. int inLen = audioIO.inputBuffer.size();
  144. int outLen = inputBuffer.capacity();
  145. inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
  146. audioIO.inputBuffer.startIncr(inLen);
  147. inputBuffer.endIncr(outLen);
  148. }
  149. else {
  150. // Give up on pulling input
  151. audioIO.active = false;
  152. debug("Audio Interface underflow");
  153. }
  154. }
  155. // Take input from buffer
  156. Frame<INPUTS> inputFrame;
  157. if (!inputBuffer.empty()) {
  158. inputFrame = inputBuffer.shift();
  159. }
  160. else {
  161. memset(&inputFrame, 0, sizeof(inputFrame));
  162. }
  163. for (int i = 0; i < INPUTS; i++) {
  164. outputs[AUDIO_OUTPUT + i].value = 10.f * inputFrame.samples[i];
  165. }
  166. // Outputs: rack engine -> audio engine
  167. if (audioIO.active && audioIO.numOutputs > 0) {
  168. // Get and push output SRC frame
  169. if (!outputBuffer.full()) {
  170. Frame<OUTPUTS> outputFrame;
  171. for (int i = 0; i < OUTPUTS; i++) {
  172. outputFrame.samples[i] = inputs[AUDIO_INPUT + i].value / 10.f;
  173. }
  174. outputBuffer.push(outputFrame);
  175. }
  176. if (outputBuffer.full()) {
  177. // Wait until enough outputs are consumed
  178. // Give up after a timeout in case the audio device is being unresponsive.
  179. std::unique_lock<std::mutex> lock(audioIO.engineMutex);
  180. auto cond = [&] {
  181. return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize);
  182. };
  183. auto timeout = std::chrono::milliseconds(200);
  184. if (audioIO.engineCv.wait_for(lock, timeout, cond)) {
  185. // Push converted output
  186. int inLen = outputBuffer.size();
  187. int outLen = audioIO.outputBuffer.capacity();
  188. outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen);
  189. outputBuffer.startIncr(inLen);
  190. audioIO.outputBuffer.endIncr(outLen);
  191. }
  192. else {
  193. // Give up on pushing output
  194. audioIO.active = false;
  195. outputBuffer.clear();
  196. debug("Audio Interface underflow");
  197. }
  198. }
  199. // Notify audio thread that an output is potentially ready
  200. audioIO.audioCv.notify_one();
  201. }
  202. // Turn on light if at least one port is enabled in the nearby pair
  203. for (int i = 0; i < INPUTS / 2; i++)
  204. lights[INPUT_LIGHT + i].value = (audioIO.active && audioIO.numOutputs >= 2*i+1);
  205. for (int i = 0; i < OUTPUTS / 2; i++)
  206. lights[OUTPUT_LIGHT + i].value = (audioIO.active && audioIO.numInputs >= 2*i+1);
  207. }
  208. struct AudioInterfaceWidget : ModuleWidget {
  209. AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) {
  210. setPanel(SVG::load(assetGlobal("res/Core/AudioInterface.svg")));
  211. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  212. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  213. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  214. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  215. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0));
  216. addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1));
  217. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2));
  218. addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3));
  219. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4));
  220. addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5));
  221. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6));
  222. addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7));
  223. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0));
  224. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1));
  225. addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2));
  226. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3));
  227. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4));
  228. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5));
  229. addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6));
  230. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7));
  231. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
  232. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
  233. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
  234. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
  235. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
  236. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
  237. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
  238. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));
  239. AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
  240. audioWidget->box.size = mm2px(Vec(44, 28));
  241. audioWidget->audioIO = &module->audioIO;
  242. addChild(audioWidget);
  243. }
  244. };
  245. Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);