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.

274 lines
11KB

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