extra RAM on Arduino Due

I understand you going to add RAM but I am curious why reading from the SD fails.

I looked at the Groovuino library and was astounded that only one file handle was uses and files are opened and closed while playing sound.

Opening a file is very slow so I would have used an array of file handles and opened all the files before playing sound. A file handle only requires about 32 bytes. Rewinding or seeking to the start of a file requires no SD access for an open file.

Yes, that's what I'm doing, and it works, I can play 4 files in the same time, but I want more !

In fact I have 2 classes : sampler.h and samplerl.h The sampler.h open the file each time it's played. It is for using different samples for each pattern. So I'm unlimited by the number of wave files to use, but it's not optimized. I can do only 2 voices polyphony. The samplerl.h opens one file for each pattern, then uses seek function to go to the beginning. Better time access, but I can only use one wave file by pattern, and reach 4 voices polyphony.

What do you call the "file handle"? It's the SdFile object ?

The SdFile object acts like a file handle in other systems. It contains information from the directory entry and cluster information for the current position. A number of blocks must be read from file structures to open a file and seek to a position.

If I wanted to optimize reads from a large number of files I would use raw SD reads.

When you copy files to freshly formatted SD, the files are contiguous. SdFat has a function to determine if a file is contiguous and where the blocks are located.

bool SdBaseFile::contiguousRange ( uint32_t * bgnBlock,
uint32_t * endBlock
)

Check for contiguous file and return its raw block range.

Parameters:
[out] bgnBlock the first block address for the file.
[out] endBlock the last block address for the file.

Returns:
The value one, true, is returned for success and the value zero, false, is returned for failure. Reasons for failure include file is not contiguous, file has zero length or an I/O error occurred.

I would open each file and find its location with the the above function.

I would then use either the Sd2Card single block read function:

bool 	Sd2Card::readBlock (uint32_t block, uint8_t *dst);

Or the Sd2Card multi-block sequence:

bool 	Sd2Card::readStart (uint32_t blockNumber);  // set start block for a multiple block read sequence.

bool Sd2Card::readData (uint8_t *  	dst);  // Read one data block in a multiple block read sequence.

bool Sd2Card::readStop ();  // End a read multiple blocks sequence.

SD cards do look ahead for multiple block reads so are very efficient in this mode.

Hi Fat16,
Thanks for the tip. It’s been 2 days since I try to read the files as you say, but I must do something wrong.

Here is the code I use :

#include <arduino.h>
#include <SdFat.h>

SdFat sd;
Sd2Card *card = sd.card();

const int chipSelect = 10;
const int bufsize = 512;

const char* samplefile[]= {"kick1.wav", "hithat1.wav", "snare1.wav", "snare2.wav"};

uint8_t buf[bufsize];

SdFile myFile;

uint32_t bgnBlock;
uint32_t posBlock;
uint32_t endBlock;

void setup() 
{ 
  Serial.begin(9600);   
  sd.begin(chipSelect, SPI_FULL_SPEED);
  
  myFile.open(samplefile[0], O_READ);
 
  posBlock = bgnBlock;

  //card->readBlock(posBlock,buf);
  card->readStart(posBlock);
  card->readData(buf);

  for(int i=0; i<10; i+=1)
  {
      Serial.print("block : ");
      Serial.println(posBlock);
      //card->readBlock(posBlock,buf);
      card->readData(buf);

      for(int j=0; j<255; j+=1)
      {
	   tes[i] = ((int16_t)buf[1+2*j]<<8) + (int16_t)buf[2*j];
	   Serial.println(tes[j]);
       }
       posBlock+=1;
       
     }
     card->readStop();
}

I’ve tried with both ReadBlock and ReadStart / Data / Stop
The first block is always ok, but after that, it seems that only the first byte of the buffer is filled… I don’t understand nothing at all.
Can you see if I’m doing something wrong in my code ?

Thanks

@ Pito and Grumpy_Mike : I received my RAM and other components. It will be hard to solder the RAM as it’s not DIP socket, but I will find a way. I keep you in touch.

It will be hard to solder the RAM as it's not DIP socket,

Sorry I though you knew that. Look for an adapter board, the ones on ebay are often 10 times cheaper than those on Farnell.

Here is a sketch that will read a file using raw reads.

I tested it with about a five MB file on a 1 GB ATP industrial SD.

The result was a read speed of about 4.5 MB/sec on Due.

