@@ -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); | |||