Buffering audio samples for DSP processing

Sub forums for various specialist XMOS applications. e.g. USB audio, motor control and robotics.
ghmfranken
Member
Posts: 14
Joined: Thu Sep 27, 2012 6:41 pm

Buffering audio samples for DSP processing

Post by ghmfranken »

Hi All,

I am new to XMOS development and looking for some help. I am working on an Audio project using the software reference design. For the task at hand I need to apply a DSP effect on the audio samples depending on the presence of (bit)markers in 20 consecutive samples. If present in these 20 consecutive samples in both channels I want to apply a DSP effect.

I managed to implement DSP processing with the help of the "Adding-DSP-to-the-USB-Audio-2.0-L1-Reference-Design(1.0)" guide. The problem with this implementation is however, that only 1 sample per channel is buffered. And for this project I need to analyze approximately 20 samples ahead. So I need some sort of buffer with at least 20 samples for both channels.

How to implement this type of buffer using the software reference design (with low latency)? And where to implement this (e.g. between decoupler and audio?). Am I on the right track by following the DSP route as described? Any help or tips are welcome.

Many thanks in advance!


bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

You can just build a circular buffer to store the samples. This should be very easy to implement. A circular buffer of size of 2^X (2, 4, 8, 16, 32, etc) is easiest. This will delay your output by sample rate * buffer.

Another possibility is the USB thread reads data as packets according to the properties of the interface. You might be able to process the data per packet, when the data is handed out of this thread.

I would have to look at the version of code you are working with to offer any more details. I am sure Ross could easily suggest something.
ghmfranken
Member
Posts: 14
Joined: Thu Sep 27, 2012 6:41 pm

Post by ghmfranken »

Thank you for your support. I like the idea of circular buffer for the reason of simplicity. The delay is the price I am willing to pay for that. I am building the project on the
USB-Audio-2.0-Software-Reference-Design(3.3)[11.2.0] version of the reference design.

To this post I attached the main.xc file in which the dsp thread is inserted between the decoupler and audio. Next to it, I attached dsp.xc which implements the dsp thread (no other changes yet to the reference design). I suspect the circular buffer needs to be implemented in the latter? Do you have a sample implementation?

Is this enough information for you to give more details? Thanks in advance!
You do not have the required permissions to view the files attached to this post.
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

After reviewing my code, double buffering is the simpler way to do a circular buffer. Also, the buffer can be any arbitrary size with double buffer. Adding another thread may or may not be the most effecient means of implementing filters. Here's some code snipets from one of my routines that implements a circular buffer:

Code: Select all

#define AUDIO_MAXFIRS (150)

{

    int sambufL[AUDIO_MAXFIRS * 2];	//Double buffer for circular access
    unsigned currentbuf = AUDIO_MAXFIRS - 1;	//Work backwords on buffer

    {
       //Read samples
       sambufL[currentbuf] = linL;
       sambufL[currentbuf + AUDIO_MAXFIRS] = linL;
    }
    {

       //Process samples sambufL[currentbuf] .. sambufL[currentbuf + AUDIO_MAXFIRS]
       //Output result

    if(currentbuf == 0)
    {
        currentbuf = AUDIO_MAXFIRS - 1;
    }
    else
    {
        currentbuf--;
    }

}
ghmfranken
Member
Posts: 14
Joined: Thu Sep 27, 2012 6:41 pm

Post by ghmfranken »

With your support I was able to make a circular buffer for outputting samples from the DSP to the Audio thread! It seems to work fine, except for the outputted sound that is now "weird". By "weird" I mean hollow/electrical (of course without applying any DSP effects). Any ideas of why this is happening? Attached is the code with my implementation of the circular buffer. Am I overlooking something obvious?
You do not have the required permissions to view the files attached to this post.
ghmfranken
Member
Posts: 14
Joined: Thu Sep 27, 2012 6:41 pm

Post by ghmfranken »

I re-examined my code carefully. The counting of the buffer position for input and output was all wrong! I corrected this in the attached file. The outputted sound is now how it should be.

The pattern of input/output of the buffer is as follows (number is the buffer position, working backwards):
in: 63, 62
in: 61, 60
...
in: 1, 0
... now the buffer is prefilled
out: 63, 62
in: 63, 62
out: 61, 60
in: 61, 60
....

The output buffer is always a full buffer cycle behind.

Thanks for all the support!
You do not have the required permissions to view the files attached to this post.
ghmfranken
Member
Posts: 14
Joined: Thu Sep 27, 2012 6:41 pm

Post by ghmfranken »

The only thing that seems not entirely right in my current implementation of the audio buffer is when the sample frequency is changing. The frequency is changed on the fly, while there are still samples in the buffer, that should have been outputted with the old sample frequency.

My idea is to first output all the samples in the buffer and only then change the frequency (in other words postpone the frequency change). Next to it, this approach also means that other frequency changes in the mean time should not be accepted.

But I can not find any usable online documentation on how to postpone the frequency change as requested by the decoupler. Your help on this is very much appreciated!

Code: Select all

if (testct(c_aud_in))
{
	// sample frequency change.
	inct(c_aud_in);
	sampFreq = inuint(c_aud_in);

	// Send SF change to audio thread.
	outct(c_aud_out, XS1_CT_END);
	outuint(c_aud_out, sampFreq);

	// wait for handshake and send back
	chkct(c_aud_out, XS1_CT_END);
	outct(c_aud_in, XS1_CT_END);
}else
{
	// Normal audio loop.
	// Get info from host indicating we're ready and tell the audio
	// thread data is on its way.
	inuint(c_aud_in);
	outuint(c_aud_out, 0);
	giveSamplesToDevice(c_aud_out);
	getSamplesFromDevice(c_aud_out);
	giveSamplesToHost(c_aud_in);
	getSamplesFromHost(c_aud_in);

	if (countBuff == NUM_USB_CHAN_OUT-1)
	{
		// Cycle through the output buffer
		countBuff = DSP_BUFF_SIZE_OUT-1;

		// The buffer is pre-filled, so come out of underflow condition
		outDSPUnderflow = 0;
	}
	else
	{
		countBuff -= NUM_USB_CHAN_OUT;
	}
}
User avatar
Ross
XCore Expert
Posts: 966
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

I would wipe the buffer on a sample rate - reset to 0's or similar, or just reset the read/write pointers.

Do it before the line:
outct(c_aud_in, XS1_CT_END);

i.e. before you handshake back to decouple informing it you have changed freq correctly.

Decouple (and the rest of the system) will hold off the host until you are done and have sent this handshake back.
Bayanaa
Active Member
Posts: 33
Joined: Fri Feb 07, 2014 3:03 pm

Post by Bayanaa »

I have implemented circularbuffer (which is different implementation that mentioned above post) per each channel. But problem is when buffer length is greater than one, MC board does not return audio samples to host (PC).
If I use only one circular buffer (with length 2) and apply it to 1 channel it returns audio samples to host. In this manner when I increase the buffer size more than 2 I meet the above problem.

I think there is a limit.

1. What is the problem with this?
2. Does reason include buffer port, channel declaration ( streaming chan, chan)?
3. Is it related to hardware constraint?
4. MIPs limit?