How to speed up SD card read for DAC playback

Hey guys,

Right now I have a program that reads a CSV one line at a time, parses the data, and then outputs the data to a multichannel DAC. Its working but its not great. I would like to speed it up. I have heard of something called a ring buffer? Could someone tell me how to implement that? Will using a buffer cause the progam to stall while it awaits to fill the buffer once it has emptied? This is undesirable in my program, I need to have a steady output with no blips or stalls. Any advice would be excellent!

Also if anyone thinks the code below is really bad please let me know how to improve it! I took most of it from one of the sdfat library examples, but I would bet the code that I have added is certainly not optimised! Thanks!

void playBack() {
  
  char line[18];//buffer to store line grabbed from sd card
                //data will look like this "FFFF,FFBB,CC2E"
  char line1[5];//buffers to store the parsed data
  char line2[5];
  char line3[5];
  int one=0;
  int two=0;
  int three=0;
  uint16_t red=0;
  uint16_t yellow=0;
  uint16_t blue=0;
  int n;
  
  // open test file
  SdFile rdfile("HEX.csv", O_READ);

  // check for open error
  if (!rdfile.isOpen()) {
    Serial.println("error opening file");
  }

  // read lines from the file
  while ((n = rdfile.fgets(line, sizeof(line))) > 0) {
    if (line[n - 1] == '\n') {

    strcpy(line1, strtok(line , ", "));//parse the line buffer using the delimiters
    strcpy(line2, strtok(NULL, ", "));
    strcpy(line3 , strtok(NULL, ", "));

   one=strtol(line1, NULL, 16);//converts the ascii represented HEX values into long int.
   two=strtol(line2, NULL, 16);
   three=strtol(line3, NULL, 16);

   lineOne=(uint16_t)one;//converts long into uint16 so it can go straight to dac write function
   lineTwo=(uint16_t)one;
   lineThree=(uint16_t)one;

   dataWrite(writeToInputRegisterN, channel1, lineOne, suffix);//outputs data to DAC
   dataWrite(writeToInputRegisterN, channel2, lineTwo, suffix);
   dataWrite(writeToInputRegisterN, channel3, lineThree, suffix);
   digitalWriteDirect(ldacPin,0);
   digitalWriteDirect(ldacPin,1);
    
 
   //Serial.print(lineOne);Serial.print("   "); Serial.print(lineTwo);Serial.print("   "); Serial.print(lineThree);Serial.println("   ");
   //delay(500);
    } 
  }
}

This is not your complete code but just an excerpt.

On what hardware is that code running? What speed do you expect? Is the CSV format fixed or can you change that to a binary format that doesn't need that much interpretation? Copying strings is not a very fast way to parse input...

This is very similar to your earlier post about outputting a decimal csv to DACs.

As before don't complicate things until you are really sure you have a problem. Why would a ring buffer make your code faster?

Below is some code that will parse the example line you gave pretty quick. It is fast because it does not copy characters about, it does not call functions, it does not increment counters or pointers, it does not have loops (loops require counters to be incremented and tested).

If the lines are fixed length and the numeric fields contain only hex digits, then you can go straight to the data.

The output is given below but you can execute the code online at;

#include <iostream>

using namespace std;

int main()
{
    uint16_t red,yellow,blue;
    
    // Assume;
    //      only upper case hex digits are used
    //      each number is always represented by 4 hex digits
    //      lines are always fixed lenth
    
    char line[]="FFFF,FFBB,CC2E\n";// parse this line given by OP

    // Show what a line looks like using character positions for the hex digits.
   printf("%c%c%c%c,%c%c%c%c,%c%c%c%c\n",line[0],line[1],line[2],line[3],line[5],line[6],line[7],line[8],line[10],line[11],line[12],line[13]);
   
   // Parse red
   if(line[0]<='9')
   {
       red=line[0]-'0';
   }
   else
   {
       red=line[0]-'7';
   }
   if(line[1]<='9')
   {
       red=red*16+line[1]-'0';
   }
   else
   {
       red=red*16+line[1]-'7';
   }
   if(line[2]<='9')
   {
       red=red*16+line[2]-'0';
   }
   else
   {
       red=red*16+line[2]-'7';
   }
   if(line[3]<='9')
   {
       red=red*16+line[3]-'0';
   }
   else
   {
       red=red*16+line[3]-'7';
   }
   
   // Parse yellow
   if(line[5]<='9')
   {
       yellow=line[5]-'0';
   }
   else
   {
       yellow=line[5]-'7';
   }
   if(line[6]<='9')
   {
       yellow=yellow*16+line[6]-'0';
   }
   else
   {
       yellow=yellow*16+line[6]-'7';
   }
   if(line[7]<='9')
   {
       yellow=yellow*16+line[7]-'0';
   }
   else
   {
       yellow=yellow*16+line[7]-'7';
   }
   if(line[8]<='9')
   {
       yellow=yellow*16+line[8]-'0';
   }
   else
   {
       yellow=yellow*16+line[8]-'7';
   }
   
   // Parse blue
   if(line[10]<='9')
   {
       blue=line[10]-'0';
   }
   else
   {
       blue=line[10]-'7';
   }
   if(line[11]<='9')
   {
       blue=blue*16+line[11]-'0';
   }
   else
   {
       blue=blue*16+line[11]-'7';
   }
   if(line[12]<='9')
   {
       blue=blue*16+line[12]-'0';
   }
   else
   {
       blue=blue*16+line[12]-'7';
   }
   if(line[13]<='9')
   {
       blue=blue*16+line[13]-'0';
   }
   else
   {
       blue=blue*16+line[13]-'7';
   }
   
   // Check the converted values match the input
   printf("%X %X %X\n",red,yellow,blue);
   
   return 0;
}
$g++ -o main *.cpp
$main
FFFF,FFBB,CC2E
FFFF FFBB CC2E

Does the device only have to play one particular HEX or is it supposed to play many different HEX depending on some situation?
How big is each hex in terms of total samples? If that is not too big, you should read it all into the memory say using MEGA (8KB memory) or Bobuino (16KB memory) or even using the quad ram module for MEGA from rugged circuits to get 512KB of memory (paged but they have code to help switching the pages). This way on booting you load all necessary HEX into memory and play away!

http://www.crossroadsfencing.com/BobuinoRev17/

Thank you very much for the help guys.

Ardly I really appreciate your help, I will be back working on this project at the end of this week, I will try your code and let you know how it works out. Thanks again.