blocks: 9765
micros: 1094559
MB/sec: 4.57

#include <SdFat.h>
SdFat sd;
SdFile file;
static const uint8_t SD_CS = SS;
uint32_t bgnBlock;
uint32_t endBlock;
uint8_t buf[512];

void setup() {
  Serial.begin(9600);
  if (!sd.begin(SD_CS) || !file.open("TEST.WAV", O_READ)) {
      Serial.println("begin/open");
      while(1);
  }
  if (!file.contiguousRange(&bgnBlock, &endBlock)) {
    Serial.println("not contiguous");
    while(1);
  }
  // count of blocks in file;
  uint32_t n = (file.fileSize() + 511)/512;
  // read start time
  uint32_t t0 = micros();
  
  // address of first block
  sd.card()->readStart(bgnBlock);

  for (uint32_t i = 0; i < n; i++) {
    if (!sd.card()->readData(buf)) {
      Serial.println("readBlock");
      while(1);
    }
  }
  sd.card()->readStop();
  uint32_t t = micros() - t0;
  Serial.print("blocks: ");
  Serial.println(n);
  Serial.print("micros: ");
  Serial.println(t);
  Serial.print("MB/sec: ");
  Serial.println(512.0*n/t);
}
void loop() {}

Edit: I did some tests with four and eight block reads to get the time to read a chunk of a file. These are using the industrial ATP card so will be faster than some consumer cards.

blocks: 4
micros: 574
MB/sec: 3.57

blocks: 8
micros: 1022
MB/sec: 4.01

So you can read a 2048 byte chunk in 574 usec and a 4096 byte chunk in 1022 usec.

Thanks, it works. With the myFile.read() function, I could load the wave data in any data type. With the readBlock function I get the wave data in a uint8_t[512] buffer. What I need is a int16_t[1024] buffer (wave format is 16 bit signed integer, and I need 1024 samples).

I've tried to load a uint8_t[4][512] buffer (calling 4 times the readBlock), then make some computations to load it into the int16_t[1024] buffer, but it takes too much cpu time, and I have to instanciate 2 buffers instead of one. So the performances are lower then myFile.read() function.

My intuition tells me to use pointers, to directly load a uint8_t[2048] buffer, and read it as it was an int16_t[1024], but I didn't find the way to do it. It's sure it has already been done (in SD wave players for exemple), but I didn't find anything on the subject.

Edit : I found that SdFatLib used the operator "reinterpret_cast", but I don't know if it can be used on a whole array.

Any idea ?

Thanks

Here is a function that will read a file chunk into any type destination.

#include <SdFat.h>
SdFat sd;
SdFile file;
const uint8_t SD_CS = SS;
uint32_t bgnBlock;
uint32_t endBlock;

uint16_t wave[1024];
//--------------------------------------------------------
bool readChunk(void* buf, uint32_t startBlock, uint16_t blockCount) {
  uint8_t* dst = (uint8_t*)buf;
  if (!sd.card()->readStart(startBlock)) return false;
  for (uint16_t i = 0; i < blockCount; i++) {
    if (!sd.card()->readData(dst + i*512L)) return false;
  }
  return sd.card()->readStop();
}
//---------------------------------------------------------
void setup() {
  Serial.begin(9600);
  if (!sd.begin(SD_CS) || !file.open("TEST.WAV", O_READ)) {
      Serial.println("begin/open");
      while(1);
  }
  if (!file.contiguousRange(&bgnBlock, &endBlock)) {
    Serial.println("not contiguous");
    while(1);
  }
  uint16_t n = 4;  
  uint32_t t0 = micros();

  if (!readChunk(wave, bgnBlock, n)) {
    Serial.println("readChunk");
    while(1);
  }
  uint32_t t = micros() - t0;
  Serial.print("blocks: ");
  Serial.println(n);
  Serial.print("micros: ");
  Serial.println(t);
  Serial.print("MB/sec: ");
  Serial.println(512.0*n/t);
}
void loop() {}

Here is timing for reading a 1024 element array of uint16_t.

blocks: 4
micros: 576
MB/sec: 3.56

Great great thanks ! Now I can manage more than 6 voices of polyphony. I couldn't even reach the limits. This Due is very surprising ! Your function is really faster than the read() function.

I will update my library with this code.

I don't need RAM anymore, but as I received it, I will make the experiments anyway.

Thanks again to all.