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.

430 lines
20KB

  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. namespace rack {
  10. namespace core {
  11. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  12. struct AudioInterfacePort : audio::Port {
  13. // std::mutex engineMutex;
  14. // std::condition_variable engineCv;
  15. // std::mutex audioMutex;
  16. // std::condition_variable audioCv;
  17. // // Audio thread produces, engine thread consumes
  18. // dsp::DoubleRingBuffer < dsp::Frame<NUM_AUDIO_INPUTS>, (1 << 15) > inputBuffer;
  19. // // Audio thread consumes, engine thread produces
  20. // dsp::DoubleRingBuffer < dsp::Frame<NUM_AUDIO_OUTPUTS>, (1 << 15) > outputBuffer;
  21. // bool active = false;
  22. // For checking getPrimaryModule()
  23. Module* module = NULL;
  24. const float* input = NULL;
  25. float* output = NULL;
  26. int frame = 0;
  27. int numFrames = 0;
  28. ~AudioInterfacePort() {
  29. // Close stream here before destructing AudioInterfacePort, so the mutexes are still valid when waiting to close.
  30. setDeviceId(-1, 0);
  31. }
  32. void processStream(const float* input, float* output, int frames) override {
  33. if (APP->engine->getPrimaryModule() != module) {
  34. // TEMP
  35. std::memset(output, 0, sizeof(float) * frames * numOutputs);
  36. return;
  37. }
  38. frame = 0;
  39. numFrames = frames;
  40. this->input = input;
  41. this->output = output;
  42. APP->engine->step(frames);
  43. // // Reactivate idle stream
  44. // if (!active) {
  45. // active = true;
  46. // inputBuffer.clear();
  47. // outputBuffer.clear();
  48. // }
  49. // if (numInputs > 0) {
  50. // // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
  51. // for (int i = 0; i < frames; i++) {
  52. // if (inputBuffer.full())
  53. // break;
  54. // dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
  55. // std::memset(&inputFrame, 0, sizeof(inputFrame));
  56. // std::memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
  57. // inputBuffer.push(inputFrame);
  58. // }
  59. // }
  60. // if (numOutputs > 0) {
  61. // std::unique_lock<std::mutex> lock(audioMutex);
  62. // auto cond = [&] {
  63. // return (outputBuffer.size() >= (size_t) frames);
  64. // };
  65. // auto timeout = std::chrono::milliseconds(100);
  66. // if (audioCv.wait_for(lock, timeout, cond)) {
  67. // // Consume audio block
  68. // for (int i = 0; i < frames; i++) {
  69. // dsp::Frame<NUM_AUDIO_OUTPUTS> f = outputBuffer.shift();
  70. // for (int j = 0; j < numOutputs; j++) {
  71. // output[numOutputs * i + j] = clamp(f.samples[j], -1.f, 1.f);
  72. // }
  73. // }
  74. // }
  75. // else {
  76. // // Timed out, fill output with zeros
  77. // std::memset(output, 0, frames * numOutputs * sizeof(float));
  78. // // DEBUG("Audio Interface Port underflow");
  79. // }
  80. // }
  81. // // Notify engine when finished processing
  82. // engineCv.notify_one();
  83. }
  84. void onCloseStream() override {
  85. // inputBuffer.clear();
  86. // outputBuffer.clear();
  87. }
  88. void onChannelsChange() override {
  89. }
  90. };
  91. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  92. struct AudioInterface : Module {
  93. enum ParamIds {
  94. NUM_PARAMS
  95. };
  96. enum InputIds {
  97. ENUMS(AUDIO_INPUTS, NUM_AUDIO_INPUTS),
  98. NUM_INPUTS
  99. };
  100. enum OutputIds {
  101. ENUMS(AUDIO_OUTPUTS, NUM_AUDIO_OUTPUTS),
  102. NUM_OUTPUTS
  103. };
  104. enum LightIds {
  105. ENUMS(INPUT_LIGHTS, NUM_AUDIO_INPUTS / 2),
  106. ENUMS(OUTPUT_LIGHTS, NUM_AUDIO_OUTPUTS / 2),
  107. NUM_LIGHTS
  108. };
  109. AudioInterfacePort<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> port;
  110. // int lastSampleRate = 0;
  111. // int lastNumOutputs = -1;
  112. // int lastNumInputs = -1;
  113. // dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
  114. // dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;
  115. // // in rack's sample rate
  116. // dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 16> inputBuffer;
  117. // dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 16> outputBuffer;
  118. AudioInterface() {
  119. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  120. for (int i = 0; i < NUM_AUDIO_INPUTS; i++)
  121. configInput(AUDIO_INPUTS + i, string::f("To device %d", i + 1));
  122. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++)
  123. configOutput(AUDIO_OUTPUTS + i, string::f("From device %d", i + 1));
  124. port.maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS);
  125. port.module = this;
  126. onSampleRateChange();
  127. }
  128. void process(const ProcessArgs& args) override {
  129. // Claim primary module if there is none
  130. if (!APP->engine->getPrimaryModule()) {
  131. APP->engine->setPrimaryModule(this);
  132. }
  133. // Get inputs
  134. for (int i = 0; i < port.numOutputs; i++) {
  135. float v = inputs[AUDIO_INPUTS + i].getVoltage() / 10.f;
  136. port.output[port.frame * port.numOutputs + i] = v;
  137. }
  138. // Set outputs
  139. for (int i = 0; i < port.numInputs; i++) {
  140. float v = port.input[port.frame * port.numInputs + i];
  141. outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * v);
  142. }
  143. for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) {
  144. outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
  145. }
  146. port.frame++;
  147. // // Update SRC states
  148. // inputSrc.setRates(port.sampleRate, args.sampleRate);
  149. // outputSrc.setRates(args.sampleRate, port.sampleRate);
  150. // inputSrc.setChannels(port.numInputs);
  151. // outputSrc.setChannels(port.numOutputs);
  152. // // Inputs: audio engine -> rack engine
  153. // if (port.active && port.numInputs > 0) {
  154. // // Wait until inputs are present
  155. // // Give up after a timeout in case the audio device is being unresponsive.
  156. // std::unique_lock<std::mutex> lock(port.engineMutex);
  157. // auto cond = [&] {
  158. // return (!port.inputBuffer.empty());
  159. // };
  160. // auto timeout = std::chrono::milliseconds(200);
  161. // if (port.engineCv.wait_for(lock, timeout, cond)) {
  162. // // Convert inputs
  163. // int inLen = port.inputBuffer.size();
  164. // int outLen = inputBuffer.capacity();
  165. // inputSrc.process(port.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
  166. // port.inputBuffer.startIncr(inLen);
  167. // inputBuffer.endIncr(outLen);
  168. // }
  169. // else {
  170. // // Give up on pulling input
  171. // port.active = false;
  172. // // DEBUG("Audio Interface underflow");
  173. // }
  174. // }
  175. // // Take input from buffer
  176. // dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
  177. // if (!inputBuffer.empty()) {
  178. // inputFrame = inputBuffer.shift();
  179. // }
  180. // else {
  181. // std::memset(&inputFrame, 0, sizeof(inputFrame));
  182. // }
  183. // for (int i = 0; i < port.numInputs; i++) {
  184. // outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * inputFrame.samples[i]);
  185. // }
  186. // for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) {
  187. // outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
  188. // }
  189. // // Outputs: rack engine -> audio engine
  190. // if (port.active && port.numOutputs > 0) {
  191. // // Get and push output SRC frame
  192. // if (!outputBuffer.full()) {
  193. // dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame;
  194. // for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
  195. // outputFrame.samples[i] = inputs[AUDIO_INPUTS + i].getVoltageSum() / 10.f;
  196. // }
  197. // outputBuffer.push(outputFrame);
  198. // }
  199. // if (outputBuffer.full()) {
  200. // // Wait until enough outputs are consumed
  201. // // Give up after a timeout in case the audio device is being unresponsive.
  202. // auto cond = [&] {
  203. // return (port.outputBuffer.size() < (size_t) port.blockSize);
  204. // };
  205. // if (!cond())
  206. // APP->engine->yieldWorkers();
  207. // std::unique_lock<std::mutex> lock(port.engineMutex);
  208. // auto timeout = std::chrono::milliseconds(200);
  209. // if (port.engineCv.wait_for(lock, timeout, cond)) {
  210. // // Push converted output
  211. // int inLen = outputBuffer.size();
  212. // int outLen = port.outputBuffer.capacity();
  213. // outputSrc.process(outputBuffer.startData(), &inLen, port.outputBuffer.endData(), &outLen);
  214. // outputBuffer.startIncr(inLen);
  215. // port.outputBuffer.endIncr(outLen);
  216. // }
  217. // else {
  218. // // Give up on pushing output
  219. // port.active = false;
  220. // outputBuffer.clear();
  221. // // DEBUG("Audio Interface underflow");
  222. // }
  223. // }
  224. // // Notify audio thread that an output is potentially ready
  225. // port.audioCv.notify_one();
  226. // }
  227. // Turn on light if at least one port is enabled in the nearby pair
  228. for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) {
  229. lights[INPUT_LIGHTS + i].setBrightness(port.numOutputs >= 2 * i + 1);
  230. }
  231. for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) {
  232. lights[OUTPUT_LIGHTS + i].setBrightness(port.numInputs >= 2 * i + 1);
  233. }
  234. }
  235. json_t* dataToJson() override {
  236. json_t* rootJ = json_object();
  237. json_object_set_new(rootJ, "audio", port.toJson());
  238. return rootJ;
  239. }
  240. void dataFromJson(json_t* rootJ) override {
  241. json_t* audioJ = json_object_get(rootJ, "audio");
  242. if (audioJ)
  243. port.fromJson(audioJ);
  244. }
  245. void onReset() override {
  246. port.setDeviceId(-1, 0);
  247. }
  248. };
  249. template <typename TAudioInterface>
  250. struct PrimaryModuleItem : MenuItem {
  251. TAudioInterface* module;
  252. void onAction(const event::Action& e) override {
  253. APP->engine->setPrimaryModule(module);
  254. }
  255. };
  256. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  257. struct AudioInterfaceWidget : ModuleWidget {
  258. typedef AudioInterface<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> TAudioInterface;
  259. AudioInterfaceWidget(TAudioInterface* module) {
  260. setModule(module);
  261. if (NUM_AUDIO_INPUTS == 8 && NUM_AUDIO_OUTPUTS == 8) {
  262. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface.svg")));
  263. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  264. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  265. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  266. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  267. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 0));
  268. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 1));
  269. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 2));
  270. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 3));
  271. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 4));
  272. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 5));
  273. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 6));
  274. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 7));
  275. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 0));
  276. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 1));
  277. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 2));
  278. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 3));
  279. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 4));
  280. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 5));
  281. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 6));
  282. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 7));
  283. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 0));
  284. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 1));
  285. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 2));
  286. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 3));
  287. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 0));
  288. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 1));
  289. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 2));
  290. addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 3));
  291. AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
  292. audioWidget->box.size = mm2px(Vec(44, 28));
  293. audioWidget->setAudioPort(module ? &module->port : NULL);
  294. addChild(audioWidget);
  295. }
  296. else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) {
  297. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface16.svg")));
  298. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  299. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  300. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  301. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  302. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 0));
  303. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 1));
  304. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 2));
  305. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 3));
  306. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 4));
  307. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 5));
  308. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 6));
  309. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 7));
  310. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 8));
  311. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 9));
  312. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 10));
  313. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 11));
  314. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 12));
  315. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 13));
  316. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 14));
  317. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 15));
  318. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 0));
  319. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 1));
  320. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 2));
  321. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 3));
  322. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 4));
  323. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 5));
  324. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 6));
  325. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 7));
  326. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 8));
  327. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 9));
  328. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 10));
  329. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 11));
  330. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 12));
  331. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 13));
  332. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 14));
  333. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 15));
  334. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 0));
  335. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 1));
  336. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 2));
  337. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 3));
  338. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 4));
  339. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 5));
  340. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 6));
  341. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 7));
  342. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 0));
  343. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 1));
  344. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 2));
  345. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 3));
  346. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(13.46, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 4));
  347. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(36.661, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 5));
  348. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(59.861, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 6));
  349. addChild(createLightCentered<SmallLight<GreenLight>>(mm2px(Vec(83.061, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 7));
  350. AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(2.57, 14.839)));
  351. audioWidget->box.size = mm2px(Vec(91.382, 28.0));
  352. audioWidget->setAudioPort(module ? &module->port : NULL);
  353. addChild(audioWidget);
  354. }
  355. }
  356. void appendContextMenu(Menu* menu) override {
  357. TAudioInterface* module = dynamic_cast<TAudioInterface*>(this->module);
  358. menu->addChild(new MenuEntry);
  359. PrimaryModuleItem<TAudioInterface>* primaryModuleItem = new PrimaryModuleItem<TAudioInterface>;
  360. primaryModuleItem->text = "Primary audio module";
  361. primaryModuleItem->rightText = CHECKMARK(APP->engine->getPrimaryModule() == module);
  362. primaryModuleItem->module = module;
  363. menu->addChild(primaryModuleItem);
  364. }
  365. };
  366. Model* modelAudioInterface = createModel<AudioInterface<8, 8>, AudioInterfaceWidget<8, 8>>("AudioInterface");
  367. Model* modelAudioInterface16 = createModel<AudioInterface<16, 16>, AudioInterfaceWidget<16, 16>>("AudioInterface16");
  368. } // namespace core
  369. } // namespace rack