News:

SMF for DIYStompboxes.com!

Main Menu

Audio Effects DSP Board

Started by markseel, June 13, 2016, 11:53:46 AM

Previous topic - Next topic

markseel

#100
Yeah that's fine - either way would work.

markseel

#101
Fixed for xTIMEcomposer 14.2.x and pushed to GitHub.

mhelin

[quote author=markseel link=topic=114354.msg1079120#msg1079120 date=1484182940]

bash-3.2$ cd flexfx_kit
bash-3.2$ /Applications/XMOS_xTIMEcomposer_Community_14.1.1/SetEnv.command
bash-3.2$ ./build.sh example
...

[/quote]

Tip for Windows users: replace the Linux line continuation characters '\' (backslash) with '^' (caret) in build.bat to be able to do the build.

The just run the commands

C:\your\workdir>build.bat example
C:\your\workdir>xsim example.xe



pruttelherrie

Quote from: markseel on January 12, 2017, 01:59:13 PM
Fixed for xTIMEcomposer 14.2.x and pushed to GitHub.
Yep, works now! Thanks!

markseel

#104
Here's another code example; A stereo 15-band graphic EQ with 4th order filters (two cascaded biquad's) for each band.  The frequency band filters are processed in cascaded form therefore the complete EQ frequency response can be processed as 30 cascaded biquad filters.

The filter coefficients are not hard-coded for specific frequency responses (although they could be if desired) but rather are set to 0.0 and later loaded up via MIDI by a host computer allowing band Q, gain, and frequency to be calculated by the host machine as settings change.  This method works well when using USB MIDI due to its high data rate and low latency.

Both left and right channels are both processed in one processing thread and easily satisfies timing for a 48 kHz sampling rate.  Keep in mind that although thread A1 is doing work the threads A2-A5, B1-B5 and C1-C5 are unused and still have approximately 100 MIPs each do to additional processing.


bash-3.2$ xsim example_eq.xe
1192 ticks (83892 Hz)



#define MIDIPROP_GRAPHICEQ          0x12345600
#define MIDIPROP_GRAPHICEQ_COEFFS_L (MIDIPROP_GRAPHICEQ+0x00)
#define MIDIPROP_GRAPHICEQ_COEFFS_R (MIDIPROP_GRAPHICEQ+0x20)
#define MIDIPROP_GRAPHICEQ_GAINS    (MIDIPROP_GRAPHICEQ+0x40)

#define GRAPHICEQ_BAND_COUNT   15
#define GRAPHICEQ_FILTER_COUNT  2

int32_t graphiceq_coeffsL_q28[GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT*5] =
{
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 01 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 01 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 02 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 02 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 03 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 03 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 04 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 04 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 05 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 05 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 06 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 06 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 07 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 07 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 08 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 08 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 09 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 09 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 10 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 10 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 11 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 11 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 12 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 12 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 13 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 13 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 14 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 14 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 15 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 15 Biquad 2
};

int32_t graphiceq_coeffsR_q28[GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT*5] =
{
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 01 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 01 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 02 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 02 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 03 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 03 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 04 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 04 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 05 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 05 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 06 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 06 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 07 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 07 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 08 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 08 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 09 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 09 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 10 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 10 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 11 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 11 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 12 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 12 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 13 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 13 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 14 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 14 Biquad 2
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 15 Biquad 1
    Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), Q28(0.0), // Band 15 Biquad 2
};

int32_t graphiceq_stateL_q28[GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT*4];
int32_t graphiceq_stateR_q28[GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT*4];

// These pre/post gains are fixed-point Q28 formatted values ranging from (-8.0 to +7.999...)
int32_t graphiceq_pregainL_q28, graphiceq_pregainR_q28;
int32_t graphiceq_postgainL_q28, graphiceq_postgainR_q28;

void thread_a1_initialize( void )
{
    graphiceq_pregainL_q28 = graphiceq_pregainR_q28 = 0;
    graphiceq_postgainL_q28 = graphiceq_postgainR_q28 = 0;
    for( int ii = 0; ii < 15*2*4; ++ii ) graphiceq_stateL_q28[ii] = 0;
    for( int ii = 0; ii < 15*2*4; ++ii ) graphiceq_stateR_q28[ii] = 0;
}

