Browse Source

Add grouping to AudioInterface

tags/v0.6.0
Andrew Belt 6 years ago
parent
commit
df069a8870
5 changed files with 130 additions and 65 deletions
  1. +9
    -7
      README.md
  2. +10
    -4
      include/audio.hpp
  3. +19
    -13
      src/app/AudioWidget.cpp
  4. +91
    -39
      src/audio.cpp
  5. +1
    -2
      src/core/AudioInterface.cpp

+ 9
- 7
README.md View File

@@ -1,19 +1,19 @@
# Rack # Rack


*Rack* is the engine for the VCV open-source virtual Eurorack DAW.
*Rack* is the engine for the VCV open-source virtual modular synthesizer.


![Rack screenshot](https://vcvrack.com/images/screenshot.png) ![Rack screenshot](https://vcvrack.com/images/screenshot.png)


This README includes instructions for building Rack from source. For information about the software, go to https://vcvrack.com/. This README includes instructions for building Rack from source. For information about the software, go to https://vcvrack.com/.


## The [Issue Tracker](https://github.com/VCVRack/Rack/issues) *is* the official developer's forum
## The [Issue Tracker](https://github.com/VCVRack/Rack/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) *is* the official developer's forum


Bug reports, feature requests, and even *questions/discussions* are welcome on the GitHub Issue Tracker for all VCVRack repos. Bug reports, feature requests, and even *questions/discussions* are welcome on the GitHub Issue Tracker for all VCVRack repos.
However, please search before posting to avoid duplicates, and limit to one issue per post. However, please search before posting to avoid duplicates, and limit to one issue per post.


You may vote on feature requests by using the Thumbs Up/Down reaction on the first post.
Please vote on feature requests by using the Thumbs Up/Down reaction on the first post.


I rarely accept Pull Requests, so please notify me in advance to plan your contribution before writing code.
I rarely accept code contributions to Rack itself, so please notify me in advance if you wish to send a pull request.


## Setting up your development environment ## Setting up your development environment


@@ -33,14 +33,16 @@ Install build dependencies with the pacman package manger.


### Linux ### Linux


With your distro's package manager, make sure you have installed `git`, `gcc`, `make`, `cmake`, `tar`, and `unzip`.
With your distro's package manager, make sure you have installed `git`, `gcc`, `make`, `cmake`, `tar`, `unzip`, and `curl`.


## Building ## Building


*If the build fails for you, please report the issue with a detailed error message to help the portability of Rack.* *If the build fails for you, please report the issue with a detailed error message to help the portability of Rack.*


Clone this repository with `git clone https://github.com/VCVRack/Rack.git` and `cd Rack`. Clone this repository with `git clone https://github.com/VCVRack/Rack.git` and `cd Rack`.
If you would like to build a previous version of Rack instead of the master branch, check out the desired tag with `git checkout v0.4.0` for example.

The `master` branch contains the latest public code and breaks its plugin [API](https://en.wikipedia.org/wiki/Application_programming_interface) and [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) frequently.
If you wish to build a previous version of Rack which is API/ABI-compatible with an official Rack release, check out the desired branch with `git checkout v0.5` for example.


Clone submodules. Clone submodules.


@@ -51,7 +53,7 @@ You may use make's `-j$(nproc)` flag to parallelize builds across all your CPU c


make dep make dep


You may use `make dep RTAUDIO_ALL_APIS=1` to attempt to build with all audio driver APIs enabled for your operating system.
You may use `make dep RTAUDIO_ALL_APIS=1` to attempt to build with all audio driver APIs enabled for your operating system, although this is unsupported.


You should see a message that all dependencies built successfully. You should see a message that all dependencies built successfully.




+ 10
- 4
include/audio.hpp View File

@@ -12,12 +12,10 @@ namespace rack {




struct AudioIO { struct AudioIO {
int maxOutputs = 8;
int maxInputs = 8;

// Stream properties // Stream properties
int driver = 0; int driver = 0;
int device = -1; int device = -1;
int offset = 0;
int sampleRate = 44100; int sampleRate = 44100;
int blockSize = 256; int blockSize = 256;
int numOutputs = 0; int numOutputs = 0;
@@ -34,8 +32,16 @@ struct AudioIO {
void setDriver(int driver); void setDriver(int driver);


int getDeviceCount(); int getDeviceCount();
bool getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo);
int getDeviceMaxChannels(int device);
std::string getDeviceName(int device); std::string getDeviceName(int device);
std::string getDeviceDetail(int device);
std::string getDeviceDetail(int device, int offset, int maxChannels);
void setDevice(int device, int offset, int maxChannels);

void setSampleRate(int sampleRate);
void setBlockSize(int blockSize);

/** Must close the stream before opening */
void openStream(); void openStream();
void closeStream(); void closeStream();
/** Returns whether the audio stream is open and running */ /** Returns whether the audio stream is open and running */


+ 19
- 13
src/app/AudioWidget.cpp View File

@@ -36,14 +36,17 @@ struct AudioDriverChoice : LedDisplayChoice {
struct AudioDeviceItem : MenuItem { struct AudioDeviceItem : MenuItem {
AudioIO *audioIO; AudioIO *audioIO;
int device; int device;
int offset;
int maxChannels;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
audioIO->device = device;
audioIO->openStream();
audioIO->setDevice(device, offset, maxChannels);
} }
}; };


struct AudioDeviceChoice : LedDisplayChoice { struct AudioDeviceChoice : LedDisplayChoice {
AudioWidget *audioWidget; AudioWidget *audioWidget;
int groupChannels = 8;
int maxTotalChannels = 64;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
Menu *menu = gScene->createMenu(); Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device")); menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device"));
@@ -57,16 +60,21 @@ struct AudioDeviceChoice : LedDisplayChoice {
menu->addChild(item); menu->addChild(item);
} }
for (int device = 0; device < deviceCount; device++) { for (int device = 0; device < deviceCount; device++) {
AudioDeviceItem *item = new AudioDeviceItem();
item->audioIO = audioWidget->audioIO;
item->device = device;
item->text = audioWidget->audioIO->getDeviceDetail(device);
item->rightText = CHECKMARK(item->device == audioWidget->audioIO->device);
menu->addChild(item);
int maxChannels = min(maxTotalChannels, audioWidget->audioIO->getDeviceMaxChannels(device));
for (int offset = 0; offset < maxChannels; offset += groupChannels) {
AudioDeviceItem *item = new AudioDeviceItem();
item->audioIO = audioWidget->audioIO;
item->device = device;
item->offset = offset;
item->maxChannels = groupChannels;
item->text = audioWidget->audioIO->getDeviceDetail(device, offset, groupChannels);
item->rightText = CHECKMARK(item->device == audioWidget->audioIO->device && item->offset == audioWidget->audioIO->offset);
menu->addChild(item);
}
} }
} }
void step() override { void step() override {
text = audioWidget->audioIO->getDeviceDetail(audioWidget->audioIO->device);
text = audioWidget->audioIO->getDeviceDetail(audioWidget->audioIO->device, audioWidget->audioIO->offset, groupChannels);
if (text.empty()) { if (text.empty()) {
text = "(No device)"; text = "(No device)";
color.a = 0.5f; color.a = 0.5f;
@@ -83,8 +91,7 @@ struct AudioSampleRateItem : MenuItem {
AudioIO *audioIO; AudioIO *audioIO;
int sampleRate; int sampleRate;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
audioIO->sampleRate = sampleRate;
audioIO->openStream();
audioIO->setSampleRate(sampleRate);
} }
}; };


@@ -112,8 +119,7 @@ struct AudioBlockSizeItem : MenuItem {
AudioIO *audioIO; AudioIO *audioIO;
int blockSize; int blockSize;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
audioIO->blockSize = blockSize;
audioIO->openStream();
audioIO->setBlockSize(blockSize);
} }
}; };




