Adding a BiQuad filter to the Long Delay demo

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
johned
XCore Addict
Posts: 185
Joined: Tue Mar 26, 2013 12:10 pm

Post by johned »

Hi Liam,

Our device is fixed point so you need to multiply numbers in that format. For example, you multiply two 32 bit numbers and get a 64 bit (long long) result. You then need to right shift the result to get back to the required fixed point scaling.

You may find this discussion useful : http://en.wikipedia.org/wiki/Fixed-point_arithmetic.

All the best,
John


markb
Member++
Posts: 18
Joined: Tue Oct 08, 2013 1:53 pm

Post by markb »

Hi Liam,

On the xCORE, integer multiplies, additions and shift operations can be done in a minimum of 1 processor cycle, (but may take slightly longer). Unfortunately, using integer divide or floating point arithmetic instructions can take up to 100 cycles. That is why the code examples do not use these instructions.

So by using floating point, you have 'broken the timing', this means that the processing can NOT keep pace with the audio data stream (48 KHz).

Most integer division and floating point arithmetic can be converted into multiplies and shifts.
e.g. Consider
A = B * 0.84 (floating point)

Convert 0.84 to 215/256. Now

A = B * (215/256) = (215 * B) / 256 = (215 * B) /2^8

Now we can use a right bit-shift to do the divide

A = (215 * B) >> 8

The above expression truncates the result. If rounding is required use

A = (215 * B + 128) >> 8

If more accuracy is required use a larger power-of-2
e.g.
0.84 = 27525/32768 = 27525/2^15

NB The fact that the program works when you use divide-by-2, is probably due to a clever compiler that substitutes the integer-division for a right bit-shift of one.

Regarding the 8000/16000/24000/32000 delay times:
This is a weird one! If you do NOT include the BiQuad filter, does the problem go away?

Cheers Mark_B
User avatar
millenliam
Member++
Posts: 23
Joined: Fri Feb 14, 2014 2:51 pm

Post by millenliam »

Thank you both for your answers.
I'll see if I can get this working today.

Mark, no the weird tone issue still happens even if I'm just modifying the sliceKIT long delay demo so that there is a delay feedback line, without adding anything else like filtering. Would it help if I posted a .wav file example or even the xtimecomposer workspace?

Thanks,
Liam.
User avatar
millenliam
Member++
Posts: 23
Joined: Fri Feb 14, 2014 2:51 pm

Post by millenliam »

Hey Mark

I've managed to get rid of the unwanted tone with a slightly modified version of your example:

Code: Select all

//when applying a feedback value to the delayed signal, this needs to be done in sections as seen below, otherwise no delay appears.

//this needs to be a 64 bit value, if 32 bit there's no delay as we're running out of bits
S64_T delayed_chan; 
		
delayed_chan = delayed_set_s.samps[chan_cnt];
		
delayed_chan *= 215; //the number here is the feedback value as a division of 256
		
delayed_chan /= 256;
		
cntrl_gs.src_set.samps[chan_cnt] = cntrl_gs.src_set.samps[chan_cnt] + delayed_chan;
A couple of notes:
- Needed to change the delayed channels to be a 64 bit value
- Had to work out the feedback value over several lines
- I used a division instead of bit shifting, as the audio data is a signed integer and as far as I understand shifting would interfere with the sign bit.

The (hopefully) very last problem I'm now left with is that with the troublesome delay time sample values (8000, 16000 etc) I'm getting periodic clicks/pops if there is any noticeable audio being processed.
Any idea what this could be?
Something to do with when the SDRAM buffer read/write positions loop back to the beginning?
I'd be happy to post a demo workspace once I've tidied up my code if that would help.
markb
Member++
Posts: 18
Joined: Tue Oct 08, 2013 1:53 pm

Post by markb »

Hi Liam,

Integer Division
----------------------
Firstly, if you use '/ 256', you are relying on the compiler to spot that you are using a power-of-2.
I'm not sure this assumption is always true.

Secondly, you are correct to worry about shifting the sign-bit, as the operation of the bit-shift operators is implementation defined.
However, nearly all compilers (including XC) have the following behaviour ...