void thread_a1_process( int* samples, const int* property, int ticks )
{
    static int pass = 0;

    // Process left channel sample
    samples[0] = lib_dsp_math_multiply( samples[0], graphiceq_pregainL_q28, 28 );
    samples[0] = lib_dsp_filters_biquads( samples[0],
                                          graphiceq_coeffsL_q28, graphiceq_stateL_q28,
                                          GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT,
                                          28 );
    samples[0] = lib_dsp_math_multiply( samples[0], graphiceq_postgainL_q28, 28 );

    // Process right channel sample
    samples[1] = lib_dsp_math_multiply( samples[1], graphiceq_pregainR_q28, 28 );
    samples[1] = lib_dsp_filters_biquads( samples[1],
                                          graphiceq_coeffsR_q28, graphiceq_stateR_q28,
                                          GRAPHICEQ_BAND_COUNT*GRAPHICEQ_FILTER_COUNT,
                                          28 );
    samples[1] = lib_dsp_math_multiply( samples[1], graphiceq_postgainR_q28, 28 );

    // Check for a property update to the pre and post gain values
    if( property[0] == MIDIPROP_GRAPHICEQ_GAINS )
    {
        graphiceq_pregainL_q28  = property[1];
        graphiceq_pregainR_q28  = property[2];
        graphiceq_postgainL_q28 = property[3];
        graphiceq_postgainR_q28 = property[4];

    }
    // Check for a property update to the left EQ filter bank
    else if( (property[0] & 0xFFFFFFF0) == MIDIPROP_GRAPHICEQ_COEFFS_L )
    {
        int filter_num = property[0] - MIDIPROP_GRAPHICEQ_COEFFS_L - 1;
        set_property( graphiceq_coeffsL_q28 + 5 * filter_num, property );
    }
    // Check for a property update to the right EQ filter bank
    else if( (property[0] & 0xFFFFFFF0) == MIDIPROP_GRAPHICEQ_COEFFS_R )
    {
        int filter_num = property[0] - MIDIPROP_GRAPHICEQ_COEFFS_L - 1;
        set_property( graphiceq_coeffsL_q28 + 5 * filter_num, property );
    }

    if( ++pass == 2 ) printf( "%u ticks (%u Hz)\n", ticks, 100000000/ticks );
}

mhelin

#105
Is it possible to do some multi-rate filtering using this framework? I mean using first the lib_dsp_filters_decimate using some decimation factor like 64 for 750 Hz intermediate sample rate to be able to have finer control of bass frequencies for controlling room modes and then interpolate back to 48 kHz using the lib_dsp_filters_interpolate? That's what some room EQ devices use like this:

http://www.dspeaker.com/en/products/20-dual-core.shtml
Reviewed here: http://www.theabsolutesound.com/articles/dspeaker-anti-mode-20-dualcore-digital-signal-processor/

---

DSP Filters:

Anti-Mode 2.0 Multi-Rate (FIR & IIR)
House Curve filter
Linear-Phase Tilt
Parametric EQs
Adjustable Infrasonic
Adjustable cross-over

---
The above product uses multiple small in-house developed DSP chips on their products, but guess a single multi-core Xcore processor could do the job easily.

Also there is the Room EQ Wizard (REW for short) application which can output the correction filters in multiple formats. If you ever decide to implement the MIDI protocol and format for the filters you could ask REW developers to include support for your format into the software.

See https://www.roomeqwizard.com/#

markseel

QuoteIs it possible to do some multi-rate filtering using this framework?

Yes it's definitely possible  :icon_biggrin:.

The framework is there to handle the USB, I2S/TDM, I2C, UART, MIDI, and data flow duties and to provide the application structure.  There's plenty of MIP's available for multi-rate DSP.  The DSP libraries' interpolation/decimation FIR functions may be fine as-is but you can also look at the source code for these functions and possibly come up with code that's better tuned for what you need to do. 

You could also use the XMOS sample-rate conversion libraries (SSRC and ASRC support).  Just copy the SRC library source files into the directory for the framework and add the .C and/or .XC files to the build script.

https://www.xmos.com/support/libraries?subcategory=Audio


pruttelherrie

Last weekend I came across this: http://amtelectronics.com/new/manuals/Pangaea-CP-16-M-ENG.pdf

Could this be an XMOS?

Haven't found it for sale yet, though.

markseel

