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.

258 lines
9.4KB

  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. ~AudioInterfaceIO() {
  28. // Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
  29. setDevice(-1, 0);
  30. }
  31. void processStream(const float *input, float *output, int frames) override {
  32. if (numInputs > 0) {
  33. // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
  34. for (int i = 0; i < frames; i++) {
  35. if (inputBuffer.full())
  36. break;
  37. Frame<INPUTS> f;
  38. memset(&f, 0, sizeof(f));
  39. memcpy(&f, &input[numInputs * i], numInputs * sizeof(float));
  40. inputBuffer.push(f);
  41. }
  42. }
  43. if (numOutputs > 0) {
  44. std::unique_lock<std::mutex> lock(audioMutex);
  45. auto cond = [&] {
  46. return (outputBuffer.size() >= (size_t) frames);
  47. };
  48. auto timeout = std::chrono::milliseconds(100);
  49. if (audioCv.wait_for(lock, timeout, cond)) {
  50. // Consume audio block
  51. for (int i = 0; i < frames; i++) {
  52. Frame<OUTPUTS> f = outputBuffer.shift();
  53. memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float));
  54. }
  55. }
  56. else {
  57. // Timed out, fill output with zeros
  58. memset(output, 0, frames * numOutputs * sizeof(float));
  59. debug("Audio Interface IO underflow");
  60. }
  61. }
  62. // Notify engine when finished processing
  63. engineCv.notify_all();
  64. }
  65. void onCloseStream() override {
  66. inputBuffer.clear();
  67. outputBuffer.clear();
  68. }
  69. };
  70. struct AudioInterface : Module {
  71. enum ParamIds {
  72. NUM_PARAMS
  73. };
  74. enum InputIds {
  75. ENUMS(AUDIO_INPUT, INPUTS),
  76. NUM_INPUTS
  77. };
  78. enum OutputIds {
  79. ENUMS(AUDIO_OUTPUT, OUTPUTS),
  80. NUM_OUTPUTS
  81. };
  82. enum LightIds {
  83. ENUMS(INPUT_LIGHT, INPUTS / 2),
  84. ENUMS(OUTPUT_LIGHT, OUTPUTS / 2),
  85. NUM_LIGHTS
  86. };
  87. AudioInterfaceIO audioIO;
  88. int lastSampleRate = 0;
  89. SampleRateConverter<INPUTS> inputSrc;
  90. SampleRateConverter<OUTPUTS> outputSrc;
  91. // in rack's sample rate
  92. DoubleRingBuffer<Frame<INPUTS>, 16> inputBuffer;
  93. DoubleRingBuffer<Frame<OUTPUTS>, 16> outputBuffer;
  94. AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  95. onSampleRateChange();
  96. }
  97. void step() override;
  98. json_t *toJson() override {
  99. json_t *rootJ = json_object();
  100. json_object_set_new(rootJ, "audio", audioIO.toJson());
  101. return rootJ;
  102. }
  103. void fromJson(json_t *rootJ) override {
  104. json_t *audioJ = json_object_get(rootJ, "audio");
  105. audioIO.fromJson(audioJ);
  106. }
  107. void onSampleRateChange() override {
  108. // for (int i = 0; i < INPUTS; i++) {
  109. // inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate());
  110. // }
  111. // for (int i = 0; i < OUTPUTS; i++) {
  112. // outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate);
  113. // }
  114. inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
  115. outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
  116. }
  117. void onReset() override {
  118. audioIO.setDevice(-1, 0);
  119. }
  120. };
  121. void AudioInterface::step() {
  122. Frame<INPUTS> inputFrame;
  123. memset(&inputFrame, 0, sizeof(inputFrame));
  124. // Update sample rate if changed by audio driver
  125. if (audioIO.sampleRate != lastSampleRate) {
  126. onSampleRateChange();
  127. lastSampleRate = audioIO.sampleRate;
  128. }
  129. if (audioIO.numInputs > 0) {
  130. if (inputBuffer.empty()) {
  131. int inLen = audioIO.inputBuffer.size();
  132. int outLen = inputBuffer.capacity();
  133. inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
  134. audioIO.inputBuffer.startIncr(inLen);
  135. inputBuffer.endIncr(outLen);
  136. }
  137. }
  138. if (!inputBuffer.empty()) {
  139. inputFrame = inputBuffer.shift();
  140. }
  141. for (int i = 0; i < INPUTS; i++) {
  142. outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i];
  143. }
  144. if (audioIO.numOutputs > 0) {
  145. // Get and push output SRC frame
  146. if (!outputBuffer.full()) {
  147. Frame<OUTPUTS> f;
  148. for (int i = 0; i < audioIO.numOutputs; i++) {
  149. f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0;
  150. }
  151. outputBuffer.push(f);
  152. }
  153. if (outputBuffer.full()) {
  154. // Wait until outputs are needed
  155. std::unique_lock<std::mutex> lock(audioIO.engineMutex);
  156. auto cond = [&] {
  157. return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize);
  158. };
  159. auto timeout = std::chrono::milliseconds(100);
  160. if (audioIO.engineCv.wait_for(lock, timeout, cond)) {
  161. // Push converted output
  162. int inLen = outputBuffer.size();
  163. int outLen = audioIO.outputBuffer.capacity();
  164. outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen);
  165. outputBuffer.startIncr(inLen);
  166. audioIO.outputBuffer.endIncr(outLen);
  167. }
  168. else {
  169. // Give up on pushing output
  170. debug("Audio Interface underflow");
  171. }
  172. }
  173. }
  174. for (int i = 0; i < INPUTS / 2; i++)
  175. lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1);
  176. for (int i = 0; i < OUTPUTS / 2; i++)
  177. lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1);
  178. audioIO.audioCv.notify_all();
  179. }
  180. struct AudioInterfaceWidget : ModuleWidget {
  181. AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) {
  182. setPanel(SVG::load(assetGlobal("res/Core/AudioInterface.svg")));
  183. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  184. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  185. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  186. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  187. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0));
  188. addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1));
  189. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2));
  190. addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3));
  191. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4));
  192. addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5));
  193. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6));
  194. addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7));
  195. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0));
  196. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1));
  197. addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2));
  198. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3));
  199. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4));
  200. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5));
  201. addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6));
  202. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7));
  203. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
  204. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
  205. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
  206. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
  207. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
  208. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
  209. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
  210. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));
  211. AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
  212. audioWidget->box.size = mm2px(Vec(44, 28));
  213. audioWidget->audioIO = &module->audioIO;
  214. addChild(audioWidget);
  215. }
  216. };
  217. Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);