#include "app/AudioWidget.hpp" #include "helpers.hpp" namespace rack { namespace app { struct AudioDriverItem : ui::MenuItem { audio::Port *port; int driverId; void onAction(const event::Action &e) override { port->setDriverId(driverId); } }; 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 : port->getDriverIds()) { AudioDriverItem *item = new AudioDriverItem; item->port = port; item->driverId = driverId; item->text = port->getDriverName(driverId); item->rightText = CHECKMARK(item->driverId == port->driverId); menu->addChild(item); } } void step() override { text = (box.size.x >= 200.0) ? "Driver: " : ""; std::string driverName = (port) ? port->getDriverName(port->driverId) : ""; if (driverName != "") { text += driverName; color.a = 1.f; } else { text += "(No driver)"; color.a = 0.5f; } } }; struct AudioDeviceItem : ui::MenuItem { audio::Port *port; int deviceId; int offset; void onAction(const event::Action &e) override { port->setDeviceId(deviceId, offset); } }; struct AudioDeviceChoice : LedDisplayChoice { audio::Port *port; /** Prevents devices with a ridiculous number of channels from being displayed */ int maxTotalChannels = 128; void onAction(const event::Action &e) override { if (!port) return; ui::Menu *menu = createMenu(); menu->addChild(createMenuLabel("Audio device")); int deviceCount = port->getDeviceCount(); { AudioDeviceItem *item = new AudioDeviceItem; item->port = port; item->deviceId = -1; item->text = "(No device)"; item->rightText = CHECKMARK(item->deviceId == port->deviceId); menu->addChild(item); } for (int deviceId = 0; deviceId < deviceCount; deviceId++) { int channels = std::min(maxTotalChannels, port->getDeviceChannels(deviceId)); 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->getDeviceDetail(deviceId, offset); item->rightText = CHECKMARK(item->deviceId == port->deviceId && item->offset == port->offset); menu->addChild(item); } } } void step() override { text = (box.size.x >= 200.0) ? "Device: " : ""; std::string detail = (port) ? port->getDeviceDetail(port->deviceId, port->offset) : ""; if (detail != "") { text += detail; color.a = (detail == "") ? 0.5f : 1.f; } else { text += "(No device)"; color.a = 0.5f; } } }; struct AudioSampleRateItem : ui::MenuItem { audio::Port *port; int sampleRate; void onAction(const event::Action &e) override { port->setSampleRate(sampleRate); } }; 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->sampleRate); menu->addChild(item); } } void step() override { text = (box.size.x >= 100.0) ? "Rate: " : ""; if (port) { text += string::f("%g kHz", port->sampleRate / 1000.0); } else { text += "0 kHz"; } } }; struct AudioBlockSizeItem : ui::MenuItem { audio::Port *port; int blockSize; void onAction(const event::Action &e) override { port->setBlockSize(blockSize); } }; 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->sampleRate * 1000.0; item->text = string::f("%d (%.1f ms)", blockSize, latency); item->rightText = CHECKMARK(item->blockSize == port->blockSize); menu->addChild(item); } } void step() override { text = (box.size.x >= 100.0) ? "Block size: " : ""; if (port) { text += string::f("%d", port->blockSize); } else { text += "0"; } } }; void AudioWidget::setAudioPort(audio::Port *port) { clearChildren(); math::Vec pos; AudioDriverChoice *driverChoice = createWidget(pos); driverChoice->box.size.x = box.size.x; driverChoice->port = port; addChild(driverChoice); pos = driverChoice->box.getBottomLeft(); this->driverChoice = driverChoice; this->driverSeparator = createWidget(pos); this->driverSeparator->box.size.x = box.size.x; addChild(this->driverSeparator); AudioDeviceChoice *deviceChoice = createWidget(pos); deviceChoice->box.size.x = box.size.x; deviceChoice->port = port; addChild(deviceChoice); pos = deviceChoice->box.getBottomLeft(); this->deviceChoice = deviceChoice; this->deviceSeparator = createWidget(pos); this->deviceSeparator->box.size.x = box.size.x; addChild(this->deviceSeparator); AudioSampleRateChoice *sampleRateChoice = createWidget(pos); sampleRateChoice->box.size.x = box.size.x / 2; sampleRateChoice->port = port; addChild(sampleRateChoice); this->sampleRateChoice = sampleRateChoice; this->sampleRateSeparator = createWidget(pos); this->sampleRateSeparator->box.pos.x = box.size.x / 2; this->sampleRateSeparator->box.size.y = this->sampleRateChoice->box.size.y; addChild(this->sampleRateSeparator); AudioBlockSizeChoice *bufferSizeChoice = createWidget(pos); bufferSizeChoice->box.pos.x = box.size.x / 2; bufferSizeChoice->box.size.x = box.size.x / 2; bufferSizeChoice->port = port; addChild(bufferSizeChoice); this->bufferSizeChoice = bufferSizeChoice; } } // namespace app } // namespace rack