i think JUCE is a fine choice. does kinda the same job as arduino + teensy audio lib in setting up everything for you except the process loop.
one decision you have to make is whether your audio process will use fixed-point or float math. i’d assume the latter; new teensys have hardware FPU…
so, you make your class implement a generic processing loop and param setters, and then wrap it in a teensy AudioStream:
#include <stdint.h>
static inline float from_short(int16_t x) { return (float)x / (float)0x8000; }
static inline int16_t to_short(float x) { return (int16_t) (x * (float)0x8000); }
class MyEffect {
float process(float x) {
float y = /* awesome stuff here */ x * param_;
return y;
}
float param_;
set_param(float val) { param_ = val; }
}
class MyAudioStream: public AudioStream {
// PJRC override
virtual void update(void);
MyEffect effect;
};
void MyAudioStream::update(void) {
audio_block_t* audio;
audio_block_t* control;
audio = receiveWritable(0);
control = receiveReadOnly(0);
if(!audio) { return; }
if(!control) { return; }
int16_t* samp = audio->data;
// say you just want one ctl value per block
int16_t ctl = control->data[0];
effect.set_param(from_short(ctl));
for(int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
float fsamp = from_short(*samp);
fsamp = effect.process(fsamp);
*samp = to_short(fsamp);
samp++;
}
transmit(audio);
return;
}
similarly in JUCE, use the exact same MyEffect and wrap in in an AudioProcessor instead of AudioStream.
the override of
AudioStream::update()
becomes
AudioProcessor::processBlock(AudioBuffer<float> &buffer, MidiBuffer &midiMessages)
you use
const float* AudioBuffer::getReadPointer()
and
float* AudioBuffer::getWritePointer()
to manipulate the sample data
an hm, i guess the new recommenation for parameters is to use a AudioProcessorParameter member in your AudioProcessor.
HTH!