@@ -27,9 +27,19 @@ struct Driver { | |||
virtual std::vector<int> getDeviceIds() { | |||
return {}; | |||
} | |||
/** Gets the name of a device without subscribing to it. */ | |||
virtual std::string getDeviceName(int deviceId) { | |||
return ""; | |||
} | |||
virtual int getDeviceNumInputs(int deviceId) { | |||
return 0; | |||
} | |||
virtual int getDeviceNumOutputs(int deviceId) { | |||
return 0; | |||
} | |||
std::string getDeviceDetail(int deviceId, int offset, int maxChannels); | |||
virtual Device* subscribe(int deviceId, Port* port) { | |||
return NULL; | |||
} | |||
@@ -47,7 +57,17 @@ struct Device { | |||
void subscribe(Port* port); | |||
void unsubscribe(Port* port); | |||
// Called by Port. | |||
virtual std::string getName() { | |||
return ""; | |||
} | |||
virtual int getNumInputs() { | |||
return 0; | |||
} | |||
virtual int getNumOutputs() { | |||
return 0; | |||
} | |||
std::string getDetail(int offset, int maxChannels); | |||
virtual std::vector<int> getSampleRates() { | |||
return {}; | |||
} | |||
@@ -64,13 +84,6 @@ struct Device { | |||
} | |||
virtual void setBlockSize(int blockSize) {} | |||
virtual int getNumInputs() { | |||
return 0; | |||
} | |||
virtual int getNumOutputs() { | |||
return 0; | |||
} | |||
// Called by this Device class, forwards to subscribed Ports. | |||
void processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames); | |||
void onOpenStream(); | |||
@@ -82,45 +95,35 @@ struct Device { | |||
//////////////////// | |||
struct Port { | |||
/** Not owned */ | |||
Driver* driver = NULL; | |||
Device* device = NULL; | |||
// Port settings | |||
int offset = 0; | |||
int maxChannels = 8; | |||
// private | |||
int driverId = -1; | |||
int deviceId = -1; | |||
/** Not owned */ | |||
Driver* driver = NULL; | |||
Device* device = NULL; | |||
Port(); | |||
virtual ~Port(); | |||
std::vector<int> getDriverIds(); | |||
Driver* getDriver() { | |||
return driver; | |||
} | |||
int getDriverId() { | |||
return driverId; | |||
} | |||
void setDriverId(int driverId); | |||
std::string getDriverName(int driverId); | |||
std::vector<int> getDeviceIds() { | |||
if (!driver) | |||
return {}; | |||
return driver->getDeviceIds(); | |||
Device* getDevice() { | |||
return device; | |||
} | |||
int getDeviceId() { | |||
return deviceId; | |||
} | |||
void setDeviceId(int deviceId); | |||
std::string getDeviceName(int deviceId) { | |||
if (!driver) | |||
return ""; | |||
return driver->getDeviceName(deviceId); | |||
} | |||
std::string getDeviceDetail(int deviceId, int offset); | |||
std::vector<int> getSampleRates() { | |||
if (!device) | |||
return {}; | |||
@@ -178,6 +181,8 @@ void init(); | |||
void destroy(); | |||
/** Registers a new audio driver. Takes pointer ownership. */ | |||
void addDriver(int driverId, Driver* driver); | |||
std::vector<int> getDriverIds(); | |||
Driver* getDriver(int driverId); | |||
} // namespace audio | |||
@@ -22,25 +22,27 @@ struct AudioDriverChoice : LedDisplayChoice { | |||
ui::Menu* menu = createMenu(); | |||
menu->addChild(createMenuLabel("Audio driver")); | |||
for (int driverId : port->getDriverIds()) { | |||
for (int driverId : audio::getDriverIds()) { | |||
AudioDriverItem* item = new AudioDriverItem; | |||
item->port = port; | |||
item->driverId = driverId; | |||
item->text = port->getDriverName(driverId); | |||
item->text = audio::getDriver(driverId)->getName(); | |||
item->rightText = CHECKMARK(item->driverId == port->getDriverId()); | |||
menu->addChild(item); | |||
} | |||
} | |||
void step() override { | |||
text = (box.size.x >= 200.0) ? "Driver: " : ""; | |||
std::string driverName = port ? port->getDriverName(port->getDriverId()) : ""; | |||
text = ""; | |||
if (box.size.x >= 200.0) | |||
text += "Driver: "; | |||
std::string driverName = (port && port->driver) ? port->getDriver()->getName() : ""; | |||
if (driverName != "") { | |||
text += driverName; | |||
color.a = 1.f; | |||
color.a = 1.0; | |||
} | |||
else { | |||
text += "(No driver)"; | |||
color.a = 0.5f; | |||
color.a = 0.5; | |||
} | |||
} | |||
}; | |||
@@ -60,7 +62,7 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
audio::Port* port; | |||
void onAction(const event::Action& e) override { | |||
if (!port) | |||
if (!port || !port->driver) | |||
return; | |||
ui::Menu* menu = createMenu(); | |||
@@ -70,12 +72,12 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
item->port = port; | |||
item->deviceId = -1; | |||
item->text = "(No device)"; | |||
item->rightText = CHECKMARK(item->deviceId == port->getDeviceId()); | |||
item->rightText = CHECKMARK(port->getDeviceId() == -1); | |||
menu->addChild(item); | |||
} | |||
for (int deviceId : port->getDeviceIds()) { | |||
int channels = std::max(port->getNumInputs(), port->getNumOutputs()); | |||
/** Prevents devices with a ridiculous number of channels from being displayed */ | |||
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); | |||
@@ -84,22 +86,24 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
item->port = port; | |||
item->deviceId = deviceId; | |||
item->offset = offset; | |||
item->text = port->getDeviceDetail(deviceId, offset); | |||
item->text = port->driver->getDeviceDetail(deviceId, offset, port->maxChannels); | |||
item->rightText = CHECKMARK(item->deviceId == port->getDeviceId() && 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) : ""; | |||
text = ""; | |||
if (box.size.x >= 200.0) | |||
text += "Device: "; | |||
std::string detail = (port && port->device) ? port->device->getDetail(port->offset, port->maxChannels) : ""; | |||
if (detail != "") { | |||
text += detail; | |||
color.a = (detail == "") ? 0.5f : 1.f; | |||
color.a = 1.0; | |||
} | |||
else { | |||
text += "(No device)"; | |||
color.a = 0.5f; | |||
color.a = 0.5; | |||
} | |||
} | |||
}; | |||
@@ -135,13 +139,19 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||
} | |||
} | |||
void step() override { | |||
text = (box.size.x >= 100.0) ? "Rate: " : ""; | |||
if (port) { | |||
text += string::f("%g kHz", port->getSampleRate() / 1000.0); | |||
text = ""; | |||
if (box.size.x >= 100.0) | |||
text += "Rate: "; | |||
int sampleRate = port ? port->getSampleRate() : 0; | |||
if (sampleRate > 0) { | |||
text += string::f("%g", sampleRate / 1000.0); | |||
color.a = 1.0; | |||
} | |||
else { | |||
text += "0 kHz"; | |||
text += "---"; | |||
color.a = 0.5; | |||
} | |||
text += " kHz"; | |||
} | |||
}; | |||
@@ -177,12 +187,17 @@ struct AudioBlockSizeChoice : LedDisplayChoice { | |||
} | |||
} | |||
void step() override { | |||
text = (box.size.x >= 100.0) ? "Block size: " : ""; | |||
if (port) { | |||
text += string::f("%d", port->getBlockSize()); | |||
text = ""; | |||
if (box.size.x >= 100.0) | |||
text += "Block size: "; | |||
int blockSize = port ? port->getBlockSize() : 0; | |||
if (blockSize > 0) { | |||
text += string::f("%d", blockSize); | |||
color.a = 1.0; | |||
} | |||
else { | |||
text += "0"; | |||
text += "---"; | |||
color.a = 0.5; | |||
} | |||
} | |||
}; | |||
@@ -8,6 +8,31 @@ 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 | |||
@@ -23,6 +48,10 @@ 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) { | |||
for (Port* port : subscribed) { | |||
port->processInput(input + port->offset, inputStride, frames); | |||
@@ -59,14 +88,6 @@ Port::~Port() { | |||
setDriverId(-1); | |||
} | |||
std::vector<int> Port::getDriverIds() { | |||
std::vector<int> driverIds; | |||
for (auto& pair : drivers) { | |||
driverIds.push_back(pair.first); | |||
} | |||
return driverIds; | |||
} | |||
void Port::setDriverId(int driverId) { | |||
// Unset device and driver | |||
setDeviceId(-1); | |||
@@ -75,32 +96,17 @@ void Port::setDriverId(int driverId) { | |||
if (driverId == -1) { | |||
// Set first driver as default | |||
if (!drivers.empty()) { | |||
driver = drivers[0].second; | |||
this->driverId = drivers[0].first; | |||
} | |||
driver = drivers[0].second; | |||
this->driverId = drivers[0].first; | |||
} | |||
else { | |||
// Set driver with driverId | |||
for (auto& pair : drivers) { | |||
if (pair.first == driverId) { | |||
driver = pair.second; | |||
this->driverId = driverId; | |||
break; | |||
} | |||
} | |||
// Find driver by ID | |||
driver = audio::getDriver(driverId); | |||
if (driver) | |||
this->driverId = driverId; | |||
} | |||
} | |||
std::string Port::getDriverName(int driverId) { | |||
for (auto& pair : drivers) { | |||
if (pair.first == driverId) { | |||
return pair.second->getName(); | |||
} | |||
} | |||
return ""; | |||
} | |||
void Port::setDeviceId(int deviceId) { | |||
// Destroy device | |||
if (driver && this->deviceId >= 0) { | |||
@@ -116,26 +122,6 @@ void Port::setDeviceId(int deviceId) { | |||
} | |||
} | |||
std::string Port::getDeviceDetail(int deviceId, int offset) { | |||
if (!driver || !device) | |||
return ""; | |||
std::string text = getDeviceName(getDeviceId()); | |||
text += " ("; | |||
int numInputs = device->getNumInputs(); | |||
int numOutputs = device->getNumOutputs(); | |||
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; | |||
} | |||
int Port::getNumInputs() { | |||
if (!device) | |||
return 0; | |||
@@ -150,13 +136,15 @@ int Port::getNumOutputs() { | |||
json_t* Port::toJson() { | |||
json_t* rootJ = json_object(); | |||
json_object_set_new(rootJ, "driver", json_integer(getDriverId())); | |||
std::string deviceName = getDeviceName(getDeviceId()); | |||
if (!deviceName.empty()) | |||
json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); | |||
json_object_set_new(rootJ, "sampleRate", json_integer(getSampleRate())); | |||
json_object_set_new(rootJ, "blockSize", json_integer(getBlockSize())); | |||
json_object_set_new(rootJ, "offset", json_integer(offset)); | |||
if (driver) { | |||
json_object_set_new(rootJ, "driver", json_integer(getDriverId())); | |||
std::string deviceName = driver->getDeviceName(getDeviceId()); | |||
if (!deviceName.empty()) | |||
json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); | |||
json_object_set_new(rootJ, "sampleRate", json_integer(getSampleRate())); | |||
json_object_set_new(rootJ, "blockSize", json_integer(getBlockSize())); | |||
json_object_set_new(rootJ, "offset", json_integer(offset)); | |||
} | |||
return rootJ; | |||
} | |||
@@ -165,14 +153,16 @@ void Port::fromJson(json_t* rootJ) { | |||
if (driverJ) | |||
setDriverId(json_number_value(driverJ)); | |||
json_t* deviceNameJ = json_object_get(rootJ, "deviceName"); | |||
if (deviceNameJ) { | |||
std::string deviceName = json_string_value(deviceNameJ); | |||
// Search for device ID with equal name | |||
for (int deviceId : getDeviceIds()) { | |||
if (getDeviceName(deviceId) == deviceName) { | |||
setDeviceId(deviceId); | |||
break; | |||
if (driver) { | |||
json_t* deviceNameJ = json_object_get(rootJ, "deviceName"); | |||
if (deviceNameJ) { | |||
std::string deviceName = json_string_value(deviceNameJ); | |||
// Search for device ID with equal name | |||
for (int deviceId : driver->getDeviceIds()) { | |||
if (driver->getDeviceName(deviceId) == deviceName) { | |||
setDeviceId(deviceId); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
@@ -210,6 +200,22 @@ void addDriver(int driverId, Driver* driver) { | |||
drivers.push_back(std::make_pair(driverId, driver)); | |||
} | |||
std::vector<int> getDriverIds() { | |||
std::vector<int> driverIds; | |||
for (auto& pair : drivers) { | |||
driverIds.push_back(pair.first); | |||
} | |||
return driverIds; | |||
} | |||
Driver* getDriver(int driverId) { | |||
for (auto& pair : drivers) { | |||
if (pair.first == driverId) | |||
return pair.second; | |||
} | |||
return NULL; | |||
} | |||
} // namespace audio | |||
} // namespace rack |
@@ -125,6 +125,16 @@ struct RtAudioDevice : audio::Device { | |||
onCloseStream(); | |||
} | |||
std::string getName() override { | |||
return deviceInfo.name; | |||
} | |||
int getNumInputs() override { | |||
return inputParameters.nChannels; | |||
} | |||
int getNumOutputs() override { | |||
return outputParameters.nChannels; | |||
} | |||
std::vector<int> getSampleRates() override { | |||
std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); | |||
return sampleRates; | |||
@@ -139,7 +149,13 @@ struct RtAudioDevice : audio::Device { | |||
} | |||
std::vector<int> getBlockSizes() override { | |||
return {32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}; | |||
std::vector<int> blockSizes; | |||
for (int i = 5; i < 12; i++) { | |||
blockSizes.push_back(1 << i); | |||
blockSizes.push_back((1 << i) / 2 * 3); | |||
} | |||
blockSizes.push_back(1 << 12); | |||
return blockSizes; | |||
} | |||
int getBlockSize() override { | |||
return blockSize; | |||
@@ -150,13 +166,6 @@ struct RtAudioDevice : audio::Device { | |||
openStream(); | |||
} | |||
int getNumInputs() override { | |||
return inputParameters.nChannels; | |||
} | |||
int getNumOutputs() override { | |||
return outputParameters.nChannels; | |||
} | |||
static int rtAudioCallback(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { | |||
RtAudioDevice* device = (RtAudioDevice*) userData; | |||
assert(device); | |||
@@ -204,11 +213,37 @@ struct RtAudioDriver : audio::Driver { | |||
return ""; | |||
} | |||
int getDeviceNumInputs(int deviceId) override { | |||
if (deviceId >= 0) { | |||
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); | |||
return deviceInfo.inputChannels; | |||
} | |||
return 0; | |||
} | |||
int getDeviceNumOutputs(int deviceId) override { | |||
if (deviceId >= 0) { | |||
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); | |||
return deviceInfo.outputChannels; | |||
} | |||
return 0; | |||
} | |||
audio::Device* subscribe(int deviceId, audio::Port* port) override { | |||
RtAudioDevice* device = devices[deviceId]; | |||
if (!device) { | |||
devices[deviceId] = device = new RtAudioDevice(rtAudio->getCurrentApi(), deviceId); | |||
// TODO Error check | |||
RtAudioDevice* device; | |||
auto it = devices.find(deviceId); | |||
if (it == devices.end()) { | |||
try { | |||
device = new RtAudioDevice(rtAudio->getCurrentApi(), deviceId); | |||
devices[deviceId] = device; | |||
} | |||
catch (Exception& e) { | |||
WARN("Could not subscribe to audio device: %s", e.what()); | |||
return NULL; | |||
} | |||
} | |||
else { | |||
device = it->second; | |||
} | |||
device->subscribe(port); | |||