@@ -46,11 +46,6 @@ struct Driver { | |||||
virtual int getDeviceNumOutputs(int deviceId) { | virtual int getDeviceNumOutputs(int deviceId) { | ||||
return 0; | 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. | /** 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. | Creates the Device if no ports are subscribed before calling. | ||||
@@ -90,11 +85,6 @@ struct Device { | |||||
virtual int getNumOutputs() { | virtual int getNumOutputs() { | ||||
return 0; | 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. | /** 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. | 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 { | struct Port { | ||||
/** The first channel index of the device to process. */ | /** The first channel index of the device to process. */ | ||||
int offset = 0; | |||||
int inputOffset = 0; | |||||
int outputOffset = 0; | |||||
/** Maximum number of channels to process. */ | /** Maximum number of channels to process. */ | ||||
int maxChannels = 8; | |||||
int maxInputs = 8; | |||||
int maxOutputs = 8; | |||||
// private | // private | ||||
int driverId = -1; | int driverId = -1; | ||||
@@ -177,9 +169,6 @@ struct Port { | |||||
int getBlockSize(); | int getBlockSize(); | ||||
void setBlockSize(int blockSize); | void setBlockSize(int blockSize); | ||||
int getOffset(); | |||||
void setOffset(int offset); | |||||
int getNumInputs(); | int getNumInputs(); | ||||
int getNumOutputs(); | int getNumOutputs(); | ||||
@@ -8,6 +8,23 @@ namespace rack { | |||||
namespace app { | 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 { | struct AudioDriverValueItem : ui::MenuItem { | ||||
audio::Port* port; | audio::Port* port; | ||||
int driverId; | int driverId; | ||||
@@ -67,10 +84,12 @@ struct AudioDriverItem : ui::MenuItem { | |||||
struct AudioDeviceValueItem : ui::MenuItem { | struct AudioDeviceValueItem : ui::MenuItem { | ||||
audio::Port* port; | audio::Port* port; | ||||
int deviceId; | int deviceId; | ||||
int offset; | |||||
int inputOffset; | |||||
int outputOffset; | |||||
void onAction(const event::Action& e) override { | void onAction(const event::Action& e) override { | ||||
port->setDeviceId(deviceId); | 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()) { | 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 | // 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; | AudioDeviceValueItem* item = new AudioDeviceValueItem; | ||||
item->port = port; | item->port = port; | ||||
item->deviceId = deviceId; | 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); | menu->addChild(item); | ||||
} | } | ||||
} | } | ||||
@@ -117,7 +142,10 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||||
text = ""; | text = ""; | ||||
if (box.size.x >= 200.0) | if (box.size.x >= 200.0) | ||||
text += "Device: "; | 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 != "") { | if (detail != "") { | ||||
text += detail; | text += detail; | ||||
color.a = 1.0; | color.a = 1.0; | ||||
@@ -8,31 +8,10 @@ namespace audio { | |||||
static std::vector<std::pair<int, Driver*>> drivers; | 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 | // 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 | // Device | ||||
@@ -48,10 +27,6 @@ void Device::unsubscribe(Port* port) { | |||||
subscribed.erase(it); | 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) { | void Device::processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames) { | ||||
// Zero output in case no Port writes values to it. | // Zero output in case no Port writes values to it. | ||||
std::memset(output, 0, frames * outputStride * sizeof(float)); | 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) { | 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. | // 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); | contextSet(port->context); | ||||
port->processInput(input + port->getOffset(), inputStride, frames); | |||||
port->processInput(input + port->inputOffset, inputStride, frames); | |||||
} | } | ||||
for (Port* port : subscribed) { | for (Port* port : subscribed) { | ||||
contextSet(port->context); | 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) { | for (Port* port : subscribed) { | ||||
contextSet(port->context); | 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]; | firstDriverId = driverIds[0]; | ||||
setDriverId(firstDriverId); | setDriverId(firstDriverId); | ||||
setOffset(0); | |||||
} | } | ||||
Driver* Port::getDriver() { | 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() { | std::set<float> Port::getSampleRates() { | ||||
if (!device) | if (!device) | ||||
return {}; | 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() { | int Port::getNumInputs() { | ||||
if (!device) | if (!device) | ||||
return 0; | return 0; | ||||
try { | try { | ||||
return std::min(device->getNumInputs() - getOffset(), maxChannels); | |||||
return std::min(device->getNumInputs() - inputOffset, maxInputs); | |||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
WARN("Audio port could not get device number of inputs: %s", e.what()); | WARN("Audio port could not get device number of inputs: %s", e.what()); | ||||
@@ -344,7 +296,7 @@ int Port::getNumOutputs() { | |||||
if (!device) | if (!device) | ||||
return 0; | return 0; | ||||
try { | try { | ||||
return std::min(device->getNumOutputs() - getOffset(), maxChannels); | |||||
return std::min(device->getNumOutputs() - outputOffset, maxOutputs); | |||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
WARN("Audio port could not get device number of outputs: %s", e.what()); | 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, "sampleRate", json_real(getSampleRate())); | ||||
json_object_set_new(rootJ, "blockSize", json_integer(getBlockSize())); | 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; | return rootJ; | ||||
} | } | ||||
@@ -397,9 +350,13 @@ void Port::fromJson(json_t* rootJ) { | |||||
if (blockSizeJ) | if (blockSizeJ) | ||||
setBlockSize(json_integer_value(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)); | configLight(OUTPUT_LIGHTS + 2 * i, string::f("Device input %d/%d status", 2 * i + 1, 2 * i + 2)); | ||||
lightDivider.setDivision(512); | lightDivider.setDivision(512); | ||||
maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS); | |||||
maxOutputs = NUM_AUDIO_INPUTS; | |||||
maxInputs = NUM_AUDIO_OUTPUTS; | |||||
inputSrc.setQuality(6); | inputSrc.setQuality(6); | ||||
outputSrc.setQuality(6); | outputSrc.setQuality(6); | ||||