Browse Source

Add sample rate conversion to/from actual device sample rate

tags/v0.4.0
Andrew Belt 8 years ago
parent
commit
5b8f8629f4
11 changed files with 263 additions and 82 deletions
  1. +1
    -3
      Makefile
  2. +48
    -0
      README.md
  3. +27
    -9
      src/Braids.cpp
  4. +2
    -2
      src/Branches.cpp
  5. +58
    -26
      src/Clouds.cpp
  6. +60
    -15
      src/Elements.cpp
  7. +3
    -4
      src/Kinks.cpp
  8. +57
    -19
      src/Rings.cpp
  9. +2
    -0
      src/Tides.cpp
  10. +4
    -4
      src/Veils.cpp
  11. +1
    -0
      src/Warps.cpp

+ 1
- 3
Makefile View File

@@ -1,9 +1,7 @@

ARCH ?= linux
CXXFLAGS = -MMD -fPIC -g -Wall -std=c++11 -O3 -ffast-math -DTEST \
CXXFLAGS = -MMD -fPIC -g -Wall -std=c++11 -O3 -msse -mfpmath=sse -ffast-math -DTEST \
-I./src -I../../include -I./eurorack \
-fasm \
-finline \
-fshort-enums

LDFLAGS =


+ 48
- 0
README.md View File

@@ -57,3 +57,51 @@ Percentages are progress amounts, X means won't port
#### [100%] Branches (Bernoulli Gate)
#### [100%] Blinds (Quad VC-polarizer)
#### [100%] Veils (Quad VCA)




