diff --git a/include/app/AudioWidget.hpp b/include/app/AudioWidget.hpp index f70c4d09..d36d4744 100644 --- a/include/app/AudioWidget.hpp +++ b/include/app/AudioWidget.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include @@ -20,5 +21,11 @@ struct AudioWidget : LedDisplay { }; +/** Appends menu items to the given menu with driver, device, etc. +Useful alternative to putting an AudioWidget on your module's panel. +*/ +void appendAudioMenu(ui::Menu* menu, audio::Port* port); + + } // namespace app } // namespace rack diff --git a/include/app/MidiWidget.hpp b/include/app/MidiWidget.hpp index 7281d056..9294b66d 100644 --- a/include/app/MidiWidget.hpp +++ b/include/app/MidiWidget.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include @@ -18,5 +19,11 @@ struct MidiWidget : LedDisplay { }; +/** Appends menu items to the given menu with driver, device, etc. +Useful alternative to putting a MidiWidget on your module's panel. +*/ +void appendMidiMenu(ui::Menu* menu, midi::Port* port); + + } // namespace app } // namespace rack diff --git a/src/app/AudioWidget.cpp b/src/app/AudioWidget.cpp index d3e953d4..8c3e8521 100644 --- a/src/app/AudioWidget.cpp +++ b/src/app/AudioWidget.cpp @@ -6,7 +6,7 @@ namespace rack { namespace app { -struct AudioDriverItem : ui::MenuItem { +struct AudioDriverValueItem : ui::MenuItem { audio::Port* port; int driverId; void onAction(const event::Action& e) override { @@ -14,22 +14,26 @@ struct AudioDriverItem : ui::MenuItem { } }; +static void appendAudioDriverMenu(ui::Menu* menu, audio::Port* port) { + if (!port) + return; + + for (int driverId : audio::getDriverIds()) { + AudioDriverValueItem* item = new AudioDriverValueItem; + item->port = port; + item->driverId = driverId; + item->text = audio::getDriver(driverId)->getName(); + item->rightText = CHECKMARK(item->driverId == port->getDriverId()); + menu->addChild(item); + } +} + struct AudioDriverChoice : LedDisplayChoice { audio::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("Audio driver")); - for (int driverId : audio::getDriverIds()) { - AudioDriverItem* item = new AudioDriverItem; - item->port = port; - item->driverId = driverId; - item->text = audio::getDriver(driverId)->getName(); - item->rightText = CHECKMARK(item->driverId == port->getDriverId()); - menu->addChild(item); - } + appendAudioDriverMenu(menu, port); } void step() override { text = ""; @@ -47,8 +51,17 @@ struct AudioDriverChoice : LedDisplayChoice { } }; +struct AudioDriverItem : ui::MenuItem { + audio::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendAudioDriverMenu(menu, port); + return menu; + } +}; + -struct AudioDeviceItem : ui::MenuItem { +struct AudioDeviceValueItem : ui::MenuItem { audio::Port* port; int deviceId; int offset; @@ -58,39 +71,44 @@ struct AudioDeviceItem : ui::MenuItem { } }; +static void appendAudioDeviceMenu(ui::Menu* menu, audio::Port* port) { + if (!port || !port->driver) + return; + + { + AudioDeviceValueItem* item = new AudioDeviceValueItem; + item->port = port; + item->deviceId = -1; + item->text = "(No device)"; + item->rightText = CHECKMARK(port->getDeviceId() == -1); + menu->addChild(item); + } + + for (int deviceId : port->driver->getDeviceIds()) { + int channels = std::max(port->driver->getDeviceNumInputs(deviceId), port->driver->getDeviceNumOutputs(deviceId)); + // Prevents devices with a ridiculous number of channels from being displayed + const int maxTotalChannels = 128; + channels = std::min(maxTotalChannels, channels); + + for (int offset = 0; offset < channels; offset += port->maxChannels) { + AudioDeviceValueItem* item = new AudioDeviceValueItem; + item->port = port; + item->deviceId = deviceId; + item->offset = offset; + item->text = port->driver->getDeviceDetail(deviceId, offset, port->maxChannels); + item->rightText = CHECKMARK(item->deviceId == port->getDeviceId() && item->offset == port->offset); + menu->addChild(item); + } + } +} + struct AudioDeviceChoice : LedDisplayChoice { audio::Port* port; void onAction(const event::Action& e) override { - if (!port || !port->driver) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("Audio device")); - { - AudioDeviceItem* item = new AudioDeviceItem; - item->port = port; - item->deviceId = -1; - item->text = "(No device)"; - item->rightText = CHECKMARK(port->getDeviceId() == -1); - menu->addChild(item); - } - for (int deviceId : port->driver->getDeviceIds()) { - int channels = std::max(port->driver->getDeviceNumInputs(deviceId), port->driver->getDeviceNumOutputs(deviceId)); - // Prevents devices with a ridiculous number of channels from being displayed - const int maxTotalChannels = 128; - channels = std::min(maxTotalChannels, channels); - - for (int offset = 0; offset < channels; offset += port->maxChannels) { - AudioDeviceItem* item = new AudioDeviceItem; - item->port = port; - item->deviceId = deviceId; - item->offset = offset; - item->text = port->driver->getDeviceDetail(deviceId, offset, port->maxChannels); - item->rightText = CHECKMARK(item->deviceId == port->getDeviceId() && item->offset == port->offset); - menu->addChild(item); - } - } + appendAudioDeviceMenu(menu, port); } void step() override { text = ""; @@ -108,8 +126,17 @@ struct AudioDeviceChoice : LedDisplayChoice { } }; +struct AudioDeviceItem : ui::MenuItem { + audio::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendAudioDeviceMenu(menu, port); + return menu; + } +}; -struct AudioSampleRateItem : ui::MenuItem { + +struct AudioSampleRateValueItem : ui::MenuItem { audio::Port* port; int sampleRate; void onAction(const event::Action& e) override { @@ -117,26 +144,30 @@ struct AudioSampleRateItem : ui::MenuItem { } }; +static void appendAudioSampleRateMenu(ui::Menu* menu, audio::Port* port) { + if (!port) + return; + + std::vector sampleRates = port->getSampleRates(); + if (sampleRates.empty()) { + menu->addChild(createMenuLabel("(Locked by device)")); + } + for (int sampleRate : sampleRates) { + AudioSampleRateValueItem* item = new AudioSampleRateValueItem; + item->port = port; + item->sampleRate = sampleRate; + item->text = string::f("%g kHz", sampleRate / 1000.0); + item->rightText = CHECKMARK(item->sampleRate == port->getSampleRate()); + menu->addChild(item); + } +} + struct AudioSampleRateChoice : LedDisplayChoice { audio::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("Sample rate")); - std::vector sampleRates = port->getSampleRates(); - if (sampleRates.empty()) { - menu->addChild(createMenuLabel("(Locked by device)")); - } - for (int sampleRate : sampleRates) { - AudioSampleRateItem* item = new AudioSampleRateItem; - item->port = port; - item->sampleRate = sampleRate; - item->text = string::f("%g kHz", sampleRate / 1000.0); - item->rightText = CHECKMARK(item->sampleRate == port->getSampleRate()); - menu->addChild(item); - } + appendAudioSampleRateMenu(menu, port); } void step() override { text = ""; @@ -155,8 +186,17 @@ struct AudioSampleRateChoice : LedDisplayChoice { } }; +struct AudioSampleRateItem : ui::MenuItem { + audio::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendAudioSampleRateMenu(menu, port); + return menu; + } +}; + -struct AudioBlockSizeItem : ui::MenuItem { +struct AudioBlockSizeValueItem : ui::MenuItem { audio::Port* port; int blockSize; void onAction(const event::Action& e) override { @@ -164,27 +204,31 @@ struct AudioBlockSizeItem : ui::MenuItem { } }; +static void appendAudioBlockSizeMenu(ui::Menu* menu, audio::Port* port) { + if (!port) + return; + + std::vector blockSizes = port->getBlockSizes(); + if (blockSizes.empty()) { + menu->addChild(createMenuLabel("(Locked by device)")); + } + for (int blockSize : blockSizes) { + AudioBlockSizeValueItem* item = new AudioBlockSizeValueItem; + item->port = port; + item->blockSize = blockSize; + float latency = (float) blockSize / port->getSampleRate() * 1000.0; + item->text = string::f("%d (%.1f ms)", blockSize, latency); + item->rightText = CHECKMARK(item->blockSize == port->getBlockSize()); + menu->addChild(item); + } +} + struct AudioBlockSizeChoice : LedDisplayChoice { audio::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("Block size")); - std::vector blockSizes = port->getBlockSizes(); - if (blockSizes.empty()) { - menu->addChild(createMenuLabel("(Locked by device)")); - } - for (int blockSize : blockSizes) { - AudioBlockSizeItem* item = new AudioBlockSizeItem; - item->port = port; - item->blockSize = blockSize; - float latency = (float) blockSize / port->getSampleRate() * 1000.0; - item->text = string::f("%d (%.1f ms)", blockSize, latency); - item->rightText = CHECKMARK(item->blockSize == port->getBlockSize()); - menu->addChild(item); - } + appendAudioBlockSizeMenu(menu, port); } void step() override { text = ""; @@ -202,6 +246,15 @@ struct AudioBlockSizeChoice : LedDisplayChoice { } }; +struct AudioBlockSizeItem : ui::MenuItem { + audio::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendAudioBlockSizeMenu(menu, port); + return menu; + } +}; + void AudioWidget::setAudioPort(audio::Port* port) { clearChildren(); @@ -250,5 +303,24 @@ void AudioWidget::setAudioPort(audio::Port* port) { } +void appendAudioMenu(ui::Menu* menu, audio::Port* port) { + AudioDriverItem* driverItem = createMenuItem("Audio driver", RIGHT_ARROW); + driverItem->port = port; + menu->addChild(driverItem); + + AudioDeviceItem* deviceItem = createMenuItem("Audio device", RIGHT_ARROW); + deviceItem->port = port; + menu->addChild(deviceItem); + + AudioSampleRateItem* sampleRateItem = createMenuItem("Sample rate", RIGHT_ARROW); + sampleRateItem->port = port; + menu->addChild(sampleRateItem); + + AudioBlockSizeItem* blockSizeItem = createMenuItem("Block size", RIGHT_ARROW); + blockSizeItem->port = port; + menu->addChild(blockSizeItem); +} + + } // namespace app } // namespace rack diff --git a/src/app/MidiWidget.cpp b/src/app/MidiWidget.cpp index 9e346793..0120b0a9 100644 --- a/src/app/MidiWidget.cpp +++ b/src/app/MidiWidget.cpp @@ -6,7 +6,7 @@ namespace rack { namespace app { -struct MidiDriverItem : ui::MenuItem { +struct MidiDriverValueItem : ui::MenuItem { midi::Port* port; int driverId; void onAction(const event::Action& e) override { @@ -14,22 +14,26 @@ struct MidiDriverItem : ui::MenuItem { } }; +static void appendMidiDriverMenu(ui::Menu* menu, midi::Port* port) { + if (!port) + return; + + for (int driverId : port->getDriverIds()) { + MidiDriverValueItem* item = new MidiDriverValueItem; + item->port = port; + item->driverId = driverId; + item->text = port->getDriverName(driverId); + item->rightText = CHECKMARK(item->driverId == port->driverId); + menu->addChild(item); + } +} + struct MidiDriverChoice : LedDisplayChoice { midi::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("MIDI driver")); - for (int driverId : port->getDriverIds()) { - MidiDriverItem* item = new MidiDriverItem; - item->port = port; - item->driverId = driverId; - item->text = port->getDriverName(driverId); - item->rightText = CHECKMARK(item->driverId == port->driverId); - menu->addChild(item); - } + appendMidiDriverMenu(menu, port); } void step() override { text = port ? port->getDriverName(port->driverId) : ""; @@ -43,7 +47,17 @@ struct MidiDriverChoice : LedDisplayChoice { } }; -struct MidiDeviceItem : ui::MenuItem { +struct MidiDriverItem : ui::MenuItem { + midi::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendMidiDriverMenu(menu, port); + return menu; + } +}; + + +struct MidiDeviceValueItem : ui::MenuItem { midi::Port* port; int deviceId; void onAction(const event::Action& e) override { @@ -51,30 +65,35 @@ struct MidiDeviceItem : ui::MenuItem { } }; +static void appendMidiDeviceMenu(ui::Menu* menu, midi::Port* port) { + if (!port) + return; + + { + MidiDeviceValueItem* item = new MidiDeviceValueItem; + item->port = port; + item->deviceId = -1; + item->text = "(No device)"; + item->rightText = CHECKMARK(item->deviceId == port->deviceId); + menu->addChild(item); + } + + for (int deviceId : port->getDeviceIds()) { + MidiDeviceValueItem* item = new MidiDeviceValueItem; + item->port = port; + item->deviceId = deviceId; + item->text = port->getDeviceName(deviceId); + item->rightText = CHECKMARK(item->deviceId == port->deviceId); + menu->addChild(item); + } +} + struct MidiDeviceChoice : LedDisplayChoice { midi::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("MIDI device")); - { - MidiDeviceItem* item = new MidiDeviceItem; - item->port = port; - item->deviceId = -1; - item->text = "(No device)"; - item->rightText = CHECKMARK(item->deviceId == port->deviceId); - menu->addChild(item); - } - for (int deviceId : port->getDeviceIds()) { - MidiDeviceItem* item = new MidiDeviceItem; - item->port = port; - item->deviceId = deviceId; - item->text = port->getDeviceName(deviceId); - item->rightText = CHECKMARK(item->deviceId == port->deviceId); - menu->addChild(item); - } + appendMidiDeviceMenu(menu, port); } void step() override { text = port ? port->getDeviceName(port->deviceId) : ""; @@ -88,7 +107,17 @@ struct MidiDeviceChoice : LedDisplayChoice { } }; -struct MidiChannelItem : ui::MenuItem { +struct MidiDeviceItem : ui::MenuItem { + midi::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendMidiDeviceMenu(menu, port); + return menu; + } +}; + + +struct MidiChannelValueItem : ui::MenuItem { midi::Port* port; int channel; void onAction(const event::Action& e) override { @@ -96,28 +125,41 @@ struct MidiChannelItem : ui::MenuItem { } }; +static void appendMidiChannelMenu(ui::Menu* menu, midi::Port* port) { + if (!port) + return; + + for (int channel : port->getChannels()) { + MidiChannelValueItem* item = new MidiChannelValueItem; + item->port = port; + item->channel = channel; + item->text = port->getChannelName(channel); + item->rightText = CHECKMARK(item->channel == port->channel); + menu->addChild(item); + } +} + struct MidiChannelChoice : LedDisplayChoice { midi::Port* port; void onAction(const event::Action& e) override { - if (!port) - return; - ui::Menu* menu = createMenu(); menu->addChild(createMenuLabel("MIDI channel")); - for (int channel : port->getChannels()) { - MidiChannelItem* item = new MidiChannelItem; - item->port = port; - item->channel = channel; - item->text = port->getChannelName(channel); - item->rightText = CHECKMARK(item->channel == port->channel); - menu->addChild(item); - } + appendMidiChannelMenu(menu, port); } void step() override { text = port ? port->getChannelName(port->channel) : "Channel 1"; } }; +struct MidiChannelItem : ui::MenuItem { + midi::Port* port; + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + appendMidiChannelMenu(menu, port); + return menu; + } +}; + void MidiWidget::setMidiPort(midi::Port* port) { clearChildren(); @@ -154,5 +196,20 @@ void MidiWidget::setMidiPort(midi::Port* port) { } +void appendMidiMenu(ui::Menu* menu, midi::Port* port) { + MidiDriverItem* driverItem = createMenuItem("MIDI driver", RIGHT_ARROW); + driverItem->port = port; + menu->addChild(driverItem); + + MidiDeviceItem* deviceItem = createMenuItem("MIDI device", RIGHT_ARROW); + deviceItem->port = port; + menu->addChild(deviceItem); + + MidiChannelItem* channelItem = createMenuItem("MIDI channel", RIGHT_ARROW); + channelItem->port = port; + menu->addChild(channelItem); +} + + } // namespace app } // namespace rack