From f129f026f677e470397083f17c4e1923f4234999 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Jan 2018 17:40:36 -0500 Subject: [PATCH] Add AudioWidget --- include/app.hpp | 15 + include/audio.hpp | 42 ++ .../{components.hpp => componentlibrary.hpp} | 16 + include/rack.hpp | 2 +- include/widgets.hpp | 3 + res/ComponentLibrary/ADAT.svg | 366 +++++++++++++ res/ComponentLibrary/MIDI_DIN.svg | 397 ++++++++++++++ res/ComponentLibrary/USB-B.svg | 376 ++++++++++++++ src/app/AudioWidget.cpp | 115 +++++ src/app/Port.cpp | 2 +- src/app/Toolbar.cpp | 21 +- src/app/WireWidget.cpp | 2 +- src/audio.cpp | 212 ++++++++ src/core/AudioInterface.cpp | 486 ++---------------- 14 files changed, 1605 insertions(+), 450 deletions(-) create mode 100644 include/audio.hpp rename include/{components.hpp => componentlibrary.hpp} (97%) create mode 100644 res/ComponentLibrary/ADAT.svg create mode 100644 res/ComponentLibrary/MIDI_DIN.svg create mode 100644 res/ComponentLibrary/USB-B.svg create mode 100644 src/app/AudioWidget.cpp create mode 100644 src/audio.cpp diff --git a/include/app.hpp b/include/app.hpp index bad646d0..1e1550c0 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -278,6 +278,21 @@ struct MomentarySwitch : virtual Switch { } }; +//////////////////// +// IO widgets +//////////////////// + +struct AudioIO; + +struct AudioWidget : OpaqueWidget { + /** Not owned */ + AudioIO *audioIO = NULL; + void onMouseDown(EventMouseDown &e) override; +}; + +struct MIDIWidget : OpaqueWidget { +}; + //////////////////// // lights //////////////////// diff --git a/include/audio.hpp b/include/audio.hpp new file mode 100644 index 00000000..6570be5d --- /dev/null +++ b/include/audio.hpp @@ -0,0 +1,42 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#include +#pragma GCC diagnostic pop + + +namespace rack { + + +struct AudioIO { + int maxOutputs = 8; + int maxInputs = 8; + + RtAudio *stream = NULL; + // Stream properties + int device = -1; + int sampleRate = 44100; + int blockSize = 256; + int numOutputs = 0; + int numInputs = 0; + + AudioIO(); + ~AudioIO(); + + std::vector listDrivers(); + std::string getDriverName(int driver); + int getDriver(); + void setDriver(int driver); + + int getDeviceCount(); + std::string getDeviceName(int device); + std::string getDeviceDetail(int device); + void openStream(); + void closeStream(); + + std::vector listSampleRates(); +}; + + +} // namespace rack diff --git a/include/components.hpp b/include/componentlibrary.hpp similarity index 97% rename from include/components.hpp rename to include/componentlibrary.hpp index cac1cf91..5ae6c671 100644 --- a/include/components.hpp +++ b/include/componentlibrary.hpp @@ -336,6 +336,22 @@ struct BefacoSlidePot : SVGFader { } }; +//////////////////// +// IO widgets +//////////////////// + +struct USB_B_AudioWidget : AudioWidget, SVGWidget { + USB_B_AudioWidget() { + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/USB-B.svg"))); + } +}; + +struct DIN_MIDIWidget : MIDIWidget, SVGWidget { + DIN_MIDIWidget() { + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/MIDI_DIN.svg"))); + } +}; + //////////////////// // Jacks //////////////////// diff --git a/include/rack.hpp b/include/rack.hpp index b4371009..6177612e 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -7,7 +7,7 @@ #include "engine.hpp" #include "gui.hpp" #include "app.hpp" -#include "components.hpp" +#include "componentlibrary.hpp" namespace rack { diff --git a/include/widgets.hpp b/include/widgets.hpp index 6c8fa9f3..c400d80a 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -25,6 +25,9 @@ inline Vec mm2px(Vec millimeters) { return millimeters.mult(SVG_DPI / 25.4); } +#define CHECKMARK_STRING "✔" +#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") + //////////////////// // resources diff --git a/res/ComponentLibrary/ADAT.svg b/res/ComponentLibrary/ADAT.svg new file mode 100644 index 00000000..5be20bcc --- /dev/null +++ b/res/ComponentLibrary/ADAT.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/res/ComponentLibrary/MIDI_DIN.svg b/res/ComponentLibrary/MIDI_DIN.svg new file mode 100644 index 00000000..58ca88f3 --- /dev/null +++ b/res/ComponentLibrary/MIDI_DIN.svg @@ -0,0 +1,397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/res/ComponentLibrary/USB-B.svg b/res/ComponentLibrary/USB-B.svg new file mode 100644 index 00000000..bed8222e --- /dev/null +++ b/res/ComponentLibrary/USB-B.svg @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/app/AudioWidget.cpp b/src/app/AudioWidget.cpp new file mode 100644 index 00000000..67ca64d9 --- /dev/null +++ b/src/app/AudioWidget.cpp @@ -0,0 +1,115 @@ +#include "app.hpp" +#include "audio.hpp" + + +namespace rack { + + +struct AudioDriverItem : MenuItem { + AudioIO *audioIO; + int driver; + void onAction(EventAction &e) override { + audioIO->setDriver(driver); + } +}; + + +struct AudioDeviceItem : MenuItem { + AudioIO *audioIO; + int device; + void onAction(EventAction &e) override { + audioIO->device = device; + audioIO->openStream(); + } +}; + + +struct AudioSampleRateItem : MenuItem { + AudioIO *audioIO; + int sampleRate; + void onAction(EventAction &e) override { + audioIO->sampleRate = sampleRate; + audioIO->openStream(); + } +}; + + +struct AudioBlockSizeItem : MenuItem { + AudioIO *audioIO; + int blockSize; + void onAction(EventAction &e) override { + audioIO->blockSize = blockSize; + audioIO->openStream(); + } +}; + + +void AudioWidget::onMouseDown(EventMouseDown &e) { + OpaqueWidget::onMouseDown(e); + + if (!audioIO) + return; + + Menu *menu = gScene->createMenu(); + + // Audio driver + menu->addChild(construct(&MenuLabel::text, "Audio driver")); + for (int driver : audioIO->listDrivers()) { + AudioDriverItem *item = new AudioDriverItem(); + item->audioIO = audioIO; + item->driver = driver; + item->text = audioIO->getDriverName(driver); + item->rightText = CHECKMARK(item->driver == audioIO->getDriver()); + menu->addChild(item); + } + menu->addChild(construct()); + + // Audio device + menu->addChild(construct(&MenuLabel::text, "Audio device")); + int deviceCount = audioIO->getDeviceCount(); + { + AudioDeviceItem *item = new AudioDeviceItem(); + item->audioIO = audioIO; + item->device = -1; + item->text = "No device"; + item->rightText = CHECKMARK(item->device == audioIO->device); + menu->addChild(item); + } + for (int device = 0; device < deviceCount; device++) { + AudioDeviceItem *item = new AudioDeviceItem(); + item->audioIO = audioIO; + item->device = device; + item->text = audioIO->getDeviceDetail(device); + item->rightText = CHECKMARK(item->device == audioIO->device); + menu->addChild(item); + } + menu->addChild(construct()); + + // Sample rate + menu->addChild(construct(&MenuLabel::text, "Sample rate")); + for (int sampleRate : audioIO->listSampleRates()) { + AudioSampleRateItem *item = new AudioSampleRateItem(); + item->audioIO = audioIO; + item->sampleRate = sampleRate; + item->text = stringf("%d Hz", sampleRate); + item->rightText = CHECKMARK(item->sampleRate == audioIO->sampleRate); + menu->addChild(item); + } + menu->addChild(construct()); + + // Block size + menu->addChild(construct(&MenuLabel::text, "Block size")); + std::vector blockSizes = {64, 128, 256, 512, 1024, 2048, 4096}; + for (int blockSize : blockSizes) { + AudioBlockSizeItem *item = new AudioBlockSizeItem(); + item->audioIO = audioIO; + item->blockSize = blockSize; + float latency = (float) blockSize / audioIO->sampleRate * 1000.0; + item->text = stringf("%d (%.1f ms)", blockSize, latency); + item->rightText = CHECKMARK(item->blockSize == audioIO->blockSize); + menu->addChild(item); + } +} + + +} // namespace rack diff --git a/src/app/Port.cpp b/src/app/Port.cpp index 8d7b3cbb..17424fc1 100644 --- a/src/app/Port.cpp +++ b/src/app/Port.cpp @@ -1,7 +1,7 @@ #include "app.hpp" #include "gui.hpp" -#include "components.hpp" #include "engine.hpp" +#include "componentlibrary.hpp" namespace rack { diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 6fe03649..00ccbdd9 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -53,13 +53,13 @@ struct FileChoice : ChoiceButton { }; -struct PauseItem : MenuItem { +struct EnginePauseItem : MenuItem { void onAction(EventAction &e) override { gPaused = !gPaused; } }; -struct SampleRateItem : MenuItem { +struct EngineSampleRateItem : MenuItem { float sampleRate; void onAction(EventAction &e) override { engineSetSampleRate(sampleRate); @@ -67,22 +67,21 @@ struct SampleRateItem : MenuItem { } }; -struct SampleRateChoice : ChoiceButton { +struct EngineSampleRateChoice : ChoiceButton { void onAction(EventAction &e) override { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; - PauseItem *pauseItem = new PauseItem(); + EnginePauseItem *pauseItem = new EnginePauseItem(); pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; menu->addChild(pauseItem); - float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; - int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); - for (int i = 0; i < sampleRatesLen; i++) { - SampleRateItem *item = new SampleRateItem(); - item->text = stringf("%.0f Hz", sampleRates[i]); - item->sampleRate = sampleRates[i]; + std::vector sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; + for (float sampleRate : sampleRates) { + EngineSampleRateItem *item = new EngineSampleRateItem(); + item->text = stringf("%.0f Hz", sampleRate); + item->sampleRate = sampleRate; menu->addChild(item); } } @@ -112,7 +111,7 @@ Toolbar::Toolbar() { xPos += margin; { - SampleRateChoice *srChoice = new SampleRateChoice(); + EngineSampleRateChoice *srChoice = new EngineSampleRateChoice(); srChoice->box.pos = Vec(xPos, margin); srChoice->box.size.x = 100; addChild(srChoice); diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 75b73516..f75574de 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -1,6 +1,6 @@ #include "app.hpp" #include "engine.hpp" -#include "components.hpp" +#include "componentlibrary.hpp" #include "gui.hpp" diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 00000000..3484160f --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1,212 @@ +#include "util.hpp" +#include "math.hpp" +#include "audio.hpp" + + +namespace rack { + + +AudioIO::AudioIO() { + setDriver(RtAudio::UNSPECIFIED); +} + +AudioIO::~AudioIO() { + closeStream(); +} + +std::vector AudioIO::listDrivers() { + std::vector apis; + RtAudio::getCompiledApi(apis); + std::vector drivers; + for (RtAudio::Api api : apis) + drivers.push_back((int) api); + return drivers; +} + +std::string AudioIO::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"; + } +} + +int AudioIO::getDriver() { + if (!stream) + return RtAudio::UNSPECIFIED; + return stream->getCurrentApi(); +} + +void AudioIO::setDriver(int driver) { + // closeStream(); + if (stream) + delete stream; + stream = new RtAudio((RtAudio::Api) driver); +} + +int AudioIO::getDeviceCount() { + if (!stream) + return 0; + return stream->getDeviceCount(); +} + +std::string AudioIO::getDeviceName(int device) { + if (!stream || device < 0) + return ""; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + return deviceInfo.name; + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return ""; + } +} + +std::string AudioIO::getDeviceDetail(int device) { + if (!stream || device < 0) + return ""; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return ""; + } +} + +static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { + AudioIO *audioIO = (AudioIO*) userData; + assert(audioIO); + // audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); + return 0; +} + +void AudioIO::openStream() { + // Close device but remember the current device number + int device = this->device; + closeStream(); + if (!stream) + return; + + // Open new device + if (device >= 0) { + RtAudio::DeviceInfo deviceInfo; + try { + deviceInfo = stream->getDeviceInfo(device); + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return; + } + + numOutputs = mini(deviceInfo.outputChannels, maxOutputs); + numInputs = mini(deviceInfo.inputChannels, maxInputs); + + if (numOutputs == 0 && numInputs == 0) { + warn("Audio device %d has 0 inputs and 0 outputs"); + return; + } + + RtAudio::StreamParameters outParameters; + outParameters.deviceId = device; + outParameters.nChannels = numOutputs; + + RtAudio::StreamParameters inParameters; + inParameters.deviceId = device; + inParameters.nChannels = numInputs; + + RtAudio::StreamOptions options; + // 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 { + debug("Opening audio stream %d", device); + stream->openStream( + numOutputs == 0 ? NULL : &outParameters, + numInputs == 0 ? NULL : &inParameters, + RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); + } + catch (RtAudioError &e) { + warn("Failed to open audio stream: %s", e.what()); + return; + } + + try { + debug("Starting audio stream %d", device); + stream->startStream(); + } + catch (RtAudioError &e) { + warn("Failed to start audio stream: %s", e.what()); + return; + } + + // Update sample rate because this may have changed + this->sampleRate = stream->getStreamSampleRate(); + this->device = device; + } +} + +void AudioIO::closeStream() { + if (stream) { + if (stream->isStreamRunning()) { + debug("Aborting audio stream %d", device); + try { + stream->abortStream(); + } + catch (RtAudioError &e) { + warn("Failed to abort stream %s", e.what()); + } + } + if (stream->isStreamOpen()) { + debug("Closing audio stream %d", device); + try { + stream->closeStream(); + } + catch (RtAudioError &e) { + warn("Failed to close stream %s", e.what()); + } + } + } + + // Reset stream settings + device = -1; + numOutputs = 0; + numInputs = 0; +} + +std::vector AudioIO::listSampleRates() { + if (!stream || device < 0) + return {}; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + std::vector sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); + return sampleRates; + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return {}; + } +} + + +} // namespace rack diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index b9bdfdca..19475460 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include "core.hpp" +#include "audio.hpp" #include "dsp/samplerate.hpp" #include "dsp/ringbuffer.hpp" @@ -29,13 +29,7 @@ struct AudioInterface : Module { NUM_OUTPUTS = AUDIO1_OUTPUT + 8 }; - RtAudio *stream = NULL; - // Stream properties - int device = -1; - float sampleRate = 44100.0; - int blockSize = 256; - int numOutputs = 0; - int numInputs = 0; + AudioIO audioIO; SampleRateConverter<8> inputSrc; SampleRateConverter<8> outputSrc; @@ -47,89 +41,43 @@ struct AudioInterface : Module { DoubleRingBuffer, (1<<15)> inputSrcBuffer; AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { - setDriver(RtAudio::UNSPECIFIED); - } - ~AudioInterface() { - closeStream(); } void step() override; void stepStream(const float *input, float *output, int numFrames); - int getDeviceCount(); - std::string getDeviceName(int device); - - 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 { json_t *rootJ = json_object(); - json_object_set_new(rootJ, "driver", json_integer(getDriver())); - json_object_set_new(rootJ, "device", json_integer(device)); - json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); - json_object_set_new(rootJ, "blockSize", json_integer(blockSize)); + // json_object_set_new(rootJ, "driver", json_integer(getDriver())); + // json_object_set_new(rootJ, "device", json_integer(device)); + // json_object_set_new(rootJ, "audioIO.sampleRate", json_real(audioIO.sampleRate)); + // json_object_set_new(rootJ, "audioIO.blockSize", json_integer(audioIO.blockSize)); return rootJ; } void fromJson(json_t *rootJ) override { - json_t *driverJ = json_object_get(rootJ, "driver"); - if (driverJ) - setDriver(json_number_value(driverJ)); + // json_t *driverJ = json_object_get(rootJ, "driver"); + // if (driverJ) + // setDriver(json_number_value(driverJ)); - json_t *deviceJ = json_object_get(rootJ, "device"); - if (deviceJ) - device = json_number_value(deviceJ); + // json_t *deviceJ = json_object_get(rootJ, "device"); + // if (deviceJ) + // device = json_number_value(deviceJ); - json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); - if (sampleRateJ) - sampleRate = json_number_value(sampleRateJ); + // json_t *sampleRateJ = json_object_get(rootJ, "audioIO.sampleRate"); + // if (sampleRateJ) + // audioIO.sampleRate = json_number_value(sampleRateJ); - json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); - if (blockSizeJ) - blockSize = json_integer_value(blockSizeJ); + // json_t *blockSizeJ = json_object_get(rootJ, "audioIO.blockSize"); + // if (blockSizeJ) + // audioIO.blockSize = json_integer_value(blockSizeJ); - openStream(); + // openStream(); } void onReset() override { - closeStream(); + audioIO.closeStream(); } }; @@ -149,15 +97,15 @@ struct AudioInterface : Module { void AudioInterface::step() { // debug("inputBuffer %d inputSrcBuffer %d outputBuffer %d", inputBuffer.size(), inputSrcBuffer.size(), outputBuffer.size()); // Read/write stream if we have enough input, OR the output buffer is empty if we have no input - if (numOutputs > 0) { - TIMED_SLEEP_LOCK(inputSrcBuffer.size() < blockSize, 100e-6, 0.2); + if (audioIO.numOutputs > 0) { + TIMED_SLEEP_LOCK(inputSrcBuffer.size() < audioIO.blockSize, 100e-6, 0.2); } - else if (numInputs > 0) { + else if (audioIO.numInputs > 0) { TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); } // Get input and pass it through the sample rate converter - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { if (!inputBuffer.full()) { Frame<8> f; for (int i = 0; i < 8; i++) { @@ -169,7 +117,7 @@ void AudioInterface::step() { // Once full, sample rate convert the input // inputBuffer -> SRC -> inputSrcBuffer if (inputBuffer.full()) { - inputSrc.setRates(engineGetSampleRate(), sampleRate); + inputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); int inLen = inputBuffer.size(); int outLen = inputSrcBuffer.capacity(); inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); @@ -189,29 +137,29 @@ void AudioInterface::step() { void AudioInterface::stepStream(const float *input, float *output, int numFrames) { if (gPaused) { - memset(output, 0, sizeof(float) * numOutputs * numFrames); + memset(output, 0, sizeof(float) * audioIO.numOutputs * numFrames); return; } - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { // Wait for enough input before proceeding TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); } - else if (numInputs > 0) { + else if (audioIO.numInputs > 0) { TIMED_SLEEP_LOCK(outputBuffer.empty(), 100e-6, 0.2); } // input stream -> output buffer - if (numInputs > 0) { + if (audioIO.numInputs > 0) { Frame<8> inputFrames[numFrames]; for (int i = 0; i < numFrames; i++) { for (int c = 0; c < 8; c++) { - inputFrames[i].samples[c] = (c < numInputs) ? input[i*numInputs + c] : 0.0; + inputFrames[i].samples[c] = (c < audioIO.numInputs) ? input[i*audioIO.numInputs + c] : 0.0; } } // Pass output through sample rate converter - outputSrc.setRates(sampleRate, engineGetSampleRate()); + outputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); int inLen = numFrames; int outLen = outputBuffer.capacity(); outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); @@ -219,7 +167,7 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames } // input buffer -> output stream - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { for (int i = 0; i < numFrames; i++) { Frame<8> f; if (inputSrcBuffer.empty()) { @@ -228,304 +176,23 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames else { f = inputSrcBuffer.shift(); } - for (int c = 0; c < numOutputs; c++) { - output[i*numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); + for (int c = 0; c < audioIO.numOutputs; c++) { + output[i*audioIO.numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); } } } } -int AudioInterface::getDeviceCount() { - if (!stream) - return 0; - return stream->getDeviceCount(); -} - -std::string AudioInterface::getDeviceName(int device) { - if (!stream || device < 0) - return ""; - - try { - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return ""; - } -} - -static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { - AudioInterface *audioInterface = (AudioInterface *) userData; - assert(audioInterface); - audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); - return 0; -} - -void AudioInterface::openStream() { - int device = this->device; - closeStream(); - if (!stream) - return; - - // Open new device - if (device >= 0) { - RtAudio::DeviceInfo deviceInfo; - try { - deviceInfo = stream->getDeviceInfo(device); - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return; - } - - 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 = device; - outParameters.nChannels = numOutputs; - - RtAudio::StreamParameters inParameters; - inParameters.deviceId = device; - inParameters.nChannels = numInputs; - - RtAudio::StreamOptions options; - // 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", device); - stream->openStream( - numOutputs == 0 ? NULL : &outParameters, - numInputs == 0 ? NULL : &inParameters, - RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); - } - catch (RtAudioError &e) { - warn("Failed to open audio stream: %s", e.what()); - return; - } - - try { - debug("Starting audio stream %d", device); - stream->startStream(); - } - catch (RtAudioError &e) { - warn("Failed to start audio stream: %s", e.what()); - return; - } - - // Update sample rate because this may have changed - this->sampleRate = stream->getStreamSampleRate(); - this->device = device; - } -} - -void AudioInterface::closeStream() { - if (stream) { - if (stream->isStreamRunning()) { - debug("Aborting audio stream %d", device); - try { - stream->abortStream(); - } - catch (RtAudioError &e) { - warn("Failed to abort stream %s", e.what()); - } - } - if (stream->isStreamOpen()) { - debug("Closing audio stream %d", device); - try { - stream->closeStream(); - } - catch (RtAudioError &e) { - warn("Failed to close stream %s", e.what()); - } - } - } - - // Reset stream settings - device = -1; - numOutputs = 0; - numInputs = 0; - - // Clear buffers - inputBuffer.clear(); - outputBuffer.clear(); - inputSrcBuffer.clear(); - inputSrc.reset(); - outputSrc.reset(); -} - -std::vector AudioInterface::getSampleRates() { - std::vector allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; - if (!stream || device < 0) - return allowedSampleRates; - - try { - std::vector sampleRates; - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - for (int sr : deviceInfo.sampleRates) { - float sampleRate = sr; - auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate); - if (allowedIt != allowedSampleRates.end()) { - sampleRates.push_back(sampleRate); - } - } - return sampleRates; - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return {}; - } -} - - - -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->addChild(audioItem); - } - } - void step() override { - text = audioInterface->getDriverName(audioInterface->getDriver()); - } -}; - - -struct AudioDeviceItem : MenuItem { - AudioInterface *audioInterface; - int device; - void onAction(EventAction &e) override { - audioInterface->device = device; - audioInterface->openStream(); - } -}; - -struct AudioDeviceChoice : ChoiceButton { - int lastDeviceId = -1; - 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; - - int deviceCount = audioInterface->getDeviceCount(); - { - AudioDeviceItem *audioItem = new AudioDeviceItem(); - audioItem->audioInterface = audioInterface; - audioItem->device = -1; - audioItem->text = "No device"; - menu->addChild(audioItem); - } - for (int device = 0; device < deviceCount; device++) { - AudioDeviceItem *audioItem = new AudioDeviceItem(); - audioItem->audioInterface = audioInterface; - audioItem->device = device; - audioItem->text = audioInterface->getDeviceName(device); - menu->addChild(audioItem); - } - } - void step() override { - if (lastDeviceId != audioInterface->device) { - std::string name = audioInterface->getDeviceName(audioInterface->device); - text = ellipsize(name, 24); - lastDeviceId = audioInterface->device; - } - } -}; - - -struct SampleRateItem : MenuItem { - AudioInterface *audioInterface; - float sampleRate; - void onAction(EventAction &e) override { - audioInterface->sampleRate = sampleRate; - audioInterface->openStream(); - } -}; - -struct SampleRateChoice : 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 (float sampleRate : audioInterface->getSampleRates()) { - SampleRateItem *item = new SampleRateItem(); - item->audioInterface = audioInterface; - item->sampleRate = sampleRate; - item->text = stringf("%.0f Hz", sampleRate); - menu->addChild(item); - } - } - void step() override { - this->text = stringf("%.0f Hz", audioInterface->sampleRate); - } -}; - -struct BlockSizeItem : MenuItem { - AudioInterface *audioInterface; - int blockSize; - void onAction(EventAction &e) override { - audioInterface->blockSize = blockSize; - audioInterface->openStream(); - } -}; +// void AudioInterface::closeStream() { +// // Clear buffers +// inputBuffer.clear(); +// outputBuffer.clear(); +// inputSrcBuffer.clear(); +// inputSrc.reset(); +// outputSrc.reset(); +// } -struct BlockSizeChoice : 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; - - const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; - int blockSizesLen = sizeof(blockSizes) / sizeof(blockSizes[0]); - for (int i = 0; i < blockSizesLen; i++) { - BlockSizeItem *item = new BlockSizeItem(); - item->audioInterface = audioInterface; - item->blockSize = blockSizes[i]; - item->text = stringf("%d", blockSizes[i]); - menu->addChild(item); - } - } - void step() override { - this->text = stringf("%d", audioInterface->blockSize); - } -}; AudioInterfaceWidget::AudioInterfaceWidget() { @@ -545,69 +212,9 @@ AudioInterfaceWidget::AudioInterfaceWidget() { Vec margin = Vec(5, 2); float labelHeight = 15; - float yPos = margin.y; + float yPos = margin.y + 100; float xPos; - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Audio driver"; - addChild(label); - yPos += labelHeight + margin.y; - - AudioDriverChoice *choice = new AudioDriverChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Audio device"; - addChild(label); - yPos += labelHeight + margin.y; - - AudioDeviceChoice *choice = new AudioDeviceChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Sample rate"; - addChild(label); - yPos += labelHeight + margin.y; - - SampleRateChoice *choice = new SampleRateChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Block size"; - addChild(label); - yPos += labelHeight + margin.y; - - BlockSizeChoice *choice = new BlockSizeChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - { Label *label = new Label(); label->box.pos = Vec(margin.x, yPos); @@ -675,4 +282,11 @@ AudioInterfaceWidget::AudioInterfaceWidget() { xPos += 37 + margin.x; } yPos += 35 + margin.y; + + AudioWidget *audioWidget = construct(); + audioWidget->audioIO = &module->audioIO; + addChild(audioWidget); + // Widget *w = construct(); + // w->box.pos = Vec(100, 0); + // addChild(w); }