+ 91
- 39
src/audio.cpp View File

@@ -75,54 +75,107 @@ int AudioIO::getDeviceCount() {
return 0; return 0;
} }


bool AudioIO::getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo) {
if (!deviceInfo)
return false;

if (rtAudio) {
if (device == this->device) {
*deviceInfo = this->deviceInfo;
return true;
}
else {
try {
*deviceInfo = rtAudio->getDeviceInfo(device);
return true;
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
return false;
}
}
}
else {
return false;
}
}

int AudioIO::getDeviceMaxChannels(int device) {
if (device < 0)
return 0;

if (rtAudio) {
RtAudio::DeviceInfo deviceInfo;
if (getDeviceInfo(device, &deviceInfo))
return max(deviceInfo.inputChannels, deviceInfo.outputChannels);
}
if (driver == BRIDGE_DRIVER) {
return 2;
}
return 0;
}

std::string AudioIO::getDeviceName(int device) { std::string AudioIO::getDeviceName(int device) {
if (device < 0) if (device < 0)
return ""; return "";


if (rtAudio) { if (rtAudio) {
try {
RtAudio::DeviceInfo deviceInfo;
if (device == this->device)
deviceInfo = this->deviceInfo;
else
deviceInfo = rtAudio->getDeviceInfo(device);
RtAudio::DeviceInfo deviceInfo;
if (getDeviceInfo(device, &deviceInfo))
return deviceInfo.name; return deviceInfo.name;
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
}
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
if (device >= 0)
return stringf("%d", device + 1);
return stringf("%d", device + 1);
} }
return ""; return "";
} }


