diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp
index bb33b9e9..cd4b8996 100644
--- a/src/core/AudioInterface.cpp
+++ b/src/core/AudioInterface.cpp
@@ -29,7 +29,7 @@ struct AudioInterface : Module {
NUM_OUTPUTS = AUDIO1_OUTPUT + 8
};
- RtAudio stream;
+ RtAudio *stream = NULL;
// Stream properties
int deviceId = -1;
float sampleRate = 44100.0;
@@ -46,9 +46,11 @@ struct AudioInterface : Module {
// in device's sample rate
DoubleRingBuffer, (1<<15)> inputSrcBuffer;
- AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
+ AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
+ setDriver(RtAudio::UNSPECIFIED);
+ }
~AudioInterface() {
- closeDevice();
+ closeStream();
}
void step() override;
@@ -57,18 +59,44 @@ struct AudioInterface : Module {
int getDeviceCount();
std::string getDeviceName(int deviceId);
- void openDevice(int deviceId, float sampleRate, int blockSize);
- void closeDevice();
-
- void setDeviceId(int deviceId) {
- openDevice(deviceId, sampleRate, blockSize);
- }
- void setSampleRate(float sampleRate) {
- openDevice(deviceId, sampleRate, blockSize);
- }
- void setBlockSize(int blockSize) {
- openDevice(deviceId, sampleRate, blockSize);
+ void openStream();
+ void closeStream();
+
+ void setDriver(int driver) {
+ closeStream();
+ if (stream)
+ delete stream;
+ stream = new RtAudio((RtAudio::Api) driver);
+ }
+ int getDriver() {
+ if (!stream)
+ return RtAudio::UNSPECIFIED;
+ return stream->getCurrentApi();
+ }
+ std::vector getAvailableDrivers() {
+ std::vector apis;
+ RtAudio::getCompiledApi(apis);
+ std::vector drivers;
+ for (RtAudio::Api api : apis)
+ drivers.push_back(api);
+ return drivers;
+ }
+ std::string getDriverName(int driver) {
+ switch (driver) {
+ case RtAudio::UNSPECIFIED: return "Unspecified";
+ case RtAudio::LINUX_ALSA: return "ALSA";
+ case RtAudio::LINUX_PULSE: return "PulseAudio";
+ case RtAudio::LINUX_OSS: return "OSS";
+ case RtAudio::UNIX_JACK: return "JACK";
+ case RtAudio::MACOSX_CORE: return "Core Audio";
+ case RtAudio::WINDOWS_WASAPI: return "WASAPI";
+ case RtAudio::WINDOWS_ASIO: return "ASIO";
+ case RtAudio::WINDOWS_DS: return "DirectSound";
+ case RtAudio::RTAUDIO_DUMMY: return "Dummy";
+ default: return "Unknown";
+ }
}
+
std::vector getSampleRates();
json_t *toJson() override {
@@ -88,7 +116,7 @@ struct AudioInterface : Module {
std::string deviceName = json_string_value(deviceNameJ);
for (int i = 0; i < getDeviceCount(); i++) {
if (deviceName == getDeviceName(i)) {
- setDeviceId(i);
+ deviceId = i;
break;
}
}
@@ -96,40 +124,42 @@ struct AudioInterface : Module {
json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
if (sampleRateJ) {
- setSampleRate(json_number_value(sampleRateJ));
+ sampleRate = json_number_value(sampleRateJ);
}
json_t *blockSizeJ = json_object_get(rootJ, "blockSize");
if (blockSizeJ) {
- setBlockSize(json_integer_value(blockSizeJ));
+ blockSize = json_integer_value(blockSizeJ);
}
+
+ openStream();
}
void reset() override {
- closeDevice();
+ closeStream();
}
};
+#define TIMED_SLEEP_LOCK(_cond, _spinTime, _totalTime) { \
+ auto startTime = std::chrono::high_resolution_clock::now(); \
+ while (!(_cond)) { \
+ std::this_thread::sleep_for(std::chrono::duration(_spinTime)); \
+ auto currTime = std::chrono::high_resolution_clock::now(); \
+ float totalTime = std::chrono::duration(currTime - startTime).count(); \
+ if (totalTime > (_totalTime)) \
+ break; \
+ } \
+}
+
+
void AudioInterface::step() {
// Read/write stream if we have enough input, OR the output buffer is empty if we have no input
if (numOutputs > 0) {
- const float maxTime = 10e-3;
- const float spinTime = 100e-6;
- for (float time = 0.0; time < maxTime; time += spinTime) {
- if (inputSrcBuffer.size() < blockSize)
- break;
- std::this_thread::sleep_for(std::chrono::duration(spinTime));
- }
+ TIMED_SLEEP_LOCK(inputSrcBuffer.size() < blockSize, 100e-6, 0.2);
}
else if (numInputs > 0) {
- const float maxTime = 10e-3;
- const float spinTime = 100e-6;
- for (float time = 0.0; time < maxTime; time += spinTime) {
- if (!outputBuffer.empty())
- break;
- std::this_thread::sleep_for(std::chrono::duration(spinTime));
- }
+ TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2);
}
// Get input and pass it through the sample rate converter
@@ -164,20 +194,14 @@ void AudioInterface::step() {
}
void AudioInterface::stepStream(const float *input, float *output, int numFrames) {
- // if (gPaused) {
- // memset(output, 0, sizeof(float) * numOutputs * numFrames);
- // return;
- // }
+ if (gPaused) {
+ memset(output, 0, sizeof(float) * numOutputs * numFrames);
+ return;
+ }
if (numOutputs > 0) {
// Wait for enough input before proceeding
- const float maxTime = 10e-3;
- const float spinTime = 100e-6;
- for (float time = 0.0; time < maxTime; time += spinTime) {
- if (inputSrcBuffer.size() >= numFrames)
- break;
- std::this_thread::sleep_for(std::chrono::duration(spinTime));
- }
+ TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2);
}
// input stream -> output buffer
@@ -215,15 +239,17 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames
}
int AudioInterface::getDeviceCount() {
- return stream.getDeviceCount();
+ if (!stream)
+ return 0;
+ return stream->getDeviceCount();
}
std::string AudioInterface::getDeviceName(int deviceId) {
- if (deviceId < 0)
+ if (!stream || deviceId < 0)
return "";
try {
- RtAudio::DeviceInfo deviceInfo = stream.getDeviceInfo(deviceId);
+ RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(deviceId);
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
}
catch (RtAudioError &e) {
@@ -239,17 +265,17 @@ static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrame
return 0;
}
-void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
- closeDevice();
-
- this->sampleRate = sampleRate;
- this->blockSize = blockSize;
+void AudioInterface::openStream() {
+ int deviceId = this->deviceId;
+ closeStream();
+ if (!stream)
+ return;
// Open new device
if (deviceId >= 0) {
RtAudio::DeviceInfo deviceInfo;
try {
- deviceInfo = stream.getDeviceInfo(deviceId);
+ deviceInfo = stream->getDeviceInfo(deviceId);
}
catch (RtAudioError &e) {
warn("Failed to query audio device: %s", e.what());
@@ -259,6 +285,11 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
numOutputs = mini(deviceInfo.outputChannels, 8);
numInputs = mini(deviceInfo.inputChannels, 8);
+ if (numOutputs == 0 && numInputs == 0) {
+ warn("Audio device %d has 0 inputs and 0 outputs");
+ return;
+ }
+
RtAudio::StreamParameters outParameters;
outParameters.deviceId = deviceId;
outParameters.nChannels = numOutputs;
@@ -268,15 +299,23 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
inParameters.nChannels = numInputs;
RtAudio::StreamOptions options;
- options.flags |= RTAUDIO_SCHEDULE_REALTIME;
+ // options.flags |= RTAUDIO_SCHEDULE_REALTIME;
+
+ // Find closest sample rate
+ unsigned int closestSampleRate = 0;
+ for (unsigned int sr : deviceInfo.sampleRates) {
+ if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) {
+ closestSampleRate = sr;
+ }
+ }
try {
// Don't use stream parameters if 0 input or output channels
debug("Opening audio stream %d", deviceId);
- stream.openStream(
+ stream->openStream(
numOutputs == 0 ? NULL : &outParameters,
numInputs == 0 ? NULL : &inParameters,
- RTAUDIO_FLOAT32, sampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL);
+ RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL);
}
catch (RtAudioError &e) {
warn("Failed to open audio stream: %s", e.what());
@@ -285,29 +324,38 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
try {
debug("Starting audio stream %d", deviceId);
- stream.startStream();
+ stream->startStream();
}
catch (RtAudioError &e) {
warn("Failed to start audio stream: %s", e.what());
return;
}
- this->sampleRate = stream.getStreamSampleRate();
+ // Update sample rate because this may have changed
+ this->sampleRate = stream->getStreamSampleRate();
this->deviceId = deviceId;
}
}
-void AudioInterface::closeDevice() {
- if (stream.isStreamOpen()) {
- try {
+void AudioInterface::closeStream() {
+ if (stream) {
+ if (stream->isStreamRunning()) {
debug("Aborting audio stream %d", deviceId);
- stream.abortStream();
- debug("Closing audio stream %d", deviceId);
- stream.closeStream();
+ try {
+ stream->abortStream();
+ }
+ catch (RtAudioError &e) {
+ warn("Failed to abort stream %s", e.what());
+ }
}
- catch (RtAudioError &e) {
- warn("Failed to abort stream %s", e.what());
- return;
+ if (stream->isStreamOpen()) {
+ debug("Closing audio stream %d", deviceId);
+ try {
+ stream->closeStream();
+ }
+ catch (RtAudioError &e) {
+ warn("Failed to close stream %s", e.what());
+ }
}
}
@@ -326,12 +374,12 @@ void AudioInterface::closeDevice() {
std::vector AudioInterface::getSampleRates() {
std::vector allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000};
- if (deviceId < 0)
+ if (!stream || deviceId < 0)
return allowedSampleRates;
try {
std::vector sampleRates;
- RtAudio::DeviceInfo deviceInfo = stream.getDeviceInfo(deviceId);
+ RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(deviceId);
for (int sr : deviceInfo.sampleRates) {
float sampleRate = sr;
auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate);
@@ -349,15 +397,45 @@ std::vector AudioInterface::getSampleRates() {
-struct AudioItem : MenuItem {
+struct AudioDriverItem : MenuItem {
+ AudioInterface *audioInterface;
+ int driver;
+ void onAction(EventAction &e) override {
+ audioInterface->setDriver(driver);
+ }
+};
+
+struct AudioDriverChoice : ChoiceButton {
+ AudioInterface *audioInterface;
+ void onAction(EventAction &e) override {
+ Menu *menu = gScene->createMenu();
+ menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
+ menu->box.size.x = box.size.x;
+
+ for (int driver : audioInterface->getAvailableDrivers()) {
+ AudioDriverItem *audioItem = new AudioDriverItem();
+ audioItem->audioInterface = audioInterface;
+ audioItem->driver = driver;
+ audioItem->text = audioInterface->getDriverName(driver);
+ menu->pushChild(audioItem);
+ }
+ }
+ void step() override {
+ text = audioInterface->getDriverName(audioInterface->getDriver());
+ }
+};
+
+
+struct AudioDeviceItem : MenuItem {
AudioInterface *audioInterface;
int deviceId;
void onAction(EventAction &e) override {
- audioInterface->setDeviceId(deviceId);
+ audioInterface->deviceId = deviceId;
+ audioInterface->openStream();
}
};
-struct AudioChoice : ChoiceButton {
+struct AudioDeviceChoice : ChoiceButton {
int lastDeviceId = -1;
AudioInterface *audioInterface;
void onAction(EventAction &e) override {
@@ -367,14 +445,14 @@ struct AudioChoice : ChoiceButton {
int deviceCount = audioInterface->getDeviceCount();
{
- AudioItem *audioItem = new AudioItem();
+ AudioDeviceItem *audioItem = new AudioDeviceItem();
audioItem->audioInterface = audioInterface;
audioItem->deviceId = -1;
audioItem->text = "No device";
menu->pushChild(audioItem);
}
for (int deviceId = 0; deviceId < deviceCount; deviceId++) {
- AudioItem *audioItem = new AudioItem();
+ AudioDeviceItem *audioItem = new AudioDeviceItem();
audioItem->audioInterface = audioInterface;
audioItem->deviceId = deviceId;
audioItem->text = audioInterface->getDeviceName(deviceId);
@@ -395,7 +473,8 @@ struct SampleRateItem : MenuItem {
AudioInterface *audioInterface;
float sampleRate;
void onAction(EventAction &e) override {
- audioInterface->setSampleRate(sampleRate);
+ audioInterface->sampleRate = sampleRate;
+ audioInterface->openStream();
}
};
@@ -424,7 +503,8 @@ struct BlockSizeItem : MenuItem {
AudioInterface *audioInterface;
int blockSize;
void onAction(EventAction &e) override {
- audioInterface->setBlockSize(blockSize);
+ audioInterface->blockSize = blockSize;
+ audioInterface->openStream();
}
};
@@ -471,6 +551,21 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
float yPos = margin;
float xPos;
+ {
+ Label *label = new Label();
+ label->box.pos = Vec(margin, yPos);
+ label->text = "Audio driver";
+ addChild(label);
+ yPos += labelHeight + margin;
+
+ AudioDriverChoice *choice = new AudioDriverChoice();
+ choice->audioInterface = module;
+ choice->box.pos = Vec(margin, yPos);
+ choice->box.size.x = box.size.x - 2*margin;
+ addChild(choice);
+ yPos += choice->box.size.y + margin;
+ }
+
{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
@@ -478,8 +573,8 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
addChild(label);
yPos += labelHeight + margin;
- AudioChoice *choice = new AudioChoice();
- choice->audioInterface = dynamic_cast(module);
+ AudioDeviceChoice *choice = new AudioDeviceChoice();
+ choice->audioInterface = module;
choice->box.pos = Vec(margin, yPos);
choice->box.size.x = box.size.x - 2*margin;
addChild(choice);
@@ -494,7 +589,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
yPos += labelHeight + margin;
SampleRateChoice *choice = new SampleRateChoice();
- choice->audioInterface = dynamic_cast(module);
+ choice->audioInterface = module;
choice->box.pos = Vec(margin, yPos);
choice->box.size.x = box.size.x - 2*margin;
addChild(choice);
@@ -509,7 +604,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
yPos += labelHeight + margin;
BlockSizeChoice *choice = new BlockSizeChoice();
- choice->audioInterface = dynamic_cast(module);
+ choice->audioInterface = module;
choice->box.pos = Vec(margin, yPos);
choice->box.size.x = box.size.x - 2*margin;
addChild(choice);