Browse Source

Add WIP sample rate conversion to AudioInterface.

tags/v2.0.0
Andrew Belt 5 years ago
parent
commit
4f8718fcbf
1 changed files with 34 additions and 146 deletions
  1. +34
    -146
      src/core/AudioInterface.cpp

+ 34
- 146
src/core/AudioInterface.cpp View File

@@ -31,15 +31,11 @@ struct AudioInterface : Module, audio::Port {
NUM_LIGHTS
};

// int lastSampleRate = 0;
// int lastNumOutputs = -1;
// int lastNumInputs = -1;
dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 8192> inputBuffer;
dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 8192> outputBuffer;

// dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
// dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;

dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_INPUTS>, 16384> inputBuffer;
dsp::DoubleRingBuffer<dsp::Frame<NUM_AUDIO_OUTPUTS>, 16384> outputBuffer;
dsp::SampleRateConverter<NUM_AUDIO_INPUTS> inputSrc;
dsp::SampleRateConverter<NUM_AUDIO_OUTPUTS> outputSrc;

AudioInterface() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -48,7 +44,8 @@ struct AudioInterface : Module, audio::Port {
for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++)
configOutput(AUDIO_OUTPUTS + i, string::f("From device %d", i + 1));
maxChannels = std::max(NUM_AUDIO_INPUTS, NUM_AUDIO_OUTPUTS);
onSampleRateChange();
inputSrc.setQuality(6);
outputSrc.setQuality(6);
}

~AudioInterface() {
@@ -74,93 +71,6 @@ struct AudioInterface : Module, audio::Port {
}
}

// // Update SRC states
// inputSrc.setRates(port.sampleRate, args.sampleRate);
// outputSrc.setRates(args.sampleRate, port.sampleRate);

// inputSrc.setChannels(port.numInputs);
// outputSrc.setChannels(port.numOutputs);

// // Inputs: audio engine -> rack engine
// if (port.active && port.numInputs > 0) {
// // Wait until inputs are present
// // Give up after a timeout in case the audio device is being unresponsive.
// std::unique_lock<std::mutex> lock(port.engineMutex);
// auto cond = [&] {
// return (!port.inputBuffer.empty());
// };
// auto timeout = std::chrono::milliseconds(200);
// if (port.engineCv.wait_for(lock, timeout, cond)) {
// // Convert inputs
// int inLen = port.inputBuffer.size();
// int outLen = inputBuffer.capacity();
// inputSrc.process(port.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
// port.inputBuffer.startIncr(inLen);
// inputBuffer.endIncr(outLen);
// }
// else {
// // Give up on pulling input
// port.active = false;
// // DEBUG("Audio Interface underflow");
// }
// }

// // Take input from buffer
// dsp::Frame<NUM_AUDIO_INPUTS> inputFrame;
// if (!inputBuffer.empty()) {
// inputFrame = inputBuffer.shift();
// }
// else {
// std::memset(&inputFrame, 0, sizeof(inputFrame));
// }
// for (int i = 0; i < port.numInputs; i++) {
// outputs[AUDIO_OUTPUTS + i].setVoltage(10.f * inputFrame.samples[i]);
// }
// for (int i = port.numInputs; i < NUM_AUDIO_INPUTS; i++) {
// outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
// }

// // Outputs: rack engine -> audio engine
// if (port.active && port.numOutputs > 0) {
// // Get and push output SRC frame
// if (!outputBuffer.full()) {
// dsp::Frame<NUM_AUDIO_OUTPUTS> outputFrame;
// for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
// outputFrame.samples[i] = inputs[AUDIO_INPUTS + i].getVoltageSum() / 10.f;
// }
// outputBuffer.push(outputFrame);
// }

// if (outputBuffer.full()) {
// // Wait until enough outputs are consumed
// // Give up after a timeout in case the audio device is being unresponsive.
// auto cond = [&] {
// return (port.outputBuffer.size() < (size_t) port.blockSize);
// };
// if (!cond())
// APP->engine->yieldWorkers();
// std::unique_lock<std::mutex> lock(port.engineMutex);
// auto timeout = std::chrono::milliseconds(200);
// if (port.engineCv.wait_for(lock, timeout, cond)) {
// // Push converted output
// int inLen = outputBuffer.size();
// int outLen = port.outputBuffer.capacity();
// outputSrc.process(outputBuffer.startData(), &inLen, port.outputBuffer.endData(), &outLen);
// outputBuffer.startIncr(inLen);
// port.outputBuffer.endIncr(outLen);
// }
// else {
// // Give up on pushing output
// port.active = false;
// outputBuffer.clear();
// // DEBUG("Audio Interface underflow");
// }
// }

// // Notify audio thread that an output is potentially ready
// port.audioCv.notify_one();
// }

// Turn on light if at least one port is enabled in the nearby pair
for (int i = 0; i < NUM_AUDIO_INPUTS / 2; i++) {
lights[INPUT_LIGHTS + i].setBrightness(numOutputs >= 2 * i + 1);
@@ -197,75 +107,53 @@ struct AudioInterface : Module, audio::Port {
// 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);

// Initialize sample rate converters
outputSrc.setRates((int) sampleRate, (int) APP->engine->getSampleRate());
outputSrc.setChannels(numInputs);
inputSrc.setRates((int) APP->engine->getSampleRate(), (int) sampleRate);
inputSrc.setChannels(numOutputs);

// audio input -> module output
dsp::Frame<NUM_AUDIO_OUTPUTS> inputAudioBuffer[frames];
std::memset(inputAudioBuffer, 0, sizeof(inputAudioBuffer));
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];
inputAudioBuffer[i].samples[j] = input[i * numInputs + j];
}
outputBuffer.push(outputFrame);
}
int inputAudioFrames = frames;
int outputFrames = outputBuffer.capacity();
outputSrc.process(inputAudioBuffer, &inputAudioFrames, outputBuffer.endData(), &outputFrames);
outputBuffer.endIncr(outputFrames);

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

// module input -> audio output
dsp::Frame<NUM_AUDIO_OUTPUTS> outputAudioBuffer[frames];
int inputFrames = inputBuffer.size();
int outputAudioFrames = frames;
inputSrc.process(inputBuffer.startData(), &inputFrames, outputAudioBuffer, &outputAudioFrames);
inputBuffer.startIncr(inputFrames);
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];
output[i * numOutputs + j] = outputAudioBuffer[i].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();
DEBUG("%p %d: frames %d, %d -> %d outputBuffer %d, %d -> %d inputBuffer %d",
this, APP->engine->getPrimaryModule() == this, frames,
inputAudioFrames, outputFrames, outputBuffer.size(),
inputFrames, outputAudioFrames, inputBuffer.size());
}

void onCloseStream() override {
// inputBuffer.clear();
// outputBuffer.clear();
// Reset outputs
for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) {
outputs[AUDIO_OUTPUTS + i].setVoltage(0.f);
}
}

void onChannelsChange() override {


Loading…
Cancel
Save