Browse Source

Basic functionality for multiple Audio modules.

tags/v2.0.0
Andrew Belt 5 years ago
parent
commit
dc37821240
1 changed files with 112 additions and 123 deletions
  1. +112
    -123
      src/core/AudioInterface.cpp

+ 112
- 123
src/core/AudioInterface.cpp View File

@@ -13,100 +13,7 @@ namespace core {




template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS> template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
struct AudioInterfacePort : audio::Port {
// std::mutex engineMutex;
// std::condition_variable engineCv;
// std::mutex audioMutex;
// std::condition_variable audioCv;
// // Audio thread produces, engine thread consumes
// dsp::DoubleRingBuffer < dsp::Frame<NUM_AUDIO_INPUTS>, (1 << 15) > inputBuffer;
// // Audio thread consumes, engine thread produces
// dsp::DoubleRingBuffer < dsp::Frame<NUM_AUDIO_OUTPUTS>, (1 << 15) > outputBuffer;
// bool active = false;

// For checking getPrimaryModule()
Module* module = NULL;
const float* input = NULL;
float* output = NULL;
int frame = 0;
int numFrames = 0;

~AudioInterfacePort() {
// Close stream here before destructing AudioInterfacePort, so the mutexes are still valid when waiting to close.
setDeviceId(-1, 0);
}

void processStream(const float* input, float* output, int frames) override {
if (APP->engine->getPrimaryModule() != module) {
// TEMP
std::memset(output, 0, sizeof(float) * frames * numOutputs);
return;
}

frame = 0;
numFrames = frames;
this->input = input;
this->output = output;

APP->engine->step(frames);

// // Reactivate idle stream
// if (!active) {
// active = true;
// inputBuffer.clear();
// outputBuffer.clear();
// }

// if (numInputs > 0) {
// // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
// for (int i = 0; i < frames; i++) {
// if (inputBuffer.full())
// break;
// dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
// std::memset(&inputFrame, 0, sizeof(inputFrame));
// std::memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
// inputBuffer.push(inputFrame);
// }
// }

// if (numOutputs > 0) {
// std::unique_lock<std::mutex> lock(audioMutex);
// auto cond = [&] {
// return (outputBuffer.size() >= (size_t) frames);
// };
// auto timeout = std::chrono::milliseconds(100);
// if (audioCv.wait_for(lock, timeout, cond)) {
// // Consume audio block
// for (int i = 0; i < frames; i++) {
// dsp::Frame<NUM_AUDIO_OUTPUTS> f = outputBuffer.shift();
// for (int j = 0; j < numOutputs; j++) {
// output[numOutputs * i + j] = clamp(f.samples[j], -1.f, 1.f);
// }
// }
// }
// else {
// // Timed out, fill output with zeros
// std::memset(output, 0, frames * numOutputs * sizeof(float));
// // DEBUG("Audio Interface Port underflow");
// }
// }

// // Notify engine when finished processing
// engineCv.notify_one();
}

void onCloseStream() override {
// inputBuffer.clear();
// outputBuffer.clear();
}

void onChannelsChange() override {
}
};


template <int NUM_AUDIO_INPUTS, int NUM_AUDIO_OUTPUTS>
struct AudioInterface : Module {
struct AudioInterface : Module, audio::Port {
enum ParamIds { enum ParamIds {
NUM_PARAMS NUM_PARAMS
}; };
@@ -124,7 +31,6 @@ struct AudioInterface : Module {
NUM_LIGHTS NUM_LIGHTS
}; };


AudioInterfacePort<NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS> port;
// int lastSampleRate = 0; // int lastSampleRate = 0;
// int lastNumOutputs = -1; // int lastNumOutputs = -1;
// int lastNumInputs = -1; // int lastNumInputs = -1;
@@ -132,9 +38,8 @@ struct AudioInterface : Module {
// dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc; // dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
// dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc; // dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;


// // in rack's sample rate
// dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 16> inputBuffer;
// dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 16> outputBuffer;
dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 16384> inputBuffer;
dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 16384> outputBuffer;


AudioInterface() { AudioInterface() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -142,33 +47,32 @@ struct AudioInterface : Module {
configInput(AUDIO_INPUTS + i, string::f("To device %d", i + 1)); configInput(AUDIO_INPUTS + i, string::f("To device %d", i + 1));
for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++)
configOutput(AUDIO_OUTPUTS + i, string::f("From device %d", i + 1)); configOutput(AUDIO_OUTPUTS + i, string::f("From device %d", i + 1));
port.maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS);
port.module = this;
maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS);
onSampleRateChange(); onSampleRateChange();
} }


void process(const ProcessArgs& args) override {
// Claim primary module if there is none
if (!APP->engine->getPrimaryModule()) {
APP->engine->setPrimaryModule(this);
}
~AudioInterface() {
// Close stream here before destructing AudioInterfacePort, so the mutexes are still valid when waiting to close.
setDeviceId(-1, 0);
}


void process(const ProcessArgs& args) override {
// Get inputs // Get inputs
for (int i = 0; i < port.numOutputs; i++) {
float v = inputs[AUDIO_INPUTS + i].getVoltage() / 10.f;
port.output[port.frame * port.numOutputs + i] = v;
if (!inputBuffer.full()) {
dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
for (int i = 0; i < NUM_AUDIO_INPUTS; i++) {
inputFrame.samples[i] = inputs[AUDIO_INPUTS + i].getVoltage() / 10.f;
}
inputBuffer.push(inputFrame);
} }


// Set outputs // Set outputs
for (int i = 0; i < port.numInputs; i++) {
float v = port.input[port.frame * port.numInputs + i];
outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * v);
if (!outputBuffer.empty()) {
dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame = outputBuffer.shift();
for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * outputFrame.samples[i]);
}
} }
for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) {
outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
}