### Sound sources
- [[Macro Oscillator]] (based on [Braids](http://mutable-instruments.net/modules/braids))
- [[Modal Synthesizer]] (based on [Elements](http://mutable-instruments.net/modules/elements))

### Modulation sources
- [[Tidal Modulator]] (based on [Tides](http://mutable-instruments.net/modules/tides))

### Sound modifiers
- [[Texture Synthesizer]] (based on [Clouds](http://mutable-instruments.net/modules/clouds))
- [[Meta Modulator]] (based on [Warps](http://mutable-instruments.net/modules/warps))
- [[Resonator]] (based on [Rings](http://mutable-instruments.net/modules/rings))

### Plumbing
- [[Multiples]] (based on [Links](http://mutable-instruments.net/modules/links))
- [[Utilities]] (based on [Kinks](http://mutable-instruments.net/modules/kinks))
- [[Mixer]] (based on [Shades](http://mutable-instruments.net/modules/shades))
- [[Bernoulli Gate]] (based on [Branches](http://mutable-instruments.net/modules/branches))
- [[Quad VC-polarizer]] (based on [Blinds](http://mutable-instruments.net/modules/blinds))
- [[Quad VCA]] (based on [Veils](http://mutable-instruments.net/modules/veils))


![](http://virtuoso.audio/images/AudibleInstruments/macro oscillator.png)

![](http://virtuoso.audio/images/AudibleInstruments/modal synthesizer.png)

![](http://virtuoso.audio/images/AudibleInstruments/tidal modulator.png)

![](http://virtuoso.audio/images/AudibleInstruments/texture synthesizer.png)

![](http://virtuoso.audio/images/AudibleInstruments/meta modulator.png)

![](http://virtuoso.audio/images/AudibleInstruments/resonator.png)

![](http://virtuoso.audio/images/AudibleInstruments/multiples.png)

![](http://virtuoso.audio/images/AudibleInstruments/utilities.png)

![](http://virtuoso.audio/images/AudibleInstruments/mixer.png)

![](http://virtuoso.audio/images/AudibleInstruments/bernoulli gate.png)

![](http://virtuoso.audio/images/AudibleInstruments/quad VC-polarizer.png)

![](http://virtuoso.audio/images/AudibleInstruments/quad VCA.png)

+ 27
- 9
src/Braids.cpp View File

@@ -1,5 +1,6 @@
#include "AudibleInstruments.hpp"
#include <string.h>
#include "AudibleInstruments.hpp"
#include "dsp.hpp"
#include "braids/macro_oscillator.h"


@@ -28,8 +29,8 @@ struct Braids : Module {
};

braids::MacroOscillator *osc;
int bufferFrame = 0;
int16_t buffer[24] = {};
SampleRateConverter<1> src;
DoubleRingBuffer<float, 256> outputBuffer;
bool lastTrig = false;

Braids();
@@ -54,9 +55,6 @@ Braids::~Braids() {
}

void Braids::step() {
// TODO Sample rate convert from 96000Hz
setf(outputs[OUT_OUTPUT], 5.0*(buffer[bufferFrame] / 32768.0));

// Trigger
bool trig = getf(inputs[TRIG_INPUT]) >= 1.0;
if (!lastTrig && trig) {
@@ -64,8 +62,8 @@ void Braids::step() {
}
lastTrig = trig;

if (++bufferFrame >= 24) {
bufferFrame = 0;
// Render frames
if (outputBuffer.empty()) {
// Set shape
int shape = roundf(params[SHAPE_PARAM]);
osc->set_shape((braids::MacroOscillatorShape) shape);
@@ -82,8 +80,28 @@ void Braids::step() {
int16_t p = clampf((pitch * 12.0 + 60) * 128, 0, INT16_MAX);
osc->set_pitch(p);

// TODO: add a sync input buffer (must be sample rate converted)
uint8_t sync_buffer[24] = {};
osc->Render(sync_buffer, buffer, 24);

int16_t render_buffer[24];
osc->Render(sync_buffer, render_buffer, 24);

// Sample rate convert
float in[24];
for (int i = 0; i < 24; i++) {
in[i] = render_buffer[i] / 32768.0;
}
src.setRatio(gRack->sampleRate / 96000.0);

int inLen = 24;
int outLen = outputBuffer.capacity();
src.process(in, &inLen, outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}

// Output
if (!outputBuffer.empty()) {
setf(outputs[OUT_OUTPUT], 5.0 * outputBuffer.shift());
}
}



+ 2
- 2
src/Branches.cpp View File

@@ -45,8 +45,8 @@ static void computeChannel(const float *in, const float *p, float threshold, flo
bool gate = (out >= 1.0);
if (gate && !*lastGate) {
// trigger
std::uniform_real_distribution<float> dist(0.0, 1.0);
bool toss = (dist(rng) < threshold + getf(p));
float r = randomf();
bool toss = (r < threshold + getf(p));
if (mode < 0.5) {
// direct mode
*outcome = toss;


+ 58
- 26
src/Clouds.cpp View File

@@ -1,5 +1,6 @@
#include "AudibleInstruments.hpp"
#include <string.h>
#include "AudibleInstruments.hpp"
#include "dsp.hpp"
#include "clouds/dsp/granular_processor.h"


@@ -33,14 +34,15 @@ struct Clouds : Module {
NUM_OUTPUTS
};

SampleRateConverter<2> inputSrc;
SampleRateConverter<2> outputSrc;
DoubleRingBuffer<Frame<2>, 256> inputBuffer;
DoubleRingBuffer<Frame<2>, 256> outputBuffer;

uint8_t *block_mem;
uint8_t *block_ccm;
clouds::GranularProcessor *processor;
int bufferFrame = 0;
float inL[32] = {};
float inR[32] = {};
float outL[32] = {};
float outR[32] = {};

bool triggered = false;

Clouds();
@@ -56,10 +58,8 @@ Clouds::Clouds() {

const int memLen = 118784;
const int ccmLen = 65536 - 128;
block_mem = new uint8_t[memLen];
memset(block_mem, 0, memLen);
block_ccm = new uint8_t[ccmLen];
memset(block_ccm, 0, ccmLen);
block_mem = new uint8_t[memLen]();
block_ccm = new uint8_t[ccmLen]();
processor = new clouds::GranularProcessor();
memset(processor, 0, sizeof(*processor));

@@ -73,21 +73,42 @@ Clouds::~Clouds() {
}

void Clouds::step() {
// TODO Sample rate conversion from 32000 Hz
inL[bufferFrame] = getf(inputs[IN_L_INPUT]);
inR[bufferFrame] = getf(inputs[IN_R_INPUT]);
setf(outputs[OUT_L_OUTPUT], outL[bufferFrame]);
setf(outputs[OUT_R_OUTPUT], outR[bufferFrame]);
// Get input
if (!inputBuffer.full()) {
Frame<2> inputFrame;
inputFrame.samples[0] = getf(inputs[IN_L_INPUT]) * params[IN_GAIN_PARAM] / 5.0;
inputFrame.samples[1] = getf(inputs[IN_R_INPUT]) * params[IN_GAIN_PARAM] / 5.0;
inputBuffer.push(inputFrame);
}

// Trigger
if (getf(inputs[TRIG_INPUT]) >= 1.0) {
triggered = true;
}

if (++bufferFrame >= 32) {
bufferFrame = 0;
// Render frames
if (outputBuffer.empty()) {
clouds::ShortFrame input[32] = {};
// Convert input buffer
{
inputSrc.setRatio(32000.0 / gRack->sampleRate);
Frame<2> inputFrames[32];
int inLen = inputBuffer.size();
int outLen = 32;
inputSrc.process((const float*) inputBuffer.startData(), &inLen, (float*) inputFrames, &outLen);
inputBuffer.startIncr(inLen);

// We might not fill all of the input buffer if there is a deficiency, but this cannot be avoided due to imprecisions between the input and output SRC.
for (int i = 0; i < outLen; i++) {
input[i].l = clampf(inputFrames[i].samples[0] * 32767.0, -32768, 32767);
input[i].r = clampf(inputFrames[i].samples[1] * 32767.0, -32768, 32767);
}
}

// Set up processor
processor->set_num_channels(2);
processor->set_low_fidelity(false);
// TODO Support the other modes
processor->set_playback_mode(clouds::PLAYBACK_MODE_GRANULAR);
processor->Prepare();

@@ -105,22 +126,33 @@ void Clouds::step() {
p->feedback = 0.0f;
p->reverb = 0.0f;

clouds::ShortFrame input[32];
clouds::ShortFrame output[32];
for (int j = 0; j < 32; j++) {
input[j].l = clampf(inL[j] * params[IN_GAIN_PARAM] / 5.0, -1.0, 1.0) * 32767;
input[j].r = clampf(inR[j] * params[IN_GAIN_PARAM] / 5.0, -1.0, 1.0) * 32767;
}

processor->Process(input, output, 32);

for (int j = 0; j < 32; j++) {
outL[j] = (float)output[j].l / 32767 * 5.0;
outR[j] = (float)output[j].r / 32767 * 5.0;
// Convert output buffer
{
Frame<2> outputFrames[32];
for (int i = 0; i < 32; i++) {
outputFrames[i].samples[0] = output[i].l / 32768.0;
outputFrames[i].samples[1] = output[i].r / 32768.0;
}

outputSrc.setRatio(gRack->sampleRate / 32000.0);
int inLen = 32;
int outLen = outputBuffer.capacity();
outputSrc.process((const float*) outputFrames, &inLen, (float*) outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}

triggered = false;
}

// Set output
if (!outputBuffer.empty()) {
Frame<2> outputFrame = outputBuffer.shift();
setf(outputs[OUT_L_OUTPUT], 5.0 * outputFrame.samples[0]);
setf(outputs[OUT_R_OUTPUT], 5.0 * outputFrame.samples[1]);
}
}




+ 60
- 15
src/Elements.cpp View File

@@ -1,5 +1,6 @@
#include "AudibleInstruments.hpp"
#include <string.h>
#include "AudibleInstruments.hpp"
#include "dsp.hpp"
#include "elements/dsp/part.h"


@@ -65,13 +66,13 @@ struct Elements : Module {
NUM_OUTPUTS
};

SampleRateConverter<2> inputSrc;
SampleRateConverter<2> outputSrc;
DoubleRingBuffer<Frame<2>, 256> inputBuffer;
DoubleRingBuffer<Frame<2>, 256> outputBuffer;

uint16_t reverb_buffer[32768] = {};
elements::Part *part;
int bufferFrame = 0;
float blow[16] = {};
float strike[16] = {};
float main[16] = {};
float aux[16] = {};
float lights[2] = {};

Elements();
@@ -99,14 +100,36 @@ Elements::~Elements() {
}

void Elements::step() {
// TODO Sample rate convert from 32000Hz
blow[bufferFrame] = getf(inputs[BLOW_INPUT]);
strike[bufferFrame] = getf(inputs[STRIKE_INPUT]);
setf(outputs[AUX_OUTPUT], 5.0*aux[bufferFrame]);
setf(outputs[MAIN_OUTPUT], 5.0*main[bufferFrame]);

if (++bufferFrame >= 16) {
bufferFrame = 0;
// Get input
if (!inputBuffer.full()) {
Frame<2> inputFrame;
inputFrame.samples[0] = getf(inputs[BLOW_INPUT]) / 5.0;
inputFrame.samples[1] = getf(inputs[STRIKE_INPUT]) / 5.0;
inputBuffer.push(inputFrame);
}

// Render frames
if (outputBuffer.empty()) {
float blow[16] = {};
float strike[16] = {};
float main[16];
float aux[16];

// Convert input buffer
{
inputSrc.setRatio(32000.0 / gRack->sampleRate);
Frame<2> inputFrames[16];
int inLen = inputBuffer.size();
int outLen = 16;
inputSrc.process((float*) inputBuffer.startData(), &inLen, (float*) inputFrames, &outLen);
inputBuffer.startIncr(inLen);

for (int i = 0; i < outLen; i++) {
blow[i] = inputFrames[i].samples[0];
strike[i] = inputFrames[i].samples[1];
}
}

// Set patch from parameters
elements::Patch* p = part->mutable_patch();
p->exciter_envelope_shape = params[CONTOUR_PARAM];
@@ -114,7 +137,7 @@ void Elements::step() {
p->exciter_blow_level = params[BLOW_PARAM];
p->exciter_strike_level = params[STRIKE_PARAM];

#define BIND(_p, _m, _i) clampf(params[_p] + 3.3*quadraticBipolar(params[_m])*getf(inputs[_i])/5.0, 0.0, 0.9995)
#define BIND(_p, _m, _i) clampf(params[_p] + 3.3*quadraticBipolar(params[_m])*getf(inputs[_i])/5.0, 0.0, 0.9995)

p->exciter_bow_timbre = BIND(BOW_TIMBRE_PARAM, BOW_TIMBRE_MOD_PARAM, BOW_TIMBRE_MOD_INPUT);
p->exciter_blow_meta = BIND(FLOW_PARAM, FLOW_MOD_PARAM, FLOW_MOD_INPUT);
@@ -137,10 +160,32 @@ void Elements::step() {
// Generate audio
part->Process(performance, blow, strike, main, aux, 16);

// Convert output buffer
{
Frame<2> outputFrames[16];
for (int i = 0; i < 16; i++) {
outputFrames[i].samples[0] = main[i];
outputFrames[i].samples[1] = aux[i];
}

outputSrc.setRatio(gRack->sampleRate / 32000.0);
int inLen = 16;
int outLen = outputBuffer.capacity();
outputSrc.process((float*) outputFrames, &inLen, (float*) outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}

// Set lights
lights[0] = part->exciter_level();
lights[1] = part->resonator_level();
}

// Set output
if (!outputBuffer.empty()) {
Frame<2> outputFrame = outputBuffer.shift();
setf(outputs[AUX_OUTPUT], 5.0 * outputFrame.samples[0]);
setf(outputs[MAIN_OUTPUT], 5.0 * outputFrame.samples[1]);
}
}




+ 3
- 4
src/Kinks.cpp View File

@@ -28,7 +28,6 @@ struct Kinks : Module {
NUM_OUTPUTS
};

std::normal_distribution<float> dist;
float lastTrig = 0.0;
float sample = 0.0;
float lights[3] = {};
@@ -38,7 +37,7 @@ struct Kinks : Module {
};


Kinks::Kinks() : dist(0.0, 1.0) {
Kinks::Kinks() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS);
@@ -46,11 +45,11 @@ Kinks::Kinks() : dist(0.0, 1.0) {

void Kinks::step() {
// Gaussian noise generator
float noise = 2.0 * dist(rng);
float noise = 2.0 * randomNormal();

// S&H
float trig = getf(inputs[TRIG_INPUT]);
float dtrig = (trig - lastTrig) * SAMPLE_RATE;
float dtrig = (trig - lastTrig) * gRack->sampleRate;
if (dtrig > DTRIG) {
sample = getf(inputs[SH_INPUT], noise);
}


+ 57
- 19
src/Rings.cpp View File

@@ -1,5 +1,6 @@
#include "AudibleInstruments.hpp"
#include <string.h>
#include "AudibleInstruments.hpp"
#include "dsp.hpp"
#include "rings/dsp/part.h"
#include "rings/dsp/strummer.h"
#include "rings/dsp/string_synth_part.h"
@@ -41,11 +42,12 @@ struct Rings : Module {
NUM_OUTPUTS
};

SampleRateConverter<1> inputSrc;
SampleRateConverter<2> outputSrc;
DoubleRingBuffer<float, 256> inputBuffer;
DoubleRingBuffer<Frame<2>, 256> outputBuffer;

uint16_t reverb_buffer[32768] = {};
int bufferFrame = 0;
float in[24] = {};
float out[24] = {};
float aux[24] = {};
rings::Part part;
rings::StringSynthPart string_synth;
rings::Strummer strummer;
@@ -76,33 +78,36 @@ Rings::~Rings() {
}

void Rings::step() {
// TODO Sample rate conversion from 48000 Hz
// TODO
// "Normalized to a pulse/burst generator that reacts to note changes on the V/OCT input."
in[bufferFrame] = clampf(getf(inputs[IN_INPUT])/5.0, -1.0, 1.0);
// "Note that you need to insert a jack into each output to split the signals: when only one jack is inserted, both signals are mixed together."
if (outputs[ODD_OUTPUT] && outputs[EVEN_OUTPUT]) {
setf(outputs[ODD_OUTPUT], clampf(out[bufferFrame], -1.0, 1.0)*5.0);
setf(outputs[EVEN_OUTPUT], clampf(aux[bufferFrame], -1.0, 1.0)*5.0);
}
else {
float v = clampf(out[bufferFrame] + aux[bufferFrame], -1.0, 1.0)*5.0;
setf(outputs[ODD_OUTPUT], v);
setf(outputs[EVEN_OUTPUT], v);
// Get input
if (!inputBuffer.full()) {
float inputFrame = getf(inputs[IN_INPUT]) / 5.0;
inputBuffer.push(inputFrame);
}

if (!strum) {
strum = getf(inputs[STRUM_INPUT]) >= 1.0;
}

if (++bufferFrame >= 24) {
bufferFrame = 0;
// Render frames
if (outputBuffer.empty()) {
float in[24] = {};
// Convert input buffer
{
inputSrc.setRatio(48000.0 / gRack->sampleRate);
int inLen = inputBuffer.size();
int outLen = 24;
inputSrc.process(inputBuffer.startData(), &inLen, (float*) in, &outLen);
inputBuffer.startIncr(inLen);
}

// modes
int polyphony = 1;
int polyphony;
switch ((int) roundf(params[POLYPHONY_PARAM])) {
case 1: polyphony = 2; break;
case 2: polyphony = 4; break;
default: polyphony = 1;
}
if (polyphony != part.polyphony()) {
part.set_polyphony(polyphony);
@@ -145,6 +150,8 @@ void Rings::step() {
performance_state.chord = clampf(roundf(structure * (rings::kNumChords - 1)), 0, rings::kNumChords - 1);

// Process audio
float out[24];
float aux[24];
if (0) {
// strummer.Process(NULL, 24, &performance_state);
// string_synth.Process(performance_state, patch, in, out, aux, 24);
@@ -153,7 +160,38 @@ void Rings::step() {
strummer.Process(in, 24, &performance_state);
part.Process(performance_state, patch, in, out, aux, 24);
}

// Convert output buffer
{
Frame<2> outputFrames[24];
for (int i = 0; i < 24; i++) {
outputFrames[i].samples[0] = out[i];
outputFrames[i].samples[1] = aux[i];
}

outputSrc.setRatio(gRack->sampleRate / 48000.0);
int inLen = 24;
int outLen = outputBuffer.capacity();
outputSrc.process((const float*) outputFrames, &inLen, (float*) outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}
}

// Set output
if (!outputBuffer.empty()) {
Frame<2> outputFrame = outputBuffer.shift();
// "Note that you need to insert a jack into each output to split the signals: when only one jack is inserted, both signals are mixed together."
if (outputs[ODD_OUTPUT] && outputs[EVEN_OUTPUT]) {
setf(outputs[ODD_OUTPUT], clampf(outputFrame.samples[0], -1.0, 1.0)*5.0);
setf(outputs[EVEN_OUTPUT], clampf(outputFrame.samples[1], -1.0, 1.0)*5.0);
}
else {
float v = clampf(outputFrame.samples[0] + outputFrame.samples[1], -1.0, 1.0)*5.0;
setf(outputs[ODD_OUTPUT], v);
setf(outputs[EVEN_OUTPUT], v);
}
}

}




+ 2
- 0
src/Tides.cpp View File

@@ -82,6 +82,8 @@ void Tides::step() {
pitch += 12.0*getf(inputs[PITCH_INPUT]);
pitch += params[FM_PARAM] * getf(inputs[FM_INPUT], 0.1) / 5.0;
pitch += 60.0;
// Scale to the global sample rate
pitch += log2f(48000.0 / gRack->sampleRate) * 12.0;
generator.set_pitch(clampf(pitch*0x80, -0x8000, 0x7fff));

// Slope, smoothness, pitch


+ 4
- 4
src/Veils.cpp View File

@@ -108,10 +108,10 @@ VeilsWidget::VeilsWidget() : ModuleWidget(new Veils()) {
addChild(createScrew(Vec(15, 365)));
addChild(createScrew(Vec(150, 365)));

addParam(createParam<SmallWhiteKnob>(Vec(8, 52), module, Veils::GAIN1_PARAM, 0.0, 1.0, 0.5));
addParam(createParam<SmallWhiteKnob>(Vec(8, 131), module, Veils::GAIN2_PARAM, 0.0, 1.0, 0.5));
addParam(createParam<SmallWhiteKnob>(Vec(8, 210), module, Veils::GAIN3_PARAM, 0.0, 1.0, 0.5));
addParam(createParam<SmallWhiteKnob>(Vec(8, 288), module, Veils::GAIN4_PARAM, 0.0, 1.0, 0.5));
addParam(createParam<SmallWhiteKnob>(Vec(8, 52), module, Veils::GAIN1_PARAM, 0.0, 1.0, 0.0));
addParam(createParam<SmallWhiteKnob>(Vec(8, 131), module, Veils::GAIN2_PARAM, 0.0, 1.0, 0.0));
addParam(createParam<SmallWhiteKnob>(Vec(8, 210), module, Veils::GAIN3_PARAM, 0.0, 1.0, 0.0));
addParam(createParam<SmallWhiteKnob>(Vec(8, 288), module, Veils::GAIN4_PARAM, 0.0, 1.0, 0.0));

addParam(createParam<TinyBlackKnob>(Vec(72, 56), module, Veils::RESPONSE1_PARAM, 0.0, 1.0, 1.0));
addParam(createParam<TinyBlackKnob>(Vec(72, 135), module, Veils::RESPONSE2_PARAM, 0.0, 1.0, 1.0));


+ 1
- 0
src/Warps.cpp View File

@@ -61,6 +61,7 @@ void Warps::step() {
p->frequency_shift_cv = clampf(getf(inputs[ALGORITHM_INPUT]) / 5.0, -1.0, 1.0);
p->phase_shift = p->modulation_algorithm;
p->note = 60.0 * params[LEVEL1_PARAM] + 12.0 * getf(inputs[LEVEL1_INPUT], 2.0) + 12.0;
p->note += log2f(96000.0 / gRack->sampleRate) * 12.0;
float state = roundf(params[STATE_PARAM]);
p->carrier_shape = (int32_t)state;
lights[0] = state - 1.0;


Loading…
Cancel
Save