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.

353 lines
17KB

  1. #include "plugin.hpp"
  2. #include "audio.hpp"
  3. #include "app.hpp"
  4. #include <mutex>
  5. #include <chrono>
  6. #include <thread>
  7. #include <mutex>
  8. #include <condition_variable>
  9. using namespace rack;
  10. template <int AUDIO_OUTPUTS, int AUDIO_INPUTS>
  11. struct AudioInterfacePort : audio::Port {
  12. std::mutex engineMutex;
  13. std::condition_variable engineCv;
  14. std::mutex audioMutex;
  15. std::condition_variable audioCv;
  16. // Audio thread produces, engine thread consumes
  17. dsp::DoubleRingBuffer<dsp::Frame<AUDIO_INPUTS>, (1<<15)> inputBuffer;
  18. // Audio thread consumes, engine thread produces
  19. dsp::DoubleRingBuffer<dsp::Frame<AUDIO_OUTPUTS>, (1<<15)> outputBuffer;
  20. bool active = false;
  21. ~AudioInterfacePort() {
  22. // Close stream here before destructing AudioInterfacePort, so the mutexes are still valid when waiting to close.
  23. setDeviceId(-1, 0);
  24. }
  25. void processStream(const float *input, float *output, int frames) override {
  26. // Reactivate idle stream
  27. if (!active) {
  28. active = true;
  29. inputBuffer.clear();
  30. outputBuffer.clear();
  31. }
  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. dsp::Frame<AUDIO_INPUTS> inputFrame;
  38. std::memset(&inputFrame, 0, sizeof(inputFrame));
  39. std::memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
  40. inputBuffer.push(inputFrame);
  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. dsp::Frame<AUDIO_OUTPUTS> f = outputBuffer.shift();
  53. for (int j = 0; j < numOutputs; j++) {
  54. output[numOutputs*i + j] = clamp(f.samples[j], -1.f, 1.f);
  55. }
  56. }
  57. }
  58. else {
  59. // Timed out, fill output with zeros
  60. std::memset(output, 0, frames * numOutputs * sizeof(float));
  61. // DEBUG("Audio Interface Port underflow");
  62. }
  63. }
  64. // Notify engine when finished processing
  65. engineCv.notify_one();
  66. }
  67. void onCloseStream() override {
  68. inputBuffer.clear();
  69. outputBuffer.clear();
  70. }
  71. void onChannelsChange() override {
  72. }
  73. };
  74. template <int AUDIO_OUTPUTS, int AUDIO_INPUTS>
  75. struct AudioInterface : Module {
  76. enum ParamIds {
  77. NUM_PARAMS
  78. };
  79. enum InputIds {
  80. ENUMS(AUDIO_INPUT, AUDIO_INPUTS),
  81. NUM_INPUTS
  82. };
  83. enum OutputIds {
  84. ENUMS(AUDIO_OUTPUT, AUDIO_OUTPUTS),
  85. NUM_OUTPUTS
  86. };
  87. enum LightIds {
  88. ENUMS(INPUT_LIGHT, AUDIO_INPUTS / 2),
  89. ENUMS(OUTPUT_LIGHT, AUDIO_OUTPUTS / 2),
  90. NUM_LIGHTS
  91. };
  92. AudioInterfacePort<AUDIO_OUTPUTS, AUDIO_INPUTS> port;
  93. int lastSampleRate = 0;
  94. int lastNumOutputs = -1;
  95. int lastNumInputs = -1;
  96. dsp::SampleRateConverter<AUDIO_INPUTS> inputSrc;
  97. dsp::SampleRateConverter<AUDIO_OUTPUTS> outputSrc;
  98. // in rack's sample rate
  99. dsp::DoubleRingBuffer<dsp::Frame<AUDIO_INPUTS>, 16> inputBuffer;
  100. dsp::DoubleRingBuffer<dsp::Frame<AUDIO_OUTPUTS>, 16> outputBuffer;
  101. AudioInterface() {
  102. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  103. onSampleRateChange();
  104. }
  105. void process(const ProcessArgs &args) override {
  106. // Update SRC states
  107. inputSrc.setRates(port.sampleRate, args.sampleRate);
  108. outputSrc.setRates(args.sampleRate, port.sampleRate);
  109. inputSrc.setChannels(port.numInputs);
  110. outputSrc.setChannels(port.numOutputs);
  111. // Inputs: audio engine -> rack engine
  112. if (port.active && port.numInputs > 0) {
  113. // Wait until inputs are present
  114. // Give up after a timeout in case the audio device is being unresponsive.
  115. std::unique_lock<std::mutex> lock(port.engineMutex);
  116. auto cond = [&] {
  117. return (!port.inputBuffer.empty());
  118. };
  119. auto timeout = std::chrono::milliseconds(200);
  120. if (port.engineCv.wait_for(lock, timeout, cond)) {
  121. // Convert inputs
  122. int inLen = port.inputBuffer.size();
  123. int outLen = inputBuffer.capacity();
  124. inputSrc.process(port.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
  125. port.inputBuffer.startIncr(inLen);
  126. inputBuffer.endIncr(outLen);
  127. }
  128. else {
  129. // Give up on pulling input
  130. port.active = false;
  131. // DEBUG("Audio Interface underflow");
  132. }
  133. }
  134. // Take input from buffer
  135. dsp::Frame<AUDIO_INPUTS> inputFrame;
  136. if (!inputBuffer.empty()) {
  137. inputFrame = inputBuffer.shift();
  138. }
  139. else {
  140. std::memset(&inputFrame, 0, sizeof(inputFrame));
  141. }
  142. for (int i = 0; i < port.numInputs; i++) {
  143. outputs[AUDIO_OUTPUT + i].setVoltage(10.f * inputFrame.samples[i]);
  144. }
  145. for (int i = port.numInputs; i < AUDIO_INPUTS; i++) {
  146. outputs[AUDIO_OUTPUT + i].setVoltage(0.f);
  147. }
  148. // Outputs: rack engine -> audio engine
  149. if (port.active && port.numOutputs > 0) {
  150. // Get and push output SRC frame
  151. if (!outputBuffer.full()) {
  152. dsp::Frame<AUDIO_OUTPUTS> outputFrame;
  153. for (int i = 0; i < AUDIO_OUTPUTS; i++) {
  154. outputFrame.samples[i] = inputs[AUDIO_INPUT + i].getVoltage() / 10.f;
  155. }
  156. outputBuffer.push(outputFrame);
  157. }
  158. if (outputBuffer.full()) {
  159. // Wait until enough outputs are consumed
  160. // Give up after a timeout in case the audio device is being unresponsive.
  161. std::unique_lock<std::mutex> lock(port.engineMutex);
  162. auto cond = [&] {
  163. return (port.outputBuffer.size() < (size_t) port.blockSize);
  164. };
  165. auto timeout = std::chrono::milliseconds(200);
  166. if (port.engineCv.wait_for(lock, timeout, cond)) {
  167. // Push converted output
  168. int inLen = outputBuffer.size();
  169. int outLen = port.outputBuffer.capacity();
  170. outputSrc.process(outputBuffer.startData(), &inLen, port.outputBuffer.endData(), &outLen);
  171. outputBuffer.startIncr(inLen);
  172. port.outputBuffer.endIncr(outLen);
  173. }
  174. else {
  175. // Give up on pushing output
  176. port.active = false;
  177. outputBuffer.clear();
  178. // DEBUG("Audio Interface underflow");
  179. }
  180. }
  181. // Notify audio thread that an output is potentially ready
  182. port.audioCv.notify_one();
  183. }
  184. // Turn on light if at least one port is enabled in the nearby pair
  185. for (int i = 0; i < AUDIO_INPUTS / 2; i++)
  186. lights[INPUT_LIGHT + i].setBrightness(port.active && port.numOutputs >= 2*i+1);
  187. for (int i = 0; i < AUDIO_OUTPUTS / 2; i++)
  188. lights[OUTPUT_LIGHT + i].setBrightness(port.active && port.numInputs >= 2*i+1);
  189. }
  190. json_t *dataToJson() override {
  191. json_t *rootJ = json_object();
  192. json_object_set_new(rootJ, "audio", port.toJson());
  193. return rootJ;
  194. }
  195. void dataFromJson(json_t *rootJ) override {
  196. json_t *audioJ = json_object_get(rootJ, "audio");
  197. port.fromJson(audioJ);
  198. }
  199. void onReset() override {
  200. port.setDeviceId(-1, 0);
  201. }
  202. };
  203. struct AudioInterface8Widget : ModuleWidget {
  204. typedef AudioInterface<8, 8> TAudioInterface;
  205. AudioInterface8Widget(TAudioInterface *module) {
  206. setModule(module);
  207. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface.svg")));
  208. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  209. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  210. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  211. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  212. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), module, TAudioInterface::AUDIO_INPUT + 0));
  213. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), module, TAudioInterface::AUDIO_INPUT + 1));
  214. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), module, TAudioInterface::AUDIO_INPUT + 2));
  215. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), module, TAudioInterface::AUDIO_INPUT + 3));
  216. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), module, TAudioInterface::AUDIO_INPUT + 4));
  217. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), module, TAudioInterface::AUDIO_INPUT + 5));
  218. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), module, TAudioInterface::AUDIO_INPUT + 6));
  219. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), module, TAudioInterface::AUDIO_INPUT + 7));
  220. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), module, TAudioInterface::AUDIO_OUTPUT + 0));
  221. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), module, TAudioInterface::AUDIO_OUTPUT + 1));
  222. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), module, TAudioInterface::AUDIO_OUTPUT + 2));
  223. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), module, TAudioInterface::AUDIO_OUTPUT + 3));
  224. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), module, TAudioInterface::AUDIO_OUTPUT + 4));
  225. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), module, TAudioInterface::AUDIO_OUTPUT + 5));
  226. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), module, TAudioInterface::AUDIO_OUTPUT + 6));
  227. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), module, TAudioInterface::AUDIO_OUTPUT + 7));
  228. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, TAudioInterface::INPUT_LIGHT + 0));
  229. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, TAudioInterface::INPUT_LIGHT + 1));
  230. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, TAudioInterface::INPUT_LIGHT + 2));
  231. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, TAudioInterface::INPUT_LIGHT + 3));
  232. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, TAudioInterface::OUTPUT_LIGHT + 0));
  233. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, TAudioInterface::OUTPUT_LIGHT + 1));
  234. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, TAudioInterface::OUTPUT_LIGHT + 2));
  235. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, TAudioInterface::OUTPUT_LIGHT + 3));
  236. AudioWidget *audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
  237. audioWidget->box.size = mm2px(Vec(44, 28));
  238. audioWidget->setAudioPort(module ? &module->port : NULL);
  239. addChild(audioWidget);
  240. }
  241. };
  242. struct AudioInterface16Widget : ModuleWidget {
  243. typedef AudioInterface<16, 16> TAudioInterface;
  244. AudioInterface16Widget(TAudioInterface *module) {
  245. setModule(module);
  246. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface16.svg")));
  247. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  248. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  249. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  250. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  251. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 59.638)), module, TAudioInterface::AUDIO_INPUT + 0));
  252. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 59.638)), module, TAudioInterface::AUDIO_INPUT + 1));
  253. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 59.638)), module, TAudioInterface::AUDIO_INPUT + 2));
  254. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 59.638)), module, TAudioInterface::AUDIO_INPUT + 3));
  255. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 59.638)), module, TAudioInterface::AUDIO_INPUT + 4));
  256. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 59.638)), module, TAudioInterface::AUDIO_INPUT + 5));
  257. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 59.638)), module, TAudioInterface::AUDIO_INPUT + 6));
  258. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 59.638)), module, TAudioInterface::AUDIO_INPUT + 7));
  259. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 74.251)), module, TAudioInterface::AUDIO_INPUT + 8));
  260. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 74.251)), module, TAudioInterface::AUDIO_INPUT + 9));
  261. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 74.251)), module, TAudioInterface::AUDIO_INPUT + 10));
  262. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 74.251)), module, TAudioInterface::AUDIO_INPUT + 11));
  263. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 74.251)), module, TAudioInterface::AUDIO_INPUT + 12));
  264. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 74.251)), module, TAudioInterface::AUDIO_INPUT + 13));
  265. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 74.251)), module, TAudioInterface::AUDIO_INPUT + 14));
  266. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 74.251)), module, TAudioInterface::AUDIO_INPUT + 15));
  267. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 0));
  268. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 1));
  269. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 2));
  270. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 3));
  271. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 4));
  272. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 5));
  273. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 6));
  274. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUT + 7));
  275. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 8));
  276. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 9));
  277. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 10));
  278. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 11));
  279. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 12));
  280. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 13));
  281. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 14));
  282. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUT + 15));
  283. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 55.667)), module, TAudioInterface::INPUT_LIGHT + 0));
  284. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 55.667)), module, TAudioInterface::INPUT_LIGHT + 1));
  285. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 55.667)), module, TAudioInterface::INPUT_LIGHT + 2));
  286. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 55.667)), module, TAudioInterface::INPUT_LIGHT + 3));
  287. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 70.248)), module, TAudioInterface::INPUT_LIGHT + 4));
  288. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 70.248)), module, TAudioInterface::INPUT_LIGHT + 5));
  289. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 70.248)), module, TAudioInterface::INPUT_LIGHT + 6));
  290. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 70.248)), module, TAudioInterface::INPUT_LIGHT + 7));
  291. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 92.238)), module, TAudioInterface::OUTPUT_LIGHT + 0));
  292. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 92.238)), module, TAudioInterface::OUTPUT_LIGHT + 1));
  293. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 92.238)), module, TAudioInterface::OUTPUT_LIGHT + 2));
  294. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 92.238)), module, TAudioInterface::OUTPUT_LIGHT + 3));
  295. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 108.259)), module, TAudioInterface::OUTPUT_LIGHT + 4));
  296. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 108.259)), module, TAudioInterface::OUTPUT_LIGHT + 5));
  297. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 108.259)), module, TAudioInterface::OUTPUT_LIGHT + 6));
  298. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 108.259)), module, TAudioInterface::OUTPUT_LIGHT + 7));
  299. AudioWidget *audioWidget = createWidget<AudioWidget>(mm2px(Vec(2.57, 14.839)));
  300. audioWidget->box.size = mm2px(Vec(91.382, 28.0));
  301. audioWidget->setAudioPort(module ? &module->port : NULL);
  302. addChild(audioWidget);
  303. }
  304. };
  305. Model *modelAudioInterface = createModel<AudioInterface<8, 8>, AudioInterface8Widget>("AudioInterface");
  306. Model *modelAudioInterface16 = createModel<AudioInterface<16, 16>, AudioInterface16Widget>("AudioInterface16");