port.frame++;


// // Update SRC states // // Update SRC states
// inputSrc.setRates(port.sampleRate, args.sampleRate); // inputSrc.setRates(port.sampleRate, args.sampleRate);
@@ -259,27 +163,112 @@ struct AudioInterface : Module {


// Turn on light if at least one port is enabled in the nearby pair // Turn on light if at least one port is enabled in the nearby pair
for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) { for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) {
lights[INPUT_LIGHTS + i].setBrightness(port.numOutputs >= 2 * i + 1);
lights[INPUT_LIGHTS + i].setBrightness(numOutputs >= 2 * i + 1);
} }
for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) { for (int i = 0; i < NUM_AUDIO_OUTPUTS / 2; i++) {
lights[OUTPUT_LIGHTS + i].setBrightness(port.numInputs >= 2 * i + 1);
lights[OUTPUT_LIGHTS + i].setBrightness(numInputs >= 2 * i + 1);
} }
} }


json_t* dataToJson() override { json_t* dataToJson() override {
json_t* rootJ = json_object(); json_t* rootJ = json_object();
json_object_set_new(rootJ, "audio", port.toJson());
json_object_set_new(rootJ, "audio", audio::Port::toJson());
return rootJ; return rootJ;
} }


void dataFromJson(json_t* rootJ) override { void dataFromJson(json_t* rootJ) override {
json_t* audioJ = json_object_get(rootJ, "audio"); json_t* audioJ = json_object_get(rootJ, "audio");
if (audioJ) if (audioJ)
port.fromJson(audioJ);
audio::Port::fromJson(audioJ);
} }


void onReset() override { void onReset() override {
port.setDeviceId(-1, 0);
setDeviceId(-1, 0);
}

// audio::Port

void processStream(const float* input, float* output, int frames) override {
// Claim primary module if there is none
if (!APP->engine->getPrimaryModule()) {
APP->engine->setPrimaryModule(this);
}

// Clear output in case the audio driver uses this buffer in another thread before this method returns. (Not sure if any do this in practice.)
std::memset(output, 0, sizeof(float) * numOutputs * frames);

// audio input -> module output
for (int i = 0; i < frames; i++) {
if (outputBuffer.full())
break;
dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame;
std::memset(&outputFrame, 0, sizeof(outputFrame));
for (int j = 0; j < std::min(numInputs, NUM_AUDIO_OUTPUTS); j++) {
outputFrame.samples[j] = input[i * numInputs + j];
}
outputBuffer.push(outputFrame);
}

// Step engine estimated number of steps
if (APP->engine->getPrimaryModule() == this) {
APP->engine->step(frames);
}

// module input -> audio output
for (int i = 0; i < frames; i++) {
if (inputBuffer.empty())
break;
dsp::Frame<NUM_AUDIO_INPUTS> inputFrame = inputBuffer.shift();
for (int j = 0; j < std::min(numOutputs, NUM_AUDIO_INPUTS); j++) {
output[i * numOutputs + j] = inputFrame.samples[j];
}
}


// if (numInputs > 0) {
// // TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
// for (int i = 0; i < frames; i++) {
// if (inputBuffer.full())
// break;
// dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
// std::memset(&inputFrame, 0, sizeof(inputFrame));
// std::memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
// inputBuffer.push(inputFrame);
// }
// }

// if (numOutputs > 0) {
// std::unique_lock<std::mutex> lock(audioMutex);
// auto cond = [&] {
// return (outputBuffer.size() >= (size_t) frames);
// };
// auto timeout = std::chrono::milliseconds(100);
// if (audioCv.wait_for(lock, timeout, cond)) {
// // Consume audio block
// for (int i = 0; i < frames; i++) {
// dsp::Frame<NUM_AUDIO_OUTPUTS> f = outputBuffer.shift();
// for (int j = 0; j < numOutputs; j++) {
// output[numOutputs * i + j] = clamp(f.samples[j], -1.f, 1.f);
// }
// }
// }
// else {
// // Timed out, fill output with zeros
// std::memset(output, 0, frames * numOutputs * sizeof(float));
// // DEBUG("Audio Interface Port underflow");
// }
// }

// // Notify engine when finished processing
// engineCv.notify_one();
}

void onCloseStream() override {
// inputBuffer.clear();
// outputBuffer.clear();
}

void onChannelsChange() override {
} }
}; };


@@ -338,7 +327,7 @@ struct AudioInterfaceWidget : ModuleWidget {


AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339))); AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
audioWidget->box.size = mm2px(Vec(44, 28)); audioWidget->box.size = mm2px(Vec(44, 28));
audioWidget->setAudioPort(module ? &module->port : NULL);
audioWidget->setAudioPort(module);
addChild(audioWidget); addChild(audioWidget);
} }
else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) { else if (NUM_AUDIO_INPUTS == 16 && NUM_AUDIO_OUTPUTS == 16) {
@@ -402,7 +391,7 @@ struct AudioInterfaceWidget : ModuleWidget {


AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(2.57, 14.839))); AudioWidget* audioWidget = createWidget<AudioWidget>(mm2px(Vec(2.57, 14.839)));
audioWidget->box.size = mm2px(Vec(91.382, 28.0)); audioWidget->box.size = mm2px(Vec(91.382, 28.0));
audioWidget->setAudioPort(module ? &module->port : NULL);
audioWidget->setAudioPort(module);
addChild(audioWidget); addChild(audioWidget);
} }
} }


Loading…
Cancel
Save