1) If the number is declared signed, the bit_shifts maintains the sign of the number (sign extended)
2) If the number is declared unsigned, all bits are shifted (as there is no sign bit!-)

In general, if you are unsure about how an operator has been implemented, write yourself a small test program.

Another advantage of using bit_shift division is that the numbers are rounded towards 'minus infinity', whereas integer division rounds to zero. So special case code for positive/negative numbers is not required.

n.4000 Sample-Delay Problem
---------------------------------------------
I will try to reproduce the problem in our long-delay demo.
However, I have lost track of exactly what state your code is in.

Currently I am making the following assumptions ...
1) You have a SliceKit with Audio-Slice and SDRAM-slice
2) You have stereo inputs and outputs @ 48 kHz
3) You are delaying both left and right channels
4) You are varying the delay of both channels by the same amount

Cheers Mark_B
markb
Member++
Posts: 18
Joined: Tue Oct 08, 2013 1:53 pm

Post by markb »

Hi Liam,

The good news (for you) is that I have managed to reproduce the 'n.4000' issue with the long-delay demo S/W.
The bad news (for me) is I now have to figure out why this is happening!-(

Cheers Mark_B
User avatar
millenliam
Member++
Posts: 23
Joined: Fri Feb 14, 2014 2:51 pm

Post by millenliam »

Hey Mark,

Thanks for the compiler behaviour info.
I'll try going back to bit shifting then.

RE: Sample delay problem…

Yes, all four of those points are true.

Other changes that need to be made to the long-delay demo to replicate my code:
1. NUM_APP_CHANS = 2

2. NUM_DELAY_TAPS = 2

3. I'm using us_delays[NUM_DELAY_TAPS] to store sample number instead of microseconds, and within sdram_delay.c -> update_common_delays() I'm just saying req_delay = us_delays[NUM_DELAY_TAPS].

4. In dsp_sdram_delay.xc -> init_parameters() set both us_delays[] values to 32000 (or another whole division of 8000).

5. Change dsp_sdram_delay.xc -> dsp_sdram_delay() to:

Code: Select all

        CNTRL_SDRAM_S cntrl_gs; // Structure containing data to control SDRAM buffering
	DELAY_PARAM_S delay_param_s; // Structure containing SDRAM-Delay configuration data
	CHAN_SET_S delayed_set_s;	// Structure containing processed/delayed sample-set
	CHAN_SET_S out_set_s;	// Structure containing intermediate audio sample-set, used for cross-fade
	S32_T chan_cnt; // Channel counter

	init_sdram_buffers( cntrl_gs ); // Initialise buffers for SDRAM access
	init_parameters( delay_param_s ); // Initialise delay parameters

	// initialise samples buffers
	for (chan_cnt = 0; chan_cnt < NUM_DELAY_CHANS; chan_cnt++)
	{
		out_set_s.samps[chan_cnt] = 0;
		delayed_set_s.samps[chan_cnt] = 0;
	}

	// Configure Delay-line parameters ...
	config_sdram_delay( delay_param_s );

	// Loop forever
	while(1)
	{
		// Send/Receive samples over Audio coar channel
#pragma loop unroll
		for (chan_cnt = 0; chan_cnt < NUM_DELAY_CHANS; chan_cnt++)
		{
			c_dsp_aud :> cntrl_gs.src_set.samps[chan_cnt];
			c_dsp_aud <: out_set_s.samps[chan_cnt];
		}

		// Do DSP Processing ...
		process_all_chans( cntrl_gs ,delay_param_s , delayed_set_s, out_set_s ,NUM_DELAY_CHANS );

		buffer_check( cntrl_gs ,c_dsp_sdram ); // Check if any buffer I/O required

	} // while(1)
6. Change the process_all_chans() function to:

Code: Select all

void process_all_chans( // Do DSP effect processing
	CNTRL_SDRAM_S &cntrl_gs, // Reference to structure containing data to control SDRAM buffering
	DELAY_PARAM_S &cur_param_s, // Reference to structure containing delay-line parameters
	CHAN_SET_S &delayed_set_s,	// Structure containing mixed output sample-set
	CHAN_SET_S &out_set_s,	// Structure containing mixed output sample-set
	S32_T num_chans	// Number of channels in set
)
{
		CHAN_SET_S dry_set_s; //Structure containing unproccessed sample set
		S32_T chan_cnt; // Channel counter

		//reduce input signal to prevent overloading/distortion when mixing afterwards
		for (chan_cnt = 0; chan_cnt < num_chans; chan_cnt++)
			cntrl_gs.src_set.samps[chan_cnt] /= 2;

		//Set the dry samples to be the input samples...
		dry_set_s = cntrl_gs.src_set;

		//Add the processed samples mutiplied by a feedback value to the input samples...
		for (chan_cnt = 0; chan_cnt < num_chans; chan_cnt++)
		{
			//when applying a feedback value to the delayed signal, this needs to be done in sections as seen below, otherwise no delay appears.
			S64_T delayed_chan = delayed_set_s.samps[chan_cnt]; //this needs to be a 64 bit value, if 32 bit there's no delay as we're running out of bits
			delayed_chan *= 90; //90 here is the feedback value as a division by 128
			delayed_chan /= 128;
			cntrl_gs.src_set.samps[chan_cnt] = cntrl_gs.src_set.samps[chan_cnt] + delayed_chan;

			//To hear the weird tone sound, comment out the above 4 lines, uncomment the below line,
			//and set both the delay time sample values to be either the exact same or atleast one of
			//them set to one of the troublsome values (8000, 16000, 24000, 32000 etc...)
			//cntrl_gs.src_set.samps[chan_cnt] = cntrl_gs.src_set.samps[chan_cnt] + (delayed_set_s.samps[chan_cnt] * 0.7); // 0.7 here is the feedback value;

		}

		// Get next delayed output sets, and delay current input set
		use_sdram_delay( cntrl_gs );

		//set the delayed samples
		for(chan_cnt = 0; chan_cnt < num_chans; chan_cnt++)
		{
			//Here we use chan_cnt to interate through both the channels and delay lines,
			//applying delay line 0 to channel 0 (left) and line 1 to channel 1 (right)
			delayed_set_s.samps[chan_cnt] = cntrl_gs.delay_sets[chan_cnt].samps[chan_cnt];

		} // for chan_cnt

		//set the output samples
		for (chan_cnt = 0; chan_cnt < NUM_DELAY_CHANS; chan_cnt++)
		{
			out_set_s.samps[chan_cnt] = dry_set_s.samps[chan_cnt] + cntrl_gs.src_set.samps[chan_cnt];
		}

#ifdef MB
#endif //MB~
} // process_all_chans
/******************************************************************************/
I think that's all the changes!

Thanks,
Liam.
User avatar
millenliam
Member++
Posts: 23
Joined: Fri Feb 14, 2014 2:51 pm

Post by millenliam »

You must have posted your last message while I was putting together my last one.

Glad you've managed to replicate the problem.
Hope it doesn't take you too long to find the issue!

Thanks,
Liam
markb
Member++
Posts: 18
Joined: Tue Oct 08, 2013 1:53 pm

Post by markb »

Hi Liam,

I have a fix!-)

The problem is a known issue with the sc_sdram_burst repository.

Since my code was published, sc_sdram_burst has been updated with the fix.
However, the XMOS Makefile system does NOT pull in the latest code automatically,
so you were still using the old version.

Here is the fix:
Go to the directory containing sw_audio_effects and sc_sdram_burst.

Move the old sc_sdram_burst somewhere safe.

Pull in a new copy of sc_sdram_burst from GitHub
The release 1.1.0rc0 or later.

Now go back to your application and do a CLEAN build.



Cheers Mark_B
User avatar
millenliam
Member++
Posts: 23
Joined: Fri Feb 14, 2014 2:51 pm

Post by millenliam »

Hey Mark,

Glad you've found the fix.

I'm unable to find the sw_audio_effects and sc_sdram_burst directories on my computer, though from looking at the GitHub repos I recognise some of the folders and files in there.

I'm currently using xtimecomposer version 12.2.0, as I was told to use this version with the DJ Kit - something to do with the USB code not being updated.

Should I be using the latest version for use with the sliceKIT?

Thanks,
Liam.