#108
Yeah another poster in this forum saw this as well. It looks like a nice little module. Based on its impulse response capability I'd guess that it's based on a ARM Cortex M4 with floating point support in hardware. Or maybe one of the smaller XMOS devices? About 1/3 of the power of my board but plenty of power for some guitar effects and delay depending on how much RAM is available. Latency is over 1msec but I suspect that it's acceptable for many folks.  Looks pretty cool but there's no API as far a I know for implementing your own stuff.

pruttelherrie

Got some more info on the AMT board: $78 in quantities <10; available Real Soon Now TM.

More questions, not necessarily FlexFX/XMOS related but more general DSP: how would a kind of guitar-synth be implemented? A whole bunch of filters + gates/triggers or by FFT? With FFT one will introduce latency because of the frame/window size, but with high samplerates (96kHz?) and small framesizes (1k?) that might be ok-ish.

I ask because daydreaming about things to use your board for might be to do a kind of EHX B-9/C-9/etc. workalike. In this case FFT might actually be ok, if you start a 'click' sample right away on the attack of the notes but only start the correct pitched sample after analysis.

markseel

#110
Quote
Got some more info on the AMT board: $78 in quantities <10; available Real Soon Now TM.
Good info.  FlexFX will offer much higher performance per cost.  I suspect that the board will be perhaps $100-$125 and have 3x the performance in addition to full programmability, DSP libraries, MIDI support and high channel count (up to 32x32) USB and I2S audio support.

Quote
how would a kind of guitar-synth be implemented?
I don't really know.  I focus on time-domain effects myself.  Perhaps others in this forum are familiar with synth techniques?  But you could react to MIDI in real-time which makes for interesting possibilities.  Also, the DSP library supports FFT and iFFT and full source code for the DSP library is included (free from of XMOS Ltd.).

markseel

#111
I added a Python script to the FlexFX kit on GitHub that creates time and frequency domain plots of output data.

Here's the test code.  It just prints the ASCII/HEX value for each Q1.31 formatted fixed-point sample for a 1000 Hz sine wave with Fs=48kHz.  Note that it generates 8192 samples.  If you simulate this it will take a long time!  The best way to run this is to use the board attached to the XTAG2 (the JTAG interface) and run the program on the board - it won't run in real-time due to the printf's but it will create the output data much faster.


#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#endif

static double PI2 = (double) (2*3.141592653);
static double sine( double time, double frequency ) {return sin(time*PI2*frequency);}

void thread_a1_initialize( void )
{
}
void thread_a1_process( int* samples, const int* property, int ticks )
{
    #ifdef TEST
    static int count = 0;
    unsigned sample = Q31( sine( count / 48000.0, 1000.0 ) );
    printf( "%08x\n", sample );
    if( ++count == 32 ) exit(0);
    #endif
}



bash-3.2$ xsim example_plot.xe > out.txt

bash-3.2$ python plot.py out.txt 31 time
bash-3.2$ python plot.py out.txt 31 time 100 300
bash-3.2$ python plot.py out.txt 31 freq lin
bash-3.2$ python plot.py out.txt 31 freq log



pruttelherrie

Quote from: markseel on January 18, 2017, 08:16:10 PM
Quote
how would a kind of guitar-synth be implemented?
I don't really know.  I focus on time-domain effects myself.  Perhaps others in this forum are familiar with synth techniques?  But you could react to MIDI in real-time which makes for interesting possibilities.

I actually meant the analysis of the guitar signal, which might then poop out MIDI to some other box (synth), or generate sounds from within the XMOS. But:
QuoteAlso, the DSP library supports FFT and iFFT and full source code for the DSP library is included (free from of XMOS Ltd.).

I have to admit I haven't really dived into this (read: googled any of it), but the FFT functionality sounds like a good thing to make use of.

So many projects, so little time! *sigh*

Digital Larry

Guitar synth a'la EHX B3 can be approached using an FFT based "Phase Vocoder".  See:  https://guitarextended.wordpress.com/2012/04/04/polyphonic-synth-using-phase-vocoder-in-pure-data/

There is also an online service called "Heavy" that purports to translate Pure Data to portable C.  It is offered as a development solution for the Bela platform.

I haven't had time to try any of this either!!!  Although I did try out the Phase Vocoder in Pure Data (just run it on your PC or Mac).

Digital Larry
Want to quickly design your own effects patches for the Spin FV-1 DSP chip?
https://github.com/HolyCityAudio/SpinCAD-Designer

markseel

