| @@ -46,11 +46,6 @@ struct Driver { | |||
| virtual int getDeviceNumOutputs(int deviceId) { | |||
| return 0; | |||
| } | |||
| /** Returns a detailed description of the device without obtaining it. | |||
| `offset` specifies the first channel (zero-indexed). | |||
| E.g. "MySoundcard (1-2 in, 1-2 out)" | |||
| */ | |||
| std::string getDeviceDetail(int deviceId, int offset, int maxChannels); | |||
| /** Adds the given port as a reference holder of a device and returns the it. | |||
| Creates the Device if no ports are subscribed before calling. | |||
| @@ -90,11 +85,6 @@ struct Device { | |||
| virtual int getNumOutputs() { | |||
| return 0; | |||
| } | |||
| /** Returns a detailed description of the device. | |||
| `offset` specifies the first channel (zero-indexed). | |||
| E.g. "MySoundcard (1-2 in, 1-2 out)" | |||
| */ | |||
| std::string getDetail(int offset, int maxChannels); | |||
| /** Returns a list of all valid (user-selectable) sample rates. | |||
| The device may accept sample rates not in this list, but it *must* accept sample rates in the list. | |||
| @@ -139,9 +129,11 @@ That is, if the active Device throws a `rack::Exception`, it is caught and logge | |||
| */ | |||
| struct Port { | |||
| /** The first channel index of the device to process. */ | |||
| int offset = 0; | |||
| int inputOffset = 0; | |||
| int outputOffset = 0; | |||
| /** Maximum number of channels to process. */ | |||
| int maxChannels = 8; | |||
| int maxInputs = 8; | |||
| int maxOutputs = 8; | |||
| // private | |||
| int driverId = -1; | |||
| @@ -177,9 +169,6 @@ struct Port { | |||
| int getBlockSize(); | |||
| void setBlockSize(int blockSize); | |||
| int getOffset(); | |||
| void setOffset(int offset); | |||
| int getNumInputs(); | |||
| int getNumOutputs(); | |||
| @@ -8,6 +8,23 @@ namespace rack { | |||
| namespace app { | |||
| static std::string getDetailTemplate(std::string name, int numInputs, int inputOffset, int maxInputs, int numOutputs, int outputOffset, int maxOutputs) { | |||
| std::string text = name; | |||
| text += " ("; | |||
| if (inputOffset < numInputs) { | |||
| text += string::f("%d-%d in", inputOffset + 1, std::min(inputOffset + maxInputs, numInputs)); | |||
| } | |||
| if (inputOffset < numInputs && outputOffset < numOutputs) { | |||
| text += ", "; | |||
| } | |||
| if (outputOffset < numOutputs) { | |||
| text += string::f("%d-%d out", outputOffset + 1, std::min(outputOffset + maxOutputs, numOutputs)); | |||
| } | |||
| text += ")"; | |||
| return text; | |||
| } | |||
| struct AudioDriverValueItem : ui::MenuItem { | |||
| audio::Port* port; | |||
| int driverId; | |||
| @@ -67,10 +84,12 @@ struct AudioDriverItem : ui::MenuItem { | |||
| struct AudioDeviceValueItem : ui::MenuItem { | |||
| audio::Port* port; | |||
| int deviceId; | |||
| int offset; | |||
| int inputOffset; | |||
| int outputOffset; | |||
| void onAction(const event::Action& e) override { | |||
| port->setDeviceId(deviceId); | |||
| port->setOffset(offset); | |||
| port->inputOffset = inputOffset; | |||
| port->outputOffset = outputOffset; | |||
| } | |||
| }; | |||
| @@ -88,18 +107,24 @@ static void appendAudioDeviceMenu(ui::Menu* menu, audio::Port* port) { | |||
| } | |||
| for (int deviceId : port->getDeviceIds()) { | |||
| int channels = std::max(port->getDeviceNumInputs(deviceId), port->getDeviceNumOutputs(deviceId)); | |||
| int numInputs = port->getDeviceNumInputs(deviceId); | |||
| int numOutputs = port->getDeviceNumOutputs(deviceId); | |||
| std::string name = port->getDeviceName(deviceId); | |||
| // Prevents devices with a ridiculous number of channels from being displayed | |||
| const int maxTotalChannels = port->maxChannels * 16; | |||
| channels = std::min(maxTotalChannels, channels); | |||
| for (int i = 0; i < 8; i++) { | |||
| int inputOffset = i * port->maxInputs; | |||
| int outputOffset = i * port->maxOutputs; | |||
| if (inputOffset >= numInputs && outputOffset >= numOutputs) | |||
| break; | |||
| 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->getDeviceDetail(deviceId, offset); | |||
| item->rightText = CHECKMARK(item->deviceId == port->getDeviceId() && item->offset == port->getOffset()); | |||
| item->inputOffset = inputOffset; | |||
| item->outputOffset = outputOffset; | |||
| item->text = getDetailTemplate(name, numInputs, inputOffset, port->maxInputs, numOutputs, outputOffset, port->maxOutputs); | |||
| item->rightText = CHECKMARK(item->deviceId == port->getDeviceId() && inputOffset == port->inputOffset && outputOffset == port->outputOffset); | |||
| menu->addChild(item); | |||
| } | |||
| } | |||
| @@ -117,7 +142,10 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
| text = ""; | |||
| if (box.size.x >= 200.0) | |||
| text += "Device: "; | |||
| std::string detail = (port && port->device) ? port->device->getDetail(port->getOffset(), port->maxChannels) : ""; | |||
| std::string detail = ""; | |||
| if (port && port->device) | |||
| detail = getDetailTemplate(port->device->getName(), port->getNumInputs(), port->inputOffset, port->maxInputs, port->getNumOutputs(), port->outputOffset, port->maxOutputs); | |||
| if (detail != "") { | |||
| text += detail; | |||
| color.a = 1.0; | |||
| @@ -8,31 +8,10 @@ namespace audio { | |||
| static std::vector<std::pair<int, Driver*>> drivers; | |||
| static std::string getDetailTemplate(std::string name, int numInputs, int numOutputs, int offset, int maxChannels) { | |||
| std::string text = name; | |||
| text += " ("; | |||
| if (offset < numInputs) { | |||
| text += string::f("%d-%d in", offset + 1, std::min(offset + maxChannels, numInputs)); | |||
| } | |||
| if (offset < numInputs && offset < numOutputs) { | |||
| text += ", "; | |||
| } | |||
| if (offset < numOutputs) { | |||
| text += string::f("%d-%d out", offset + 1, std::min(offset + maxChannels, numOutputs)); | |||
| } | |||
| text += ")"; | |||
| return text; | |||
| } | |||
| //////////////////// | |||
| // Driver | |||
| //////////////////// | |||
| std::string Driver::getDeviceDetail(int deviceId, int offset, int maxChannels) { | |||
| if (deviceId < 0) | |||
| return ""; | |||
| return getDetailTemplate(getDeviceName(deviceId), getDeviceNumInputs(deviceId), getDeviceNumOutputs(deviceId), offset, maxChannels); | |||
| } | |||
| //////////////////// | |||
| // Device | |||
| @@ -48,10 +27,6 @@ void Device::unsubscribe(Port* port) { | |||
| subscribed.erase(it); | |||
| } | |||
| std::string Device::getDetail(int offset, int maxChannels) { | |||
| return getDetailTemplate(getName(), getNumInputs(), getNumOutputs(), offset, maxChannels); | |||
| } | |||
| void Device::processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames) { | |||
| // Zero output in case no Port writes values to it. | |||
| std::memset(output, 0, frames * outputStride * sizeof(float)); | |||
| @@ -59,15 +34,15 @@ void Device::processBuffer(const float* input, int inputStride, float* output, i | |||
| for (Port* port : subscribed) { | |||
| // Setting the thread context should probably be the responsibility of Port, but because processInput() etc are overridden, this is the only good place for it. | |||
| contextSet(port->context); | |||
| port->processInput(input + port->getOffset(), inputStride, frames); | |||
| port->processInput(input + port->inputOffset, inputStride, frames); | |||
| } | |||
| for (Port* port : subscribed) { | |||
| contextSet(port->context); | |||
| port->processBuffer(input + port->getOffset(), inputStride, output + port->getOffset(), outputStride, frames); | |||
| port->processBuffer(input + port->inputOffset, inputStride, output + port->outputOffset, outputStride, frames); | |||
| } | |||
| for (Port* port : subscribed) { | |||
| contextSet(port->context); | |||
| port->processOutput(output + port->getOffset(), outputStride, frames); | |||
| port->processOutput(output + port->outputOffset, outputStride, frames); | |||
| } | |||
| } | |||
| @@ -106,7 +81,6 @@ void Port::reset() { | |||
| firstDriverId = driverIds[0]; | |||
| setDriverId(firstDriverId); | |||
| setOffset(0); | |||
| } | |||
| Driver* Port::getDriver() { | |||
| @@ -236,19 +210,6 @@ std::string Port::getDeviceName(int deviceId) { | |||
| } | |||
| } | |||
| std::string Port::getDeviceDetail(int deviceId, int offset) { | |||
| if (!driver) | |||
| return ""; | |||
| try { | |||
| // Use maxChannels from Port. | |||
| return driver->getDeviceDetail(deviceId, offset, maxChannels); | |||
| } | |||
| catch (Exception& e) { | |||
| WARN("Audio port could not get device detail: %s", e.what()); | |||
| return 0; | |||
| } | |||
| } | |||
| std::set<float> Port::getSampleRates() { | |||
| if (!device) | |||
| return {}; | |||
| @@ -319,20 +280,11 @@ void Port::setBlockSize(int blockSize) { | |||
| } | |||
| } | |||
| int Port::getOffset() { | |||
| return offset; | |||
| } | |||
| void Port::setOffset(int offset) { | |||
| this->offset = offset; | |||
| } | |||
| int Port::getNumInputs() { | |||
| if (!device) | |||
| return 0; | |||
| try { | |||
| return std::min(device->getNumInputs() - getOffset(), maxChannels); | |||
| return std::min(device->getNumInputs() - inputOffset, maxInputs); | |||
| } | |||
| catch (Exception& e) { | |||
| WARN("Audio port could not get device number of inputs: %s", e.what()); | |||
| @@ -344,7 +296,7 @@ int Port::getNumOutputs() { | |||
| if (!device) | |||
| return 0; | |||
| try { | |||
| return std::min(device->getNumOutputs() - getOffset(), maxChannels); | |||
| return std::min(device->getNumOutputs() - outputOffset, maxOutputs); | |||
| } | |||
| catch (Exception& e) { | |||
| WARN("Audio port could not get device number of outputs: %s", e.what()); | |||
| @@ -363,7 +315,8 @@ json_t* Port::toJson() { | |||
| json_object_set_new(rootJ, "sampleRate", json_real(getSampleRate())); | |||
| json_object_set_new(rootJ, "blockSize", json_integer(getBlockSize())); | |||
| json_object_set_new(rootJ, "offset", json_integer(getOffset())); | |||
| json_object_set_new(rootJ, "inputOffset", json_integer(inputOffset)); | |||
| json_object_set_new(rootJ, "outputOffset", json_integer(outputOffset)); | |||
| return rootJ; | |||
| } | |||
| @@ -397,9 +350,13 @@ void Port::fromJson(json_t* rootJ) { | |||
| if (blockSizeJ) | |||
| setBlockSize(json_integer_value(blockSizeJ)); | |||
| json_t* offsetJ = json_object_get(rootJ, "offset"); | |||
| if (offsetJ) | |||
| setOffset(json_integer_value(offsetJ)); | |||
| json_t* inputOffsetJ = json_object_get(rootJ, "inputOffset"); | |||
| if (inputOffsetJ) | |||
| inputOffset = json_integer_value(inputOffsetJ); | |||
| json_t* outputOffsetJ = json_object_get(rootJ, "outputOffset"); | |||
| if (outputOffsetJ) | |||
| outputOffset = json_integer_value(outputOffsetJ); | |||
| } | |||
| //////////////////// | |||
| @@ -71,7 +71,8 @@ struct AudioInterface : Module, audio::Port { | |||
| configLight(OUTPUT_LIGHTS + 2 * i, string::f("Device input %d/%d status", 2 * i + 1, 2 * i + 2)); | |||
| lightDivider.setDivision(512); | |||
| maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS); | |||
| maxOutputs = NUM_AUDIO_INPUTS; | |||
| maxInputs = NUM_AUDIO_OUTPUTS; | |||
| inputSrc.setQuality(6); | |||
| outputSrc.setQuality(6); | |||