std::string AudioIO::getDeviceDetail(int device) {
std::string AudioIO::getDeviceDetail(int device, int offset, int maxChannels) {
if (device < 0) if (device < 0)
return ""; return "";


if (rtAudio) { if (rtAudio) {
try {
RtAudio::DeviceInfo deviceInfo;
if (device == this->device)
deviceInfo = this->deviceInfo;
else
deviceInfo = rtAudio->getDeviceInfo(device);
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
RtAudio::DeviceInfo deviceInfo;
if (getDeviceInfo(device, &deviceInfo)) {
std::string deviceDetail = stringf("%s (", deviceInfo.name.c_str());
if (offset < (int) deviceInfo.inputChannels)
deviceDetail += stringf("%d-%d in", offset + 1, min(offset + maxChannels, deviceInfo.inputChannels));
if (offset < (int) deviceInfo.inputChannels && offset < (int) deviceInfo.outputChannels)
deviceDetail += ", ";
if (offset < (int) deviceInfo.outputChannels)
deviceDetail += stringf("%d-%d out", offset + 1, min(offset + maxChannels, deviceInfo.outputChannels));
deviceDetail += ")";
return deviceDetail;
} }
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
if (device >= 0)
return stringf("Channel %d", device + 1);
return stringf("Channel %d", device + 1);
} }
return ""; return "";
} }


void AudioIO::setDevice(int device, int offset, int maxChannels) {
closeStream();
this->device = device;
this->offset = offset;
this->numOutputs = maxChannels;
this->numInputs = maxChannels;
openStream();
}

void AudioIO::setSampleRate(int sampleRate) {
closeStream();
this->sampleRate = sampleRate;
openStream();
}

void AudioIO::setBlockSize(int blockSize) {
closeStream();
this->blockSize = blockSize;
openStream();
}


static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
AudioIO *audioIO = (AudioIO*) userData; AudioIO *audioIO = (AudioIO*) userData;
assert(audioIO); assert(audioIO);
@@ -131,10 +184,6 @@ static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrame
} }


