Buffered read for audio from SD card

Hello,

I'm working on an arduino project in which audio data are read from an SD and via I2C DAC interface they are sent finally to a speaker.
The audio has been generated to be 8KHz sampled, meaning that I'd need to send each package audio data every 125us.

The file is formatted like <1234> <5424> ... (unsigned int 4 digits).

Below is the portion of code that right now uses around 160us.

string receive = "";
char c = myFile.read();
while (c != ' ') { //TO READ A SINGLE VALUE
    receive += c;
    c = myFile.read();
}
int a = receive.toInt();
sendtoDAC(a) //function that send the single value to the DAC

Unfortunately the code is too slow and out of the speaker The file is reproduced so slowly to be impossible to understand it.

I read about a buffer reading of read(void *buf, uint16_t nbyte) but I dont understand how can I implemented and if it would be a faster way of reading.

Can you guys help me?

could you double buffer reading the SD card?

If I understand the snippet that you posted, the data on the card are written as ASCII strings. That is a very inefficient way to store audio data. You should have written the audio samples in binary. Each sample would then be exactly two bytes and would allow you to read, say, 512 bytes into a buffer and then you can write the 256 samples from there.

Which Arduino are you using?

Pete

Thank you guys I'll work in that direction and update you through the process. I'm using an arduino micro.

Horace what do you mean by double buffer? How can I implement it?

Nick

I converted the input files in binary unsigned 2 bytes. Then I wrote a simple program just to check if the binary numbers are correctly read from Arduino shown below.
I have some problem: in the serial I don't get what I expect to see, for example:

What I have in the SD: 0000010110110100 -> (1460)dec what I see in the serial: 11000000110000 -> (12336)dec

What I'm missing? Am I reading the buffer in the wrong way?

const int S = 512; 
  short buffer[S];
  int i;
  
  myFile.read(buffer,sizeof(buffer));
  
  while(myFile.available()!=0)
    for(i=0;i<S;i++)
        Serial.print(buffer[i],BIN);
        Serial.print(buffer[i],BIN);

Serial.print in BIN format doesn't print leading zeros, nor does it print a space or linefeed between each element of the buffer. You have no way of knowing where a number ends and the next begins.
11000000110000 could be 1 10000001 10000, or 11000000 1 100000 or even 11000000110000, etc.
This snippet:

const int S = 512;
  short buffer[S];
  int i;
 
  buffer[0] = 1460;
  buffer[1] = 12336;
 
    for(i=0;i<2;i++)
        Serial.print(buffer[i],BIN);

prints this:

1011011010011000000110000

Changing to println from print it prints this:

10110110100
11000000110000

which is still missing leading zeros but now you can reliably determine what each number is.

What I have in the SD: 0000010110110100

Prove it.

Pete

I still don't get the right results I suspect the problem might be the way I generate the input file. I used matlab to convert each number to its binary representation. Maybe it's seen as a text file from Arduino?.

I link a sample below.

Just to clarify here there are some of the binary stream I'm getting now

100001001000001
100010001000011
100011001000101
100100001000111
100101001001001
100110001001011

If you look closer you can see that it's actually counting..eliminating the MSB the LSB and the 7th bit we get something like:

0001  0000  
0010  0001
0011  0010
etc

Definitely not my input.

-Nick

output.txt (94.3 KB)

eliminating the MSB the LSB and the 7th bit

Why do that?

They are all 16 bit numbers which are only missing the high order zero. If you insert the missing high order zero, split it into bytes and convert the result as ASCII characters you get this:

01000010 01000001 BA
01000100 01000011 DC
01000110 01000101 FE
01001000 01000111 HG
01001010 01001001 JI
01001100 01001011 LK

The Arduino is a little-endian device, so this is the ASCII string "ABCDEFGHIJKL"

Pete