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* 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)

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.
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

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

### 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

*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`.
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.

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

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.



+ 10
- 4
include/audio.hpp View File

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


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

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

int getDeviceCount();
bool getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo);
int getDeviceMaxChannels(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 closeStream();
/** 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 {
AudioIO *audioIO;
int device;
int offset;
int maxChannels;
void onAction(EventAction &e) override {
audioIO->device = device;
audioIO->openStream();
audioIO->setDevice(device, offset, maxChannels);
}
};

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

@@ -112,8 +119,7 @@ struct AudioBlockSizeItem : MenuItem {
AudioIO *audioIO;
int blockSize;
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;
}

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) {
if (device < 0)
return "";

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;
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
}
}
if (driver == BRIDGE_DRIVER) {
if (device >= 0)
return stringf("%d", device + 1);
return stringf("%d", device + 1);
}
return "";
}

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

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 (device >= 0)
return stringf("Channel %d", device + 1);
return stringf("Channel %d", device + 1);
}
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) {
AudioIO *audioIO = (AudioIO*) userData;
assert(audioIO);
@@ -131,10 +184,6 @@ static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrame
}

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

if (device < 0)
return;

@@ -148,8 +197,11 @@ void AudioIO::openStream() {
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) {
warn("RtAudio device %d has 0 inputs and 0 outputs");
@@ -159,10 +211,12 @@ void AudioIO::openStream() {
RtAudio::StreamParameters outParameters;
outParameters.deviceId = device;
outParameters.nChannels = numOutputs;
outParameters.firstChannel = offset;

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

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

try {
debug("Opening audio RtAudio device %d", device);
debug("Opening audio RtAudio device %d with %d in %d out", device, numInputs, numOutputs);
rtAudio->openStream(
numOutputs == 0 ? NULL : &outParameters,
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) {
warn("Failed to open RtAudio stream: %s", e.what());
@@ -197,12 +252,11 @@ void AudioIO::openStream() {

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

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

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

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

json_t *driverJ = json_object_get(rootJ, "driver");
if (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;

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

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



Loading…
Cancel
Save