Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 16 of 16

Thread: Feasibility? Looper 8 Tracks Mono with storage onto SD-card

  1. #1
    Senior Member
    Join Date
    Dec 2017
    Posts
    119

    Feasibility? Looper 8 Tracks Mono with storage onto SD-card

    Hi,
    it would be nice to get some feedback about feasibility with Teensy4.1 of the following idea:

    I would like to build a looper, which shall have 8 mono tracks and a song length of up to 5 Minutes.
    I want to store 32bits per sample.
    As storage an SD-card shall be used.
    To deal with wear of the SD-card, the data shall have parity (hence 32bits per sample).
    Each file shall hold all 8 tracks in parallel.
    Only one track (or 2 tracks for stereo) is recorded at a time, all other tracks are just copied.
    Mixer only for output of all tracks with stereo panning.
    No overdubbing
    Data shall be written in parallel to two redundant files.
    Data shall be read from the two redundant files to be able to discard bad data.

    Storage for Song:
    32bit, 8Tracks, 5 Minutes
    4*8*5*60*44100/1024/1024=403,7476 Megabytes

    Bandwidth 2 files read, 2 files written in parallel
    4 Files, 32bit,
    4*4*44100=705600 Bytes/sec

    Circular Buffers in Ram.
    Additionally the start of the song shall be kept in Ram to be able to go to the start of the record quickly.

    Teensy 4.1
    Use of threads library
    ? Use of audio library ?
    Minimal latency!
    Display ILI Touch screen
    4 foot switches, rotary encoder

    Main question is about the needed bandwidth. I know, that SD-cards do need some time sometimes.
    1. Is the average bandwidth of about 1Megabyte/sec possible?
    2. What size of the RAM buffers is needed?
    3. Is this possible with a file system or will it need some raw access to the SD-card?
    4. I there a 24-bit ADC/DAC breakout board with a driver as alternative to the Teensy audio board?
    5. A good link to basics about SD-cards and their internal organisation (datasheet?) would be helpful .
    6. Any other comments?


    Many thanks in advance!
    Christof

  2. #2
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    You don't have to deal with wear - the card does this - it has a builtin cpu.
    I don't think it's possible to read 8 channels and write 2 simultanously. Not without a really large buffer.
    However, with Teensy 4.1 and by using both PSRAMs it might be possible.
    - You should use a kind of threading or a good state machine - with higher priority to read, and write short chunks when some time between the reads is left.
    - Read and write large chunks (4kB minimum) - that's faster.
    - SD Cards will take some breaks (up to dozens of ms) - that's the wear leveling etc. -> buffer, buffer, buffer.....
    - use a new card, and preallocate the recorded file (just allocate a really large file.. 1GB or more. It's easy to cut it later)

    But easier is to use a 2nd Teensy for recording. If needed, you can copy the file from the recording teensy to the "playing" teensy after recording is done.

    Use the I2S as audio connection between both Teensy. Playing is master, Recorder is I2s slave.

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Quote Originally Posted by cebersp View Post
    Bandwidth 2 files read, 2 files written in parallel
    4 Files, 32bit,
    4*4*44100=705600 Bytes/sec
    eh, no. Not even in a ideal world
    You have to add the time to switch between the files (re-adressing - that's SLOW!), the time the card needs to do it's internal wear-magic, the time to handle fat entries for recording...

  4. #4
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    Thank you very much, Frank, for your fast answer!
    I do not really understand, what is the bottleneck? "Dozends of ms" sounds like <1sec and would not need too much buffer?
    Would it make sense to use 2 SD- cards in parallel - after each loop record and playback are swapped? There are two holders on T4.1+ on the audio shield.
    Which file system would you recommend? Is it possible to use 2 for 2 cards in parallel?
    (A looper needs to be able to repeat the loop without any delay, so I don't see the possibility to use 2 Teensies.)

    Edit, your second answer came in parallel:
    If wear is dealt by the SD card internally, then only one file is to be written and a second is to be read in parallel. So with 2 cards no re-adressing would be needed from outside perspective.
    Any chance?
    Last edited by cebersp; 01-20-2022 at 08:42 AM.

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Bottlenecks are:
    - random access (happens whenever you read data from an other file) re-adressing is slow.
    - writing
    If you write a specialized program that does only this, perhaps without the audio library (which can handle 16bit only anyway, and has a tight 2.9ms corsett), and use big buffers... it can work with one card.
    I don't know if using two cards on one Teensy would be helpful. I don't even know if that is supported by the libraries.

  6. #6
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    Thanks again!
    So at this moment some measurements about writing speeds with parallel reading of a second file will be necessary first. Maybe I could start here: https://forum.pjrc.com/threads/62158...-Audio-adapter

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Quote Originally Posted by cebersp View Post
    Thanks again!
    So at this moment some measurements about writing speeds with parallel reading of a second file will be necessary first. Maybe I could start here: https://forum.pjrc.com/threads/62158...-Audio-adapter
    There is also an example "SdCardTest" which might be useful. You can extend it to 8 files + record.

    Click image for larger version. 

Name:	2022-01-20 11_43_46-Window.png 
Views:	11 
Size:	70.0 KB 
ID:	27276

  8. #8
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    If I want to copy a file with 32bit values and 8 channels at 44100 samples/second, a copy transfer rate of 4*8*44100= 1.5 MBytes/second is needed.

    32 GB SanDisk Ultra, used not very often:
    So I modified the testprogram from M. Borgerson to copy a file in chunks of 16384 bytes and got an average of a copy (includes reading and writing) transfer rate 3.6 Mbytes/sec.

    The minimum transferrate for one buffer of 16kB does vary very much starting from 0.12MBytes/sec.

    With 64kB length buffer average 5.9MB/sec and minimum 0.97MBytes/sec

    With 128kB length buffer average 7.5MB/sec everything was looking very nice until there was a value of minimum 0.29MBytes/sec!

    As 128kB buffer length should be about maximum, as I think I will need 4 of them, this is not looking too good. :-(

    ???
    Can I use a UHS-Card?


    Code:
    SdFs Benchmark  Compiled on Jan 20 2022 18:01:00
    SDIO initialization done.
    Type is FAT32
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.68 seconds For an average rate of  7.600 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.71 seconds For an average rate of  7.594 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.464286 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 34.26 seconds For an average rate of  7.473 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 0.294811 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.75 seconds For an average rate of  7.586 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 3.048780 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.98 seconds For an average rate of  7.535 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 1.041667 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.77 seconds For an average rate of  7.581 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 1.785714 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.68 seconds For an average rate of  7.601 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.68 seconds For an average rate of  7.601 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.69 seconds For an average rate of  7.599 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.79 seconds For an average rate of  7.577 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 1.923077 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.76 seconds For an average rate of  7.583 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 1.865672 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.97 seconds For an average rate of  7.536 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 0.446429 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.91 seconds For an average rate of  7.549 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 2.232143 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 33.95 seconds For an average rate of  7.541 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 2.155172 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 34.04 seconds For an average rate of  7.521 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 1.213592

    Code:
    /*********************************************************************
     * Benchmark test for SD FAT32 sequential and random access
     * Modified for copy of files by CWE from
     * M. Borgerson 8/24/2020
     * https://forum.pjrc.com/threads/62158-SD-card-interface-Teensy-4-1-vs-Teensy-4-0-Audio-adapter
     ********************************************************************/
    #include "SdFat.h"
    #include "sdios.h"
    #include <TimeLib.h>
    
    // NOTE:  I used SdFs because it will use either FAT32 or ExFAT, depending on the format of the disk
    SdFs sd;
    // FsFile BenchFile;
    
    
    #define SD_CONFIG SdioConfig(FIFO_SDIO)
    
    // SDCARD_SS_PIN is defined for the built-in SD on some boards.
    #ifndef SDCARD_SS_PIN
    const uint8_t SD_CS_PIN = SS;
    #else  // SDCARD_SS_PIN
    // Assume built-in SD is used.
    const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
    #endif  // SDCARD_SS_PIN
    
    
    const char compileTime [] = "SdFs Benchmark  Compiled on " __DATE__ " " __TIME__;
    
    /*****************************************************************************
       Read the Teensy RTC and return a time_t (Unix Seconds) value
    
     ******************************************************************************/
    time_t getTeensy3Time() {
      return Teensy3Clock.get();
    }
    
    
    void setup() {
      // put your setup code here, to run once:
    
      while (!Serial) {}
      Serial.begin(9600);
      delay(1000);
    
      Serial.println(compileTime); 
      if (!sd.begin(SD_CONFIG)) {
        Serial.println("\nSDIO Card initialization failed.\n");
      } else  Serial.println("SDIO initialization done.");
    
      if (sd.fatType() == FAT_TYPE_EXFAT) {
        Serial.println("Type is exFAT");
      } else {
        Serial.printf("Type is FAT%d\n", int16_t(sd.fatType()));
      }
      // set date time callback function  so file gets a good date
      SdFile::dateTimeCallback(dateTime);
      setSyncProvider(getTeensy3Time);
    }
    
    char wfname[] = "btest1.dat";
    char fname[] = "btest.dat";
    void loop() {
      // put your main code here, to run repeatedly:
      char ch;
      if (Serial.available()) {
        ch = Serial.read();
        if (ch == 'w')  WriteBenchFile(fname);
        if (ch == 'c')  CopyBenchFile(fname);
        if (ch == 's')  SeqReadBenchFile(fname);
        if (ch == 'r')  RandReadBenchFile(fname);
        if (ch == 'd')  sd.ls(LS_SIZE | LS_DATE | LS_R);
      }
    }
    
    #define RBUFFSIZE 4096*4
    
    // Write a 256MB  contiguous file for benchmark testing
    //  
    // data in the file will be random data from stack area
    void WriteBenchFile(char *filename) {
      uint64_t alloclength;
      uint32_t i, num;
      FsFile benchFile, readFile;
      float seconds;
      uint32_t startmilli, dmilli, blockmax, mbytes;
      unsigned char benchbuff[RBUFFSIZE];
    
      Serial.printf("\n\nCopy 256MB Benchmark file.\n");
      // Open the file
      if (!benchFile.open(filename,  O_RDWR | O_CREAT | O_TRUNC)) {
        Serial.printf("Unable to open <%s> for writing.", filename);
        return;
      }
    
    
      // now write the data in blocks of RBUFFSIZE --   65536 blocks
      // send out a message every 10MB or 256 blocks
      blockmax = 65536;
      mbytes = 0;
      startmilli = millis();
      for(i=0; i<blockmax; i++){
        benchFile.write(&benchbuff, 4096);
        if((i%2560) == 0){
          Serial.printf("%lu MBytes\n",mbytes);
          mbytes+= 10;
        }
      }
      dmilli = millis()-startmilli;
    
      benchFile.close();
      seconds = (float)dmilli/1000.0;
      Serial.printf("\nWriting 256MBytes took %4.2f seconds", (float)dmilli/1000.0);
      Serial.printf(" For an average rate of %6.3f Mbytes/second\n", (256)/seconds);
    }
    
    // Write a 256MB  contiguous file for benchmark testing
    //  
    // data in the file will be random data from stack area
    void CopyBenchFile(char *filename) {
      uint64_t alloclength;
      uint32_t i, num;
      FsFile benchFile, readFile;
      float seconds;
      uint32_t startmilli, dmilli, blockmax, mbytes, smilli, minmilli=0;
      unsigned char benchbuff[RBUFFSIZE];
    
      Serial.printf("\n\nCopy 256MB Benchmark file.\n");
      // Open the file
      if (!benchFile.open(wfname,  O_RDWR | O_CREAT | O_TRUNC)) {
        Serial.printf("Unable to open <%s> for writing.", filename);
        return;
      }
    
      if (!readFile.open(filename, O_READ)) {
        Serial.printf("\nCould not open <%s> for reading.", filename);
        return;
      }
    
      // now write the data in blocks of RBUFFSIZE --   65536 blocks
      // send out a message every 10MB or 256 blocks
      blockmax = 65536*4096/(RBUFFSIZE);
      mbytes = 0;
      startmilli = millis();
      for(i=0; i<blockmax; i++){
        smilli=millis();
        readFile.read(&benchbuff, RBUFFSIZE);
        benchFile.write(&benchbuff, RBUFFSIZE);
        smilli= millis()-smilli;
        if(smilli>minmilli) minmilli=smilli;
        if((i%2560) == 0){
          //Serial.printf("%lu MBytes\n",mbytes);
          Serial.printf("*");
          mbytes+= 10;
        }
      }
      dmilli = millis()-startmilli;
    
      benchFile.close();
      readFile.close();
      seconds = (float)dmilli/1000.0;
      Serial.printf("\nCopying 256MBytes took %4.2f seconds", (float)dmilli/1000.0);
      Serial.printf(" For an average rate of %6.3f Mbytes/second\n", (256)/seconds);
      Serial.printf(" Minimum rate for one Block of %d Bytes was MBytes/second: %f \n", 
        RBUFFSIZE, 1000.0*RBUFFSIZE/1024/1024/minmilli);
    }
    
    
    
    // Read 40 MBytes from benchFile and keep track of max and average read time
    
    #define BUFFSTOREAD 10240   // 40MByte read
    void SeqReadBenchFile(const char *filename) {
      uint16_t idx, numread;
      FsFile benchFile;
      uint8_t rbuffer[RBUFFSIZE];
      uint32_t startmicro, dmicro, maxmicro, gt500us;
      float microsum;
    
      if (!benchFile.open(filename, O_READ)) {
        Serial.printf("\nCould not open <%s> for reading.", filename);
        return;
      }
      startmicro = micros();  // save starting time
      Serial.printf("\n\nReading %lu sequential buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
      maxmicro = 0; 
      microsum = 0.0;
      gt500us = 0;
      for(idx = 0; idx<BUFFSTOREAD; idx++){
        startmicro = micros();
        numread = benchFile.read(&rbuffer, RBUFFSIZE);
        dmicro = micros()-startmicro;
        if(dmicro > maxmicro) maxmicro = dmicro;
        if(dmicro > 500) gt500us++;
        microsum += dmicro;
    
      }
      Serial.printf("Buffer read times: average = %4.2f usec   Maximum = %lu usec    >500uSec: %lu\n",
                                  microsum/BUFFSTOREAD, maxmicro, gt500us);
      Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
      
      benchFile.close();
      Serial.println();
    
    }
    
    void RandReadBenchFile(const char *filename) {
    
      uint16_t idx, numread;
      FsFile benchFile;
      uint8_t rbuffer[RBUFFSIZE];
      uint32_t startmicro, dmicro, maxmicro, gt500us, fpos;
      float microsum;
    
      if (!benchFile.open(filename, O_READ)) {
        Serial.printf("\nCould not open <%s> for reading.", filename);
        return;
      }
      startmicro = micros();  // save starting time
      Serial.printf("\n\nReading %lu random buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
      maxmicro = 0; 
      microsum = 0.0;
      gt500us = 0;
      for(idx = 0; idx<BUFFSTOREAD; idx++){
        startmicro = micros();
        fpos = random(0,5000)*RBUFFSIZE;
        benchFile.seek(fpos);
        numread = benchFile.read(&rbuffer, RBUFFSIZE);
        dmicro = micros()-startmicro;
        if(dmicro > maxmicro) maxmicro = dmicro;
        if(dmicro > 999) gt500us++;
        microsum += dmicro;
    
      }
      Serial.printf("Buffer read times: average = %4.2f usec   Maximum = %lu usec    >999uSec: %lu\n",
                                  microsum/BUFFSTOREAD, maxmicro, gt500us);
      Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
      benchFile.close();
      Serial.println();
    
    }
    
    //------------------------------------------------------------------------------
    /*
       User provided date time callback function.
       See SdFile::dateTimeCallback() for usage.
    */
    void dateTime(uint16_t* date, uint16_t* time) {
      // use the year(), month() day() etc. functions from timelib
    
      // return date using FAT_DATE macro to format fields
      *date = FAT_DATE(year(), month(), day());
    
      // return time using FAT_TIME macro to format fields
      *time = FAT_TIME(hour(), minute(), second());
    }
    Last edited by cebersp; 01-20-2022 at 04:32 PM.

  9. #9
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    242
    Would a hard drive on the USB host be any faster? Does that work on the teensy?

  10. #10
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Maybe @KurtE, @MJS513 or @Defragster can answer that.

  11. #11
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    Update: bought a new SDXC-card Sandisk Extreme 64GB, which carries speed markings "A2" (number of commands) and "V30" (minimum write speed for video).

    With this I can get a minimum copy transfer rate of 128k blocklength >2.5Megabytes/sec.
    64k blocklength: >1.78Megabytes/sec
    32k blocklength: >0.8Megabytes/sec

    So :-) from these tests the project looks to be feasible with 64k buffer length! :-)

    I have to learn more about files, perhaps I only need one open file, because I can file.seek to the right position, read, modify and write back into the file.


    Code:
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.12 seconds For an average rate of  9.441 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 2.500000 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.451 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.07 seconds For an average rate of  9.457 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.807692 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.08 seconds For an average rate of  9.453 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.464286 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.450 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.310345 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.08 seconds For an average rate of  9.455 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.807692 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.07 seconds For an average rate of  9.455 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.807692 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.449 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.629630 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.13 seconds For an average rate of  9.435 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 2.777778 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.451 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.629630 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.08 seconds For an average rate of  9.452 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.629630 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.451 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.629630 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.10 seconds For an average rate of  9.446 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 3.787879 
    
    
    Copy 256MB Benchmark file.
    *
    Copying 256MBytes took 27.09 seconds For an average rate of  9.449 Mbytes/second
     Minimum rate for one Block of 131072 Bytes was MBytes/second: 4.629630

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    To be sure i'd make a more "real world" test, and test exactly your usage-szenario.

    read 8 files + write one.

  13. #13
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    You can also take a look here: https://github.com/FrankBoesing/Teensy-WavePlayer
    It can play many files at the same time (tested with 14 with a very fast card), and has basic looping.
    Recording is there, also - but not tested much.
    Also, it does only 16 bit.

    Looping: https://forum.pjrc.com/threads/67754...l=1#post291793
    Last edited by Frank B; 01-22-2022 at 09:18 AM.

  14. #14
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    Quote Originally Posted by Frank B View Post
    To be sure i'd make a more "real world" test, and test exactly your usage-szenario.

    read 8 files + write one.
    The idea is to hold all 8 tracks in one file. At each loop cycle just copy 7 tracks and modify one, so I think/hope the test was rather "real world".

  15. #15
    Junior Member
    Join Date
    Feb 2022
    Posts
    4
    I'm not sure this helps but I did a ton of reading and I think I gained some knowledge because of posts of some other very well informed and kind forum members.
    - If at all possible try to use an SDIO connection to your SD card.
    Here is why: https://forum.pjrc.com/threads/62158...-Audio-adapter
    - It's also important to use the SDFAT File system which ( I think is written by Bill Greiman ) and which library is included in the latest Teensyduino too.
    I would make sure you use this library and you can actually test your SD card speed.
    Using this sketch when you installed Teensyduino:
    \hardware\teensy\avr\libraries\SD\examples\Data logger\Datalogger.ino
    You can than also further optimize your read and write speed by thinking/experimenting about what blocksize you will be writing to your SD card since your SD card is formatted with a block size which mean that in order to write let say 32k of data to your SD card but you formatted with a blocksize of 64bB you think you are writing/using 32k of "diskspace" but in actuality you are writing 64k of "diskspace" since that is your blocksize.
    Another (for me at least) interesting topic is that flash chips by design work in blocks which is a physical design limitation of flash memory it's is impossible to only change 1 but or byte in flash memory you will have to change a whole block. So if you change just one bit or byte in the data the SD card has to write a whole block of data. It depends on your SD card what that block size is. More reading on this subject in: https://electronics.stackexchange.co...ash-block-size
    You also can define the sector size of your media when you format your SD card ( I think they call this allocation size) and I think it's best to make this either is the same as the block size or make it a multiple of that size since on top of everything else you also have to worry about how a the filesystem knows what sectors are occupied by what file and it actually logs this in the FAT table which it also has to keep track what sectors are occupied by a file.
    While all of this is going on the CPU in your SD card is trying it's best to make sure all the parts of the SD card are equally used ( wear levelling)
    All this account keeping means that your SD card is, at times, a lot slower than expected and you need buffers to compensate for that but if you know a little bit about how the cards and filesystems actually work you can compensate for that. Maybe just by formatting the card every now and than or choose a certain sector size or if you know you have a couple of files that are "read only" place them on your card first after a clean format.
    Also it's not just a card with memory it has a CPU and the better vendors seem to have put more effort into programming that CPU and compensate for latency and read write speed especially when the files are more segmented.
    Also keep in mind that when you write audio files and they are not actual audio files they also add a header to the file which increases the block file so you might actually be writing more than you think.

    I initially wondered why SD reading and writing was such a problem( before a lot of very smart people made libraries and hardware available in this forum) and I think what threw me off is that I know that SD cards can handle incredible read/write speeds with incredible low latency which don't seem feasible on the Teensy. It seems that the reason for that is mainly hardware since faster speeds to an SD card are attained by a different hardware than the Teensy is capable of right now. This is discussed in the following post:
    "
    I won't be implementing faster SD modes. T4 can't support 1.8V signaling. See this.
    https://forum.pjrc.com/threads/57690...l=1#post216367
    "
    So it's a hardware problem but also a software problem since you would also need the software to write files to the cards differently to attain the speeds printed on the cards.

    Keep in mind please the when I wrote this post every other word I typed "I think" and "as I understand it" so please keep that in mind when reading there most certainly are mistakes in this post so also please read the links I added for better informed posts.
    Off course also if anyone knows better please post below and I will try and add and edit this information. I love this community and I'm not deliberately trying to spread misinformation. I'm also deliberately not trying to be ignorant, my ignorance is not a choice and I'm trying to cure it by hanging around with smart people and hope they tolerate me.

  16. #16
    Senior Member
    Join Date
    Dec 2017
    Posts
    119
    Thank you, Tonde, for your hints!
    Yes, it meanwhile turned out, that it is feasible, see here: https://forum.pjrc.com/threads/69362...titrack-Looper
    There is the problem, that I could only buy a fast card with the speed markings "A2 V30" with >= 64GB, which means "extended" FAT. So you need a library which can handle this. (Perhaps you could format it for only 32GB)
    Yes, with slower SD-cards, the bottle neck is "write latency".
    No, the access speed of Teensy 4.1 to the builtin SD-card holder is fast enough.
    I learned in this thread, that wear errors are handled inside the SD-card, so I switched to only 1 file with 16bits per sample. This cut down the amount of data to be handled.
    Christof

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •