|
|
@@ -18,22 +18,82 @@ |
|
|
|
namespace rack { |
|
|
|
|
|
|
|
|
|
|
|
static const std::map<RtAudio::Api, std::string> RTAUDIO_API_NAMES = { |
|
|
|
{RtAudio::LINUX_ALSA, "ALSA"}, |
|
|
|
{RtAudio::UNIX_JACK, "JACK"}, |
|
|
|
{RtAudio::LINUX_PULSE, "PulseAudio"}, |
|
|
|
{RtAudio::LINUX_OSS, "OSS"}, |
|
|
|
{RtAudio::WINDOWS_WASAPI, "WASAPI"}, |
|
|
|
{RtAudio::WINDOWS_ASIO, "ASIO"}, |
|
|
|
{RtAudio::WINDOWS_DS, "DirectSound"}, |
|
|
|
{RtAudio::MACOSX_CORE, "Core Audio"}, |
|
|
|
{RtAudio::RTAUDIO_DUMMY, "Dummy"}, |
|
|
|
{RtAudio::UNSPECIFIED, "Unspecified"}, |
|
|
|
struct RtAudioDevice; |
|
|
|
|
|
|
|
|
|
|
|
struct RtAudioDriver : audio::Driver { |
|
|
|
RtAudio::Api api; |
|
|
|
std::string name; |
|
|
|
RtAudio* rtAudio = NULL; |
|
|
|
// deviceId -> Device |
|
|
|
std::map<int, RtAudioDevice*> devices; |
|
|
|
|
|
|
|
RtAudioDriver(RtAudio::Api api, std::string name) { |
|
|
|
this->api = api; |
|
|
|
this->name = name; |
|
|
|
|
|
|
|
INFO("Creating RtAudio %s driver", name.c_str()); |
|
|
|
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) { |
|
|
|
WARN("RtAudio error %d: %s", type, errorText.c_str()); |
|
|
|
}); |
|
|
|
|
|
|
|
rtAudio->showWarnings(false); |
|
|
|
} |
|
|
|
|
|
|
|
~RtAudioDriver() { |
|
|
|
assert(devices.empty()); |
|
|
|
if (rtAudio) |
|
|
|
delete rtAudio; |
|
|
|
} |
|
|
|
|
|
|
|
std::string getName() override { |
|
|
|
return name; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<int> getDeviceIds() override { |
|
|
|
std::vector<int> deviceIds; |
|
|
|
if (rtAudio) { |
|
|
|
for (unsigned int id : rtAudio->getDeviceIds()) { |
|
|
|
deviceIds.push_back(id); |
|
|
|
} |
|
|
|
} |
|
|
|
return deviceIds; |
|
|
|
} |
|
|
|
|
|
|
|
std::string getDeviceName(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.name; |
|
|
|
} |
|
|
|
return ""; |
|
|
|
} |
|
|
|
|
|
|
|
int getDeviceNumInputs(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.inputChannels; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int getDeviceNumOutputs(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.outputChannels; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
audio::Device* subscribe(int deviceId, audio::Port* port) override; |
|
|
|
void unsubscribe(int deviceId, audio::Port* port) override; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct RtAudioDevice : audio::Device { |
|
|
|
RtAudio::Api api; |
|
|
|
RtAudioDriver* driver; |
|
|
|
int deviceId; |
|
|
|
RtAudio* rtAudio; |
|
|
|
RtAudio::DeviceInfo deviceInfo; |
|
|
@@ -43,13 +103,13 @@ struct RtAudioDevice : audio::Device { |
|
|
|
int blockSize = 0; |
|
|
|
float sampleRate = 0; |
|
|
|
|
|
|
|
RtAudioDevice(RtAudio::Api api, int deviceId) { |
|
|
|
this->api = api; |
|
|
|
RtAudioDevice(RtAudioDriver* driver, int deviceId) { |
|
|
|
this->driver = driver; |
|
|
|
this->deviceId = deviceId; |
|
|
|
|
|
|
|
// Create RtAudio object |
|
|
|
INFO("Creating RtAudio %s device", RTAUDIO_API_NAMES.at(api).c_str()); |
|
|
|
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) { |
|
|
|
INFO("Creating RtAudio %s device", driver->getName().c_str()); |
|
|
|
rtAudio = new RtAudio(driver->api, [](RtAudioErrorType type, const std::string& errorText) { |
|
|
|
WARN("RtAudio error %d: %s", type, errorText.c_str()); |
|
|
|
}); |
|
|
|
|
|
|
@@ -59,7 +119,7 @@ struct RtAudioDevice : audio::Device { |
|
|
|
// Query device ID |
|
|
|
deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID == 0) |
|
|
|
throw Exception("Failed to query RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
throw Exception("Failed to query RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
|
|
|
|
openStream(); |
|
|
|
} |
|
|
@@ -78,7 +138,7 @@ struct RtAudioDevice : audio::Device { |
|
|
|
void openStream() { |
|
|
|
// Open new device |
|
|
|
if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) { |
|
|
|
throw Exception("RtAudio %s device %d has 0 inputs and 0 outputs", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
throw Exception("RtAudio %s device %d has 0 inputs and 0 outputs", driver->getName().c_str(), deviceId); |
|
|
|
} |
|
|
|
|
|
|
|
inputParameters = RtAudio::StreamParameters(); |
|
|
@@ -109,25 +169,25 @@ struct RtAudioDevice : audio::Device { |
|
|
|
|
|
|
|
if (blockSize <= 0) { |
|
|
|
// DirectSound should use a higher default block size |
|
|
|
if (api == RtAudio::WINDOWS_DS) |
|
|
|
if (driver->api == RtAudio::WINDOWS_DS) |
|
|
|
blockSize = 1024; |
|
|
|
else |
|
|
|
blockSize = 256; |
|
|
|
} |
|
|
|
|
|
|
|
INFO("Opening RtAudio %s device %d: %s (%d in, %d out, %d sample rate, %d block size)", RTAUDIO_API_NAMES.at(api).c_str(), deviceId, deviceInfo.name.c_str(), inputParameters.nChannels, outputParameters.nChannels, closestSampleRate, blockSize); |
|
|
|
INFO("Opening RtAudio %s device %d: %s (%d in, %d out, %d sample rate, %d block size)", driver->getName().c_str(), deviceId, deviceInfo.name.c_str(), inputParameters.nChannels, outputParameters.nChannels, closestSampleRate, blockSize); |
|
|
|
if (rtAudio->openStream( |
|
|
|
outputParameters.nChannels > 0 ? &outputParameters : NULL, |
|
|
|
inputParameters.nChannels > 0 ? &inputParameters : NULL, |
|
|
|
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, |
|
|
|
&rtAudioCallback, this, &options)) { |
|
|
|
throw Exception("Failed to open RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
throw Exception("Failed to open RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
INFO("Starting RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
INFO("Starting RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
if (rtAudio->startStream()) { |
|
|
|
throw Exception("Failed to start RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
throw Exception("Failed to start RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
} |
|
|
|
|
|
|
|
// Update sample rate to actual value |
|
|
@@ -143,11 +203,11 @@ struct RtAudioDevice : audio::Device { |
|
|
|
|
|
|
|
void closeStream() { |
|
|
|
if (rtAudio->isStreamRunning()) { |
|
|
|
INFO("Stopping RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
INFO("Stopping RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
rtAudio->stopStream(); |
|
|
|
} |
|
|
|
if (rtAudio->isStreamOpen()) { |
|
|
|
INFO("Closing RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId); |
|
|
|
INFO("Closing RtAudio %s device %d", driver->getName().c_str(), deviceId); |
|
|
|
rtAudio->closeStream(); |
|
|
|
} |
|
|
|
|
|
|
@@ -220,126 +280,72 @@ struct RtAudioDevice : audio::Device { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct RtAudioDriver : audio::Driver { |
|
|
|
RtAudio::Api api; |
|
|
|
// deviceId -> Device |
|
|
|
std::map<int, RtAudioDevice*> devices; |
|
|
|
RtAudio* rtAudio = NULL; |
|
|
|
|
|
|
|
RtAudioDriver(RtAudio::Api api) { |
|
|
|
this->api = api; |
|
|
|
audio::Device* RtAudioDriver::subscribe(int deviceId, audio::Port* port) { |
|
|
|
RtAudioDevice* device; |
|
|
|
auto it = devices.find(deviceId); |
|
|
|
if (it == devices.end()) { |
|
|
|
// ASIO only allows one device to be used simultaneously |
|
|
|
if (api == RtAudio::WINDOWS_ASIO && devices.size() >= 1) |
|
|
|
throw Exception("ASIO driver only allows one audio device to be used simultaneously"); |
|
|
|
|
|
|
|
INFO("Creating RtAudio %s driver", RTAUDIO_API_NAMES.at(api).c_str()); |
|
|
|
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) { |
|
|
|
WARN("RtAudio error %d: %s", type, errorText.c_str()); |
|
|
|
}); |
|
|
|
|
|
|
|
rtAudio->showWarnings(false); |
|
|
|
// Can throw Exception |
|
|
|
device = new RtAudioDevice(this, deviceId); |
|
|
|
devices[deviceId] = device; |
|
|
|
} |
|
|
|
|
|
|
|
~RtAudioDriver() { |
|
|
|
assert(devices.empty()); |
|
|
|
if (rtAudio) |
|
|
|
delete rtAudio; |
|
|
|
} |
|
|
|
|
|
|
|
std::string getName() override { |
|
|
|
return RTAUDIO_API_NAMES.at(api); |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<int> getDeviceIds() override { |
|
|
|
std::vector<int> deviceIds; |
|
|
|
if (rtAudio) { |
|
|
|
for (unsigned int id : rtAudio->getDeviceIds()) { |
|
|
|
deviceIds.push_back(id); |
|
|
|
} |
|
|
|
} |
|
|
|
return deviceIds; |
|
|
|
else { |
|
|
|
device = it->second; |
|
|
|
} |
|
|
|
|
|
|
|
std::string getDeviceName(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.name; |
|
|
|
} |
|
|
|
return ""; |
|
|
|
} |
|
|
|
|
|
|
|
int getDeviceNumInputs(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.inputChannels; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
device->subscribe(port); |
|
|
|
return device; |
|
|
|
} |
|
|
|
|
|
|
|
int getDeviceNumOutputs(int deviceId) override { |
|
|
|
if (rtAudio) { |
|
|
|
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId); |
|
|
|
if (deviceInfo.ID > 0) |
|
|
|
return deviceInfo.outputChannels; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
audio::Device* subscribe(int deviceId, audio::Port* port) override { |
|
|
|
RtAudioDevice* device; |
|
|
|
auto it = devices.find(deviceId); |
|
|
|
if (it == devices.end()) { |
|
|
|
// ASIO only allows one device to be used simultaneously |
|
|
|
if (api == RtAudio::WINDOWS_ASIO && devices.size() >= 1) |
|
|
|
throw Exception("ASIO driver only allows one audio device to be used simultaneously"); |
|
|
|
|
|
|
|
// Can throw Exception |
|
|
|
device = new RtAudioDevice(api, deviceId); |
|
|
|
devices[deviceId] = device; |
|
|
|
} |
|
|
|
else { |
|
|
|
device = it->second; |
|
|
|
} |
|
|
|
void RtAudioDriver::unsubscribe(int deviceId, audio::Port* port) { |
|
|
|
auto it = devices.find(deviceId); |
|
|
|
if (it == devices.end()) |
|
|
|
return; |
|
|
|
RtAudioDevice* device = it->second; |
|
|
|
device->unsubscribe(port); |
|
|
|
|
|
|
|
device->subscribe(port); |
|
|
|
return device; |
|
|
|
if (device->subscribed.empty()) { |
|
|
|
devices.erase(it); |
|
|
|
delete device; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void unsubscribe(int deviceId, audio::Port* port) override { |
|
|
|
auto it = devices.find(deviceId); |
|
|
|
if (it == devices.end()) |
|
|
|
return; |
|
|
|
RtAudioDevice* device = it->second; |
|
|
|
device->unsubscribe(port); |
|
|
|
|
|
|
|
if (device->subscribed.empty()) { |
|
|
|
devices.erase(it); |
|
|
|
delete device; |
|
|
|
} |
|
|
|
} |
|
|
|
struct ApiInfo { |
|
|
|
// Should match indices in https://github.com/VCVRack/rtaudio/blob/ece277bd839603648c80c8a5f145678e13bc23f3/RtAudio.cpp#L107-L118 |
|
|
|
int driverId; |
|
|
|
RtAudio::Api rtApi; |
|
|
|
// Used instead of RtAudio::getApiName() |
|
|
|
std::string name; |
|
|
|
}; |
|
|
|
// The vector order here defines the order in the audio driver menu |
|
|
|
static const std::vector<ApiInfo> API_INFOS = { |
|
|
|
{1, RtAudio::LINUX_ALSA, "ALSA"}, |
|
|
|
{2, RtAudio::LINUX_PULSE, "PulseAudio"}, |
|
|
|
{4, RtAudio::UNIX_JACK, "JACK"}, |
|
|
|
{5, RtAudio::MACOSX_CORE, "Core Audio"}, |
|
|
|
{6, RtAudio::WINDOWS_WASAPI, "WASAPI"}, |
|
|
|
{7, RtAudio::WINDOWS_ASIO, "ASIO"}, |
|
|
|
{8, RtAudio::WINDOWS_DS, "DirectSound"}, |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
void rtaudioInit() { |
|
|
|
// Get RtAudio's driver list |
|
|
|
std::vector<RtAudio::Api> apis; |
|
|
|
RtAudio::getCompiledApi(apis); |
|
|
|
|
|
|
|
// I don't like the order returned by getCompiledApi(), so reorder it here. |
|
|
|
std::vector<RtAudio::Api> orderedApis = { |
|
|
|
RtAudio::LINUX_ALSA, |
|
|
|
RtAudio::LINUX_PULSE, |
|
|
|
RtAudio::UNIX_JACK, |
|
|
|
RtAudio::LINUX_OSS, |
|
|
|
RtAudio::WINDOWS_WASAPI, |
|
|
|
RtAudio::WINDOWS_ASIO, |
|
|
|
RtAudio::WINDOWS_DS, |
|
|
|
RtAudio::MACOSX_CORE, |
|
|
|
}; |
|
|
|
for (RtAudio::Api api : orderedApis) { |
|
|
|
auto it = std::find(apis.begin(), apis.end(), api); |
|
|
|
if (it != apis.end()) { |
|
|
|
RtAudioDriver* driver = new RtAudioDriver(api); |
|
|
|
audio::addDriver((int) api, driver); |
|
|
|
} |
|
|
|
for (const ApiInfo& apiInfo : API_INFOS) { |
|
|
|
auto it = std::find(apis.begin(), apis.end(), apiInfo.rtApi); |
|
|
|
if (it == apis.end()) |
|
|
|
continue; |
|
|
|
// Create and add driver |
|
|
|
RtAudioDriver* driver = new RtAudioDriver(apiInfo.rtApi, apiInfo.name); |
|
|
|
audio::addDriver(apiInfo.driverId, driver); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|