#114
Going to start adding effects and effects support modules while waiting for hardware.  Will add LFO, up/down sampling, parametric and graphic EQ's, TANH approximation, etc.  And eventually chorus, flanging, overdrive, etc.

Just added support for LFO's.  You can create as many LFO's as you want - limited only by memory and MIP's.
The LFO supports sine, half-sine, triangle, saw-up, and saw down (triangle and saw still need testing).

The sine mode uses a lookup table and 2nd order Lagrange interpolation.  Graphs show how clean the a 10 Hz sine at 48k Fs is.

Both the original lookup table (green) result and the interpolated result (blue) are plotted - you can see how jagged the non-interpolated version is and in the FFT plot the interpolated result doesn't have the harmonics from abrupt look-up table value changes.  Nice and smooth.

There's been other small changes to DSP support and changes will continue as the kit matures so be sure to pull updates if you're trying out the code :-)

Here's how to add some LFO's:

#include "efx_lfo.h"

static efx_lfo_t lfo1;

void thread_a1_initialize( void )
{
    // Initialize LFO: 10 Hz Sine wave at 48000 Fs
    int period_q30 = FQ(30,+10.0/48000.0); // Q30 fixed point value from floating point value
    efx_lfo_initialize( &lfo1, EFFECTS_LFO_TYPE_SINE, period_q30 );
}
void thread_a1_process( int* samples, const int* property, int ticks )
{
    // Get next LFO sample
    int sample = efx_lfo_process( &lfo1 );
}


Time and FFT plots of 10 Hz sine:


briandress

would this be something to make for an amp sim, cab sim, IR loader box that I could carry around in my pocket?

i am just a guitar nerd and really have not much knowledge about what you are all talking about in this thread. trying to read through but im getting confused lol

markseel

#116
This board is intended to be a small and powerful DSP and USB audio/MIDI for effects and cabsims.  It's just the DSP, DAC/ADC digital interfaces, and USB audio/MIDI interfaces - so sort of the central portion of a full MIDI controlled multi-effects / cabsim system.  My plans are to pair it with an audio board (with guitar input), put in a small case (2.5" x 2.5" x 1.0") and allow it to be loaded with effects/cabsims via USB and function as a DI/effects/cabsim chain in one small USB-powered box.  Pretty much like the other multi-effects units out there that support direct-input for recording, output for playback or jamming, effects control and multi-effects chains, and MIDI support.  Only this unit is smaller, simpler (only USB - which may be good for some folks bad for others :-) and lower latency - somewhere on the order of 1 msec or less for effects + cabsim, and <0.5 msec for just cabsim.  Hope to have final numbers soon.  If this goes well I'd like to kick-starter the building of more units to sell.  There's a software dev kit started for those who want to roll their own effects, experiment with DSP, or make high channel-count and/or high sample-rate audio interfaces.

markseel

#117
Board/PCB is being laid out (by the pro's, not me :-) - here's initial component placement.  Pretty tight - there's a lot of stuff in a small space.  The first rev of layout with full routing should be finished next week!



I'm designing the audio daughter board in parallel with the XMOS board layout.  The audio board will be using the CS4272 DAC/ADC.  Will post that layout soon.

pruttelherrie

Quote from: markseel on January 09, 2017, 05:11:04 PM
Hmmm, maybe if the MCU was listening to UART RX for MIDI data from XMOS (in case parameters were updated elsewhere e.g. USB) then I suppose the MCU could light up an LED that indicated that the pots were out of sync -- or something along those lines.  Or you could use rotary encoders and I2C digi-pots (and some kind of display?) but that adds work and complexity.  Not an easy one to solve I guess.

A while back I thought of putting two tiny led's below each knob with either one lit up indicating that the pot value was out of sync with the actual property value.  The left led indicating that the pot's value was too large and that knob needed to be turned counter-clockwise until it reached the correct value, and the other led doing the same except for the value being too small and requiring the knob to be turned clockwise.  LED's would turn of once the pot value matched.
Over the weekend I came across these:

http://www.ebay.com/itm/1-3-Annular-LED-Ring-Display-Green-Bars-Red-Dot-Rotary-Encoder-or-Clock-/251437248842?hash=item3a8ad3f14a:g:saQAAOxyrM5TJHaW

free electron

Or a DIY option i designed a while ago: I2C controlled LED ring:
http://www.diystompboxes.com/smfforum/index.php?topic=113822



Only 2 pins can drive up to 8 of such boards.