Here's a cabinet simulator for up to 50 msec of impulse response processing. The IR data can be loaded/modified via MIDI. Not tested yet - just for illustration for now to show how to use the framework. Actual code can be obtained from GitHub.
To build: ./build.sh cabsim
// ================================================================================================
// "cabsim.c"
// ================================================================================================
//
// A custom effects processing signal chain is constructed by adding DSP functionality to any of
// the seven audio processing threads below. Each processing thread is allocated 100 MIPS of the
// total 500 MIPs available and executes once per audio cycle (48 kHz).
//
// Audio sample data flow is managed by the provided audio processing foundation object code that
// implements USB audio, USB MIDI and serial UART MIDI, and firmware upgrades. Audio flows from
// threads 1 through 7 in sequential order - all threads are executed once per audio cycle.
//
// The 'init' functions are each called once at program startup and should be used to initialize
// data used for audio processing. The 'exec' functions are each called once per audio cycle. Note
// that audio samples are in Q1.31 fixed-point format (one sign bit, 31 fractional bits).
//
// Each 'exec' function accepts 'samples[8]' and 'property[6]' as arguments.
//
// The 'samples' array contains 8 input samples upon function entry and represent the 8 output
// samples on function exit. If no values in the 'samples' array are modified then the audio data
// is passed through the thread on to the next thread unmodified. Each value in the samples array
// represents:
//
// For thread 1 (the first processing block in the audio processing chain):
// Sample[0] is the ADC audio from the instrument (left channel)
// Sample[1] is the ADC audio from the instrument (right channel)
// Sample[2] is the USB audio output from the USB host (left channel)
// Sample[3] is the USB audio output from the USB host (right channel)
// Sample[4,5,6,7] are user defined and be used to pass other data from thread 1 to thread 2
//
// For threads 2,3,4:
// Sample[4,5,6,7] are user defined and can be used to pass additional samples or data from
// threads 2,3 to threads 3,4 respectively. Note that the FlexFX audio framework only makes
// assumptions of sample mappings to/from USB/ADC/DAC audio channels in threads 1 and 5.
//
// For thread 5 (the last processing block in the audio processing chain):
// Sample[0] is the DAC audio to the monitor or amplifier (left channel)
// Sample[1] is the DAC audio to the monitor or amplifier (right channel)
// Sample[2] is the USB audio input to the USB host (left channel)
// Sample[3] is the USB audio input to the USB host (right channel)
// ------------------------------------------------------------------------------------------------
#include "core_types.h"
#include "core_dsp.h"
#define PROPERTY_CABSIM_PARAMS 0x1400 // First word is enable / disable (bypass)
#define PROPERTY_CABSIM_SET_A 0x1401
#define PROPERTY_CABSIM_SET_B 0x1402
#define PROPERTY_CABSIM_SET_C 0x1403
#define PROPERTY_CABSIM_SET_D 0x1404
#define PROPERTY_CABSIM_SET_E 0x1405
int cabsimA_coeff[480], cabsimA_state[480];
int cabsimB_coeff[480], cabsimB_state[480];
int cabsimC_coeff[480], cabsimC_state[480];
int cabsimD_coeff[480], cabsimD_state[480];
int cabsimE_coeff[480], cabsimE_state[480];
void _copy_property( int* dst, int index, const int* src )
{
dst += 5 * index;
dst[0] = src[1]; dst[1] = src[2]; dst[2] = src[3];
dst[3] = src[4]; dst[4] = src[5];
}
// ------------------------------------------------------------------------------------------------
void audio_thread_1_init( void )
{
for( int ii = 0; ii < 480; ++ii ) cabsimA_coeff[ii] = cabsimA_state[ii] = 0;
cabsimA_coeff[0] = Q31(+0.199);
}
void audio_thread_1_exec( int samples[8], const int property[6] )
{
static int index = 0, enabled = false;
// Check to see if property is being updated. Each time the incoming property ID (property[0])
// matches our CabSim IR data property ID then copy the property data (property[1-5] and then
// increment the index into the cab sim data array so that successive properties with repated
// property ID's can be used to load all cab sim data.
if( property[0] == PROPERTY_CABSIM_SET_A && index < 96 )
{
_copy_property( cabsimA_coeff, index++, property+1 );
}
else index = 0;
// Check for cab sim parameters (first word of the five is the enable/disable flag).
if( property[0] == PROPERTY_CABSIM_PARAMS ) enabled = property[1];
if( !enabled ) return; // Leave sample[0] unmodified
int *cc = cabsimA_coeff, *ss = cabsimA_state;
dsp_fir_preamble( samples[2], 31 ); // Start with instrument input (ADC) left channel
... repeat this set or macros below 20 times (left out here for brevity)
... dsp_fir_body( 0 ); dsp_fir_body( 1 ); dsp_fir_body( 2 );
... dsp_fir_body( 3 ); dsp_fir_body( 4 ); dsp_fir_body( 5 );
... cc += 24; ss += 24;
// Pass the instrument (left channel) sample to the next stage in case the cabsim is disabled
// Instrument (right) and both USB channels are unmodified
// Pass intermediate FIR 64-bit accumulator words for the next FIR stage
samples[0] = s0; samples[4] = ah; samples[5] = al;
}
// ------------------------------------------------------------------------------------------------
void audio_thread_2_init( void )
{
for( int ii = 0; ii < 480; ++ii ) cabsimB_coeff[ii] = cabsimB_state[ii] = 0;
}
void audio_thread_2_exec( int samples[8], const int property[6] )
{
static int index = 0, enabled = false;
if( property[0] == PROPERTY_CABSIM_SET_B && index < 96 )
{
_copy_property( cabsimB_coeff, index++, property+1 );
}
else index = 0;
// Check for cab sim parameters (first word of the five is the enable/disable flag).
if( property[0] == PROPERTY_CABSIM_PARAMS ) enabled = property[1];
if( !enabled ) return; // Leave sample[0] unmodified
int *cc = cabsimB_coeff, *ss = cabsimB_state;
// Resume the FIR using instrument left channel and the 64-bit accumulator words
dsp_fir_continue( samples[0], samples[4], samples[5] );
... repeat this set or macros below 20 times (left out here for brevity)
... dsp_fir_body( 0 ); dsp_fir_body( 1 ); dsp_fir_body( 2 );
... dsp_fir_body( 3 ); dsp_fir_body( 4 ); dsp_fir_body( 5 );
... cc += 24; ss += 24;
// Pass the instrument (left channel) sample to the next stage in case the cabsim is disabled
// Instrument (right) and both USB channels are unmodified
// Pass intermediate FIR 64-bit accumulator words for the next FIR stage
samples[0] = s0; samples[4] = ah; samples[5] = al;
}
// ------------------------------------------------------------------------------------------------
void audio_thread_3_init( void )
{
for( int ii = 0; ii < 480; ++ii ) cabsimC_coeff[ii] = cabsimC_state[ii] = 0;
}
void audio_thread_3_exec( int samples[8], const int property[6] )
{
static int index = 0, enabled = false;
if( property[0] == PROPERTY_CABSIM_SET_C && index < 96 )
{
_copy_property( cabsimC_coeff, index++, property+1 );
}
else index = 0;
// Check for cab sim parameters (first word of the five is the enable/disable flag).
if( property[0] == PROPERTY_CABSIM_PARAMS ) enabled = property[1];
if( !enabled ) return; // Leave sample[0] unmodified
int *cc = cabsimC_coeff, *ss = cabsimC_state;
// Resume the FIR using instrument left channel and the 64-bit accumulator words
dsp_fir_continue( samples[0], samples[4], samples[5] );
... repeat this set or macros below 20 times (left out here for brevity)
... dsp_fir_body( 0 ); dsp_fir_body( 1 ); dsp_fir_body( 2 );
... dsp_fir_body( 3 ); dsp_fir_body( 4 ); dsp_fir_body( 5 );
... cc += 24; ss += 24;
// Pass the instrument (left channel) sample to the next stage in case the cabsim is disabled
// Instrument (right) and both USB channels are unmodified
// Pass intermediate FIR 64-bit accumulator words for the next FIR stage
samples[0] = s0; samples[4] = ah; samples[5] = al;
}
// ------------------------------------------------------------------------------------------------
void audio_thread_4_init( void )
{
for( int ii = 0; ii < 480; ++ii ) cabsimD_coeff[ii] = cabsimD_state[ii] = 0;
}
void audio_thread_4_exec( int samples[8], const int property[6] )
{
static int index = 0, enabled = false;
if( property[0] == PROPERTY_CABSIM_SET_D && index < 96 )
{
_copy_property( cabsimD_coeff, index++, property+1 );
}
else index = 0;
// Check for cab sim parameters (first word of the five is the enable/disable flag).
if( property[0] == PROPERTY_CABSIM_PARAMS ) enabled = property[1];
if( !enabled ) return; // Leave sample[0] unmodified
int *cc = cabsimD_coeff, *ss = cabsimD_state;
// Resume the FIR using instrument left channel and the 64-bit accumulator words
dsp_fir_continue( samples[0], samples[4], samples[5] );
... repeat this set or macros below 20 times (left out here for brevity)
... dsp_fir_body( 0 ); dsp_fir_body( 1 ); dsp_fir_body( 2 );
... dsp_fir_body( 3 ); dsp_fir_body( 4 ); dsp_fir_body( 5 );
... cc += 24; ss += 24;
// Pass the instrument (left channel) sample to the next stage in case the cabsim is disabled
// Instrument (right) and both USB channels are unmodified
// Pass intermediate FIR 64-bit accumulator words for the next FIR stage
samples[0] = s0; samples[4] = ah; samples[5] = al;
}
// ------------------------------------------------------------------------------------------------
void audio_thread_5_init( void )
{
for( int ii = 0; ii < 480; ++ii ) cabsimE_coeff[ii] = cabsimE_state[ii] = 0;
}
void audio_thread_5_exec( int samples[8], const int property[6] )
{
static int index = 0, enabled = false;
if( property[0] == PROPERTY_CABSIM_SET_E && index < 96 )
{
_copy_property( cabsimE_coeff, index++, property+1 );
}
else index = 0;
// Check for cab sim parameters (first word of the five is the enable/disable flag).
if( property[0] == PROPERTY_CABSIM_PARAMS ) enabled = property[1];
if( !enabled ) return; // Leave sample[0] unmodified
int *cc = cabsimE_coeff, *ss = cabsimE_state;
// Resume the FIR using instrument left channel and the 64-bit accumulator words
dsp_fir_continue( samples[0], samples[4], samples[5] );
... repeat this set or macros below 20 times (left out here for brevity)
... dsp_fir_body( 0 ); dsp_fir_body( 1 ); dsp_fir_body( 2 );
... dsp_fir_body( 3 ); dsp_fir_body( 4 ); dsp_fir_body( 5 );
... cc += 24; ss += 24;
// Pass the instrument left channel sample (when cabsim is disabled/bypassed) ...
// ... or the FIR result as left instrument channel (cabsim is enabled)
dsp_fir_postamble( samples[0], 31 );
}
// ===============================================================================================
// ===============================================================================================