Here's a FlexFX application example. It doesn't implement any algorithms but just shows what configuration, events, and functions are used to make a complete application. I put them in one file for illustration. This is the only code that needs to be written for the board - all of the USB, I2S, MIDI, I2C and UART code is precompiled and part of the downloadable framework. An example of how to build (compile this file and link with the framework code to make the firmware executable image) is earlier in this thread.
// ================================================================================================
// FlexFX sample application
// ================================================================================================
typedef unsigned char byte;
// ================================================================================================
// System Configuration. Set these constants to configure USB and I2C.
// ================================================================================================
// ------------------------------------------------------------------------------------------------
// USB Audio configuration.
//
// usb_fs_bit_mask: Each bit indicates what sampling frequency is to be supported for USB audio.
// Any combination of bits can be set as long as the ADC/DAC can be configured
// accordingly via the I2C functions.
// Bit 0 for 44k1, bit 1 for 48K0, bit 2 for 88k2, bit 3 8 for 96k,
// bit 4 for 176k4, bit 5 for 192k, bit 6 for 352k8, bit 7 for 384k0
//
// usb_audio_product_id: 16-bit value representing the USB audio product ID
// usb_audio_vendor_id: 16-bit value representing the USB audio vendor ID
// usb_audio_product_str: 10 character string representing the USB audio product name
// usb_audio_vendor_str: 10 character string representing the USB audio vendor name
// ------------------------------------------------------------------------------------------------
const byte usb_fs_bit_mask = 0x02;
const int usb_audio_product_id = 0x1234;
const int usb_audio_vendor_id = 0x1234;
const char* usb_audio_product_str = "FlexFX";
const char* usb_audio_vendor_str = "Example";
// ------------------------------------------------------------------------------------------------
// I2S Bus configuration. The I2S bus consists of MCLK (master audio clock), BCLK (sample bit
// clock), WCLK (word select clock), four SDOUT wires for up to four DAC's, and four SDIN wires for
// up to four ADC's. All sample words are 32-bits supporting 16/24/32 bit samples with either left
// or right justification within the 32-bit word slot. Time division multiplexing is supported -
// 2 (stereo), 4, 6, 8, 10, 12, 14, and 16 samples per audio cycle, per SDIN/SDOUT wire is allowed
// for up to 64x64 channel I2S/TDM. The word select format can be I2S or PCM.
//
// i2s_sync_format: 0 for I2S format (low-high WCLK transition one BCLK period before audio cycle.
// 1 for PCM format (high-low WCLK transition aligned with each audio cycle.
//
// i2s_tdm_slot_count: Defines how many samples are transfered on each SDIN/SDOUT wire for each
// audio cycle. Minimum is 2 (stereo), max is 16. Must be even number.
// ------------------------------------------------------------------------------------------------
const int i2s_sync_format = 1; // 0 for I2S, 1 for PCM
const int i2s_tdm_slot_count = 2; // 2 (stereo), 4, 6, 8, 10, 12, 14, 16
// ================================================================================================
// System Control. Use these functions to control system peripherals (using I2C) and to
// distribute effects parameters to audio processing threads (using MIDI data).
// ================================================================================================
// ------------------------------------------------------------------------------------------------
// I2C Functions. Use these functions to configure the audio ADC/DAC/CODEC upon USB audio config
// change events or to sense pot/switch values via low-speed ADC's and/or I2C port expanders.
//
// i2c_write: Write a sequence of bytes to the addressed device
// i2c_read: Read a sequence of bytes from the addressed device
// i2c_write_reg: Write an 8-bit value to the specified 8-bit register of the addressed device
// i2c_read_reg: Read an 8-bit value from the specified 8-bit register of the addressed device
// ------------------------------------------------------------------------------------------------
extern void i2c_write ( byte dev_addr, const byte* data, int count );
extern void i2c_read ( byte dev_addr, byte* data, int count );
extern void i2c_write_reg( byte dev_addr, byte reg_num, byte reg_value );
extern byte i2c_read_reg ( byte dev_addr, byte reg_num );
// ------------------------------------------------------------------------------------------------
// MIDI write function. Use this function to send MIDI-based property data to each audio thread,
// typically as the result of reading new pot/switch values in the function 'handle_timer' below.
// ------------------------------------------------------------------------------------------------
extern void write_midi_property( const int property[6] );
// ================================================================================================
// Non-audio event handlers. Code in these event handlers will not effect audio flow.
// ================================================================================================
// ------------------------------------------------------------------------------------------------
// USB and Timer event handlers. Implement ADC/DAC/CODEC changes via I2C here.
//
// handle_usb_mute: Occurs whenever a USB audio channel is muted or un-muted.
//
// handle_usb_volume_change: Occurs whenever a USB audio channel volume is changed. 0 is minimum
// volume and 255 is maximum volume.
//
// handle_usb_rate_change: Occurs whenever the sample rate is changed by the USB host. The
// application should use the I2C functions to set the audio master clock
// and configure the ADC/DAC accordingly.
//
// handle_timer: Called at a rate of 1000 Hz. Implement low-speed ADC reading (for pots and/or
// switches) in this function.
// ------------------------------------------------------------------------------------------------
void handle_usb_mute( int audio_channel, int mute_flag )
{
//i2c_write_reg( 0x00, 0x00, 0x00 );
//i2c_write_reg( 0x00, 0x00, 0x00 );
}
void handle_usb_volume_change( int audio_channel, byte volume )
{
//i2c_write_reg( 0x00, 0x00, 0x00 );
//i2c_write_reg( 0x00, 0x00, 0x00 );
}
void handle_usb_rate_change( int frequency )
{
//i2c_write_reg( 0x00, 0x00, 0 );
//i2c_write_reg( 0x00, 0x00, 0 );
}
void handle_timer( void )
{
//int property[6];
//int x = i2c_read_reg( 0x00, 0x00 );
//i2c_write_reg( 0x00, 0x00, 0 );
//write_midi_property( property );
}
// ================================================================================================
// Audio event handlers.
//
// The initialization functions are all called before audio flow starts and should be used to
// initialize data used by audio processing threads. The audio processing functions are all called
// once for every audio cycle meaning that they will be expected finish processing before the next
// audio cycle starts. Each processing thread runs in its own 32-bit core and has approximately 100
// MIPs or processing allocation. The processing functions A1-A5, B1-B5, and C1-C5 form a 15-stage
// processing pipeline for 64 audio samples at each pipeline stage.
//
// Data arriving to thread A1 is from the USB and I2S interfaces and is arranged as 32 USB samples
// followed by 32 I2C samples. The 64-sample array departing from thread C5 is sent to the USB and
// I2S interfaces and is therefore assumed to be arranged as 32 USB samples followed by 32 I2C
// samples. The 64-sample array passed between all other processes (A2-A5,B1-B5,C1-C4) is user
// defined.
//
// thread_nn_initialize: Initialize data for audio thread nn (A1-A5, B1-B5, or C1-C5)
//
// thread_nn_process: Process audio samples for one audio cycle. 'samples' points to an array of
// 64 fixed-point (Q1.31 format) audio samples. 'property' points to an array
// if 6 integers forming the property used to configure audio processing
// functions in real time. Property[0] is the 32-bit property ID. Property[1:5]
// contains the five 32-bit values for the property.
// ================================================================================================
void thread_a1_initialize( void )
{
}
void thread_a1_process( int* samples, const int* property )
{
}
void thread_a2_initialize( void )
{
}
void thread_a2_process( int* samples, const int* property )
{
// Process audio samples from thread A1, results are sent to thread A3.
if( property[0] != 0 ) {
// Check property ID (property[0]) to see of this property applies to this processing
// thread.
}
}
void thread_a3_initialize( void )
{
}
void thread_a3_process( int* samples, const int* property )
{
}
void thread_a4_initialize( void )
{
}
void thread_a4_process( int* samples, const int* property )
{
}
void thread_a5_initialize( void )
{
}
void thread_a5_process( int* samples, const int* property )
{
}
void thread_b1_initialize( void )
{
}
void thread_b1_process( int* samples, const int* property )
{
}
void thread_b2_initialize( void )
{
}
void thread_b2_process( int* samples, const int* property )
{
}
void thread_b3_initialize( void )
{
}
void thread_b3_process( int* samples, const int* property )
{
}
void thread_b4_initialize( void )
{
}
void thread_b4_process( int* samples, const int* property )
{
}
void thread_b5_initialize( void )
{
}
void thread_b5_process( int* samples, const int* property )
{
}
void thread_c1_initialize( void )
{
}
void thread_c1_process( int* samples, const int* property )
{
}
void thread_c2_initialize( void )
{
}
void thread_c2_process( int* samples, const int* property )
{
}
void thread_c3_initialize( void )
{
}
void thread_c3_process( int* samples, const int* property )
{
}
void thread_c4_initialize( void )
{
}
void thread_c4_process( int* samples, const int* property )
{
}
void thread_c5_initialize( void )
{
}
void thread_c5_process( int* samples, const int* property )
{
}
// ================================================================================================
// ================================================================================================