void AudioIO::openStream() { void AudioIO::openStream() {
// Close device but remember the current device number
int device = this->device;
closeStream();

if (device < 0) if (device < 0)
return; return;


@@ -148,8 +197,11 @@ void AudioIO::openStream() {
return; return;
} }


numOutputs = min(deviceInfo.outputChannels, maxOutputs);
numInputs = min(deviceInfo.inputChannels, maxInputs);
if (rtAudio->isStreamOpen())
return;

numOutputs = clamp((int) deviceInfo.outputChannels - offset, 0, numOutputs);
numInputs = clamp((int) deviceInfo.inputChannels - offset, 0, numInputs);


if (numOutputs == 0 && numInputs == 0) { if (numOutputs == 0 && numInputs == 0) {
warn("RtAudio device %d has 0 inputs and 0 outputs"); warn("RtAudio device %d has 0 inputs and 0 outputs");
@@ -159,10 +211,12 @@ void AudioIO::openStream() {
RtAudio::StreamParameters outParameters; RtAudio::StreamParameters outParameters;
outParameters.deviceId = device; outParameters.deviceId = device;
outParameters.nChannels = numOutputs; outParameters.nChannels = numOutputs;
outParameters.firstChannel = offset;


RtAudio::StreamParameters inParameters; RtAudio::StreamParameters inParameters;
inParameters.deviceId = device; inParameters.deviceId = device;
inParameters.nChannels = numInputs; inParameters.nChannels = numInputs;
inParameters.firstChannel = offset;


RtAudio::StreamOptions options; RtAudio::StreamOptions options;
// options.flags |= RTAUDIO_SCHEDULE_REALTIME; // options.flags |= RTAUDIO_SCHEDULE_REALTIME;
@@ -175,11 +229,12 @@ void AudioIO::openStream() {
} }


try { try {
debug("Opening audio RtAudio device %d", device);
debug("Opening audio RtAudio device %d with %d in %d out", device, numInputs, numOutputs);
rtAudio->openStream( rtAudio->openStream(
numOutputs == 0 ? NULL : &outParameters, numOutputs == 0 ? NULL : &outParameters,
numInputs == 0 ? NULL : &inParameters, numInputs == 0 ? NULL : &inParameters,
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL);
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize,
&rtCallback, this, &options, NULL);
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to open RtAudio stream: %s", e.what()); warn("Failed to open RtAudio stream: %s", e.what());
@@ -197,12 +252,11 @@ void AudioIO::openStream() {


// Update sample rate because this may have changed // Update sample rate because this may have changed
this->sampleRate = rtAudio->getStreamSampleRate(); this->sampleRate = rtAudio->getStreamSampleRate();
this->device = device;
onOpenStream(); onOpenStream();
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
if (device < BRIDGE_CHANNELS) { if (device < BRIDGE_CHANNELS) {
this->device = device;
// TODO
} }
} }
} }
@@ -230,10 +284,6 @@ void AudioIO::closeStream() {
deviceInfo = RtAudio::DeviceInfo(); deviceInfo = RtAudio::DeviceInfo();
} }


// Reset rtAudio settings
device = -1;
numOutputs = 0;
numInputs = 0;
onCloseStream(); onCloseStream();
} }


@@ -274,6 +324,8 @@ json_t *AudioIO::toJson() {
} }


void AudioIO::fromJson(json_t *rootJ) { void AudioIO::fromJson(json_t *rootJ) {
closeStream();

json_t *driverJ = json_object_get(rootJ, "driver"); json_t *driverJ = json_object_get(rootJ, "driver");
if (driverJ) if (driverJ)
setDriver(json_number_value(driverJ)); setDriver(json_number_value(driverJ));


+ 1
- 2
src/core/AudioInterface.cpp View File

@@ -35,11 +35,10 @@ struct AudioInterfaceIO : AudioIO {
DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer; DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer;


AudioInterfaceIO() { AudioInterfaceIO() {
maxOutputs = OUTPUTS;
maxInputs = INPUTS;
} }


~AudioInterfaceIO() { ~AudioInterfaceIO() {
// Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
closeStream(); closeStream();
} }




Loading…
Cancel
Save