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.

635 lines
28KB

  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 AudioPort : audio::Port {
  12. Module* module;
  13. dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 32768> engineInputBuffer;
  14. dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 32768> engineOutputBuffer;
  15. dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
  16. dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;
  17. // Port variable caches
  18. int deviceNumInputs = 0;
  19. int deviceNumOutputs = 0;
  20. float deviceSampleRate = 0.f;
  21. int requestedEngineFrames = 0;
  22. AudioPort(Module* module) {
  23. this->module = module;
  24. maxOutputs = NUM_AUDIO_INPUTS;
  25. maxInputs = NUM_AUDIO_OUTPUTS;
  26. inputSrc.setQuality(6);
  27. outputSrc.setQuality(6);
  28. }
  29. void setMaster(bool master = true) {
  30. if (master) {
  31. APP->engine->setMasterModule(module);
  32. }
  33. else {
  34. // Unset master only if module is currently master
  35. if (isMaster())
  36. APP->engine->setMasterModule(NULL);
  37. }
  38. }
  39. bool isMaster() {
  40. return APP->engine->getMasterModule() == module;
  41. }
  42. void processInput(const float* input, int inputStride, int frames) override {
  43. deviceNumInputs = std::min(getNumInputs(), NUM_AUDIO_OUTPUTS);
  44. deviceNumOutputs = std::min(getNumOutputs(), NUM_AUDIO_INPUTS);
  45. deviceSampleRate = getSampleRate();
  46. // DEBUG("%p: new device block ____________________________", this);
  47. // Claim master module if there is none
  48. if (!APP->engine->getMasterModule()) {
  49. setMaster();
  50. }
  51. bool isMasterCached = isMaster();
  52. // Set sample rate of engine if engine sample rate is "auto".
  53. if (isMasterCached) {
  54. APP->engine->setSuggestedSampleRate(deviceSampleRate);
  55. }
  56. float engineSampleRate = APP->engine->getSampleRate();
  57. float sampleRateRatio = engineSampleRate / deviceSampleRate;
  58. // DEBUG("%p: %d block, engineOutputBuffer still has %d", this, frames, (int) engineOutputBuffer.size());
  59. // Consider engine buffers "too full" if they contain a bit more than the audio device's number of frames, converted to engine sample rate.
  60. int maxEngineFrames = (int) std::ceil(frames * sampleRateRatio * 2.0) - 1;
  61. // If the engine output buffer is too full, clear it to keep latency low. No need to clear if master because it's always cleared below.
  62. if (!isMasterCached && (int) engineOutputBuffer.size() > maxEngineFrames) {
  63. engineOutputBuffer.clear();
  64. // DEBUG("%p: clearing engine output", this);
  65. }
  66. if (deviceNumInputs > 0) {
  67. // Always clear engine output if master
  68. if (isMasterCached) {
  69. engineOutputBuffer.clear();
  70. }
  71. // Set up sample rate converter
  72. outputSrc.setRates(deviceSampleRate, engineSampleRate);
  73. outputSrc.setChannels(deviceNumInputs);
  74. // Convert audio input -> engine output
  75. dsp::Frame<NUM_AUDIO_OUTPUTS> audioInputBuffer[frames];
  76. std::memset(audioInputBuffer, 0, sizeof(audioInputBuffer));
  77. for (int i = 0; i < frames; i++) {
  78. for (int j = 0; j < deviceNumInputs; j++) {
  79. float v = input[i * inputStride + j];
  80. audioInputBuffer[i].samples[j] = v;
  81. }
  82. }
  83. int audioInputFrames = frames;
  84. int outputFrames = engineOutputBuffer.capacity();
  85. outputSrc.process(audioInputBuffer, &audioInputFrames, engineOutputBuffer.endData(), &outputFrames);
  86. engineOutputBuffer.endIncr(outputFrames);
  87. // Request exactly as many frames as we have in the engine output buffer.
  88. requestedEngineFrames = engineOutputBuffer.size();
  89. }
  90. else {
  91. // Upper bound on number of frames so that `audioOutputFrames >= frames` when processOutput() is called.
  92. requestedEngineFrames = std::max((int) std::ceil(frames * sampleRateRatio) - (int) engineInputBuffer.size(), 0);
  93. }
  94. }
  95. void processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames) override {
  96. // Step engine
  97. if (isMaster() && requestedEngineFrames > 0) {
  98. // DEBUG("%p: %d block, stepping %d", this, frames, requestedEngineFrames);
  99. APP->engine->stepBlock(requestedEngineFrames);
  100. }
  101. }
  102. void processOutput(float* output, int outputStride, int frames) override {
  103. // bool isMasterCached = isMaster();
  104. float engineSampleRate = APP->engine->getSampleRate();
  105. float sampleRateRatio = engineSampleRate / deviceSampleRate;
  106. if (deviceNumOutputs > 0) {
  107. // Set up sample rate converter
  108. inputSrc.setRates(engineSampleRate, deviceSampleRate);
  109. inputSrc.setChannels(deviceNumOutputs);
  110. // Convert engine input -> audio output
  111. dsp::Frame<NUM_AUDIO_OUTPUTS> audioOutputBuffer[frames];
  112. int inputFrames = engineInputBuffer.size();
  113. int audioOutputFrames = frames;
  114. inputSrc.process(engineInputBuffer.startData(), &inputFrames, audioOutputBuffer, &audioOutputFrames);
  115. engineInputBuffer.startIncr(inputFrames);
  116. // Copy the audio output buffer
  117. for (int i = 0; i < audioOutputFrames; i++) {
  118. for (int j = 0; j < deviceNumOutputs; j++) {
  119. float v = audioOutputBuffer[i].samples[j];
  120. v = clamp(v, -1.f, 1.f);
  121. output[i * outputStride + j] = v;
  122. }
  123. }
  124. // Fill the rest of the audio output buffer with zeros
  125. for (int i = audioOutputFrames; i < frames; i++) {
  126. for (int j = 0; j < deviceNumOutputs; j++) {
  127. output[i * outputStride + j] = 0.f;
  128. }
  129. }
  130. }
  131. // DEBUG("%p: %d block, engineInputBuffer left %d", this, frames, (int) engineInputBuffer.size());
  132. // If the engine input buffer is too full, clear it to keep latency low.
  133. int maxEngineFrames = (int) std::ceil(frames * sampleRateRatio * 2.0) - 1;
  134. if ((int) engineInputBuffer.size() > maxEngineFrames) {
  135. engineInputBuffer.clear();
  136. // DEBUG("%p: clearing engine input", this);
  137. }
  138. // DEBUG("%p %s:\tframes %d requestedEngineFrames %d\toutputBuffer %d engineInputBuffer %d\t", this, isMasterCached ? "master" : "secondary", frames, requestedEngineFrames, engineOutputBuffer.size(), engineInputBuffer.size());
  139. }
  140. void onStartStream() override {
  141. engineInputBuffer.clear();
  142. engineOutputBuffer.clear();
  143. // DEBUG("onStartStream");
  144. }
  145. void onStopStream() override {
  146. deviceNumInputs = 0;
  147. deviceNumOutputs = 0;
  148. deviceSampleRate = 0.f;
  149. engineInputBuffer.clear();
  150. engineOutputBuffer.clear();
  151. setMaster(false);
  152. // DEBUG("onStopStream");
  153. }
  154. };
  155. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  156. struct Audio : Module {
  157. static constexpr int NUM_INPUT_LIGHTS = (NUM_AUDIO_INPUTS > 2) ? (NUM_AUDIO_INPUTS / 2) : 0;
  158. static constexpr int NUM_OUTPUT_LIGHTS = (NUM_AUDIO_OUTPUTS > 2) ? (NUM_AUDIO_OUTPUTS / 2) : 0;
  159. enum ParamIds {
  160. ENUMS(LEVEL_PARAM, NUM_AUDIO_INPUTS == 2),
  161. NUM_PARAMS
  162. };
  163. enum InputIds {
  164. ENUMS(AUDIO_INPUTS, NUM_AUDIO_INPUTS),
  165. NUM_INPUTS
  166. };
  167. enum OutputIds {
  168. ENUMS(AUDIO_OUTPUTS, NUM_AUDIO_OUTPUTS),
  169. NUM_OUTPUTS
  170. };
  171. enum LightIds {
  172. ENUMS(INPUT_LIGHTS, NUM_INPUT_LIGHTS * 2),
  173. ENUMS(OUTPUT_LIGHTS, NUM_OUTPUT_LIGHTS * 2),
  174. ENUMS(VU_LIGHTS, (NUM_AUDIO_INPUTS == 2) ? (2 * 6) : 0),
  175. NUM_LIGHTS
  176. };
  177. AudioPort<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> port;
  178. dsp::RCFilter dcFilters[NUM_AUDIO_INPUTS];
  179. bool dcFilterEnabled = false;
  180. dsp::ClockDivider lightDivider;
  181. // For each pair of inputs/outputs
  182. float inputClipTimers[(NUM_AUDIO_INPUTS > 0) ? NUM_INPUT_LIGHTS : 0] = {};
  183. float outputClipTimers[(NUM_AUDIO_INPUTS > 0) ? NUM_OUTPUT_LIGHTS : 0] = {};
  184. dsp::VuMeter2 vuMeter[(NUM_AUDIO_INPUTS == 2) ? 2 : 0];
  185. Audio() : port(this) {
  186. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  187. if (NUM_AUDIO_INPUTS == 2)
  188. configParam(LEVEL_PARAM, 0.f, 2.f, 1.f, "Level", " dB", -10, 40);
  189. for (int i = 0; i < NUM_AUDIO_INPUTS; i++)
  190. configInput(AUDIO_INPUTS + i, string::f("To \"device output %d\"", i + 1));
  191. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++)
  192. configOutput(AUDIO_OUTPUTS + i, string::f("From \"device input %d\"", i + 1));
  193. for (int i = 0; i < NUM_INPUT_LIGHTS; i++)
  194. configLight(INPUT_LIGHTS + 2 * i, string::f("Device output %d/%d status", 2 * i + 1, 2 * i + 2));
  195. for (int i = 0; i < NUM_OUTPUT_LIGHTS; i++)
  196. configLight(OUTPUT_LIGHTS + 2 * i, string::f("Device input %d/%d status", 2 * i + 1, 2 * i + 2));
  197. lightDivider.setDivision(512);
  198. float sampleTime = APP->engine->getSampleTime();
  199. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  200. dcFilters[i].setCutoffFreq(10.f * sampleTime);
  201. }
  202. onReset();
  203. }
  204. ~Audio() {
  205. // Close stream here before destructing AudioPort, so processBuffer() etc are not called on another thread while destructing.
  206. port.setDriverId(-1);
  207. }
  208. void onReset() override {
  209. port.setDriverId(-1);
  210. if (NUM_AUDIO_INPUTS == 2)
  211. dcFilterEnabled = true;
  212. else
  213. dcFilterEnabled = false;
  214. }
  215. void onSampleRateChange(const SampleRateChangeEvent& e) override {
  216. port.engineInputBuffer.clear();
  217. port.engineOutputBuffer.clear();
  218. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  219. dcFilters[i].setCutoffFreq(10.f * e.sampleTime);
  220. }
  221. }
  222. void process(const ProcessArgs& args) override {
  223. const float clipTime = 0.25f;
  224. // Push inputs to buffer
  225. if (port.deviceNumOutputs > 0) {
  226. dsp::Frame<NUM_AUDIO_INPUTS> inputFrame = {};
  227. for (int i = 0; i < port.deviceNumOutputs; i++) {
  228. // Get input
  229. float v = 0.f;
  230. if (inputs[AUDIO_INPUTS + i].isConnected())
  231. v = inputs[AUDIO_INPUTS + i].getVoltageSum() / 10.f;
  232. // Normalize right input to left on Audio-2
  233. else if (i == 1 && NUM_AUDIO_INPUTS == 2)
  234. v = inputFrame.samples[0];
  235. // Apply DC filter
  236. if (dcFilterEnabled) {
  237. dcFilters[i].process(v);
  238. v = dcFilters[i].highpass();
  239. }
  240. // Detect clipping
  241. if (NUM_AUDIO_INPUTS > 2) {
  242. if (std::fabs(v) >= 1.f)
  243. inputClipTimers[i / 2] = clipTime;
  244. }
  245. inputFrame.samples[i] = v;
  246. }
  247. // Audio-2: Apply gain from knob
  248. if (NUM_AUDIO_INPUTS == 2) {
  249. float gain = std::pow(params[LEVEL_PARAM].getValue(), 2.f);
  250. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  251. inputFrame.samples[i] *= gain;
  252. }
  253. }
  254. if (!port.engineInputBuffer.full()) {
  255. port.engineInputBuffer.push(inputFrame);
  256. }
  257. // Audio-2: VU meter process
  258. if (NUM_AUDIO_INPUTS == 2) {
  259. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  260. vuMeter[i].process(args.sampleTime, inputFrame.samples[i]);
  261. }
  262. }
  263. }
  264. else {
  265. // Audio-2: Clear VU meter
  266. if (NUM_AUDIO_INPUTS == 2) {
  267. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  268. vuMeter[i].reset();
  269. }
  270. }
  271. }
  272. // Pull outputs from buffer
  273. if (!port.engineOutputBuffer.empty()) {
  274. dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame = port.engineOutputBuffer.shift();
  275. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
  276. float v = outputFrame.samples[i];
  277. outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * v);
  278. // Detect clipping
  279. if (NUM_AUDIO_OUTPUTS > 2) {
  280. if (std::fabs(v) >= 1.f)
  281. outputClipTimers[i / 2] = clipTime;
  282. }
  283. }
  284. }
  285. else {
  286. // Zero outputs
  287. for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
  288. outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
  289. }
  290. }
  291. // Lights
  292. if (lightDivider.process()) {
  293. float lightTime = args.sampleTime * lightDivider.getDivision();
  294. // Audio-2: VU meter
  295. if (NUM_AUDIO_INPUTS == 2) {
  296. for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
  297. lights[VU_LIGHTS + i * 6 + 0].setBrightness(vuMeter[i].getBrightness(0, 0));
  298. lights[VU_LIGHTS + i * 6 + 1].setBrightness(vuMeter[i].getBrightness(-3, 0));
  299. lights[VU_LIGHTS + i * 6 + 2].setBrightness(vuMeter[i].getBrightness(-6, -3));
  300. lights[VU_LIGHTS + i * 6 + 3].setBrightness(vuMeter[i].getBrightness(-12, -6));
  301. lights[VU_LIGHTS + i * 6 + 4].setBrightness(vuMeter[i].getBrightness(-24, -12));
  302. lights[VU_LIGHTS + i * 6 + 5].setBrightness(vuMeter[i].getBrightness(-36, -24));
  303. }
  304. }
  305. // Audio-8 and Audio-16: pair state lights
  306. else {
  307. // Turn on light if at least one port is enabled in the nearby pair.
  308. for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) {
  309. bool active = port.deviceNumOutputs >= 2 * i + 1;
  310. bool clip = inputClipTimers[i] > 0.f;
  311. if (clip)
  312. inputClipTimers[i] -= lightTime;
  313. lights[INPUT_LIGHTS + i * 2 + 0].setBrightness(active && !clip);
  314. lights[INPUT_LIGHTS + i * 2 + 1].setBrightness(active && clip);
  315. }
  316. for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) {
  317. bool active = port.deviceNumInputs >= 2 * i + 1;
  318. bool clip = outputClipTimers[i] > 0.f;
  319. if (clip)
  320. outputClipTimers[i] -= lightTime;
  321. lights[OUTPUT_LIGHTS + i * 2 + 0].setBrightness(active & !clip);
  322. lights[OUTPUT_LIGHTS + i * 2 + 1].setBrightness(active & clip);
  323. }
  324. }
  325. }
  326. }
  327. json_t* dataToJson() override {
  328. json_t* rootJ = json_object();
  329. json_object_set_new(rootJ, "audio", port.toJson());
  330. json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled));
  331. return rootJ;
  332. }
  333. void dataFromJson(json_t* rootJ) override {
  334. json_t* audioJ = json_object_get(rootJ, "audio");
  335. if (audioJ)
  336. port.fromJson(audioJ);
  337. json_t* dcFilterJ = json_object_get(rootJ, "dcFilter");
  338. if (dcFilterJ)
  339. dcFilterEnabled = json_boolean_value(dcFilterJ);
  340. }
  341. };
  342. /** For Audio-2 module. */
  343. struct Audio2Display : LedDisplay {
  344. AudioDeviceMenuChoice* deviceChoice;
  345. LedDisplaySeparator* deviceSeparator;
  346. void setAudioPort(audio::Port* port) {
  347. math::Vec pos;
  348. deviceChoice = createWidget<AudioDeviceMenuChoice>(math::Vec());
  349. deviceChoice->box.size.x = box.size.x;
  350. deviceChoice->port = port;
  351. addChild(deviceChoice);
  352. pos = deviceChoice->box.getBottomLeft();
  353. deviceSeparator = createWidget<LedDisplaySeparator>(pos);
  354. deviceSeparator->box.size.x = box.size.x;
  355. addChild(deviceSeparator);
  356. }
  357. void drawLayer(const DrawArgs& args, int layer) override {
  358. if (layer == 1) {
  359. static const std::vector<float> posY = {
  360. mm2px(28.899 - 13.039),
  361. mm2px(34.196 - 13.039),
  362. mm2px(39.494 - 13.039),
  363. mm2px(44.791 - 13.039),
  364. mm2px(50.089 - 13.039),
  365. mm2px(55.386 - 13.039),
  366. };
  367. static const std::vector<std::string> texts = {
  368. " 0", "-3", "-6", "-12", "-24", "-36",
  369. };
  370. std::string fontPath = asset::system("res/fonts/Nunito-Bold.ttf");
  371. std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
  372. if (!font)
  373. return;
  374. nvgSave(args.vg);
  375. nvgFontFaceId(args.vg, font->handle);
  376. nvgFontSize(args.vg, 11);
  377. nvgTextLetterSpacing(args.vg, 0.0);
  378. nvgTextAlign(args.vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
  379. nvgFillColor(args.vg, nvgRGB(99, 99, 99));
  380. for (int i = 0; i < 6; i++) {
  381. nvgText(args.vg, 36.0, posY[i], texts[i].c_str(), NULL);
  382. }
  383. nvgRestore(args.vg);
  384. }
  385. LedDisplay::drawLayer(args, layer);
  386. }
  387. };
  388. template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
  389. struct AudioWidget : ModuleWidget {
  390. typedef Audio<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> TAudio;
  391. AudioWidget(TAudio* module) {
  392. setModule(module);
  393. if (NUM_AUDIO_INPUTS == 8 && NUM_AUDIO_OUTPUTS == 8) {
  394. setPanel(Svg::load(asset::system("res/Core/Audio8.svg")));
  395. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  396. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  397. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  398. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  399. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.81, 57.929)), module, TAudio::AUDIO_INPUTS + 0));
  400. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.359, 57.929)), module, TAudio::AUDIO_INPUTS + 1));
  401. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.909, 57.929)), module, TAudio::AUDIO_INPUTS + 2));
  402. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.459, 57.929)), module, TAudio::AUDIO_INPUTS + 3));
  403. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.81, 74.286)), module, TAudio::AUDIO_INPUTS + 4));
  404. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.359, 74.286)), module, TAudio::AUDIO_INPUTS + 5));
  405. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.909, 74.286)), module, TAudio::AUDIO_INPUTS + 6));
  406. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.459, 74.286)), module, TAudio::AUDIO_INPUTS + 7));
  407. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.81, 96.859)), module, TAudio::AUDIO_OUTPUTS + 0));
  408. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.359, 96.859)), module, TAudio::AUDIO_OUTPUTS + 1));
  409. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.909, 96.859)), module, TAudio::AUDIO_OUTPUTS + 2));
  410. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.459, 96.859)), module, TAudio::AUDIO_OUTPUTS + 3));
  411. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.81, 113.115)), module, TAudio::AUDIO_OUTPUTS + 4));
  412. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.359, 113.115)), module, TAudio::AUDIO_OUTPUTS + 5));
  413. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.909, 113.115)), module, TAudio::AUDIO_OUTPUTS + 6));
  414. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.459, 113.115)), module, TAudio::AUDIO_OUTPUTS + 7));
  415. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.54, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 0));
  416. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.774, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 1));
  417. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.54, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 2));
  418. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.774, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 3));
  419. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.54, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 0));
  420. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.638, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 1));
  421. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.54, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 2));
  422. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.638, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 3));
  423. AudioDisplay* display = createWidget<AudioDisplay>(mm2px(Vec(0.0, 13.039)));
  424. display->box.size = mm2px(Vec(50.8, 29.021));
  425. display->setAudioPort(module ? &module->port : NULL);
  426. addChild(display);
  427. }
  428. else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) {
  429. setPanel(Svg::load(asset::system("res/Core/Audio16.svg")));
  430. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  431. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  432. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  433. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  434. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.815, 57.929)), module, TAudio::AUDIO_INPUTS + 0));
  435. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.364, 57.929)), module, TAudio::AUDIO_INPUTS + 1));
  436. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.914, 57.929)), module, TAudio::AUDIO_INPUTS + 2));
  437. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.464, 57.929)), module, TAudio::AUDIO_INPUTS + 3));
  438. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.015, 57.929)), module, TAudio::AUDIO_INPUTS + 4));
  439. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.565, 57.914)), module, TAudio::AUDIO_INPUTS + 5));
  440. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.114, 57.914)), module, TAudio::AUDIO_INPUTS + 6));
  441. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.664, 57.914)), module, TAudio::AUDIO_INPUTS + 7));
  442. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.815, 74.276)), module, TAudio::AUDIO_INPUTS + 8));
  443. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.364, 74.276)), module, TAudio::AUDIO_INPUTS + 9));
  444. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(30.914, 74.276)), module, TAudio::AUDIO_INPUTS + 10));
  445. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.464, 74.276)), module, TAudio::AUDIO_INPUTS + 11));
  446. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(54.015, 74.291)), module, TAudio::AUDIO_INPUTS + 12));
  447. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(65.565, 74.276)), module, TAudio::AUDIO_INPUTS + 13));
  448. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(77.114, 74.276)), module, TAudio::AUDIO_INPUTS + 14));
  449. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(88.664, 74.276)), module, TAudio::AUDIO_INPUTS + 15));
  450. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.815, 96.859)), module, TAudio::AUDIO_OUTPUTS + 0));
  451. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.364, 96.859)), module, TAudio::AUDIO_OUTPUTS + 1));
  452. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.914, 96.859)), module, TAudio::AUDIO_OUTPUTS + 2));
  453. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.464, 96.859)), module, TAudio::AUDIO_OUTPUTS + 3));
  454. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.015, 96.859)), module, TAudio::AUDIO_OUTPUTS + 4));
  455. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.565, 96.859)), module, TAudio::AUDIO_OUTPUTS + 5));
  456. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.114, 96.859)), module, TAudio::AUDIO_OUTPUTS + 6));
  457. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.664, 96.859)), module, TAudio::AUDIO_OUTPUTS + 7));
  458. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.815, 113.115)), module, TAudio::AUDIO_OUTPUTS + 8));
  459. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(19.364, 113.115)), module, TAudio::AUDIO_OUTPUTS + 9));
  460. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(30.914, 113.115)), module, TAudio::AUDIO_OUTPUTS + 10));
  461. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(42.464, 113.115)), module, TAudio::AUDIO_OUTPUTS + 11));
  462. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(65.565, 112.962)), module, TAudio::AUDIO_OUTPUTS + 12));
  463. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(54.015, 113.115)), module, TAudio::AUDIO_OUTPUTS + 13));
  464. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(88.664, 112.962)), module, TAudio::AUDIO_OUTPUTS + 14));
  465. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(77.114, 113.115)), module, TAudio::AUDIO_OUTPUTS + 15));
  466. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.545, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 0));
  467. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.779, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 1));
  468. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.745, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 2));
  469. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(82.98, 52.168)), module, TAudio::INPUT_LIGHTS + 2 * 3));
  470. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.545, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 4));
  471. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.779, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 5));
  472. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.745, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 6));
  473. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(82.98, 68.53)), module, TAudio::INPUT_LIGHTS + 2 * 7));
  474. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.545, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 0));
  475. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.644, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 1));
  476. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.745, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 2));
  477. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(82.844, 90.791)), module, TAudio::OUTPUT_LIGHTS + 2 * 3));
  478. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(13.545, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 4));
  479. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(36.644, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 5));
  480. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(59.745, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 6));
  481. addChild(createLightCentered<SmallLight<GreenRedLight>>(mm2px(Vec(82.844, 107.097)), module, TAudio::OUTPUT_LIGHTS + 2 * 7));
  482. AudioDisplay* display = createWidget<AudioDisplay>(mm2px(Vec(0.0, 13.039)));
  483. display->box.size = mm2px(Vec(96.52, 29.021));
  484. display->setAudioPort(module ? &module->port : NULL);
  485. addChild(display);
  486. }
  487. else if (NUM_AUDIO_INPUTS == 2 && NUM_AUDIO_OUTPUTS == 2) {
  488. setPanel(Svg::load(asset::system("res/Core/Audio2.svg")));
  489. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  490. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  491. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  492. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  493. addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(12.869, 77.362)), module, TAudio::LEVEL_PARAM));
  494. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.285, 96.859)), module, TAudio::AUDIO_INPUTS + 0));
  495. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.122, 96.859)), module, TAudio::AUDIO_INPUTS + 1));
  496. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.285, 113.115)), module, TAudio::AUDIO_OUTPUTS + 0));
  497. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.122, 113.115)), module, TAudio::AUDIO_OUTPUTS + 1));
  498. Audio2Display* display = createWidget<Audio2Display>(mm2px(Vec(0.0, 13.039)));
  499. display->box.size = mm2px(Vec(25.4, 47.726));
  500. display->setAudioPort(module ? &module->port : NULL);
  501. addChild(display);
  502. addChild(createLightCentered<SmallSimpleLight<RedLight>>(mm2px(Vec(6.691, 28.899)), module, TAudio::VU_LIGHTS + 6 * 0 + 0));
  503. addChild(createLightCentered<SmallSimpleLight<RedLight>>(mm2px(Vec(18.709, 28.899)), module, TAudio::VU_LIGHTS + 6 * 1 + 0));
  504. addChild(createLightCentered<SmallSimpleLight<YellowLight>>(mm2px(Vec(6.691, 34.196)), module, TAudio::VU_LIGHTS + 6 * 0 + 1));
  505. addChild(createLightCentered<SmallSimpleLight<YellowLight>>(mm2px(Vec(18.709, 34.196)), module, TAudio::VU_LIGHTS + 6 * 1 + 1));
  506. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(6.691, 39.494)), module, TAudio::VU_LIGHTS + 6 * 0 + 2));
  507. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(18.709, 39.494)), module, TAudio::VU_LIGHTS + 6 * 1 + 2));
  508. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(6.691, 44.791)), module, TAudio::VU_LIGHTS + 6 * 0 + 3));
  509. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(18.709, 44.791)), module, TAudio::VU_LIGHTS + 6 * 1 + 3));
  510. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(6.691, 50.089)), module, TAudio::VU_LIGHTS + 6 * 0 + 4));
  511. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(18.709, 50.089)), module, TAudio::VU_LIGHTS + 6 * 1 + 4));
  512. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(6.691, 55.386)), module, TAudio::VU_LIGHTS + 6 * 0 + 5));
  513. addChild(createLightCentered<SmallSimpleLight<GreenLight>>(mm2px(Vec(18.709, 55.386)), module, TAudio::VU_LIGHTS + 6 * 1 + 5));
  514. // AudioButton example
  515. // AudioButton* audioButton_ADAT = createWidget<AudioButton_ADAT>(Vec(0, 0));
  516. // audioButton_ADAT->setAudioPort(module ? &module->port : NULL);
  517. // addChild(audioButton_ADAT);
  518. // AudioButton* audioButton_USB_B = createWidget<AudioButton_USB_B>(Vec(0, 40));
  519. // audioButton_USB_B->setAudioPort(module ? &module->port : NULL);
  520. // addChild(audioButton_USB_B);
  521. }
  522. }
  523. void appendContextMenu(Menu* menu) override {
  524. TAudio* module = dynamic_cast<TAudio*>(this->module);
  525. menu->addChild(new MenuSeparator);
  526. menu->addChild(createBoolMenuItem("Master audio module", "",
  527. [=]() {return module->port.isMaster();},
  528. [=](bool master) {module->port.setMaster(master);}
  529. ));
  530. menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled));
  531. }
  532. };
  533. Model* modelAudio2 = createModel<Audio<2, 2>, AudioWidget<2, 2>>("AudioInterface2");
  534. Model* modelAudio8 = createModel<Audio<8, 8>, AudioWidget<8, 8>>("AudioInterface");
  535. Model* modelAudio16 = createModel<Audio<16, 16>, AudioWidget<16, 16>>("AudioInterface16");
  536. } // namespace core
  537. } // namespace rack