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.

478 lines
24KB

  1. #include <mutex>
  2. #include <chrono>
  3. #include <thread>
  4. #include <condition_variable>
  5. #include "plugin.hpp"
  6. #include <audio.hpp>
  7. #include <context.hpp>
  8. namespace rack {
  9. namespace core {
  10. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  11. struct AudioInterface : Module, audio::Port {
  12. static constexpr int NUM_INPUT_LIGHTS = (NUM_AUDIO_INPUTS > 2) ? (NUM_AUDIO_INPUTS / 2) : 0;
  13. static constexpr int NUM_OUTPUT_LIGHTS = (NUM_AUDIO_OUTPUTS > 2) ? (NUM_AUDIO_OUTPUTS / 2) : 0;
  14. enum ParamIds {
  15. ENUMS(GAIN_PARAM, NUM_AUDIO_INPUTS == 2),
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. ENUMS(AUDIO_INPUTS, NUM_AUDIO_INPUTS),
  20. NUM_INPUTS
  21. };
  22. enum OutputIds {
  23. ENUMS(AUDIO_OUTPUTS, NUM_AUDIO_OUTPUTS),
  24. NUM_OUTPUTS
  25. };
  26. enum LightIds {
  27. ENUMS(INPUT_LIGHTS, NUM_INPUT_LIGHTS * 2),
  28. ENUMS(OUTPUT_LIGHTS, NUM_OUTPUT_LIGHTS * 2),
  29. ENUMS(VU_LIGHTS, (NUM_AUDIO_INPUTS == 2) ? (2 * 6) : 0),
  30. NUM_LIGHTS
  31. };
  32. dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 32768> inputBuffer;
  33. dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 32768> outputBuffer;
  34. dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
  35. dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;
  36. dsp::ClockDivider lightDivider;
  37. // For each pair of inputs/outputs
  38. float inputClipTimers[(NUM_AUDIO_INPUTS > 0) ? NUM_INPUT_LIGHTS : 0] = {};
  39. float outputClipTimers[(NUM_AUDIO_INPUTS > 0) ? NUM_OUTPUT_LIGHTS : 0] = {};
  40. dsp::VuMeter2 vuMeter[(NUM_AUDIO_INPUTS == 2) ? 2 : 0];
  41. // Port variables
  42. int requestedEngineFrames = 0;
  43. int maxEngineFrames = 0;
  44. AudioInterface() {
  45. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  46. if (NUM_AUDIO_INPUTS == 2)
  47. configParam(GAIN_PARAM, 0.f, 2.f, 1.f, "Level", " dB", -10, 40);
  48. for (int i = 0; i < NUM_AUDIO_INPUTS; i++)
  49. configInput(AUDIO_INPUTS + i, string::f("To \"device output %d\"", i + 1));
  50. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++)
  51. configOutput(AUDIO_OUTPUTS + i, string::f("From \"device input %d\"", i + 1));
  52. for (int i = 0; i < NUM_INPUT_LIGHTS; i++)
  53. configLight(INPUT_LIGHTS + 2 * i, string::f("Device output %d/%d status", 2 * i + 1, 2 * i + 2));
  54. for (int i = 0; i < NUM_OUTPUT_LIGHTS; i++)
  55. configLight(OUTPUT_LIGHTS + 2 * i, string::f("Device input %d/%d status", 2 * i + 1, 2 * i + 2));
  56. lightDivider.setDivision(512);
  57. maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS);
  58. inputSrc.setQuality(6);
  59. outputSrc.setQuality(6);
  60. }
  61. ~AudioInterface() {
  62. // Close stream here before destructing AudioInterfacePort, so processBuffer() etc are not called on another thread while destructing.
  63. setDriverId(-1);
  64. }
  65. void onReset() override {
  66. setDriverId(-1);
  67. }
  68. void onSampleRateChange(const SampleRateChangeEvent& e) override {
  69. inputBuffer.clear();
  70. outputBuffer.clear();
  71. }
  72. void process(const ProcessArgs& args) override {
  73. const float clipTime = 0.25f;
  74. // Push inputs to buffer
  75. dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
  76. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  77. // Get input
  78. float v = 0.f;
  79. if (inputs[AUDIO_INPUTS + i].isConnected())
  80. v = inputs[AUDIO_INPUTS + i].getVoltageSum() / 10.f;
  81. // Normalize right input to left on Audio-2
  82. else if (i > 0 && NUM_AUDIO_INPUTS == 2)
  83. v = inputFrame.samples[i - 1];
  84. // Detect clipping
  85. if (NUM_AUDIO_INPUTS > 2) {
  86. if (std::fabs(v) >= 1.f)
  87. inputClipTimers[i / 2] = clipTime;
  88. }
  89. inputFrame.samples[i] = v;
  90. }
  91. // Apply gain from knob
  92. if (NUM_AUDIO_INPUTS == 2) {
  93. float gain = std::pow(params[GAIN_PARAM].getValue(), 2.f);
  94. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  95. inputFrame.samples[i] *= gain;
  96. }
  97. }
  98. if (!inputBuffer.full()) {
  99. inputBuffer.push(inputFrame);
  100. }
  101. // Pull outputs from buffer
  102. if (!outputBuffer.empty()) {
  103. dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame = outputBuffer.shift();
  104. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
  105. float v = outputFrame.samples[i];
  106. outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * v);
  107. // Detect clipping
  108. if (NUM_AUDIO_OUTPUTS > 2) {
  109. if (std::fabs(v) >= 1.f)
  110. outputClipTimers[i / 2] = clipTime;
  111. }
  112. }
  113. }
  114. else {
  115. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
  116. outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
  117. }
  118. }
  119. // Lights
  120. if (NUM_AUDIO_INPUTS == 2) {
  121. for (int i = 0; i < 2; i++) {
  122. vuMeter[i].process(args.sampleTime, inputFrame.samples[i]);
  123. }
  124. }
  125. if (lightDivider.process()) {
  126. float lightTime = args.sampleTime * lightDivider.getDivision();
  127. if (NUM_AUDIO_INPUTS == 2) {
  128. for (int i = 0; i < 2; i++) {
  129. lights[VU_LIGHTS + i * 6 + 0].setBrightness(vuMeter[i].getBrightness(0, 0));
  130. lights[VU_LIGHTS + i * 6 + 1].setBrightness(vuMeter[i].getBrightness(-3, 0));
  131. lights[VU_LIGHTS + i * 6 + 2].setBrightness(vuMeter[i].getBrightness(-6, -3));
  132. lights[VU_LIGHTS + i * 6 + 3].setBrightness(vuMeter[i].getBrightness(-12, -6));
  133. lights[VU_LIGHTS + i * 6 + 4].setBrightness(vuMeter[i].getBrightness(-24, -12));
  134. lights[VU_LIGHTS + i * 6 + 5].setBrightness(vuMeter[i].getBrightness(-36, -24));
  135. }
  136. }
  137. else {
  138. int numDeviceInputs = getNumInputs();
  139. int numDeviceOutputs = getNumOutputs();
  140. // Turn on light if at least one port is enabled in the nearby pair.
  141. for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) {
  142. bool active = numDeviceOutputs >= 2 * i + 1;
  143. bool clip = inputClipTimers[i] > 0.f;
  144. if (clip)
  145. inputClipTimers[i] -= lightTime;
  146. lights[INPUT_LIGHTS + i * 2 + 0].setBrightness(active && !clip);
  147. lights[INPUT_LIGHTS + i * 2 + 1].setBrightness(active && clip);
  148. }
  149. for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) {
  150. bool active = numDeviceInputs >= 2 * i + 1;
  151. bool clip = outputClipTimers[i] > 0.f;
  152. if (clip)
  153. outputClipTimers[i] -= lightTime;
  154. lights[OUTPUT_LIGHTS + i * 2 + 0].setBrightness(active & !clip);
  155. lights[OUTPUT_LIGHTS + i * 2 + 1].setBrightness(active & clip);
  156. }
  157. }
  158. }
  159. }
  160. json_t* dataToJson() override {
  161. json_t* rootJ = json_object();
  162. json_object_set_new(rootJ, "audio", audio::Port::toJson());
  163. return rootJ;
  164. }
  165. void dataFromJson(json_t* rootJ) override {
  166. json_t* audioJ = json_object_get(rootJ, "audio");
  167. if (audioJ)
  168. audio::Port::fromJson(audioJ);
  169. }
  170. // audio::Port
  171. void processInput(const float* input, int inputStride, int frames) override {
  172. // Claim primary module if there is none
  173. if (!APP->engine->getPrimaryModule()) {
  174. APP->engine->setPrimaryModule(this);
  175. }
  176. bool isPrimary = (APP->engine->getPrimaryModule() == this);
  177. // Initialize sample rate converters
  178. int numInputs = getNumInputs();
  179. int engineSampleRate = (int) APP->engine->getSampleRate();
  180. int sampleRate = getSampleRate();
  181. double sampleRateRatio = (double) engineSampleRate / sampleRate;
  182. outputSrc.setRates(sampleRate, engineSampleRate);
  183. outputSrc.setChannels(numInputs);
  184. // Consider engine buffers "too full" if they contain a bit more than the audio device's number of frames, converted to engine sample rate.
  185. maxEngineFrames = (int) std::ceil(frames * sampleRateRatio * 1.5);
  186. // If this is a secondary audio module and the engine output buffer is too full, flush it.
  187. if (!isPrimary && (int) outputBuffer.size() > maxEngineFrames) {
  188. outputBuffer.clear();
  189. // DEBUG("%p: flushing engine output", this);
  190. }
  191. if (numInputs > 0) {
  192. // audio input -> engine output
  193. dsp::Frame<NUM_AUDIO_OUTPUTS> inputAudioBuffer[frames];
  194. std::memset(inputAudioBuffer, 0, sizeof(inputAudioBuffer));
  195. for (int i = 0; i < frames; i++) {
  196. for (int j = 0; j < std::min(numInputs, NUM_AUDIO_OUTPUTS); j++) {
  197. float v = input[i * inputStride + j];
  198. inputAudioBuffer[i].samples[j] = v;
  199. }
  200. }
  201. int inputAudioFrames = frames;
  202. int outputFrames = outputBuffer.capacity();
  203. outputSrc.process(inputAudioBuffer, &inputAudioFrames, outputBuffer.endData(), &outputFrames);
  204. outputBuffer.endIncr(outputFrames);
  205. // Request exactly as many frames as we have.
  206. requestedEngineFrames = outputBuffer.size();
  207. }
  208. else {
  209. // Upper bound on number of frames so that `outputAudioFrames >= frames` at the end of this method.
  210. requestedEngineFrames = (int) std::ceil(frames * sampleRateRatio) - inputBuffer.size();
  211. }
  212. }
  213. void processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames) override {
  214. bool isPrimary = (APP->engine->getPrimaryModule() == this);
  215. // Step engine
  216. if (isPrimary && requestedEngineFrames > 0) {
  217. APP->engine->stepBlock(requestedEngineFrames);
  218. }
  219. }
  220. void processOutput(float* output, int outputStride, int frames) override {
  221. bool isPrimary = (APP->engine->getPrimaryModule() == this);
  222. int numOutputs = getNumOutputs();
  223. int engineSampleRate = (int) APP->engine->getSampleRate();
  224. int sampleRate = getSampleRate();
  225. inputSrc.setRates(engineSampleRate, sampleRate);
  226. inputSrc.setChannels(numOutputs);
  227. if (numOutputs > 0) {
  228. // engine input -> audio output
  229. dsp::Frame<NUM_AUDIO_OUTPUTS> outputAudioBuffer[frames];
  230. int inputFrames = inputBuffer.size();
  231. int outputAudioFrames = frames;
  232. inputSrc.process(inputBuffer.startData(), &inputFrames, outputAudioBuffer, &outputAudioFrames);
  233. inputBuffer.startIncr(inputFrames);
  234. for (int i = 0; i < outputAudioFrames; i++) {
  235. for (int j = 0; j < std::min(numOutputs, NUM_AUDIO_INPUTS); j++) {
  236. float v = outputAudioBuffer[i].samples[j];
  237. v = clamp(v, -1.f, 1.f);
  238. output[i * outputStride + j] = v;
  239. }
  240. }
  241. }
  242. // If this is a secondary audio module and the engine input buffer is too full, flush it.
  243. if (!isPrimary && (int) inputBuffer.size() > maxEngineFrames) {
  244. inputBuffer.clear();
  245. // DEBUG("%p: flushing engine input", this);
  246. }
  247. // DEBUG("%p %s:\tframes %d requestedEngineFrames %d\toutputBuffer %d inputBuffer %d\t", this, isPrimary ? "primary" : "secondary", frames, requestedEngineFrames, outputBuffer.size(), inputBuffer.size());
  248. }
  249. void onOpenStream() override {
  250. inputBuffer.clear();
  251. outputBuffer.clear();
  252. }
  253. void onCloseStream() override {
  254. inputBuffer.clear();
  255. outputBuffer.clear();
  256. }
  257. };
  258. template <typename TAudioInterface>
  259. struct PrimaryModuleItem : MenuItem {
  260. TAudioInterface* module;
  261. void onAction(const event::Action& e) override {
  262. APP->engine->setPrimaryModule(module);
  263. }
  264. };
  265. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  266. struct AudioInterfaceWidget : ModuleWidget {
  267. typedef AudioInterface<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> TAudioInterface;
  268. AudioInterfaceWidget(TAudioInterface* module) {
  269. setModule(module);
  270. if (NUM_AUDIO_INPUTS == 8 && NUM_AUDIO_OUTPUTS == 8) {
  271. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface.svg")));
  272. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  273. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  274. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  275. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  276. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 0));
  277. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 1));
  278. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 2));
  279. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 3));
  280. addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 4));
  281. addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 5));
  282. addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 6));
  283. addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 7));
  284. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 0));
  285. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 1));
  286. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 2));
  287. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 3));
  288. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 4));
  289. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 5));
  290. addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 6));
  291. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 7));
  292. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(12.524985, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 0 * 2));
  293. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(35.725647, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 1 * 2));
  294. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(12.524985, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 2 * 2));
  295. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(35.725647, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 3 * 2));
  296. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(12.524985, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 0 * 2));
  297. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(35.725647, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 1 * 2));
  298. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(12.524985, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 2 * 2));
  299. addChild(createLight<SmallLight<GreenRedLight>>(mm2px(Vec(35.725647, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 3 * 2));
  300. AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
  301. audioWidget->box.size = mm2px(Vec(44, 28));
  302. audioWidget->setAudioPort(module);
  303. addChild(audioWidget);
  304. }
  305. else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) {
  306. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface16.svg")));
  307. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  308. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  309. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  310. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  311. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 0));
  312. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 1));
  313. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 2));
  314. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 3));
  315. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 4));
  316. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 5));
  317. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 6));
  318. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 7));
  319. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 8));
  320. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 9));
  321. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 10));
  322. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.461, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 11));
  323. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.06, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 12));
  324. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 13));
  325. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 14));
  326. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 15));
  327. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 0));
  328. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 1));
  329. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 2));
  330. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 3));
  331. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 4));
  332. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 5));
  333. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 6));
  334. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 7));
  335. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 8));
  336. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 9));
  337. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 10));
  338. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.461, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 11));
  339. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.06, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 12));
  340. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 13));
  341. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 14));
  342. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 15));
  343. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.46, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 0 * 2));
  344. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.661, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 1 * 2));
  345. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.861, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 2 * 2));
  346. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(83.061, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 3 * 2));
  347. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.46, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 4 * 2));
  348. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.661, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 5 * 2));
  349. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.861, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 6 * 2));
  350. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(83.061, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 7 * 2));
  351. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.46, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 0 * 2));
  352. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.661, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 1 * 2));
  353. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.861, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 2 * 2));
  354. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(83.061, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 3 * 2));
  355. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.46, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 4 * 2));
  356. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.661, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 5 * 2));
  357. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.861, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 6 * 2));
  358. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(83.061, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 7 * 2));
  359. AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(2.57, 14.839)));
  360. audioWidget->box.size = mm2px(Vec(91.382, 28.0));
  361. audioWidget->setAudioPort(module);
  362. addChild(audioWidget);
  363. }
  364. else if (NUM_AUDIO_INPUTS == 2 && NUM_AUDIO_OUTPUTS == 2) {
  365. setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface2.svg")));
  366. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  367. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  368. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  369. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  370. addParam(createParamCentered<RoundBigBlackKnob>(mm2px(Vec(12.7, 74.019)), module, TAudioInterface::GAIN_PARAM));
  371. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.697, 94.253)), module, TAudioInterface::AUDIO_INPUTS + 0));
  372. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.703, 94.253)), module, TAudioInterface::AUDIO_INPUTS + 1));
  373. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.699, 112.254)), module, TAudioInterface::AUDIO_OUTPUTS + 0));
  374. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.7, 112.254)), module, TAudioInterface::AUDIO_OUTPUTS + 1));
  375. addChild(createLightCentered<MediumLight<RedLight>>(mm2px(Vec(6.7, 29.759)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 0));
  376. addChild(createLightCentered<MediumLight<RedLight>>(mm2px(Vec(18.7, 29.759)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 0));
  377. addChild(createLightCentered<MediumLight<YellowLight>>(mm2px(Vec(6.7, 34.753)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 1));
  378. addChild(createLightCentered<MediumLight<YellowLight>>(mm2px(Vec(18.7, 34.753)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 1));
  379. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(6.7, 39.749)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 2));
  380. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(18.7, 39.749)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 2));
  381. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(6.7, 44.744)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 3));
  382. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(18.7, 44.744)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 3));
  383. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(6.7, 49.744)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 4));
  384. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(18.7, 49.744)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 4));
  385. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(6.7, 54.745)), module, TAudioInterface::VU_LIGHTS + 0 * 6 + 5));
  386. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(18.7, 54.745)), module, TAudioInterface::VU_LIGHTS + 1 * 6 + 5));
  387. AudioDeviceWidget* audioWidget = createWidget<AudioDeviceWidget>(mm2px(Vec(2.135, 14.259)));
  388. audioWidget->box.size = mm2px(Vec(21.128, 6.725));
  389. audioWidget->setAudioPort(module);
  390. // Adjust deviceChoice position
  391. audioWidget->deviceChoice->textOffset = Vec(6, 14);
  392. addChild(audioWidget);
  393. }
  394. }
  395. void appendContextMenu(Menu* menu) override {
  396. TAudioInterface* module = dynamic_cast<TAudioInterface*>(this->module);
  397. menu->addChild(new MenuSeparator);
  398. PrimaryModuleItem<TAudioInterface>* primaryModuleItem = new PrimaryModuleItem<TAudioInterface>;
  399. primaryModuleItem->text = "Primary audio module";
  400. primaryModuleItem->rightText = CHECKMARK(APP->engine->getPrimaryModule() == module);
  401. primaryModuleItem->module = module;
  402. menu->addChild(primaryModuleItem);
  403. }
  404. };
  405. Model* modelAudioInterface2 = createModel<AudioInterface<2, 2>, AudioInterfaceWidget<2, 2>>("AudioInterface2");
  406. // Legacy name for Audio-8
  407. Model* modelAudioInterface = createModel<AudioInterface<8, 8>, AudioInterfaceWidget<8, 8>>("AudioInterface");
  408. Model* modelAudioInterface16 = createModel<AudioInterface<16, 16>, AudioInterfaceWidget<16, 16>>("AudioInterface16");
  409. } // namespace core
  410. } // namespace rack