8 years, 5 months ago.

Getting doubles of USB audio packages

With the folllowing code it looks like the USB packages comes two times. Can the usb audio-driver in Win7 be wrong or where to start looking for error?

/media/uploads/tvillingett/image.png

// USBAudio speaker example

#include "mbed.h"
#include "USBAudio.h"

// frequency: 48 kHz
#define FREQ 48000

// 1 channel: mono
#define NB_CHA 1

// length of an audio packet: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1
#define AUDIO_LENGTH_PACKET (FREQ / 1000) * 2 * NB_CHA

// USBAudio (we just use audio packets received, we don't send audio packets to the computer in this example)
USBAudio audio(FREQ, NB_CHA, 8000, 1, 0x7180, 0x7500);

// speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker
AnalogOut DAC(DAC0_OUT);
DigitalOut gpo(PTC1);

// ticker to send data to the speaker at the good frequency
Ticker tic;

// buffer where will be store one audio packet (LENGTH_AUDIO_PACKET/2 because we are storing int16 and not uint8)
int16_t buf[AUDIO_LENGTH_PACKET/2];

// show if an audio packet is available
volatile bool available = false;

// index of the value which will be send to the speaker
int index_buf = 0;

// previous value sent to the speaker
uint16_t p_val = 0;

// function executed each 1/FREQ s
void tic_handler()
{
    float speaker_value;

    if (available) {
            speaker_value = (float)(buf[index_buf]); //convert 2 bytes in float
            speaker_value += 32768.0; // speaker_value between 0 and 65535
        //temp = audio.getVolume(); // get current volume (dont use)
        index_buf++; // as two bytes has been read, we move the index of two bytes

        if (index_buf == AUDIO_LENGTH_PACKET / 2 ) { // if we have read all the buffer, no more data available
            index_buf = 0;
            available = false;
        }
    } else {
        speaker_value = p_val; // keep last value if no new
    }
    p_val = speaker_value;
    DAC.write_u16((uint16_t)speaker_value); // send value to the speaker
}

int main()
{
    wait_ms(3); // wait for 3 packages?
    tic.attach_us(tic_handler, 1000000.0/(float)FREQ + 1 ); // attach a function executed each 1/FREQ s

    while (1) {
        static int n = true;
       
        audio.read((uint8_t *)buf);
        //if (n == true)
            available = true;
        n = !n; // toggle
        gpo = n; // out on PTC1
    }
}

Question relating to:

Please edit to use <<code>> not <code>, the preview button is there for a reason.

posted by Andy A 27 Oct 2015

Ok, edited that.

posted by Anders Eriksson 27 Oct 2015

Thanks.

posted by Andy A 27 Oct 2015

1 Answer

8 years, 5 months ago.

Your main while loop is reading one audio packet per ms. Once the packet is received it sets available to true and starts receiving the next packet into the buffer.

At the same time your tic_handler is outputting the data in the buffer and once it finishes it sets available to false.

You have a race condition, if things happen in the wrong order you skip every other packet of data:

Packet 1 arrives.

Available becomes true

tic_handler starts outputting data.

1ms later packet 2 finishes arriving

Available is set to true.

tic_handler finishes outputting the first packet and sets available to false.

available is false so nothing further is output.

packet 3 arrives.

Available is set to true.

tic_handler starts outputting data again.

You also have the bug that you are reading out of the same buffer as data is being written into, this could give you some nasty audio glitches.

You can fix both problems by having two buffers, while bufferA is being received you output from bufferB, once buffer A is complete you switch and start outputting from A while receiving into B. If you use pointers to the buffers you can avoid having if statements on every buffer read so there is only a minimal performance hit.

By switching buffers once the frame has been received rather than once you've finished outputting a full buffer you make skip the odd sample but that's better than the alternative of gradually falling more and more behind.

And on a performance point, when adding the offset to the output value it would be far more efficient to cast to an int32 add 32768 and cast to a uint16 than the current method of converting to a float and back.

I don't have the means to test this but I think this should do what you want.

// USBAudio speaker example

#include "mbed.h"
#include "USBAudio.h"

// frequency: 48 kHz
#define FREQ 48000

// 1 channel: mono
#define NB_CHA 1

// length of an audio packet: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1
#define AUDIO_LENGTH_PACKET (FREQ / 1000) * 2 * NB_CHA

// USBAudio (we just use audio packets received, we don't send audio packets to the computer in this example)
USBAudio audio(FREQ, NB_CHA, 8000, 1, 0x7180, 0x7500);

// speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker
AnalogOut DAC(DAC0_OUT);
DigitalOut gpo(PTC1);

// ticker to send data to the speaker at the good frequency
Ticker tic;

// buffer where will be store one audio packet (LENGTH_AUDIO_PACKET/2 because we are storing int16 and not uint8)
int16_t bufA[AUDIO_LENGTH_PACKET/2];
int16_t bufB[AUDIO_LENGTH_PACKET/2];

// show if an audio packet is available
volatile bool available = false;

int16_t *rxBuffer = NULL;
int16_t *txBuffer = NULL;

// index of the value which will be send to the speaker
volatile int index_buf = 0;

// function executed each 1/FREQ s
void tic_handler()
{
// previous value sent to the speaker
    static uint16_t p_val = 32768; // start mid range.

    int32_t speaker_value;

    if (txBuffer) {
        speaker_value = (int32_t)(txBuffer[index_buf]);
        speaker_value += 32768; // speaker_value between 0 and 65535
        //temp = audio.getVolume(); // get current volume (dont use)
        index_buf++; // as two bytes has been read, we move the index of two bytes

        if (index_buf == AUDIO_LENGTH_PACKET / 2 ) { // if we have read all the buffer, no more data available
            txBuffer = NULL;
        }
        p_val = (uint16_t) speaker_value;
    }
    DAC.write_u16(p_val); // send value to the speaker
}


int main()
{
    rxBuffer = bufA;
    wait_ms(3); // wait for 3 packages?
    tic.attach_us(tic_handler, 1000000.0/(float)FREQ + 1 ); // attach a function executed each 1/FREQ s

    int n = true;

    while (1) {
        audio.read((uint8_t *)rxBuffer);
        __disable_irq();  // switch buffers - to be safe make sure we don't get interrupted while doing this.
        index_buf = 0;
        txBuffer = rxBuffer;
        __enable_irq();
        if (rxBuffer == bufA) // switch to the next buffer
            rxBuffer = bufB;
        else
            rxBuffer = bufA;

        n = !n; // toggle
        gpo = n; // out on PTC1
    }
}
posted by Andy A 27 Oct 2015

Thanks, but unfortunately there is still problem with same sample twice. I think this image shows it better: /media/uploads/tvillingett/image_double_sample.png

posted by Anders Eriksson 28 Oct 2015