#include #include #include namespace rack { namespace audio { static std::vector> drivers; //////////////////// // Driver //////////////////// //////////////////// // Device //////////////////// void Device::subscribe(Port* port) { std::lock_guard lock(processMutex); subscribed.insert(port); } void Device::unsubscribe(Port* port) { std::lock_guard lock(processMutex); auto it = subscribed.find(port); if (it != subscribed.end()) subscribed.erase(it); } void Device::processBuffer(const float* input, int inputStride, float* output, int outputStride, int frames) { // Zero output since Ports might not write to all elements, or no Ports exist std::fill_n(output, frames * outputStride, 0.f); std::lock_guard lock(processMutex); 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->inputOffset, inputStride, frames); } for (Port* port : subscribed) { contextSet(port->context); port->processBuffer(input + port->inputOffset, inputStride, output + port->outputOffset, outputStride, frames); } for (Port* port : subscribed) { contextSet(port->context); port->processOutput(output + port->outputOffset, outputStride, frames); } } void Device::onStartStream() { for (Port* port : subscribed) { contextSet(port->context); port->onStartStream(); } } void Device::onStopStream() { for (Port* port : subscribed) { contextSet(port->context); port->onStopStream(); } } //////////////////// // Port //////////////////// Port::Port() { context = contextGet(); reset(); } Port::~Port() { setDeviceId(-1); } void Port::reset() { // Set default driver setDriverId(-1); } Driver* Port::getDriver() { return driver; } int Port::getDriverId() { return driverId; } void Port::setDriverId(int driverId) { // Unset device and driver setDeviceId(-1); driver = NULL; this->driverId = -1; // Find driver by ID driver = audio::getDriver(driverId); if (driver) { this->driverId = driverId; } else if (!drivers.empty()) { // Set first driver as default driver = drivers[0].second; this->driverId = drivers[0].first; } else { // No fallback drivers return; } // Set default device if exists try { int defaultDeviceId = driver->getDefaultDeviceId(); if (defaultDeviceId >= 0) setDeviceId(defaultDeviceId); } catch (Exception& e) { WARN("Audio port could not get default device ID: %s", e.what()); } } std::string Port::getDriverName() { if (!driver) return ""; try { return driver->getName(); } catch (Exception& e) { WARN("Audio port could not get driver name: %s", e.what()); return ""; } } Device* Port::getDevice() { return device; } std::vector Port::getDeviceIds() { if (!driver) return {}; try { return driver->getDeviceIds(); } catch (Exception& e) { WARN("Audio port could not get device IDs: %s", e.what()); return {}; } } int Port::getDeviceId() { return deviceId; } void Port::setDeviceId(int deviceId) { if (!driver) return; if (deviceId == this->deviceId) return; // Destroy device if (this->deviceId >= 0) { try { driver->unsubscribe(this->deviceId, this); onStopStream(); } catch (Exception& e) { WARN("Audio port could not unsubscribe from device: %s", e.what()); } } device = NULL; this->deviceId = -1; // Create device if (deviceId >= 0) { try { device = driver->subscribe(deviceId, this); if (device) { this->deviceId = deviceId; onStartStream(); } } catch (Exception& e) { WARN("Audio port could not subscribe to device: %s", e.what()); } } } int Port::getDeviceNumInputs(int deviceId) { if (!driver) return 0; try { return driver->getDeviceNumInputs(deviceId); } catch (Exception& e) { WARN("Audio port could not get device number of inputs: %s", e.what()); return 0; } } int Port::getDeviceNumOutputs(int deviceId) { if (!driver) return 0; try { return driver->getDeviceNumOutputs(deviceId); } catch (Exception& e) { WARN("Audio port could not get device number of outputs: %s", e.what()); return 0; } } std::string Port::getDeviceName(int deviceId) { if (!driver) return ""; try { return driver->getDeviceName(deviceId); } catch (Exception& e) { WARN("Audio port could not get device name: %s", e.what()); return 0; } } std::set Port::getSampleRates() { if (!device) return {}; try { return device->getSampleRates(); } catch (Exception& e) { WARN("Audio port could not get device sample rates: %s", e.what()); return {}; } } float Port::getSampleRate() { if (!device) return 0; try { return device->getSampleRate(); } catch (Exception& e) { WARN("Audio port could not get device sample rate: %s", e.what()); return 0; } } void Port::setSampleRate(float sampleRate) { if (!device) return; try { device->setSampleRate(sampleRate); } catch (Exception& e) { WARN("Audio port could not set device sample rate: %s", e.what()); } } std::set Port::getBlockSizes() { if (!device) return {}; try { return device->getBlockSizes(); } catch (Exception& e) { WARN("Audio port could not get device block sizes: %s", e.what()); return {}; } } int Port::getBlockSize() { if (!device) return 0; try { return device->getBlockSize(); } catch (Exception& e) { WARN("Audio port could not get device block size: %s", e.what()); return 0; } } void Port::setBlockSize(int blockSize) { if (!device) return; try { device->setBlockSize(blockSize); } catch (Exception& e) { WARN("Audio port could not set device block size: %s", e.what()); } } int Port::getNumInputs() { if (!device) return 0; try { return math::clamp(device->getNumInputs() - inputOffset, 0, maxInputs); } catch (Exception& e) { WARN("Audio port could not get device number of inputs: %s", e.what()); return 0; } } int Port::getNumOutputs() { if (!device) return 0; try { return math::clamp(device->getNumOutputs() - outputOffset, 0, maxOutputs); } catch (Exception& e) { WARN("Audio port could not get device number of outputs: %s", e.what()); return 0; } } json_t* Port::toJson() { json_t* rootJ = json_object(); json_object_set_new(rootJ, "driver", json_integer(getDriverId())); if (device) { try { std::string deviceName = device->getName(); json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); } catch (Exception& e) { WARN("Audio port could not get device name: %s", e.what()); } } json_object_set_new(rootJ, "sampleRate", json_real(getSampleRate())); json_object_set_new(rootJ, "blockSize", json_integer(getBlockSize())); json_object_set_new(rootJ, "inputOffset", json_integer(inputOffset)); json_object_set_new(rootJ, "outputOffset", json_integer(outputOffset)); return rootJ; } void Port::fromJson(json_t* rootJ) { setDriverId(-1); json_t* driverJ = json_object_get(rootJ, "driver"); 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()) { std::string deviceNameCurr = getDeviceName(deviceId); if (deviceNameCurr == "") continue; if (deviceNameCurr == deviceName) { setDeviceId(deviceId); break; } } } json_t* sampleRateJ = json_object_get(rootJ, "sampleRate"); if (sampleRateJ) setSampleRate(json_number_value(sampleRateJ)); json_t* blockSizeJ = json_object_get(rootJ, "blockSize"); if (blockSizeJ) setBlockSize(json_integer_value(blockSizeJ)); 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); } //////////////////// // audio //////////////////// void init() { } void destroy() { for (auto& pair : drivers) { delete pair.second; } drivers.clear(); } void addDriver(int driverId, Driver* driver) { assert(driver); assert(driverId != -1); drivers.push_back(std::make_pair(driverId, driver)); } std::vector getDriverIds() { std::vector driverIds; for (auto& pair : drivers) { driverIds.push_back(pair.first); } return driverIds; } Driver* getDriver(int driverId) { if (driverId == -1) return NULL; // Search for driver by ID for (auto& pair : drivers) { if (pair.first == driverId) return pair.second; } return NULL; } } // namespace audio } // namespace rack