Browse Source

Disable sample rate converter when inRate == outRate, only convert

required channels
tags/v0.6.0
Andrew Belt 6 years ago
parent
commit
4d7922b005
3 changed files with 99 additions and 53 deletions
  1. +59
    -30
      include/dsp/samplerate.hpp
  2. +39
    -22
      src/Core/AudioInterface.cpp
  3. +1
    -1
      src/Core/MIDIToCVInterface.cpp

+ 59
- 30
include/dsp/samplerate.hpp View File

@@ -10,53 +10,82 @@ namespace rack {

template<int CHANNELS>
struct SampleRateConverter {
SpeexResamplerState *state;
bool bypass = false;
SpeexResamplerState *st = NULL;
int channels = CHANNELS;
int quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
int inRate = 44100;
int outRate = 44100;

SampleRateConverter() {
int error;
state = speex_resampler_init(CHANNELS, 44100, 44100, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error);
assert(error == RESAMPLER_ERR_SUCCESS);
refreshState();
}
~SampleRateConverter() {
speex_resampler_destroy(state);
if (st) {
speex_resampler_destroy(st);
}
}

/** Sets the number of channels to actually process. This can be at most CHANNELS. */
void setChannels(int channels) {
assert(channels <= CHANNELS);
this->channels = channels;
refreshState();
}

void setQuality(int quality) {
speex_resampler_set_quality(state, quality);
this->quality = quality;
refreshState();
}

void setRates(int inRate, int outRate) {
int oldInRate, oldOutRate;
getRates(&oldInRate, &oldOutRate);
if (inRate == oldInRate && outRate == oldOutRate)
return;
int error = speex_resampler_set_rate(state, inRate, outRate);
assert(error == RESAMPLER_ERR_SUCCESS);
this->inRate = inRate;
this->outRate = outRate;
refreshState();
}

void getRates(int *inRate, int *outRate) {
spx_uint32_t inRate32, outRate32;
speex_resampler_get_rate(state, &inRate32, &outRate32);
if (inRate) *inRate = inRate32;
if (outRate) *outRate = outRate32;
void refreshState() {
if (st) {
speex_resampler_destroy(st);
st = NULL;
}

if (channels > 0 && inRate != outRate) {
int err;
st = speex_resampler_init(channels, inRate, outRate, quality, &err);
assert(st);
assert(err == RESAMPLER_ERR_SUCCESS);

speex_resampler_set_input_stride(st, CHANNELS);
speex_resampler_set_output_stride(st, CHANNELS);
}
}

/** `in` and `out` are interlaced with the number of channels */
void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) {
if (bypass) {
int len = std::min(*inFrames, *outFrames);
memcpy(out, in, len * sizeof(Frame<CHANNELS>));
*inFrames = len;
*outFrames = len;
return;
assert(in);
assert(inFrames);
assert(out);
assert(outFrames);
if (st) {
// Resample each channel at a time
spx_uint32_t inLen;
spx_uint32_t outLen;
for (int i = 0; i < channels; i++) {
inLen = *inFrames;
outLen = *outFrames;
int err = speex_resampler_process_float(st, i, ((const float*) in) + i, &inLen, ((float*) out) + i, &outLen);
assert(err == RESAMPLER_ERR_SUCCESS);
}
*inFrames = inLen;
*outFrames = outLen;
}
else {
// Simply copy the buffer without conversion
int frames = min(*inFrames, *outFrames);
memcpy(out, in, frames * sizeof(Frame<CHANNELS>));
*inFrames = frames;
*outFrames = frames;
}
speex_resampler_process_interleaved_float(state, (const float*)in, (unsigned int*)inFrames, (float*)out, (unsigned int*)outFrames);
}

void reset() {
int error = speex_resampler_reset_mem(state);
assert(error == RESAMPLER_ERR_SUCCESS);
}
};



+ 39
- 22
src/Core/AudioInterface.cpp View File

@@ -15,8 +15,8 @@
#pragma GCC diagnostic pop


#define OUTPUTS 8
#define INPUTS 8
#define AUDIO_OUTPUTS 8
#define AUDIO_INPUTS 8


using namespace rack;
@@ -28,9 +28,9 @@ struct AudioInterfaceIO : AudioIO {
std::mutex audioMutex;
std::condition_variable audioCv;
// Audio thread produces, engine thread consumes
DoubleRingBuffer<Frame<INPUTS>, (1<<15)> inputBuffer;
DoubleRingBuffer<Frame<AUDIO_INPUTS>, (1<<15)> inputBuffer;
// Audio thread consumes, engine thread produces
DoubleRingBuffer<Frame<OUTPUTS>, (1<<15)> outputBuffer;
DoubleRingBuffer<Frame<AUDIO_OUTPUTS>, (1<<15)> outputBuffer;
bool active = false;

~AudioInterfaceIO() {
@@ -51,7 +51,7 @@ struct AudioInterfaceIO : AudioIO {
for (int i = 0; i < frames; i++) {
if (inputBuffer.full())
break;
Frame<INPUTS> inputFrame;
Frame<AUDIO_INPUTS> inputFrame;
memset(&inputFrame, 0, sizeof(inputFrame));
memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float));
inputBuffer.push(inputFrame);
@@ -67,7 +67,7 @@ struct AudioInterfaceIO : AudioIO {
if (audioCv.wait_for(lock, timeout, cond)) {
// Consume audio block
for (int i = 0; i < frames; i++) {
Frame<OUTPUTS> f = outputBuffer.shift();
Frame<AUDIO_OUTPUTS> f = outputBuffer.shift();
for (int j = 0; j < numOutputs; j++) {
output[numOutputs*i + j] = clamp(f.samples[j], -1.f, 1.f);
}
@@ -99,28 +99,30 @@ struct AudioInterface : Module {
NUM_PARAMS
};
enum InputIds {
ENUMS(AUDIO_INPUT, INPUTS),
ENUMS(AUDIO_INPUT, AUDIO_INPUTS),
NUM_INPUTS
};
enum OutputIds {
ENUMS(AUDIO_OUTPUT, OUTPUTS),
ENUMS(AUDIO_OUTPUT, AUDIO_OUTPUTS),
NUM_OUTPUTS
};
enum LightIds {
ENUMS(INPUT_LIGHT, INPUTS / 2),
ENUMS(OUTPUT_LIGHT, OUTPUTS / 2),
ENUMS(INPUT_LIGHT, AUDIO_INPUTS / 2),
ENUMS(OUTPUT_LIGHT, AUDIO_OUTPUTS / 2),
NUM_LIGHTS
};

AudioInterfaceIO audioIO;
int lastSampleRate = 0;
int lastNumOutputs = -1;
int lastNumInputs = -1;

SampleRateConverter<INPUTS> inputSrc;
SampleRateConverter<OUTPUTS> outputSrc;
SampleRateConverter<AUDIO_INPUTS> inputSrc;
SampleRateConverter<AUDIO_OUTPUTS> outputSrc;

// in rack's sample rate
DoubleRingBuffer<Frame<INPUTS>, 16> inputBuffer;
DoubleRingBuffer<Frame<OUTPUTS>, 16> outputBuffer;
DoubleRingBuffer<Frame<AUDIO_INPUTS>, 16> inputBuffer;
DoubleRingBuffer<Frame<AUDIO_OUTPUTS>, 16> outputBuffer;

AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
onSampleRateChange();
@@ -140,8 +142,13 @@ struct AudioInterface : Module {
}

void onSampleRateChange() override {
inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
inputSrc.setRates(audioIO.sampleRate, (int) engineGetSampleRate());
outputSrc.setRates((int) engineGetSampleRate(), audioIO.sampleRate);
}

void onChannelsChange() {
inputSrc.setChannels(audioIO.numInputs);
outputSrc.setChannels(audioIO.numOutputs);
}

void onReset() override {
@@ -157,6 +164,13 @@ void AudioInterface::step() {
lastSampleRate = audioIO.sampleRate;
}

// Update number of channels if changed by audio driver
if (audioIO.numOutputs != lastNumOutputs || audioIO.numInputs != lastNumInputs) {
lastNumOutputs = audioIO.numOutputs;
lastNumInputs = audioIO.numInputs;
onChannelsChange();
}

// Inputs: audio engine -> rack engine
if (audioIO.active && audioIO.numInputs > 0) {
// Wait until inputs are present
@@ -182,23 +196,26 @@ void AudioInterface::step() {
}

// Take input from buffer
Frame<INPUTS> inputFrame;
Frame<AUDIO_INPUTS> inputFrame;
if (!inputBuffer.empty()) {
inputFrame = inputBuffer.shift();
}
else {
memset(&inputFrame, 0, sizeof(inputFrame));
}
for (int i = 0; i < INPUTS; i++) {
for (int i = 0; i < audioIO.numInputs; i++) {
outputs[AUDIO_OUTPUT + i].value = 10.f * inputFrame.samples[i];
}
for (int i = audioIO.numInputs; i < AUDIO_INPUTS; i++) {
outputs[AUDIO_OUTPUT + i].value = 0.f;
}

// Outputs: rack engine -> audio engine
if (audioIO.active && audioIO.numOutputs > 0) {
// Get and push output SRC frame
if (!outputBuffer.full()) {
Frame<OUTPUTS> outputFrame;
for (int i = 0; i < OUTPUTS; i++) {
Frame<AUDIO_OUTPUTS> outputFrame;
for (int i = 0; i < AUDIO_OUTPUTS; i++) {
outputFrame.samples[i] = inputs[AUDIO_INPUT + i].value / 10.f;
}
outputBuffer.push(outputFrame);
@@ -233,9 +250,9 @@ void AudioInterface::step() {
}

// Turn on light if at least one port is enabled in the nearby pair
for (int i = 0; i < INPUTS / 2; i++)
for (int i = 0; i < AUDIO_INPUTS / 2; i++)
lights[INPUT_LIGHT + i].value = (audioIO.active && audioIO.numOutputs >= 2*i+1);
for (int i = 0; i < OUTPUTS / 2; i++)
for (int i = 0; i < AUDIO_OUTPUTS / 2; i++)
lights[OUTPUT_LIGHT + i].value = (audioIO.active && audioIO.numInputs >= 2*i+1);
}



+ 1
- 1
src/Core/MIDIToCVInterface.cpp View File

@@ -301,7 +301,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {
Menu *createChildMenu() override {
Menu *menu = new Menu();
std::vector<int> divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1};
std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "48th", "96th"};
std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
for (size_t i = 0; i < divisions.size(); i++) {
ClockDivisionItem *item = MenuItem::create<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i]));
item->module = module;


Loading…
Cancel
Save