#include "plugin.hpp" #include #include #include #include #include #include #include namespace rack { namespace core { template struct AudioInterfacePort : audio::Port { // std::mutex engineMutex; // std::condition_variable engineCv; // std::mutex audioMutex; // std::condition_variable audioCv; // // Audio thread produces, engine thread consumes // dsp::DoubleRingBuffer < dsp::Frame, (1 << 15) > inputBuffer; // // Audio thread consumes, engine thread produces // dsp::DoubleRingBuffer < dsp::Frame, (1 << 15) > outputBuffer; // bool active = false; // For checking getPrimaryModule() Module* module = NULL; const float* input = NULL; float* output = NULL; int frame = 0; int numFrames = 0; ~AudioInterfacePort() { // Close stream here before destructing AudioInterfacePort, so the mutexes are still valid when waiting to close. setDeviceId(-1, 0); } void processStream(const float* input, float* output, int frames) override { if (APP->engine->getPrimaryModule() != module) { // TEMP std::memset(output, 0, sizeof(float) * frames * numOutputs); return; } frame = 0; numFrames = frames; this->input = input; this->output = output; APP->engine->step(frames); // // Reactivate idle stream // if (!active) { // active = true; // inputBuffer.clear(); // outputBuffer.clear(); // } // if (numInputs > 0) { // // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't. // for (int i = 0; i < frames; i++) { // if (inputBuffer.full()) // break; // dsp::Frame inputFrame; // std::memset(&inputFrame, 0, sizeof(inputFrame)); // std::memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float)); // inputBuffer.push(inputFrame); // } // } // if (numOutputs > 0) { // std::unique_lock lock(audioMutex); // auto cond = [&] { // return (outputBuffer.size() >= (size_t) frames); // }; // auto timeout = std::chrono::milliseconds(100); // if (audioCv.wait_for(lock, timeout, cond)) { // // Consume audio block // for (int i = 0; i < frames; i++) { // dsp::Frame f = outputBuffer.shift(); // for (int j = 0; j < numOutputs; j++) { // output[numOutputs * i + j] = clamp(f.samples[j], -1.f, 1.f); // } // } // } // else { // // Timed out, fill output with zeros // std::memset(output, 0, frames * numOutputs * sizeof(float)); // // DEBUG("Audio Interface Port underflow"); // } // } // // Notify engine when finished processing // engineCv.notify_one(); } void onCloseStream() override { // inputBuffer.clear(); // outputBuffer.clear(); } void onChannelsChange() override { } }; template struct AudioInterface : Module { enum ParamIds { NUM_PARAMS }; enum InputIds { ENUMS(AUDIO_INPUTS, NUM_AUDIO_INPUTS), NUM_INPUTS }; enum OutputIds { ENUMS(AUDIO_OUTPUTS, NUM_AUDIO_OUTPUTS), NUM_OUTPUTS }; enum LightIds { ENUMS(INPUT_LIGHTS, NUM_AUDIO_INPUTS / 2), ENUMS(OUTPUT_LIGHTS, NUM_AUDIO_OUTPUTS / 2), NUM_LIGHTS }; AudioInterfacePort port; // int lastSampleRate = 0; // int lastNumOutputs = -1; // int lastNumInputs = -1; // dsp::SampleRateConverter inputSrc; // dsp::SampleRateConverter outputSrc; // // in rack's sample rate // dsp::DoubleRingBuffer, 16> inputBuffer; // dsp::DoubleRingBuffer, 16> outputBuffer; AudioInterface() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); for (int i = 0; i < NUM_AUDIO_INPUTS; i++) configInput(AUDIO_INPUTS + i, string::f("To device %d", i + 1)); for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) configOutput(AUDIO_OUTPUTS + i, string::f("From device %d", i + 1)); port.maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS); port.module = this; onSampleRateChange(); } void process(const ProcessArgs& args) override { // Claim primary module if there is none if (!APP->engine->getPrimaryModule()) { APP->engine->setPrimaryModule(this); } // Get inputs for (int i = 0; i < port.numOutputs; i++) { float v = inputs[AUDIO_INPUTS + i].getVoltage() / 10.f; port.output[port.frame * port.numOutputs + i] = v; } // Set outputs for (int i = 0; i < port.numInputs; i++) { float v = port.input[port.frame * port.numInputs + i]; outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * v); } for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) { outputs[AUDIO_OUTPUTS + i].setVoltage(0.f); } port.frame++; // // Update SRC states // inputSrc.setRates(port.sampleRate, args.sampleRate); // outputSrc.setRates(args.sampleRate, port.sampleRate); // inputSrc.setChannels(port.numInputs); // outputSrc.setChannels(port.numOutputs); // // Inputs: audio engine -> rack engine // if (port.active && port.numInputs > 0) { // // Wait until inputs are present // // Give up after a timeout in case the audio device is being unresponsive. // std::unique_lock lock(port.engineMutex); // auto cond = [&] { // return (!port.inputBuffer.empty()); // }; // auto timeout = std::chrono::milliseconds(200); // if (port.engineCv.wait_for(lock, timeout, cond)) { // // Convert inputs // int inLen = port.inputBuffer.size(); // int outLen = inputBuffer.capacity(); // inputSrc.process(port.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); // port.inputBuffer.startIncr(inLen); // inputBuffer.endIncr(outLen); // } // else { // // Give up on pulling input // port.active = false; // // DEBUG("Audio Interface underflow"); // } // } // // Take input from buffer // dsp::Frame inputFrame; // if (!inputBuffer.empty()) { // inputFrame = inputBuffer.shift(); // } // else { // std::memset(&inputFrame, 0, sizeof(inputFrame)); // } // for (int i = 0; i < port.numInputs; i++) { // outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * inputFrame.samples[i]); // } // for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) { // outputs[AUDIO_OUTPUTS + i].setVoltage(0.f); // } // // Outputs: rack engine -> audio engine // if (port.active && port.numOutputs > 0) { // // Get and push output SRC frame // if (!outputBuffer.full()) { // dsp::Frame outputFrame; // for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) { // outputFrame.samples[i] = inputs[AUDIO_INPUTS + i].getVoltageSum() / 10.f; // } // outputBuffer.push(outputFrame); // } // if (outputBuffer.full()) { // // Wait until enough outputs are consumed // // Give up after a timeout in case the audio device is being unresponsive. // auto cond = [&] { // return (port.outputBuffer.size() < (size_t) port.blockSize); // }; // if (!cond()) // APP->engine->yieldWorkers(); // std::unique_lock lock(port.engineMutex); // auto timeout = std::chrono::milliseconds(200); // if (port.engineCv.wait_for(lock, timeout, cond)) { // // Push converted output // int inLen = outputBuffer.size(); // int outLen = port.outputBuffer.capacity(); // outputSrc.process(outputBuffer.startData(), &inLen, port.outputBuffer.endData(), &outLen); // outputBuffer.startIncr(inLen); // port.outputBuffer.endIncr(outLen); // } // else { // // Give up on pushing output // port.active = false; // outputBuffer.clear(); // // DEBUG("Audio Interface underflow"); // } // } // // Notify audio thread that an output is potentially ready // port.audioCv.notify_one(); // } // Turn on light if at least one port is enabled in the nearby pair for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) { lights[INPUT_LIGHTS + i].setBrightness(port.numOutputs >= 2 * i + 1); } for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) { lights[OUTPUT_LIGHTS + i].setBrightness(port.numInputs >= 2 * i + 1); } } json_t* dataToJson() override { json_t* rootJ = json_object(); json_object_set_new(rootJ, "audio", port.toJson()); return rootJ; } void dataFromJson(json_t* rootJ) override { json_t* audioJ = json_object_get(rootJ, "audio"); if (audioJ) port.fromJson(audioJ); } void onReset() override { port.setDeviceId(-1, 0); } }; template struct PrimaryModuleItem : MenuItem { TAudioInterface* module; void onAction(const event::Action& e) override { APP->engine->setPrimaryModule(module); } }; template struct AudioInterfaceWidget : ModuleWidget { typedef AudioInterface TAudioInterface; AudioInterfaceWidget(TAudioInterface* module) { setModule(module); if (NUM_AUDIO_INPUTS == 8 && NUM_AUDIO_OUTPUTS == 8) { setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface.svg"))); addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addInput(createInput(mm2px(Vec(3.7069211, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 0)); addInput(createInput(mm2px(Vec(15.307249, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 1)); addInput(createInput(mm2px(Vec(26.906193, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 2)); addInput(createInput(mm2px(Vec(38.506519, 55.530807)), module, TAudioInterface::AUDIO_INPUTS + 3)); addInput(createInput(mm2px(Vec(3.7069209, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 4)); addInput(createInput(mm2px(Vec(15.307249, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 5)); addInput(createInput(mm2px(Vec(26.906193, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 6)); addInput(createInput(mm2px(Vec(38.506519, 70.144905)), module, TAudioInterface::AUDIO_INPUTS + 7)); addOutput(createOutput(mm2px(Vec(3.7069209, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 0)); addOutput(createOutput(mm2px(Vec(15.307249, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 1)); addOutput(createOutput(mm2px(Vec(26.906193, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 2)); addOutput(createOutput(mm2px(Vec(38.506519, 92.143906)), module, TAudioInterface::AUDIO_OUTPUTS + 3)); addOutput(createOutput(mm2px(Vec(3.7069209, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 4)); addOutput(createOutput(mm2px(Vec(15.307249, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 5)); addOutput(createOutput(mm2px(Vec(26.906193, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 6)); addOutput(createOutput(mm2px(Vec(38.506523, 108.1443)), module, TAudioInterface::AUDIO_OUTPUTS + 7)); addChild(createLight>(mm2px(Vec(12.524985, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 0)); addChild(createLight>(mm2px(Vec(35.725647, 54.577202)), module, TAudioInterface::INPUT_LIGHTS + 1)); addChild(createLight>(mm2px(Vec(12.524985, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 2)); addChild(createLight>(mm2px(Vec(35.725647, 69.158226)), module, TAudioInterface::INPUT_LIGHTS + 3)); addChild(createLight>(mm2px(Vec(12.524985, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 0)); addChild(createLight>(mm2px(Vec(35.725647, 91.147583)), module, TAudioInterface::OUTPUT_LIGHTS + 1)); addChild(createLight>(mm2px(Vec(12.524985, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 2)); addChild(createLight>(mm2px(Vec(35.725647, 107.17003)), module, TAudioInterface::OUTPUT_LIGHTS + 3)); AudioWidget* audioWidget = createWidget(mm2px(Vec(3.2122073, 14.837339))); audioWidget->box.size = mm2px(Vec(44, 28)); audioWidget->setAudioPort(module ? &module->port : NULL); addChild(audioWidget); } else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) { setPanel(APP->window->loadSvg(asset::system("res/Core/AudioInterface16.svg"))); addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addInput(createInputCentered(mm2px(Vec(7.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 0)); addInput(createInputCentered(mm2px(Vec(19.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 1)); addInput(createInputCentered(mm2px(Vec(30.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 2)); addInput(createInputCentered(mm2px(Vec(42.461, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 3)); addInput(createInputCentered(mm2px(Vec(54.06, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 4)); addInput(createInputCentered(mm2px(Vec(65.661, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 5)); addInput(createInputCentered(mm2px(Vec(77.26, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 6)); addInput(createInputCentered(mm2px(Vec(88.86, 59.638)), module, TAudioInterface::AUDIO_INPUTS + 7)); addInput(createInputCentered(mm2px(Vec(7.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 8)); addInput(createInputCentered(mm2px(Vec(19.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 9)); addInput(createInputCentered(mm2px(Vec(30.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 10)); addInput(createInputCentered(mm2px(Vec(42.461, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 11)); addInput(createInputCentered(mm2px(Vec(54.06, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 12)); addInput(createInputCentered(mm2px(Vec(65.661, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 13)); addInput(createInputCentered(mm2px(Vec(77.26, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 14)); addInput(createInputCentered(mm2px(Vec(88.86, 74.251)), module, TAudioInterface::AUDIO_INPUTS + 15)); addOutput(createOutputCentered(mm2px(Vec(7.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 0)); addOutput(createOutputCentered(mm2px(Vec(19.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 1)); addOutput(createOutputCentered(mm2px(Vec(30.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 2)); addOutput(createOutputCentered(mm2px(Vec(42.461, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 3)); addOutput(createOutputCentered(mm2px(Vec(54.06, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 4)); addOutput(createOutputCentered(mm2px(Vec(65.661, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 5)); addOutput(createOutputCentered(mm2px(Vec(77.26, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 6)); addOutput(createOutputCentered(mm2px(Vec(88.86, 96.251)), module, TAudioInterface::AUDIO_OUTPUTS + 7)); addOutput(createOutputCentered(mm2px(Vec(7.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 8)); addOutput(createOutputCentered(mm2px(Vec(19.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 9)); addOutput(createOutputCentered(mm2px(Vec(30.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 10)); addOutput(createOutputCentered(mm2px(Vec(42.461, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 11)); addOutput(createOutputCentered(mm2px(Vec(54.06, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 12)); addOutput(createOutputCentered(mm2px(Vec(65.661, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 13)); addOutput(createOutputCentered(mm2px(Vec(77.26, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 14)); addOutput(createOutputCentered(mm2px(Vec(88.86, 112.252)), module, TAudioInterface::AUDIO_OUTPUTS + 15)); addChild(createLightCentered>(mm2px(Vec(13.46, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 0)); addChild(createLightCentered>(mm2px(Vec(36.661, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 1)); addChild(createLightCentered>(mm2px(Vec(59.861, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 2)); addChild(createLightCentered>(mm2px(Vec(83.061, 55.667)), module, TAudioInterface::INPUT_LIGHTS + 3)); addChild(createLightCentered>(mm2px(Vec(13.46, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 4)); addChild(createLightCentered>(mm2px(Vec(36.661, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 5)); addChild(createLightCentered>(mm2px(Vec(59.861, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 6)); addChild(createLightCentered>(mm2px(Vec(83.061, 70.248)), module, TAudioInterface::INPUT_LIGHTS + 7)); addChild(createLightCentered>(mm2px(Vec(13.46, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 0)); addChild(createLightCentered>(mm2px(Vec(36.661, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 1)); addChild(createLightCentered>(mm2px(Vec(59.861, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 2)); addChild(createLightCentered>(mm2px(Vec(83.061, 92.238)), module, TAudioInterface::OUTPUT_LIGHTS + 3)); addChild(createLightCentered>(mm2px(Vec(13.46, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 4)); addChild(createLightCentered>(mm2px(Vec(36.661, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 5)); addChild(createLightCentered>(mm2px(Vec(59.861, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 6)); addChild(createLightCentered>(mm2px(Vec(83.061, 108.259)), module, TAudioInterface::OUTPUT_LIGHTS + 7)); AudioWidget* audioWidget = createWidget(mm2px(Vec(2.57, 14.839))); audioWidget->box.size = mm2px(Vec(91.382, 28.0)); audioWidget->setAudioPort(module ? &module->port : NULL); addChild(audioWidget); } } void appendContextMenu(Menu* menu) override { TAudioInterface* module = dynamic_cast(this->module); menu->addChild(new MenuEntry); PrimaryModuleItem* primaryModuleItem = new PrimaryModuleItem; primaryModuleItem->text = "Primary audio module"; primaryModuleItem->rightText = CHECKMARK(APP->engine->getPrimaryModule() == module); primaryModuleItem->module = module; menu->addChild(primaryModuleItem); } }; Model* modelAudioInterface = createModel, AudioInterfaceWidget<8, 8>>("AudioInterface"); Model* modelAudioInterface16 = createModel, AudioInterfaceWidget<16, 16>>("AudioInterface16"); } // namespace core } // namespace rack