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.

309 lines
7.6KB

  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 MAX_OUTPUTS 8
  16. #define MAX_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<MAX_INPUTS>, (1<<15)> inputBuffer;
  26. // Audio thread consumes, engine thread produces
  27. DoubleRingBuffer<Frame<MAX_OUTPUTS>, (1<<15)> outputBuffer;
  28. AudioInterfaceIO() {
  29. maxOutputs = MAX_OUTPUTS;
  30. maxInputs = MAX_INPUTS;
  31. }
  32. ~AudioInterfaceIO() {
  33. closeStream();
  34. }
  35. void processStream(const float *input, float *output, int length) override {
  36. if (numInputs > 0) {
  37. // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
  38. for (int i = 0; i < length; i++) {
  39. if (inputBuffer.full())
  40. break;
  41. Frame<MAX_INPUTS> f;
  42. memset(&f, 0, sizeof(f));
  43. memcpy(&f, &input[numInputs * i], numInputs * sizeof(float));
  44. inputBuffer.push(f);
  45. }
  46. }
  47. if (numOutputs > 0) {
  48. std::unique_lock<std::mutex> lock(audioMutex);
  49. auto cond = [&] {
  50. return outputBuffer.size() >= length;
  51. };
  52. if (audioCv.wait_for(lock, audioTimeout, cond)) {
  53. // Consume audio block
  54. for (int i = 0; i < length; i++) {
  55. Frame<MAX_OUTPUTS> f = outputBuffer.shift();
  56. memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float));
  57. }
  58. }
  59. else {
  60. // Timed out, fill output with zeros
  61. memset(output, 0, length * numOutputs * sizeof(float));
  62. }
  63. }
  64. // Notify engine when finished processing
  65. engineCv.notify_all();
  66. }
  67. void onCloseStream() override {
  68. inputBuffer.clear();
  69. outputBuffer.clear();
  70. }
  71. };
  72. struct AudioInterface : Module {
  73. enum ParamIds {
  74. NUM_PARAMS
  75. };
  76. enum InputIds {
  77. ENUMS(AUDIO_INPUT, MAX_INPUTS),
  78. NUM_INPUTS
  79. };
  80. enum OutputIds {
  81. ENUMS(AUDIO_OUTPUT, MAX_OUTPUTS),
  82. NUM_OUTPUTS
  83. };
  84. enum LightIds {
  85. ACTIVE_LIGHT,
  86. NUM_LIGHTS
  87. };
  88. AudioInterfaceIO audioIO;
  89. int lastSampleRate = 0;
  90. SampleRateConverter<MAX_INPUTS> inputSrc;
  91. SampleRateConverter<MAX_OUTPUTS> outputSrc;
  92. // in rack's sample rate
  93. DoubleRingBuffer<Frame<MAX_INPUTS>, 16> inputBuffer;
  94. DoubleRingBuffer<Frame<MAX_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 < MAX_INPUTS; i++) {
  110. // inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate());
  111. // }
  112. // for (int i = 0; i < MAX_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<MAX_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 < MAX_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<MAX_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. audioIO.audioCv.notify_all();
  174. // Lights
  175. lights[ACTIVE_LIGHT].value = audioIO.isActive() ? 1.0 : 0.0;
  176. }
  177. AudioInterfaceWidget::AudioInterfaceWidget() {
  178. AudioInterface *module = new AudioInterface();
  179. setModule(module);
  180. box.size = Vec(15*12, 380);
  181. {
  182. Panel *panel = new LightPanel();
  183. panel->box.size = box.size;
  184. addChild(panel);
  185. }
  186. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  187. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
  188. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  189. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
  190. Vec margin = Vec(5, 2);
  191. float labelHeight = 15;
  192. float yPos = margin.y + 100;
  193. float xPos;
  194. {
  195. Label *label = new Label();
  196. label->box.pos = Vec(margin.x, yPos);
  197. label->text = "Outputs (DACs)";
  198. addChild(label);
  199. yPos += labelHeight + margin.y;
  200. }
  201. yPos += 5;
  202. xPos = 10;
  203. for (int i = 0; i < 4; i++) {
  204. addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i));
  205. Label *label = new Label();
  206. label->box.pos = Vec(xPos + 4, yPos + 28);
  207. label->text = stringf("%d", i + 1);
  208. addChild(label);
  209. xPos += 37 + margin.x;
  210. }
  211. yPos += 35 + margin.y;
  212. yPos += 5;
  213. xPos = 10;
  214. for (int i = 4; i < 8; i++) {
  215. addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i));
  216. Label *label = new Label();
  217. label->box.pos = Vec(xPos + 4, yPos + 28);
  218. label->text = stringf("%d", i + 1);
  219. addChild(label);
  220. xPos += 37 + margin.x;
  221. }
  222. yPos += 35 + margin.y;
  223. {
  224. Label *label = new Label();
  225. label->box.pos = Vec(margin.x, yPos);
  226. label->text = "Inputs (ADCs)";
  227. addChild(label);
  228. yPos += labelHeight + margin.y;
  229. }
  230. yPos += 5;
  231. xPos = 10;
  232. for (int i = 0; i < 4; i++) {
  233. Port *port = createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i);
  234. addOutput(port);
  235. Label *label = new Label();
  236. label->box.pos = Vec(xPos + 4, yPos + 28);
  237. label->text = stringf("%d", i + 1);
  238. addChild(label);
  239. xPos += 37 + margin.x;
  240. }
  241. yPos += 35 + margin.y;
  242. yPos += 5;
  243. xPos = 10;
  244. for (int i = 4; i < 8; i++) {
  245. addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i));
  246. Label *label = new Label();
  247. label->box.pos = Vec(xPos + 4, yPos + 28);
  248. label->text = stringf("%d", i + 1);
  249. addChild(label);
  250. xPos += 37 + margin.x;
  251. }
  252. yPos += 35 + margin.y;
  253. AudioWidget *audioWidget = construct<USB_B_AudioWidget>();
  254. audioWidget->audioIO = &module->audioIO;
  255. addChild(audioWidget);
  256. // Lights
  257. addChild(createLight<SmallLight<GreenLight>>(Vec(40, 20), module, AudioInterface::ACTIVE_LIGHT));
  258. }