Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 35

Thread: Using SdFat to acces Teensy 3.6 SD internal card (& with audio board)

  1. #1
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384

    Using SdFat to acces Teensy 3.6 SD internal card (& with audio board)

    I am trying to use SdFat for fast writing of files with a bat detector while operating the Teensy audio board with 192k sample rate. I need a speed of at least 375kbytes/sec write for writing mono RAW files with a samplerate of 192k / 16bit.

    SdFat seems to be very mighty, but I fail with the very basics. I am using the Quick Start example and do not know what to enter for DISABLE_CHIP SELECT when using the Teensy 3.6 with attached audio board. (I want to use the Teensy 3.6 SD card, not the audio board SD card, well I do not really care which one to use, but I want to use the one which is fastest).

    Which CHIP_SELECT_PIN do I use for the Teensy 3.6. internal SD card?

    Sorry, if that has already been answered on the forum, I did not find it. A link would be sufficient, thanks in advance!

    Frank

  2. #2
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    OK, some light in the fog now. I managed to get the example TeensySdioDemo to work, which is very nice! Here is the output:

    Code:
    SdFatSdioEX uses extended multi-block transfers without DMA.
    SdFatSdio uses a traditional DMA SDIO implementation.
    Note the difference is speed and busy yield time.
     
    Type '1' for SdFatSdioEX or '2' for SdFatSdio
     
    size,write,read
    bytes,KB/sec,KB/sec
    512,7552.23,18592.19
    1024,15144.32,18728.88
    2048,15380.40,18859.45
    4096,15642.60,18955.42
    8192,15643.38,19007.39
    16384,15452.96,19027.96
    32768,15716.21,19038.80
     
    totalMicros  7468400
    yieldMicros  338415
    yieldCalls   176
    yieldMaxUsec 2921
    kHzSdClk     45000
    Done
    Type '1' for SdFatSdioEX or '2' for SdFatSdio
     
    size,write,read
    bytes,KB/sec,KB/sec
    512,200.19,1795.49
    1024,415.26,3182.02
    2048,835.82,5471.95
    4096,1528.06,9142.26
    8192,2771.66,12969.06
    16384,5669.03,16055.24
    32768,9345.98,18372.42
     
    totalMicros  94418126
    yieldMicros  94074591
    yieldCalls   65279
    yieldMaxUsec 81826
    kHzSdClk     45000
    Done
    So it seems, for my purpose I want to use SdFatSdioEX (7.5MByte/sec Write, wow! ), SdFatSdio is too slow (200kbytes/sec) with 512 byte buffer size!

    Then I tried the example RawWrite and
    - substituted SdFat by SdFatSdioEX
    - called begin with "begin()"

    Code:
    // file system
    //SdFat sd;
    SdFatSdioEX sd;
    
    line 67:
      // Initialize at the highest speed supported by the board that is
      // not over 50 MHz. Try a lower speed if SPI errors occur.
      if (!sd.begin()) {  //chipSelect, SD_SCK_MHZ(50))) {
        sd.initErrorHalt();
      }
    This is the result:

    Code:
    Type any character to start
    FreeStack: 255372
    Start raw write of 10000 KB
    Target rate: 100 KB/sec
    Target time: 100 seconds
     
    Q: 1
    ..........
    ..........
    error: writeStop failed
    SD errorCode: 0X28,0X10001
    Hmm, how do I interpret that? And is it possible to use SdFatSdioEX for writing RAW files (with long file names) to the Teensy 3.6 SD card with high speed? And if so, how? ;-)

    Any help greatly acknowledged!

    Frank

  3. #3
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Spent some hours with SdFat now. Some examples work when you use the SdFatSdioEX routines. I get speeds like 11MBytes/sec write and 18MBytes/sec read, which is very nice and sufficient.

    However, I have no clue how to use SdFat together with the Audio library to write audio from the record queue object to the SD.

    Teensyduino finds several SD.h-files and gives out an error, probably because Sd_Fat AND audio libs refer to an SD.h . . .

    Code:
    In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/play_sd_raw.h:32:0,
    
                     from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/Audio.h:96,
    
                     from F:\Privat\AMATEURFUNK\Teensy SDR\Teensy 3_5\Queue_HIER\Test_write_queue_audio_to_SD_SDIO\Test_write_queue_audio_to_SD_SDIO.ino:12:
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD/SD.h:27:0: warning: "FILE_WRITE" redefined [enabled by default]
    
     #define FILE_WRITE (O_READ | O_WRITE | O_CREAT)
    
     ^
    
    In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/FatLib/FatLib.h:22:0,
    
                     from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/SdFat.h:28,
    
                     from F:\Privat\AMATEURFUNK\Teensy SDR\Teensy 3_5\Queue_HIER\Test_write_queue_audio_to_SD_SDIO\Test_write_queue_audio_to_SD_SDIO.ino:10:
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/FatLib/ArduinoFiles.h:34:0: note: this is the location of the previous definition
    
     #define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END)
    
     ^
    
    In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/play_sd_raw.h:32:0,
    
                     from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/Audio.h:96,
    
                     from F:\Privat\AMATEURFUNK\Teensy SDR\Teensy 3_5\Queue_HIER\Test_write_queue_audio_to_SD_SDIO\Test_write_queue_audio_to_SD_SDIO.ino:12:
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD/SD.h:29:7: error: redefinition of 'class File'
    
     class File : public Stream {
    
           ^
    
    In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/FatLib/FatLib.h:22:0,
    
                     from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/SdFat.h:28,
    
                     from F:\Privat\AMATEURFUNK\Teensy SDR\Teensy 3_5\Queue_HIER\Test_write_queue_audio_to_SD_SDIO\Test_write_queue_audio_to_SD_SDIO.ino:10:
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat\src/FatLib/ArduinoFiles.h:117:7: error: previous definition of 'class File'
    
     class File : public FatFile, public Stream {
    
           ^
    
    In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/play_sd_raw.h:32:0,
    
                     from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/Audio.h:96,
    
                     from F:\Privat\AMATEURFUNK\Teensy SDR\Teensy 3_5\Queue_HIER\Test_write_queue_audio_to_SD_SDIO\Test_write_queue_audio_to_SD_SDIO.ino:12:
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD/SD.h:64:3: error: 'SdVolume' does not name a type
    
       SdVolume volume;
    
       ^
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD/SD.h:72:33: error: 'SD_CHIP_SELECT_PIN' was not declared in this scope
    
       boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
    I used this test script (which is based on the bench example from the SdFat_beta library), maybe someone finds the time to point me to a way how I could proceed? Thanks in advance!

    Code:
    /*
     * Testing to write audio from a record queue object as RAW to an SD card 
     * 
     * hopefully at a speed larger than 2MBytes/sec, as I need this for bat detector recording at 192k
     * 
     * 
     * This program is a simple binary write/read benchmark.
     */
    #include <SPI.h>
    #include "SdFat.h"
    #include "FreeStack.h"
    #include <Audio.h>
    
    // this audio comes from the codec by I2S2
    AudioInputI2S               i2s_in; // MIC input
    AudioRecordQueue            recorder; 
    //AudioSynthWaveformSine      sine1; // local oscillator
    //AudioEffectMultiply         mult1; // multiply = mix
    //AudioAnalyzeFFT256          myFFT; // for spectrum display
    //AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
    AudioPlaySdRaw              player; 
    AudioMixer4                 mix1;
    AudioOutputI2S              i2s_out; // headphone output          
    
    AudioConnection patch3      (i2s_in, 0, recorder, 0);
    AudioConnection patch4      (i2s_in, 0, mix1, 0);
    //AudioConnection path7      (i2s_in, 0, fft1024_1, 0);
    //AudioConnection path7      (i2s_in, 0, myFFT, 0);
    AudioConnection patch5      (player, 0, mix1, 1);
    AudioConnection patch8      (mix1, 0, i2s_out, 0);
    AudioConnection patch9      (mix1, 0, i2s_out, 1);
    
    AudioControlSGTL5000        sgtl5000_1;  
    
    const int myInput = AUDIO_INPUT_MIC;
    int8_t mic_gain = 40; // start detecting with this MIC_GAIN in dB
    
    
    
    // Size of read/write.
    const size_t BUF_SIZE = 512;
    
    // File size in MB where MB = 1,000,000 bytes.
    const uint32_t FILE_SIZE_MB = 5;
    
    // Write pass count.
    const uint8_t WRITE_COUNT = 2;
    
    // Read pass count.
    const uint8_t READ_COUNT = 2;
    //==============================================================================
    // End of configuration constants.
    //------------------------------------------------------------------------------
    // File size in bytes.
    const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB;
    
    uint8_t buf[BUF_SIZE];
    
    SdFatSdioEX sd;
    
    // test file
    SdFile file;
    
    // Serial output stream
    ArduinoOutStream cout(Serial);
    //------------------------------------------------------------------------------
    // Store error strings in flash to save RAM.
    #define error(s) sd.errorHalt(F(s))
    //------------------------------------------------------------------------------
    void cidDmp() {
      cid_t cid;
      if (!sd.card()->readCID(&cid)) {
        error("readCID failed");
      }
      cout << F("\nManufacturer ID: ");
      cout << hex << int(cid.mid) << dec << endl;
      cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
      cout << F("Product: ");
      for (uint8_t i = 0; i < 5; i++) {
        cout << cid.pnm[i];
      }
      cout << F("\nVersion: ");
      cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
      cout << F("Serial number: ") << hex << cid.psn << dec << endl;
      cout << F("Manufacturing date: ");
      cout << int(cid.mdt_month) << '/';
      cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
      cout << endl;
    }
    //------------------------------------------------------------------------------
    void setup() {
      Serial.begin(9600);
      
      // Wait for USB Serial 
      while (!Serial) {
        SysCall::yield();
      }
      delay(1000);
      cout << F("\nUse a freshly formatted SD for best performance.\n");
    
      // use uppercase in hex and use 0X base prefix
      cout << uppercase << showbase << endl;
    
      // Audio connections require memory. 
      AudioMemory(100);
    /*
      setSyncProvider(getTeensy3Time);
    */
    // Enable the audio shield. select input. and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
      sgtl5000_1.micGain (mic_gain);
      sgtl5000_1.adcHighPassFilterDisable(); // does not help too much!
    
      mix1.gain(0,1); 
      mix1.gain(1,1); 
    
    }
    //------------------------------------------------------------------------------
    void loop() {
      float s;
      uint32_t t;
      uint32_t maxLatency;
      uint32_t minLatency;
      uint32_t totalLatency;
    
      // Discard any input.
      do {
        delay(10);
      } while (Serial.available() && Serial.read() >= 0);
    
      // F( stores strings in flash to save RAM
      cout << F("Type any character to start\n");
      while (!Serial.available()) {
        SysCall::yield();
      }
    
      cout << F("FreeStack: ") << FreeStack() << endl;
    
      if (!sd.begin()) {
        sd.initErrorHalt();
      }
    
      cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
      cout << F("Card size: ") << sd.card()->cardSize()*512E-9;
      cout << F(" GB (GB = 1E9 bytes)") << endl;
    
      cidDmp();
    
      // open or create file - truncate existing file.
      if (!file.open("bench.dat", O_CREAT | O_TRUNC | O_RDWR)) {
        error("open failed");
      }
    
      // fill buf with known data
      for (uint16_t i = 0; i < (BUF_SIZE-2); i++) {
        buf[i] = 'A' + (i % 26);
      }
      buf[BUF_SIZE-2] = '\r';
      buf[BUF_SIZE-1] = '\n';
    
      cout << F("File size ") << FILE_SIZE_MB << F(" MB\n");
      cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n");
      cout << F("Starting write test, please wait.") << endl << endl;
    
      // do write test
      uint32_t n = FILE_SIZE/sizeof(buf);
      cout <<F("write speed and latency") << endl;
      cout << F("speed,max,min,avg") << endl;
      cout << F("KB/Sec,usec,usec,usec") << endl;
      for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
        file.truncate(0);
        maxLatency = 0;
        minLatency = 9999999;
        totalLatency = 0;
        t = millis();
        for (uint32_t i = 0; i < n; i++) {
          uint32_t m = micros();
          if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
            sd.errorPrint("write failed");
            file.close();
            return;
          }
          m = micros() - m;
          if (maxLatency < m) {
            maxLatency = m;
          }
          if (minLatency > m) {
            minLatency = m;
          }
          totalLatency += m;
        }
        file.sync();
        t = millis() - t;
        s = file.fileSize();
        cout << s/t <<',' << maxLatency << ',' << minLatency;
        cout << ',' << totalLatency/n << endl;
      }
      cout << endl << F("Starting read test, please wait.") << endl;
      cout << endl <<F("read speed and latency") << endl;
      cout << F("speed,max,min,avg") << endl;
      cout << F("KB/Sec,usec,usec,usec") << endl;
    
      // do read test
      for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
        file.rewind();
        maxLatency = 0;
        minLatency = 9999999;
        totalLatency = 0;
        t = millis();
        for (uint32_t i = 0; i < n; i++) {
          buf[BUF_SIZE-1] = 0;
          uint32_t m = micros();
          int32_t nr = file.read(buf, sizeof(buf)); 
          if (nr != sizeof(buf)) {   
            sd.errorPrint("read failed");
            file.close();
            return;
          }
          m = micros() - m;
          if (maxLatency < m) {
            maxLatency = m;
          }
          if (minLatency > m) {
            minLatency = m;
          }
          totalLatency += m;
          if (buf[BUF_SIZE-1] != '\n') {
            error("data check");
          }
        }
        s = file.fileSize();
        t = millis() - t;
        cout << s/t <<',' << maxLatency << ',' << minLatency;
        cout << ',' << totalLatency/n << endl;
      }
      cout << endl << F("Done") << endl;
      file.close();
    }

  4. #4
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,665
    Quote Originally Posted by DD4WH View Post
    Spent some hours with SdFat now. Some examples work when you use the SdFatSdioEX routines. I get speeds like 11MBytes/sec write and 18MBytes/sec read, which is very nice and sufficient.

    However, I have no clue how to use SdFat together with the Audio library to write audio from the record queue object to the SD.

    Teensyduino finds several SD.h-files and gives out an error, probably because Sd_Fat AND audio libs refer to an SD.h . . .
    Great, thank you !
    Yes, the audio-lib is a problem. As long there are objects that rely on the "inbuilt" SD.h, there's no way..
    I've made a new mp3-decoder already, without such assumptions.

  5. #5
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    thanks, Frank. That does not sound too encouraging ;-).

    Did I understand that correctly, it is not possible to use SdFat while you use the audio lib? --> Well, then there will not be many people using SdFat with the teensy . . .

    However, I would not particularly like to give up at this point. Would SdFat run, if I delete all references to SD.h from audio.h ? However, this would not be a nice option, because nobody would be able to use my scripts unless they ruin their audio lib installation . . .

    Any more hints?

    Frank

  6. #6
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,665
    Yes: Take a look at Audio.h, and just remove SD.h, the wav-player and other objects that need "SD.h" - OR - Edit the files where SD.h is included, and it will work - OR - don't use Audio.h und include only the parts you need.
    Last edited by Frank B; 11-06-2016 at 09:50 AM.

  7. #7
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    OK, I give up, that is certainly not the way to do it :-(

    conclusion: SdFat does not work with the audio lib, even if I delete the references to SD.h or use single files. I seem not to be able to switch off all those dependencies of all the audio lib files.

    One last trial could be to use the SD library by WMXZ. But my suspicion is that it has the same problem of not working together with the audio lib?

    Frank

  8. #8
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by DD4WH View Post
    OK, I give up, that is certainly not the way to do it :-(

    conclusion: SdFat does not work with the audio lib, even if I delete the references to SD.h or use single files. I seem not to be able to switch off all those dependencies of all the audio lib files.

    One last trial could be to use the SD library by WMXZ. But my suspicion is that it has the same problem of not working together with the audio lib?

    Frank
    Com'on you can do it, at least for the specific application.

    What is it exactly you wanted to do with sd card?

  9. #9
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Thanks for encouraging!

    I would like to write an audio RAW file to the SD card (it does not matter, if its the audio board SD or the Teensy builtin SD). Audio comes from the MIC, is sampled with 192k by the Teensy audio board, comes to the Teensy by I2S, is given to a queue object and then written to the RAW file in realtime. That works all nicely up to 96k sample rate, but at 176k and 192k the write seems to be limited by either the processor speed OR/AND the writing speed. So I am looking for a faster way to write to the SD card, that´s why I tried SdFat. But as I need the audio lib, that did not fit together. I also considered your SD lib, but my knowledge is limited with regards to these SD things, and there is not such a huge amount of documentation within your github that would fit my knowledge level, it seems to me very technical specialist stuff ;-). So I am a bit lost now.

    What would help me is a hint how I can use the audio lib AND use some of the faster SD write routines for incoming realtime audio.

    Frank

    This is the code I use at the moment for testing, it works for REC with the SD lib from Teensyduino beta3 up to 96k, but does not work at 176k and 192k.

    Code:
    /***********************************************************************
     *  (c) 2016  Frank DD4WH 2016_11_06 - MIT license 
     *  Teensy Bat & Ultrasound detector 
     *         
     *         
     *     RECORD TEST 1    
     *         
     *         
     *  Version 1.1 (recording alpha)       
     *  https://forum.pjrc.com/threads/38988-Bat-detector
     *         
     *        made possible by the samplerate code by Frank Boesing, thanks Frank!
     * 
     *  tested on Teensy 3.5 + Teensy audio board 
     *  + standard tiny electret MIC soldered to the MIC Input of the audio board 
     *  
     *  
     *  User adjustments - with buttons
     *  
     *  MIC-GAIN                  33 + 34 -
     *  FREQUENCY                 35 + 36 -
     *  SAMPLE RATE               37 + 38 -
     *  WATERFALL/SPECTRUM        39  
     *  REC                       31
     *  STOP                      30
     *  PLAY                      29
     *  
     * Audio sample rate code - function setI2SFreq  
     * Copyright (c) 2016, Frank Bösing, f.boesing@gmx.de
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include <Audio.h>
    #include <SD.h>
    #include <SPI.h>
    #include <Bounce.h>
    #include <Metro.h>
    
    #include <ILI9341_t3.h>
    #include "font_Arial.h"
    
    #define VERSION     " v0.3"
    
    #define BACKLIGHT_PIN 0
    #define TFT_DC      20
    #define TFT_CS      21
    #define TFT_RST     32  // 255 = unused. connect to 3.3V
    #define TFT_MOSI     7
    #define TFT_SCLK    14
    #define TFT_MISO    12
    
    // would be nice to use the fast DMA lib, but it does not work on my Teensy 3.5
    //ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    
    #define BUTTON_MIC_GAIN_P       33     
    #define BUTTON_MIC_GAIN_M       34
    #define BUTTON_FREQ_P           35     
    #define BUTTON_FREQ_M           36     
    #define BUTTON_SAMPLE_RATE_P    37     
    #define BUTTON_SAMPLE_RATE_M    38     
    #define BUTTON_TOGGLE_WATERFALL 39
    #define BUTTON_RECORD           31
    #define BUTTON_STOP             30
    #define BUTTON_PLAY             29
    
    Bounce mic_gain_P = Bounce(BUTTON_MIC_GAIN_P, 50); 
    Bounce mic_gain_M = Bounce(BUTTON_MIC_GAIN_M, 50); 
    Bounce freq_P = Bounce(BUTTON_FREQ_P, 50); 
    Bounce freq_M = Bounce(BUTTON_FREQ_M, 50); 
    Bounce sample_rate_P = Bounce(BUTTON_SAMPLE_RATE_P, 50);
    Bounce sample_rate_M = Bounce(BUTTON_SAMPLE_RATE_M, 50);
    Bounce toggle_waterfall = Bounce(BUTTON_TOGGLE_WATERFALL, 50);
    Bounce button_Record = Bounce(BUTTON_RECORD, 50);
    Bounce button_Stop = Bounce(BUTTON_STOP, 50);
    Bounce button_Play = Bounce(BUTTON_PLAY, 50);
      
    #define SAMPLE_RATE_MIN               0
    #define SAMPLE_RATE_8K                0
    #define SAMPLE_RATE_11K               1
    #define SAMPLE_RATE_16K               2  
    #define SAMPLE_RATE_22K               3
    #define SAMPLE_RATE_32K               4
    #define SAMPLE_RATE_44K               5
    #define SAMPLE_RATE_48K               6
    #define SAMPLE_RATE_88K               7
    #define SAMPLE_RATE_96K               8
    #define SAMPLE_RATE_176K              9
    #define SAMPLE_RATE_192K              10
    #define SAMPLE_RATE_MAX               10
    
    // this audio comes from the codec by I2S2
    AudioInputI2S               i2s_in; // MIC input
    AudioRecordQueue            recorder; 
    //AudioSynthWaveformSine      sine1; // local oscillator
    //AudioEffectMultiply         mult1; // multiply = mix
    //AudioAnalyzeFFT256          myFFT; // for spectrum display
    //AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
    AudioPlaySdRaw              player; 
    AudioMixer4                 mix1;
    AudioOutputI2S              i2s_out; // headphone output          
    
    AudioConnection patch3      (i2s_in, 0, recorder, 0);
    AudioConnection patch4      (i2s_in, 0, mix1, 0);
    //AudioConnection path7      (i2s_in, 0, fft1024_1, 0);
    //AudioConnection path7      (i2s_in, 0, myFFT, 0);
    AudioConnection patch5      (player, 0, mix1, 1);
    AudioConnection patch8      (mix1, 0, i2s_out, 0);
    AudioConnection patch9      (mix1, 0, i2s_out, 1);
    
    AudioControlSGTL5000        sgtl5000_1;  
    
    // Metro 1 second
    Metro second = Metro(1000);
    
    const int8_t    MODE_STOP = 0;
    const int8_t    MODE_REC = 1;
    const int8_t    MODE_PLAY = 2;
    
    int mode = MODE_STOP; 
    File frec; // audio is recorded to this file first
    int file_number = 0;
    
    int count_help = 0;
    int8_t waterfall_flag = 0;
    int idx_t = 0;
    int idx = 0;
    int64_t sum;
    float32_t mean;
    
    int8_t mic_gain = 40; // start detecting with this MIC_GAIN in dB
    int freq_real = 22000; // start detecting at this frequency
    int sample_rate = SAMPLE_RATE_96K;
    int sample_rate_real = 96000;
    String text="96k";
    int freq_LO = 7000;
    int16_t FFT_bin [128]; 
    int barm[512];
    
    typedef struct SR_Descriptor
    {
        const int SR_n;
        const char* const f1;
        const char* const f2;
        const char* const f3;
        const char* const f4;
        const float32_t x_factor;
    } SR_Desc;
    
    // Text and position for the FFT spectrum display scale
    const SR_Descriptor SR [SAMPLE_RATE_MAX + 1] =
    {
        //   SR_n ,  f1, f2, f3, f4, x_factor = pixels per f1 kHz in spectrum display
        {  SAMPLE_RATE_8K,  " 1", " 2", " 3", " 4", 64.0}, // which means 64 pixels per 1 kHz
        {  SAMPLE_RATE_11K,  " 1", " 2", " 3", " 4", 43.1}, 
        {  SAMPLE_RATE_16K,  " 2", " 4", " 6", " 8", 64.0}, 
        {  SAMPLE_RATE_22K,  " 2", " 4", " 6", " 8", 43.1}, 
        {  SAMPLE_RATE_32K,  "5", "10", "15", "20", 80.0}, 
        {  SAMPLE_RATE_44K,  "5", "10", "15", "20", 58.05}, 
        {  SAMPLE_RATE_48K,  "5", "10", "15", "20", 53.33},
        {  SAMPLE_RATE_88K,  "10", "20", "30", "40", 58.05},
        {  SAMPLE_RATE_96K,  "10", "20", "30", "40", 53.33},
        {  SAMPLE_RATE_176K,  "20", "40", "60", "80", 58.05},
        {  SAMPLE_RATE_192K,  "20", "40", "60", "80", 53.33} // which means 53.33 pixels per 20kHz
    };    
    
    //const int myInput = AUDIO_INPUT_LINEIN;
    const int myInput = AUDIO_INPUT_MIC;
    
    void setup() {
      Serial.begin(115200);
      delay(200);
    
      if(!(SD.begin(BUILTIN_SDCARD))) 
      {
          while (1) {
              Serial.println("Unable to access the SD card");
              delay(500);  
          }
      }
    
    //setup pins with pullups
      pinMode(BUTTON_MIC_GAIN_P,INPUT_PULLUP);
      pinMode(BUTTON_MIC_GAIN_M,INPUT_PULLUP);
      pinMode(BUTTON_FREQ_P,INPUT_PULLUP);  
      pinMode(BUTTON_FREQ_M,INPUT_PULLUP);  
      pinMode(BUTTON_SAMPLE_RATE_P,INPUT_PULLUP);  
      pinMode(BUTTON_SAMPLE_RATE_M,INPUT_PULLUP);  
      pinMode(BUTTON_TOGGLE_WATERFALL,INPUT_PULLUP);  
      pinMode(BUTTON_RECORD,INPUT_PULLUP);  
      pinMode(BUTTON_STOP,INPUT_PULLUP);  
      pinMode(BUTTON_PLAY,INPUT_PULLUP);  
    
      // Audio connections require memory. 
      AudioMemory(200);
    /*
      setSyncProvider(getTeensy3Time);
    */
    // Enable the audio shield. select input. and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
      sgtl5000_1.micGain (mic_gain);
      sgtl5000_1.adcHighPassFilterDisable(); // does not help too much!
    
    // Init SD card use
    //  SPI.setMOSI(7); 
    //  SPI.setSCK(14);
    
    
    
    // Init TFT display  
      pinMode( BACKLIGHT_PIN, OUTPUT );
      analogWrite( BACKLIGHT_PIN, 1023 );
    
      tft.begin();
      tft.setRotation( 3 );
      tft.fillScreen(ILI9341_BLACK);
      tft.setCursor(14, 7);
      tft.setTextColor(ILI9341_ORANGE);
      tft.setFont(Arial_12);
      tft.print("Teensy Bat Detector  "); tft.print(VERSION);
      tft.setTextColor(ILI9341_WHITE);
    
      display_settings();
    
      // sorry, couldn´t resist ;-)
      logo_head();
      for (int v = 0; v < 6; v++) {
      logo(true);
      delay(200);
      logo(false);
      delay(200);
      }
    
      set_sample_rate (sample_rate);
    
    
      mix1.gain(0,1); 
      mix1.gain(1,1); 
    
    
    } // END SETUP
    
    
    void loop() {
       controls();
    //   spectrum(); 
           check_processor();
    }
    
    /* void spectrum() { // spectrum analyser code by rheslip - modified
         if (myFFT.available()) {
    //     if (fft1024_1.available()) {
        int scale;
        scale = 5;
      for (int16_t x = 2; x < 128; x++) {
    //  for (int16_t x = 8; x < 512; x+=4) {
         FFT_bin[x] = abs(myFFT.output[x]); 
    //     FFT_bin[x/4] = abs(fft1024_1.output[x]); 
         int bar = (FFT_bin[x] * scale);
         if (bar >175) bar=175;
         // this is a very simple first order IIR filter to smooth the reaction of the bars
         bar = 0.05 * bar + 0.95 * barm[x]; 
    //     if (bar > peak[x]) peak[x]=bar;
    //     tft.drawFastVLine(x, 210-bar,bar, ILI9341_PURPLE);
    //     tft.drawFastVLine(x*2+10, 210-bar,bar, ILI9341_PINK);
    
         tft.drawPixel(x*2+10, 210-barm[x], ILI9341_BLACK);
         tft.drawPixel(x*2+10, 210-bar, ILI9341_WHITE);
    
    //     tft.drawFastVLine(x*2+10, 20, 210-bar-20, ILI9341_BLACK);    
    
    //     tft.drawPixel(x*2+10,209-peak[x], ILI9341_YELLOW);
    
    //     if(peak[x]>0) peak[x]-=1;
         barm[x] = bar;
      }
    //     search_bats();     
      } //end if
    } // end void spectrum
    */
    
    void controls() {
    
    // first, check buttons
      mic_gain_P.update();
      mic_gain_M.update();
      freq_P.update();
      freq_M.update();
      sample_rate_P.update();
      sample_rate_M.update();
      toggle_waterfall.update();
      button_Record.update();
      button_Stop.update();
      button_Play.update();
    
      // Respond to button presses
      if (button_Record.fallingEdge()) {
        Serial.println("Record Button Press");
        if (mode == MODE_PLAY) stopPlaying();
        if (mode == MODE_STOP) startRecording();
      }
      if (button_Stop.fallingEdge()) {
        Serial.println("Stop Button Press");
        if (mode == MODE_REC) stopRecording();
        if (mode == MODE_PLAY) stopPlaying();
      }
      if (button_Play.fallingEdge()) {
        Serial.println("Play Button Press");
        if (mode == MODE_REC) stopRecording();
        if (mode == MODE_STOP) startPlaying();
      }
    
      // If we're playing or recording, carry on...
      if (mode == MODE_REC) {
        continueRecording();
      }
      if (mode == MODE_PLAY) {
        continuePlaying();
      }
    
      // change MIC GAIN  
      if ( mic_gain_P.fallingEdge()) { 
          mic_gain = mic_gain + 2;
          if (mic_gain > 63) {
            mic_gain = 63;
          }
          set_mic_gain(mic_gain);
      }
      if ( mic_gain_M.fallingEdge()) { 
          mic_gain = mic_gain - 2;
          if (mic_gain < 0) {
            mic_gain = 0;
          }
          set_mic_gain(mic_gain);
      }
    
      // change sample rate
      if ( sample_rate_P.fallingEdge()) { 
          sample_rate = sample_rate + 1;
                if (sample_rate > SAMPLE_RATE_MAX) {
            sample_rate = SAMPLE_RATE_MAX;
          }
            set_sample_rate (sample_rate);
      }
      if ( sample_rate_M.fallingEdge()) { 
          sample_rate = sample_rate - 1;
                if (sample_rate < SAMPLE_RATE_MIN) {
            sample_rate = SAMPLE_RATE_MIN;
          }
            set_sample_rate (sample_rate);
      }
    
    } // END function "controls"
    
    void       set_mic_gain(int8_t gain) {
        AudioNoInterrupts();
        sgtl5000_1.micGain (mic_gain);
        AudioInterrupts();
        display_settings();    
    } // end function set_mic_gain
    
    void      display_settings() {
        tft.fillRect(14,32,200,17,ILI9341_BLACK);
        tft.setCursor(14, 32);
        tft.setFont(Arial_12); 
        tft.print("gain: "); tft.print (mic_gain);
        tft.print("     "); 
        tft.print("freq: "); tft.print (freq_real);
        tft.print("    "); 
        tft.fillRect(232,32,88,17,ILI9341_BLACK);
        tft.setCursor(232, 32);
        tft.print("       "); 
        tft.print (text);
        Serial.println(text);       
     /*  // only for debugging  
        tft.fillRect(0,122,200,17,ILI9341_BLACK);
        tft.setCursor(0, 122);
        tft.print("LO: "); tft.print (freq_LO);
        tft.print("   "); 
        */
    }
    
    void      set_sample_rate (int sr) {
      switch (sr) {
        case SAMPLE_RATE_8K:
        sample_rate_real = 8000;
        text = " 8k";
        break;
        case SAMPLE_RATE_11K:
        sample_rate_real = 11025;
        text = "11k";
        break;
        case SAMPLE_RATE_16K:
        sample_rate_real = 16000;
        text = "16k";
        break;
        case SAMPLE_RATE_22K:
        sample_rate_real = 22050;
        text = "22k";
        break;
        case SAMPLE_RATE_32K:
        sample_rate_real = 32000;
        text = "32k";
        break;
        case SAMPLE_RATE_44K:
        sample_rate_real = 44100;
        text = "44.1k";
        break;
        case SAMPLE_RATE_48K:
        sample_rate_real = 48000;
        text = "48k";
        break;
        case SAMPLE_RATE_88K:
        sample_rate_real = 88200;
        text = "88.2k";
        break;
        case SAMPLE_RATE_96K:
        sample_rate_real = 96000;
        text = "96k";
        break;
        case SAMPLE_RATE_176K:
        sample_rate_real = 176400;
        text = "176k";
        break;
        case SAMPLE_RATE_192K:
        sample_rate_real = 192000;
        text = "192k";
        break;
      }
        AudioNoInterrupts();
        setI2SFreq (sample_rate_real); 
        delay(200); // this delay seems to be very essential !
        AudioInterrupts();
        delay(20);
        display_settings();
    //    prepare_spectrum_display();
    } // END function set_sample_rate
    
    
    // set samplerate code by Frank Boesing 
    void setI2SFreq(int freq) {
      typedef struct {
        uint8_t mult;
        uint16_t div;
      } tmclk;
    
      const int numfreqs = 14;
      const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 176400, (int)44117.64706 * 4, 192000};
    
    #if (F_PLL==16000000)
      const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
    #elif (F_PLL==72000000)
      const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
    #elif (F_PLL==96000000)
      const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
    #elif (F_PLL==120000000)
      const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
    #elif (F_PLL==144000000)
      const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
    #elif (F_PLL==168000000)
      const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
    #elif (F_PLL==180000000)
      const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
    #elif (F_PLL==192000000)
      const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
    #elif (F_PLL==216000000)
      const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
    #elif (F_PLL==240000000)
      const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
    #endif
    
      for (int f = 0; f < numfreqs; f++) {
        if ( freq == samplefreqs[f] ) {
          while (I2S0_MCR & I2S_MCR_DUF) ;
          I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
          return;
        }
      }
    }
    
    //  bat logo taken from Shezzy:  
    //  http://sisterzpsptreasures.freeforums.org/easy-animated-bat-t77.html
    //  copyright free graphics
    //  "The image you create by following this tutorial belongs to you and you may do whatever you want with it."
    void logo(bool wing1) {
          // Logo ;-)      
         int x = 265;
         int y = 10; 
    //      grey background and white line rectangle around it 
         tft.fillRect(x + 23, y - 2,16,14,ILI9341_DARKGREY);
         tft.fillRect(x - 2, y - 2,16,14,ILI9341_DARKGREY);
    //     tft.fillRect(x - 2, y - 2, 41, 14,ILI9341_DARKGREY);
        if (wing1) {
         //Wing1
         tft.drawFastHLine(x + 2, y + 0, 5, ILI9341_BLACK);
         tft.drawFastHLine(x + 31, y + 0, 5, ILI9341_BLACK);
         tft.drawPixel(x + 1, y + 1, ILI9341_BLACK);
         tft.drawPixel(x + 35, y + 1, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 1, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 1, ILI9341_BLACK);
    //     tft.drawPixel(x + 6, y + 2, ILI9341_BLACK);
    //     tft.drawPixel(x + 30, y + 2, ILI9341_BLACK);
         tft.drawFastVLine(x , y + 2, 7, ILI9341_BLACK);
         tft.drawFastVLine(x + 36 , y + 2, 7, ILI9341_BLACK);
         tft.drawFastHLine(x + 7, y + 2, 2, ILI9341_BLACK);
         tft.drawFastHLine(x + 28, y + 2, 2, ILI9341_BLACK);
         tft.drawPixel(x + 13, y + 2, ILI9341_BLACK);
         tft.drawPixel(x + 23, y + 2, ILI9341_BLACK);
         tft.drawFastHLine(x + 9, y + 3, 4, ILI9341_BLACK);
         tft.drawFastHLine(x + 24, y + 3, 4, ILI9341_BLACK);
         tft.drawFastHLine(x + 3, y + 5, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 31, y + 5, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 7, y + 5, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 27, y + 5, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 11, y + 5, 2, ILI9341_BLACK);
         tft.drawFastHLine(x + 24, y + 5, 2, ILI9341_BLACK);
         tft.drawPixel(x + 2, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 10, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 13, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 23, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 26, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 34, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 1, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 10, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 26, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 35, y + 7, ILI9341_BLACK);
        }
        else {
         //Wing2
         tft.drawFastHLine(x + 2, y + 2, 5, ILI9341_BLACK);
         tft.drawFastHLine(x + 31, y + 2, 5, ILI9341_BLACK);
         tft.drawPixel(x + 1, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 35, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 3, ILI9341_BLACK);
    //     tft.drawPixel(x + 6, y + 4, ILI9341_BLACK);
    //     tft.drawPixel(x + 30, y + 4, ILI9341_BLACK);
         tft.drawFastVLine(x , y + 4, 7, ILI9341_BLACK);
         tft.drawFastVLine(x + 36 , y + 4, 7, ILI9341_BLACK);
         tft.drawFastHLine(x + 7, y + 4, 2, ILI9341_BLACK);
         tft.drawFastHLine(x + 28, y + 4, 2, ILI9341_BLACK);
    //     tft.drawPixel(x + 13, y + 4, ILI9341_BLACK);
    //     tft.drawPixel(x + 23, y + 4, ILI9341_BLACK);
         tft.drawFastHLine(x + 9, y + 5, 5, ILI9341_BLACK);
         tft.drawFastHLine(x + 24, y + 5, 5, ILI9341_BLACK);
         tft.drawFastHLine(x + 3, y + 7, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 31, y + 7, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 7, y + 7, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 27, y + 7, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 11, y + 7, 3, ILI9341_BLACK);
         tft.drawFastHLine(x + 24, y + 7, 3, ILI9341_BLACK);
         tft.drawPixel(x + 2, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 10, y + 8, ILI9341_BLACK);
    //     tft.drawPixel(x + 13, y + 8, ILI9341_BLACK);
    //     tft.drawPixel(x + 23, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 26, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 34, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 1, y + 9, ILI9341_BLACK);
         tft.drawPixel(x + 6, y + 9, ILI9341_BLACK);
         tft.drawPixel(x + 10, y + 9, ILI9341_BLACK);
         tft.drawPixel(x + 26, y + 9, ILI9341_BLACK);
         tft.drawPixel(x + 30, y + 9, ILI9341_BLACK);
         tft.drawPixel(x + 35, y + 9, ILI9341_BLACK);
        }
    }
    
    void logo_head() {
         int x = 265;
         int y = 10; 
         tft.fillRect(x + 14, y - 2,9,14,ILI9341_DARKGREY);
         // Head 
         tft.drawPixel(x + 15, y + 2, ILI9341_BLACK);
         tft.drawPixel(x + 21, y + 2, ILI9341_BLACK);
         tft.drawPixel(x + 14, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 16, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 20, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 22, y + 3, ILI9341_BLACK);
         tft.drawPixel(x + 14, y + 4, ILI9341_BLACK);
         tft.drawPixel(x + 22, y + 4, ILI9341_BLACK);
         tft.drawFastHLine(x + 17, y + 4, 3, ILI9341_BLACK);
         tft.drawPixel(x + 14, y + 5, ILI9341_BLACK);
         tft.drawPixel(x + 22, y + 5, ILI9341_BLACK);
         tft.drawPixel(x + 16, y + 5, ILI9341_RED);
         tft.drawPixel(x + 20, y + 5, ILI9341_RED);
         tft.drawPixel(x + 17, y + 6, ILI9341_RED);
         tft.drawPixel(x + 19, y + 6, ILI9341_RED);
         tft.drawPixel(x + 14, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 22, y + 6, ILI9341_BLACK);
         tft.drawPixel(x + 15, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 21, y + 7, ILI9341_BLACK);
         tft.drawPixel(x + 16, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 20, y + 8, ILI9341_BLACK);
         tft.drawPixel(x + 17, y + 8, ILI9341_WHITE);
         tft.drawPixel(x + 19, y + 8, ILI9341_WHITE);
         tft.drawFastHLine(x + 17, y + 9, 3, ILI9341_BLACK);
    }
    
    void check_processor() {
          if (second.check() == 1) {
          Serial.print("Proc = ");
          Serial.print(AudioProcessorUsage());
          Serial.print(" (");    
          Serial.print(AudioProcessorUsageMax());
          Serial.print("),  Mem = ");
          Serial.print(AudioMemoryUsage());
          Serial.print(" (");    
          Serial.print(AudioMemoryUsageMax());
          Serial.println(")");
    /*      tft.fillRect(100,120,200,80,ILI9341_BLACK);
          tft.setCursor(10, 120);
          tft.setTextSize(2);
          tft.setTextColor(ILI9341_WHITE);
          tft.setFont(Arial_14);
          tft.print ("Proc = ");
          tft.setCursor(100, 120);
          tft.print (AudioProcessorUsage());
          tft.setCursor(180, 120);
          tft.print (AudioProcessorUsageMax());
          tft.setCursor(10, 150);
          tft.print ("Mem  = ");
          tft.setCursor(100, 150);
          tft.print (AudioMemoryUsage());
          tft.setCursor(180, 150);
          tft.print (AudioMemoryUsageMax());
         */ 
          AudioProcessorUsageMaxReset();
          AudioMemoryUsageMaxReset();
        }
    } // END function check_processor
    
    
    const char* filename (int pointer) {
          String NAME = "REC"+String(pointer)+".RAW";
          char str[15];
          NAME.toCharArray(str, sizeof(NAME));
          return str;
    }
    
    void startRecording() {
      Serial.print("startRecording");
    /*  if (SD.exists("RECORD.RAW")) {
        // The SD library writes new data to the end of the
        // file, so to start a new recording, the old file
        // must be deleted before new data is written.
        SD.remove("RECORD.RAW");
      } */
      file_number++;
      Serial.println(file_number);
      String NAME = "REC"+String(file_number)+".RAW";
      char fi[15];
      NAME.toCharArray(fi, sizeof(NAME));
       if (SD.exists(fi)) {
        // The SD library writes new data to the end of the
        // file, so to start a new recording, the old file
        // must be deleted before new data is written.
        SD.remove(fi);
      }    
      frec = SD.open(fi, FILE_WRITE);
      if (frec) {
        recorder.begin();
        mode = MODE_REC;
        Serial.println("RECORDING!");
      }
    }
    
    void continueRecording() {
      if (recorder.available() >= 32) {
        byte buffer[512];
        // Fetch 2 blocks from the audio library and copy
        // into a 512 byte buffer.  The Arduino SD library
        // is most efficient when full 512 byte sector size
        // writes are used.
        memcpy(buffer, recorder.readBuffer(), 256);
        recorder.freeBuffer();
        memcpy(buffer+256, recorder.readBuffer(), 256);
        recorder.freeBuffer();
        // write all 512 bytes to the SD card
    //    elapsedMicros usec = 0;
        frec.write(buffer, 512);
        // Uncomment these lines to see how long SD writes
        // are taking.  A pair of audio blocks arrives every
        // 5802 microseconds, so hopefully most of the writes
        // take well under 5802 us.  Some will take more, as
        // the SD library also must write to the FAT tables
        // and the SD card controller manages media erase and
        // wear leveling.  The recorder object can buffer
        // approximately 301700 us of audio, to allow time
        // for occasional high SD card latency, as long as
        // the average write time is under 5802 us.
        //    if (one_sec.check() == 1) {
        //      Serial.print("SD write, us=");
        //    }
        //    Serial.println(usec);
      }
    }
    
    void stopRecording() {
      Serial.print("stopRecording");
      recorder.end();
      if (mode == MODE_REC) {
        while (recorder.available() > 0) {
          frec.write((byte*)recorder.readBuffer(), 256);
          recorder.freeBuffer();
        }
        frec.close();
    //    playfile = recfile;
      }
      mode = MODE_STOP;
    //  clearname();
      Serial.println (" Recording stopped!");
    }
    
    void startPlaying() {
          String NAME = "REC"+String(file_number)+".RAW";
          char fi[15];
          NAME.toCharArray(fi, sizeof(NAME));
          mix1.gain(1,1);
          mix1.gain(0,0);       
      Serial.println("startPlaying ");
    //  const char* fi = filename(playfile);
    //  const char* fi = "REC13.RAW";
    //  Serial.print ("File: ");Serial.print(playfile);
      Serial.println ("Playfile: "); Serial.print (file_number);
      Serial.println ("Name: "); Serial.print (filename(file_number));
      delay(100);
    //  playSd.play(filename(playfile));
    //  playSd.play("REC13.RAW");
        player.play(fi);
    
      mode = MODE_PLAY;
    /*  tft.fillRect(0, 102, 160, 12, ST7735_GREEN);   //ST7735_BLACK);
      tft.setCursor(0, 105);
      tft.setTextColor(ST7735_BLACK);
      tft.print (" Playing !");
      tft.setTextColor(ST7735_WHITE);
      showtrack();
    */
    }
      
    void continuePlaying() {
      if (!player.isPlaying()) {
        player.stop();
        mode = MODE_STOP;
          mix1.gain(1,0);
          mix1.gain(0,1);       
        Serial.println("End of recording");
      }
    }
    
    void stopPlaying() {
          mix1.gain(1,0);
          mix1.gain(0,1);       
      Serial.print("stopPlaying");
      if (mode == MODE_PLAY) player.stop();
      mode = MODE_STOP;
      Serial.println (" Playing stopped");
    }

  10. #10
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Walter, but now I need your help ;-).

    I managed to modify your example script RAWWrite and add my audio chain that I use for the bat detector. So now there is continuous audio coming from the MIC into the headphones and into the queue. If you press the REC button, the audio is saved as RAW to the SD card with your uSDFS lib. Results:

    1.) REC of RAW files to SD works at 96k & 192k --> playback with Audacity indicates good quality at 96k, not sure at 192k (because my computer soundcard does not support 192k, but playback at 96k indicates good quality too)
    2.) playback not (yet) working [EDIT: solved! and edited in the script]
    3.) loud stuttering noise in the audio chain while recording (but that is not on the recordings! [EDIT: not true, it is on the recordings, but not so loud as in realtime)

    @1: that is very nice!
    @2: I would need a little hint here, see the functions startPlaying and following [EDIT: solved! and edited in the script]
    @3: I fear that is a feature of the internals of your library uSDFS. Could that be due to some interrupt switching on/off during recording? If so, could there be any fix for that? I really need a clean audio chain while the Teensy records to the SD card

    Any help greatly appreciated!

    Frank

    [EDIT]script edited 2016_11_7

    Code:
    /*//Copyright 2016 by Walter Zimmer
    // Version 20-10-16
    //
    // Test-script with audio queue by DD4WH 2016_11_06
    // Audio: MIC-I2S-buffer-write to SD card
    // while simultaneously listening to audio
    // at 96k to 192k sample rate
    // 
    // - REC of RAW files to SD works at 96k & 192k --> playback with Audacity indicates good quality at 96k, not sure at 192k 
    // - playback not working 
    // - loud stuttering noise in the audio chain while recording (but that is not on the recordings!)
    //
     *        made possible by the samplerate code by Frank Boesing, thanks Frank!
     * 
     *  tested on Teensy 3.5 + Teensy audio board 
     *  + standard tiny electret MIC soldered to the MIC Input of the audio board 
     *  
     *  
     *  User adjustments - with buttons
     *  
     *  MIC-GAIN                  33 + 34 -
     *  FREQUENCY                 35 + 36 -
     *  SAMPLE RATE               37 + 38 -
     *  WATERFALL/SPECTRUM        39  
     *  REC                       31
     *  STOP                      30
     *  PLAY                      29
     *  
     * Audio sample rate code - function setI2SFreq  
     * Copyright (c) 2016, Frank Bösing, f.boesing@gmx.de
    */
    
    #include "ff.h"
    #include "ff_utils.h"
    #include <Audio.h>
    
    #define USE_USB_SERIAL
    #ifdef USE_USB_SERIAL
      #define SERIALX Serial
    #else
      #define SERIALX Serial1
    #endif
    
    FRESULT rc;        /* Result code */
    FATFS fatfs;      /* File system object */
    FIL fil;        /* File object */
    
    #define MXFN 100 // maximal number of files 
    #if defined(__MK20DX256__)
      #define BUFFSIZE (8*1024) // size of buffer to be written
    #elif defined(__MK66FX1M0__)
      #define BUFFSIZE (128*1024) // size of buffer to be written
    #endif
    
    uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
    UINT wr;
    uint32_t nj = 0;
    
    #include <Bounce.h>
    #include <Metro.h>
    
    #include <ILI9341_t3.h>
    #include "font_Arial.h"
    
    #define VERSION     " v0.3"
    
    #define BACKLIGHT_PIN 0
    #define TFT_DC      20
    #define TFT_CS      21
    #define TFT_RST     32  // 255 = unused. connect to 3.3V
    #define TFT_MOSI     7
    #define TFT_SCLK    14
    #define TFT_MISO    12
    
    // would be nice to use the fast DMA lib, but it does not work on my Teensy 3.5
    //ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    
    #define BUTTON_MIC_GAIN_P       33     
    #define BUTTON_MIC_GAIN_M       34
    #define BUTTON_FREQ_P           35     
    #define BUTTON_FREQ_M           36     
    #define BUTTON_SAMPLE_RATE_P    37     
    #define BUTTON_SAMPLE_RATE_M    38     
    #define BUTTON_TOGGLE_WATERFALL 39
    #define BUTTON_RECORD           31
    #define BUTTON_STOP             30
    #define BUTTON_PLAY             29
    
    Bounce mic_gain_P = Bounce(BUTTON_MIC_GAIN_P, 50); 
    Bounce mic_gain_M = Bounce(BUTTON_MIC_GAIN_M, 50); 
    Bounce freq_P = Bounce(BUTTON_FREQ_P, 50); 
    Bounce freq_M = Bounce(BUTTON_FREQ_M, 50); 
    Bounce sample_rate_P = Bounce(BUTTON_SAMPLE_RATE_P, 50);
    Bounce sample_rate_M = Bounce(BUTTON_SAMPLE_RATE_M, 50);
    Bounce toggle_waterfall = Bounce(BUTTON_TOGGLE_WATERFALL, 50);
    Bounce button_Record = Bounce(BUTTON_RECORD, 50);
    Bounce button_Stop = Bounce(BUTTON_STOP, 50);
    Bounce button_Play = Bounce(BUTTON_PLAY, 50);
      
    #define SAMPLE_RATE_MIN               0
    #define SAMPLE_RATE_8K                0
    #define SAMPLE_RATE_11K               1
    #define SAMPLE_RATE_16K               2  
    #define SAMPLE_RATE_22K               3
    #define SAMPLE_RATE_32K               4
    #define SAMPLE_RATE_44K               5
    #define SAMPLE_RATE_48K               6
    #define SAMPLE_RATE_88K               7
    #define SAMPLE_RATE_96K               8
    #define SAMPLE_RATE_176K              9
    #define SAMPLE_RATE_192K              10
    #define SAMPLE_RATE_MAX               10
    
    // this audio comes from the codec by I2S2
    AudioInputI2S               i2s_in; // MIC input
    AudioRecordQueue            recorder; 
    //AudioSynthWaveformSine      sine1; // local oscillator
    //AudioEffectMultiply         mult1; // multiply = mix
    //AudioAnalyzeFFT256          myFFT; // for spectrum display
    //AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
    AudioPlaySdRaw              player; 
    AudioMixer4                 mix1;
    AudioOutputI2S              i2s_out; // headphone output          
    
    AudioConnection patch3      (i2s_in, 0, recorder, 0);
    AudioConnection patch4      (i2s_in, 0, mix1, 0);
    //AudioConnection path7      (i2s_in, 0, fft1024_1, 0);
    //AudioConnection path7      (i2s_in, 0, myFFT, 0);
    AudioConnection patch5      (player, 0, mix1, 1);
    AudioConnection patch8      (mix1, 0, i2s_out, 0);
    AudioConnection patch9      (mix1, 0, i2s_out, 1);
    
    AudioControlSGTL5000        sgtl5000_1;  
    
    // Metro 1 second
    Metro second = Metro(1000);
    
    const int8_t    MODE_STOP = 0;
    const int8_t    MODE_REC = 1;
    const int8_t    MODE_PLAY = 2;
    ulong en=0;
    int mode = MODE_STOP; 
    //File frec; // audio is recorded to this file first
    uint32_t file_number = 0;
    
    int count_help = 0;
    int8_t waterfall_flag = 0;
    int idx_t = 0;
    int idx = 0;
    int64_t sum;
    float32_t mean;
    
    int8_t mic_gain = 34; // start detecting with this MIC_GAIN in dB
    int freq_real = 22000; // start detecting at this frequency
    int sample_rate = SAMPLE_RATE_96K;
    int sample_rate_real = 96000;
    String text="96k";
    int freq_LO = 7000;
    int16_t FFT_bin [128]; 
    int barm[512];
    
    typedef struct SR_Descriptor
    {
        const int SR_n;
        const char* const f1;
        const char* const f2;
        const char* const f3;
        const char* const f4;
        const float32_t x_factor;
    } SR_Desc;
    
    // Text and position for the FFT spectrum display scale
    const SR_Descriptor SR [SAMPLE_RATE_MAX + 1] =
    {
        //   SR_n ,  f1, f2, f3, f4, x_factor = pixels per f1 kHz in spectrum display
        {  SAMPLE_RATE_8K,  " 1", " 2", " 3", " 4", 64.0}, // which means 64 pixels per 1 kHz
        {  SAMPLE_RATE_11K,  " 1", " 2", " 3", " 4", 43.1}, 
        {  SAMPLE_RATE_16K,  " 2", " 4", " 6", " 8", 64.0}, 
        {  SAMPLE_RATE_22K,  " 2", " 4", " 6", " 8", 43.1}, 
        {  SAMPLE_RATE_32K,  "5", "10", "15", "20", 80.0}, 
        {  SAMPLE_RATE_44K,  "5", "10", "15", "20", 58.05}, 
        {  SAMPLE_RATE_48K,  "5", "10", "15", "20", 53.33},
        {  SAMPLE_RATE_88K,  "10", "20", "30", "40", 58.05},
        {  SAMPLE_RATE_96K,  "10", "20", "30", "40", 53.33},
        {  SAMPLE_RATE_176K,  "20", "40", "60", "80", 58.05},
        {  SAMPLE_RATE_192K,  "20", "40", "60", "80", 53.33} // which means 53.33 pixels per 20kHz
    };    
    
    //const int myInput = AUDIO_INPUT_LINEIN;
    const int myInput = AUDIO_INPUT_MIC;
    
    
    
    
    // Stop with dying message 
    void die(char *str, FRESULT rc);
    void setup();
    void loop();
    
    extern "C" uint32_t usd_getError(void);
    struct tm seconds2tm(uint32_t tt);
    
    void die(char *str, FRESULT rc) 
    { SERIALX.printf("%s: Failed with rc=%u.\n", str, rc); for (;;) delay(100); }
    
    //=========================================================================
    uint32_t count=0;
    uint32_t ifn=0;
    uint32_t isFileOpen=0;
    char filename[80];
    TCHAR wfilename[80];
    uint32_t t0=0;
    uint32_t t1=0;
    
    void blink(uint16_t msec)
    {
      digitalWriteFast(13,!digitalReadFast(13)); delay(msec);
    }
    
    void setup()
    {
    
        Serial.begin(115200);
      delay(200);
    
    // only for playback !
      if(!(SD.begin(BUILTIN_SDCARD))) 
      {
          while (1) {
              Serial.println("Unable to access the SD card");
              delay(500);  
          }
      }
    // this is for recording
      f_mount (&fatfs, (TCHAR *)_T("0:/"), 0);      /* Mount/Unmount a logical drive */
    
    //setup pins with pullups
      pinMode(BUTTON_MIC_GAIN_P,INPUT_PULLUP);
      pinMode(BUTTON_MIC_GAIN_M,INPUT_PULLUP);
      pinMode(BUTTON_FREQ_P,INPUT_PULLUP);  
      pinMode(BUTTON_FREQ_M,INPUT_PULLUP);  
      pinMode(BUTTON_SAMPLE_RATE_P,INPUT_PULLUP);  
      pinMode(BUTTON_SAMPLE_RATE_M,INPUT_PULLUP);  
      pinMode(BUTTON_TOGGLE_WATERFALL,INPUT_PULLUP);  
      pinMode(BUTTON_RECORD,INPUT_PULLUP);  
      pinMode(BUTTON_STOP,INPUT_PULLUP);  
      pinMode(BUTTON_PLAY,INPUT_PULLUP);  
    
      // Audio connections require memory. 
      AudioMemory(200);
    /*
      setSyncProvider(getTeensy3Time);
    */
    // Enable the audio shield. select input. and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
      sgtl5000_1.micGain (mic_gain);
      sgtl5000_1.adcHighPassFilterDisable(); // does not help too much!
    
    // Init TFT display  
      pinMode( BACKLIGHT_PIN, OUTPUT );
      analogWrite( BACKLIGHT_PIN, 1023 );
    
      tft.begin();
      tft.setRotation( 3 );
      tft.fillScreen(ILI9341_BLACK);
      tft.setCursor(14, 7);
      tft.setTextColor(ILI9341_ORANGE);
      tft.setFont(Arial_12);
      tft.print("Teensy Bat Detector  "); tft.print(VERSION);
      tft.setTextColor(ILI9341_WHITE);
    
      display_settings();
    
      set_sample_rate (sample_rate);
    
      mix1.gain(0,1); 
      mix1.gain(1,1); 
      
    }
    
    void loop()
    {
       controls();
    //   spectrum(); 
           check_processor();
    } // end LOOP
    
    
    /* void spectrum() { // spectrum analyser code by rheslip - modified
         if (myFFT.available()) {
    //     if (fft1024_1.available()) {
        int scale;
        scale = 5;
      for (int16_t x = 2; x < 128; x++) {
    //  for (int16_t x = 8; x < 512; x+=4) {
         FFT_bin[x] = abs(myFFT.output[x]); 
    //     FFT_bin[x/4] = abs(fft1024_1.output[x]); 
         int bar = (FFT_bin[x] * scale);
         if (bar >175) bar=175;
         // this is a very simple first order IIR filter to smooth the reaction of the bars
         bar = 0.05 * bar + 0.95 * barm[x]; 
    //     if (bar > peak[x]) peak[x]=bar;
    //     tft.drawFastVLine(x, 210-bar,bar, ILI9341_PURPLE);
    //     tft.drawFastVLine(x*2+10, 210-bar,bar, ILI9341_PINK);
    
         tft.drawPixel(x*2+10, 210-barm[x], ILI9341_BLACK);
         tft.drawPixel(x*2+10, 210-bar, ILI9341_WHITE);
    
    //     tft.drawFastVLine(x*2+10, 20, 210-bar-20, ILI9341_BLACK);    
    
    //     tft.drawPixel(x*2+10,209-peak[x], ILI9341_YELLOW);
    
    //     if(peak[x]>0) peak[x]-=1;
         barm[x] = bar;
      }
    //     search_bats();     
      } //end if
    } // end void spectrum
    */
    
    void controls() {
    
    // first, check buttons
      mic_gain_P.update();
      mic_gain_M.update();
      freq_P.update();
      freq_M.update();
      sample_rate_P.update();
      sample_rate_M.update();
      toggle_waterfall.update();
      button_Record.update();
      button_Stop.update();
      button_Play.update();
    
      // Respond to button presses
      if (button_Record.fallingEdge()) {
        Serial.println("Record Button Press");
        if (mode == MODE_PLAY) stopPlaying();
        if (mode == MODE_STOP) startRecording();
      }
      if (button_Stop.fallingEdge()) {
        Serial.println("Stop Button Press");
        if (mode == MODE_REC) stopRecording();
        if (mode == MODE_PLAY) stopPlaying();
      }
      if (button_Play.fallingEdge()) {
        Serial.println("Play Button Press");
        if (mode == MODE_REC) stopRecording();
        if (mode == MODE_STOP) startPlaying();
      }
    
      // If we're playing or recording, carry on...
      if (mode == MODE_REC) {
        continueRecording();
      }
      if (mode == MODE_PLAY) {
        continuePlaying();
      }
    
      // change MIC GAIN  
      if ( mic_gain_P.fallingEdge()) { 
          mic_gain = mic_gain + 2;
          if (mic_gain > 63) {
            mic_gain = 63;
          }
          set_mic_gain(mic_gain);
      }
      if ( mic_gain_M.fallingEdge()) { 
          mic_gain = mic_gain - 2;
          if (mic_gain < 0) {
            mic_gain = 0;
          }
          set_mic_gain(mic_gain);
      }
    
      // change sample rate
      if ( sample_rate_P.fallingEdge()) { 
          sample_rate = sample_rate + 1;
                if (sample_rate > SAMPLE_RATE_MAX) {
            sample_rate = SAMPLE_RATE_MAX;
          }
            set_sample_rate (sample_rate);
      }
      if ( sample_rate_M.fallingEdge()) { 
          sample_rate = sample_rate - 1;
                if (sample_rate < SAMPLE_RATE_MIN) {
            sample_rate = SAMPLE_RATE_MIN;
          }
            set_sample_rate (sample_rate);
      }
    
    } // END function "controls"
    
    void       set_mic_gain(int8_t gain) {
        AudioNoInterrupts();
        sgtl5000_1.micGain (mic_gain);
        AudioInterrupts();
        display_settings();    
    } // end function set_mic_gain
    
    void      display_settings() {
        tft.fillRect(14,32,200,17,ILI9341_BLACK);
        tft.setCursor(14, 32);
        tft.setFont(Arial_12); 
        tft.print("gain: "); tft.print (mic_gain);
        tft.print("     "); 
        tft.print("freq: "); tft.print (freq_real);
        tft.print("    "); 
        tft.fillRect(232,32,88,17,ILI9341_BLACK);
        tft.setCursor(232, 32);
        tft.print("       "); 
        tft.print (text);
        Serial.println(text);       
     /*  // only for debugging  
        tft.fillRect(0,122,200,17,ILI9341_BLACK);
        tft.setCursor(0, 122);
        tft.print("LO: "); tft.print (freq_LO);
        tft.print("   "); 
        */
    }
    
    void      set_sample_rate (int sr) {
      switch (sr) {
        case SAMPLE_RATE_8K:
        sample_rate_real = 8000;
        text = " 8k";
        break;
        case SAMPLE_RATE_11K:
        sample_rate_real = 11025;
        text = "11k";
        break;
        case SAMPLE_RATE_16K:
        sample_rate_real = 16000;
        text = "16k";
        break;
        case SAMPLE_RATE_22K:
        sample_rate_real = 22050;
        text = "22k";
        break;
        case SAMPLE_RATE_32K:
        sample_rate_real = 32000;
        text = "32k";
        break;
        case SAMPLE_RATE_44K:
        sample_rate_real = 44100;
        text = "44.1k";
        break;
        case SAMPLE_RATE_48K:
        sample_rate_real = 48000;
        text = "48k";
        break;
        case SAMPLE_RATE_88K:
        sample_rate_real = 88200;
        text = "88.2k";
        break;
        case SAMPLE_RATE_96K:
        sample_rate_real = 96000;
        text = "96k";
        break;
        case SAMPLE_RATE_176K:
        sample_rate_real = 176400;
        text = "176k";
        break;
        case SAMPLE_RATE_192K:
        sample_rate_real = 192000;
        text = "192k";
        break;
      }
        AudioNoInterrupts();
        setI2SFreq (sample_rate_real); 
        delay(200); // this delay seems to be very essential !
        AudioInterrupts();
        delay(20);
        display_settings();
    //    prepare_spectrum_display();
    } // END function set_sample_rate
    
    
    // set samplerate code by Frank Boesing 
    void setI2SFreq(int freq) {
      typedef struct {
        uint8_t mult;
        uint16_t div;
      } tmclk;
    
      const int numfreqs = 14;
      const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 176400, (int)44117.64706 * 4, 192000};
    
    #if (F_PLL==16000000)
      const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
    #elif (F_PLL==72000000)
      const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
    #elif (F_PLL==96000000)
      const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
    #elif (F_PLL==120000000)
      const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
    #elif (F_PLL==144000000)
      const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
    #elif (F_PLL==168000000)
      const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
    #elif (F_PLL==180000000)
      const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
    #elif (F_PLL==192000000)
      const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
    #elif (F_PLL==216000000)
      const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
    #elif (F_PLL==240000000)
      const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
    #endif
    
      for (int f = 0; f < numfreqs; f++) {
        if ( freq == samplefreqs[f] ) {
          while (I2S0_MCR & I2S_MCR_DUF) ;
          I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
          return;
        }
      }
    }
    
    void check_processor() {
          if (second.check() == 1) {
          Serial.print("Proc = ");
          Serial.print(AudioProcessorUsage());
          Serial.print(" (");    
          Serial.print(AudioProcessorUsageMax());
          Serial.print("),  Mem = ");
          Serial.print(AudioMemoryUsage());
          Serial.print(" (");    
          Serial.print(AudioMemoryUsageMax());
          Serial.println(")");
    /*      tft.fillRect(100,120,200,80,ILI9341_BLACK);
          tft.setCursor(10, 120);
          tft.setTextSize(2);
          tft.setTextColor(ILI9341_WHITE);
          tft.setFont(Arial_14);
          tft.print ("Proc = ");
          tft.setCursor(100, 120);
          tft.print (AudioProcessorUsage());
          tft.setCursor(180, 120);
          tft.print (AudioProcessorUsageMax());
          tft.setCursor(10, 150);
          tft.print ("Mem  = ");
          tft.setCursor(100, 150);
          tft.print (AudioMemoryUsage());
          tft.setCursor(180, 150);
          tft.print (AudioMemoryUsageMax());
         */ 
          AudioProcessorUsageMaxReset();
          AudioMemoryUsageMaxReset();
        }
    } // END function check_processor
    
    void startRecording() {
      recorder.begin();
      mode = MODE_REC;
      Serial.print("startRecording");
        // close file
        if(isFileOpen)
        {
          //close file
          rc = f_close(&fil);
          if (rc) die("close", rc);
          //
          isFileOpen=0;
        }
      
      if(!isFileOpen)
      {
          file_number++;
      sprintf(filename, "Bat_%u.raw", file_number);
        Serial.println(filename);
      char2tchar(filename, 80, wfilename);
      rc = f_stat (wfilename, 0);
        Serial.printf("stat %d %x\n",rc,fil.obj.sclust);
      rc = f_open (&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
        Serial.printf(" opened %d %x\n\r",rc,fil.obj.sclust);
     
        // check if file is Good
        if(rc == FR_INT_ERR)
        { // only option is to close file
            rc = f_close(&fil);
            if(rc == FR_INVALID_OBJECT)
            { Serial.println("unlinking file");
              rc = f_unlink(wfilename);
              if (rc) die("unlink", rc);
            }
            else
              die("close", rc);
        }
        // retry open file
        rc = f_open(&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
        if(rc) die("open", rc);
        
        isFileOpen=1;
      }
    }
    
    void continueRecording() {
      const uint32_t N_BUFFER = 1;
      const uint32_t N_LOOPS = 256;
      // buffer size total = 256 * n_buffer * n_loops
      // new plan: use 32*1024 = 32768 byte buffer size
      // queue: write n_buffer blocks * 256 bytes to buffer at a time; free queue buffer;
      // repeat n_loops times ( * n_buffer * 256 = total amount to write at one time)
      // then write to SD card
    
      if (recorder.available() >= N_BUFFER  ) {// one buffer = 256 (8bit)-bytes = block of 128 16-bit samples
        for (int i = 0; i < N_BUFFER; i++) {
        memcpy(buffern + i*256 + nj * 256 * N_BUFFER, recorder.readBuffer(), 256);
        recorder.freeBuffer();
    //    Serial.println(en);
    //    en++;
        } 
        if (nj >=  (N_LOOPS - 1)) {
          nj = 0;
        rc = f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
    //    Serial.println ("Writing");
           if (rc== FR_DISK_ERR) // IO error
         {  uint32_t usd_error = usd_getError();
            Serial.printf(" write FR_DISK_ERR : %x\n\r",usd_error);
            // only option is to close file
            // force closing file
         }
         else if(rc) die("write",rc);
        }
      nj ++;
      }
    
    }
    
    void stopRecording() {
      Serial.print("stopRecording");
      recorder.end();
      if (mode == MODE_REC) {
        while (recorder.available() > 0) {
        rc = f_write (&fil, (byte*)recorder.readBuffer(), 256, &wr);
    //      frec.write((byte*)recorder.readBuffer(), 256);
          recorder.freeBuffer();
        }
          //close file
          rc = f_close(&fil);
          if (rc) die("close", rc);
          //
          isFileOpen=0;
    //    frec.close();
    //    playfile = recfile;
      }
      mode = MODE_STOP;
    //  clearname();
      Serial.println (" Recording stopped!");
    }
    
    void startPlaying() {
    //      String NAME = "Bat_"+String(file_number)+".raw";
    //      char fi[15];
    //      NAME.toCharArray(fi, sizeof(NAME));
          mix1.gain(1,1);
          mix1.gain(0,0);       
      Serial.println("startPlaying ");
      Serial.println ("Playfile: "); Serial.print (filename);
      Serial.println ("Name: "); Serial.print (filename);
      delay(100);
        player.play(filename);
    //    player.play("Bat_1.raw");
    //    player.play("REC1.RAW");
    
      mode = MODE_PLAY;
    }
      
    void continuePlaying() {
      if (!player.isPlaying()) {
        player.stop();
        mode = MODE_STOP;
          mix1.gain(1,0);
          mix1.gain(0,1);       
        Serial.println("End of recording");
      }
    }
    
    void stopPlaying() {
          mix1.gain(1,0);
          mix1.gain(0,1);       
      Serial.print("stopPlaying");
      if (mode == MODE_PLAY) player.stop();
      mode = MODE_STOP;
      Serial.println (" Playing stopped");
    }
    Last edited by DD4WH; 11-07-2016 at 08:46 AM.

  11. #11
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    @2: solved. Playback now works in the script (script in #10 updated!)

    @3: seems to be related to processor load AND the no. of audio buffers I take for the queue object AND the size of the buffer that is written to the SD card. AND the noise ticks are also on the recordings, however a lot more silent than in the realtime audio. The tick seems to be the writing sound of one buffer file to the SD card, as the frequency of the ticks is inversely related to the buffer size ;-). Can you switch that off? ;-)

    It seems that the audio is getting distorted less heavily when I choose a low number of audio queue buffers and a large buffer file to be written to the SD. Would be very interested in comments and advice on how to optimise that.
    Last edited by DD4WH; 11-07-2016 at 08:46 AM.

  12. #12
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Very strange effect of SD card type and associated processor load while writing to these cards (taking the script from post #10 plus a running FFT1024 object !):

    SD card Fuji 8GB HC Class 10:
    -clicking sound on the recordings and while recording
    - The clicking sound while writing to the SD card is coupled with a displayed processor usage that exceeds 100% at times!

    SD card 2GB Medion (the oldest and ugliest SD I ever had)
    - no more clicking sound while recording and also no clicking sound in the recording
    - processor usage < 30%

    SD card 2GB SanDisk (also a very old one)
    - no more clicking sound while recording and also no clicking sound in the recording
    - processor usage < 30%

    So, do I have to buy a bunch of antique SD cards to make the uSDFS library and my bat detector work properly? ;-)

    Frank
    Last edited by DD4WH; 11-07-2016 at 09:30 AM.

  13. #13
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by DD4WH View Post
    Walter, but now I need your help ;-).
    Sorry, was offline this morning.
    will read your posts and try to figure out a solution

  14. #14
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by DD4WH View Post
    So, do I have to buy a bunch of antique SD cards to make the uSDFS library and my bat detector work properly? ;-)
    Frank
    This is very likely.
    If you do not need the capacity I would stick with low capacity, low density SD cards. Manufacturer, quality play also a role.
    All SD cards have a small mcu that translate logic block units into physical blocks, to avoid bad blocks (less frequent on low density cards) and to allow wear levelling.

    While most of the times SD cards write with the specified speed, form time to time, sd cards need extreme long times to write a block, as they are internal busy. So some buffering is required.

    Fortunately the T3.6 has more memory than T3.2 so you can provide some significant buffer.

    I will see if I can make a fast example for this.

    Also you could try a 32 GB card formatted in exFAT and not FAT32. exFAT has some particularities that facilitate fast logging, so I would try it.

  15. #15
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Thanks, Walter,

    now things become clearer. I was just very surprised that the old, slow cards are in fact more compatible with fast logging of audio samples than the new "fast" cards.

    Below you can see my buffering and write code at the moment. (I omitted the code to create/open/close the file).

    I take the audio from the queue buffer, wait until 2 or more buffers (of 128 16bit samples) are in the queue and put them in a large buffer. That is repeated until I have 64kbytes in the buffer which are then written at a time. Is that large enough? It makes a click noise every time it is written.

    Would be good if someone could have a look on that and give some hints on improvement with the buffering and queueing, because I do not know much about that and might be doing something fundamentally wrong.

    Frank

    P.S.: How can I format an SD card in exFAT ?


    Code:
    #define BUFFSIZE (64*1024) // size of buffer to be written
    uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
    uint16_t nj = 0;
    
    . . . .
    
    void continueRecording() {
      const uint32_t N_BUFFER = 2;
      const uint32_t N_LOOPS = 128;
      // buffer size total = 256 * n_buffer * n_loops
      // queue: write n_buffer blocks * 256 bytes to buffer at a time; free queue buffer;
      // repeat n_loops times ( * n_buffer * 256 = total amount to write at one time)
      // then write to SD card
    
      if (recorder.available() >= N_BUFFER  ) {// one buffer = 256 (8bit)-bytes = block of 128 16-bit samples
        for (int i = 0; i < N_BUFFER; i++) {
        memcpy(buffern + i*256 + nj * 256 * N_BUFFER, recorder.readBuffer(), 256);
        recorder.freeBuffer();
    //    Serial.println(en);
    //    en++;
        } 
        if (nj >=  (N_LOOPS - 1)) {
          nj = 0;
        rc = f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
    //    Serial.println ("Writing");
           if (rc== FR_DISK_ERR) // IO error
         {  uint32_t usd_error = usd_getError();
            Serial.printf(" write FR_DISK_ERR : %x\n\r",usd_error);
            // only option is to close file
            // force closing file
         }
         else if(rc) die("write",rc);
        }
      nj ++;
      }
    
    }

  16. #16
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by DD4WH View Post
    Thanks, Walter,

    now things become clearer. I was just very surprised that the old, slow cards are in fact more compatible with fast logging of audio samples than the new "fast" cards.

    Below you can see my buffering and write code at the moment. (I omitted the code to create/open/close the file).

    I take the audio from the queue buffer, wait until 2 or more buffers (of 128 16bit samples) are in the queue and put them in a large buffer. That is repeated until I have 64kbytes in the buffer which are then written at a time. Is that large enough? It makes a click noise every time it is written.

    Would be good if someone could have a look on that and give some hints on improvement with the buffering and queueing, because I do not know much about that and might be doing something fundamentally wrong.

    Frank

    P.S.: How can I format an SD card in exFAT ?

    I have not digested yet your example code, as I was trying t setup an example myself
    Code:
    //Copyright 2016 by Walter Zimmer
    // Version 07-11-16
    //
    #include "ff.h"
    #include "ff_utils.h"
    
    #define USE_USB_SERIAL
    #ifdef USE_USB_SERIAL
    	#define SERIALX Serial
    #else
    	#define SERIALX Serial1
    #endif
    
    FRESULT rc;        /* Result code */
    FATFS fatfs;      /* File system object */
    FIL fil;        /* File object */
    
    #define NSAMP 128 // number of words in data buffer
    #define NDAT (2*NSAMP) // bytes in data buffer (input)
    #define FSAMP 192000
    uint8_t Data[NDAT] __attribute__( ( aligned ( 16 ) ) ); // frome source
    
    uint32_t count=0;
    UINT wr;
    
    /* Stop with dying message */
    void die(char *str, FRESULT rc);
    void setup();
    void loop();
    
    extern "C" uint32_t usd_getError(void);
    struct tm seconds2tm(uint32_t tt);
    
    
    void die(char *str, FRESULT rc) 
    { SERIALX.printf("%s: Failed with rc=%u.\n", str, rc); for (;;) delay(100); }
    
    //=========================================================================
    void blink(uint16_t msec)
    { digitalWriteFast(13,!digitalReadFast(13)); delay(msec);
    }
    /***************************************************************/
    void  doLogging( uint8_t *buffer, uint16_t ndat);
    
    #define FREQ (FSAMP/NSAMP) // firing rate for data block input (simulated acquisition)
    
    void timer_init(void)
    {
      SIM_SCGC6 |= SIM_SCGC6_PIT;
      // turn on PIT     
      PIT_MCR = 0x00;
    }
    
    void timer_start(void) 
    {//    
      PIT_LDVAL0 = F_BUS/FREQ; // setup timer 0 for (F_BUS/frequency) cycles     
      PIT_TCTRL0 = 2; // enable Timer 0 interrupts      
      NVIC_SET_PRIORITY(IRQ_PIT_CH0, 7*16); 
      NVIC_ENABLE_IRQ(IRQ_PIT_CH0);
      PIT_TCTRL0 |= 1; // start Timer 0
    }
    
    //
    void pit0_isr(void)
    { //
      PIT_TFLG0=1;
      for(int ii=0; ii<NDAT; ii++) Data[ii]=(count + ii) % 256;
      doLogging(Data, NDAT);
    }
    
    /*-----------------------------------------------------------*/
    void setup()
    {
      // wait for serial line to come up
      pinMode(13,OUTPUT);
      pinMode(13,HIGH);
      while(!SERIALX) blink(100);
      
      #ifndef USB_SERIAL
      	SERIALX.begin(115200,SERIAL_8N1_RXINV_TXINV);
      #endif
      SERIALX.println("\nLogger Buffered Write Test");
      SERIALX.printf("F_BUS %d MHz\n\r",F_BUS/1000000);
      SERIALX.printf("Sample Rate %d Hz\n\r",FSAMP);
      SERIALX.printf("Buffer Update Rate %d Hz\n\r",FREQ);
    
      rc = f_mount (&fatfs, (TCHAR *)_T("0:/"), 0);      /* Mount/Unmount a logical drive */
      if (rc) die("mount", rc);
    
      timer_init();
      timer_start();
    }
    
    void loop()
    { delay(1000);
    }
    
    /***************************** LOGGING ***********************/
    #define MXFN 100 // maximal number of files 
    #define MAX_BLOCK_COUNT 1000  // number of 512-byte blocks in file
    
    #if defined(__MK20DX256__)
      #define BUFFSIZE (8*1024) // size of buffer to be written
      #define DISK_BUFFSIZE (2*BUFFSIZE) // size of buffer to be written
    #elif defined(__MK66FX1M0__)
      #define BUFFSIZE (32*1024) // size of buffer to be written
      #define DISK_BUFFSIZE (4*BUFFSIZE) // size of buffer to be written
    #endif
    
    #define INF ((uint32_t) (-1))
    uint8_t Buffer[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) ); // to disk
    uint8_t diskBuffer[DISK_BUFFSIZE] __attribute__( ( aligned ( 16 ) ) ); // storage
    
    uint32_t ifn=0;
    uint32_t isFileOpen=0;
    char filename[80];
    TCHAR wfilename[80];
    uint32_t t0=0;
    uint32_t t1=0;
    
    
    /*-------------------------------------------------------------------------*/
    void doLogging(uint8_t *data, uint16_t nbuf)
    { // is called whenever new data are available
      // keeps state of filing 
      // does open/close of file when required
      //
      static uint32_t n0_dat=0, n1_dat = INF;
      static uint16_t overrunRisk=0;
      static uint16_t gotData=0; 
    
      static uint16_t isLogging = 0;
    
      if(isLogging) return;
      isLogging=1;
      if(ifn>MXFN) 
      { blink(500); isLogging=0; return; }
    
      if(!count && gotData)
      {
        // close file
        if(isFileOpen)
        {
          //close file
          rc = f_close(&fil);
          if (rc) die("close", rc);
          //
          isFileOpen=0;
          t1=micros();
          float MBs = (1000.0f*BUFFSIZE)/(1.0*(t1-t0));
          SERIALX.printf(" (%d - %f MB/s)\n\r",t1-t0,MBs);
          gotData = 0;
        }
      }
        
      //
      if(!isFileOpen)
      {
        // open new file
        ifn++;
        if(ifn>MXFN) 
        { pinMode(13,OUTPUT); isLogging=0; return; } // at end of test: prepare for blinking
    
        sprintf(filename,"X_%05d.dat",ifn);
        SERIALX.println(filename);
        char2tchar(filename,80,wfilename);
        //
        // check status of file
        rc =f_stat(wfilename,0);
        SERIALX.printf("stat %d %x\n",rc,fil.obj.sclust);
        
        rc = f_open(&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
        SERIALX.printf(" opened %d %x\n\r",rc,fil.obj.sclust);
        // check if file is Good
        if(rc == FR_INT_ERR)
        { // only option is to close file
            rc = f_close(&fil);
            if(rc == FR_INVALID_OBJECT)
            { SERIALX.println("unlinking file");
              rc = f_unlink(wfilename);
              if (rc) die("unlink", rc);
            }
            else
              die("close", rc);
            
        }
        // retry open file
        rc = f_open(&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
        if(rc) die("open", rc);
        
        isFileOpen=1;
        t0=micros();
      }
      
      if(isFileOpen)
      {
        // copy data to disk buffer
        if(n1_dat==INF) // reset pointers
        { n1_dat=0;
          n0_dat=0;
        }
        else if((n1_dat - n0_dat + DISK_BUFFSIZE)%DISK_BUFFSIZE>=(DISK_BUFFSIZE-nbuf)) 
        { // too close to buffer overrun
          SERIALX.printf("x %d %d %d\n\r",n0_dat,n1_dat,overrunRisk);
          n1_dat = INF;
          isLogging=0;
          return;
        }
    
        //    
        // fill temp buffer
        for(int ii=0; ii<nbuf; ii++) diskBuffer[n1_dat+ii]= data[ii]; 
        n1_dat += nbuf;
        n1_dat %= DISK_BUFFSIZE;
        //
        // send to disk
        while((n1_dat - n0_dat + DISK_BUFFSIZE)%DISK_BUFFSIZE>=BUFFSIZE)
        {  // copy now data
           for(int ii=0;ii<BUFFSIZE;ii++) Buffer[ii]= diskBuffer[n0_dat+ii];
           n0_dat += BUFFSIZE; 
           n0_dat %= DISK_BUFFSIZE;
           overrunRisk = (n0_dat==n1_dat);
           count++;
           //
           //write data to file 
           if(!(count%10))SERIALX.printf(".");
           if(!(count%640)) SERIALX.println(); SERIALX.flush();
           //
           rc = f_write(&fil, Buffer, BUFFSIZE, &wr);
           if (rc== FR_DISK_ERR) // IO error
           {  uint32_t usd_error = usd_getError();
              SERIALX.printf(" write FR_DISK_ERR : %x\n\r",usd_error);
              // only option is to close file
              // force closing file
              count=MAX_BLOCK_COUNT; // set to max block count
           }
           else if(rc) die("write",rc);
          //
           count %= MAX_BLOCK_COUNT;
           gotData = 1;
        }
      }   
      isLogging=0; 
    }
    I put all logging actions into a single function that maintains the state of the logging operation. (For experts, improvements are welcome!)
    I will put it later as example onto github.

    Re formatting: the easiest way is to use windows formatting utility: right click on disk, choose format, choose exFAT, tick quick format and format.
    (exFAT is the default for disk >32 GB) Unfortunately I have no small disk available to see the options there.
    Otherwise, there are formatting tools on the Web.
    Also, you could use uSDFS f_mkfs function. Have a look in the uSDFS/doc directory there is the complete documentation of Elm-CHaN, the originator of FATFS, the basis of uSDFS. CHaN gives an example of how to format the disks.

  17. #17
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,665
    I've read, the windows-formatting is not good for SD-Cards - Best is to use the official tool ? Is that true ?
    https://www.sdcard.org/downloads/formatter_4/



  18. #18
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by Frank B View Post
    I've read, the windows-formatting is not good for SD-Cards - Best is to use the official tool ? Is that true ?
    https://www.sdcard.org/downloads/formatter_4/


    It may depend. exFAT is property of Microsoft and only available by 'error' as some company (I don't recall who) published the source code.
    Nevertheless, format is so easy that is can be reverse engineered.
    But you are right, sdcard.org provides a formatter that I use if MS-Format does not work to my expectation.

  19. #19
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Thanks Walter, for the example! It looks like I implemented something very similar taking code from your old RawWrite example, however there are many more if-cases to ensure proper functioning in the new example. I will implement that in the bat detector code too, thanks for that!

    However, some questions remain:

    1. as I am taking audio samples from the queue object, my "logging device" is the audio queue. I am not sure whether I have properly implemented the interplay between the queue and the buffer I use for the write process. Would be good, if someone among the specialists could comment on that. Code is in #15.

    2. my buffer is this:
    Code:
    #define BUFFSIZE (64*1024) // size of buffer to be written
    uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
    which I write to the SD using this:
    Code:
      const uint32_t N_BUFFER = 2;
    const uint32_t N_LOOPS = 128;
    . . . .
    rc = f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
    Why do I need a 64k buffer to write 32768 bytes? Or is that buffer too large? And is it possible to write 64kbyte at one time with the f_write command?

    3. I also tried to use more audio buffers in the queue and write smaller files at a time to the SD, but that results in more clicks in the audio.

    4. Playback of the files is done by using the playSDRaw-object of the audio lib and that uses the Sd-lib for playback. However, that does not seem to be able to use exFAT formatted SDs!? So, is there any alternative how I could playback audio-RAW files from an exFAT-formatted SD card on the Teensy 3.6?

    Sorry for so many questions, but as I go along in optimizing the Teensy Bat Detector, so many questions arise ;-) [and I learn so much, that´s what it´s all about, I think ;-)].

    Would be nice if someone had some hints on one of the questions above, thanks in advance!

    Frank

  20. #20
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by DD4WH View Post
    However, some questions remain:

    1. as I am taking audio samples from the queue object, my "logging device" is the audio queue. I am not sure whether I have properly implemented the interplay between the queue and the buffer I use for the write process. Would be good, if someone among the specialists could comment on that. Code is in #15.
    I have to search the forum for some examples on how to access the I2S buffers properly.
    2. my buffer is this:
    Code:
    #define BUFFSIZE (64*1024) // size of buffer to be written
    uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
    which I write to the SD using this:
    Code:
      const uint32_t N_BUFFER = 2;
    const uint32_t N_LOOPS = 128;
    . . . .
    rc = f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
    Why do I need a 64k buffer to write 32768 bytes? Or is that buffer too large? And is it possible to write 64kbyte at one time with the f_write command?
    You don't need it when everything is sequentially and you can write 64k with one write statement

    But in real-time applications you may wanted to fill the intermediate storage with smaller chunks, without corrupting the buffer that is transferred to uSD card.
    So, you typically store new data to parts of the buffer, that is not used for the write to disk operation. This dual buffering mechanism is a common approach.

    3. I also tried to use more audio buffers in the queue and write smaller files at a time to the SD, but that results in more clicks in the audio.
    The clicks you hear are voltage drops during an uSD operation. I have a larger Elko (47uF) cross the supply of my custom audiocard, but it would be useful that qualified EEs comment on proper way to eliminate voltage drops on ADC during uSD operations.

    4. Playback of the files is done by using the playSDRaw-object of the audio lib and that uses the Sd-lib for playback. However, that does not seem to be able to use exFAT formatted SDs!? So, is there any alternative how I could playback audio-RAW files from an exFAT-formatted SD card on the Teensy 3.6?
    Re exFAT, so far only uSDFS can handle exFAT, so you need to reverse the operation; use uSDFS to read from disk and feed it into audio library (sorry about that)
    As a starting point I would take the playSDRaw object and say generate a playuSDFSRaw class, that handles the proper operations, but I have not yet looked into that.

  21. #21
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Thanks a lot, Walter, that is excellent information!

    So, just for me to be sure I understand it right:

    @dual buffer: so, it could be that the f_write command is still processed internally (using my single buffer variable "buffern") while already my "buffern" is filled with new samples from the audio queue? Wow, never thought about that . . . I will implement a dual buffer strategy and see whether that helps! Maybe like this: audio queue blocks (1 or 2) --> memcpy the blocks to 32k buffer1 --> if buffer 1 is full, copy to another 32k buffer2 --> f_write buffer2 (during writing of the file to the SD card, buffer1 is already being filled again with new data)

    @ADC voltage drops: Sounds reasonable, the Teensy and also the audio board only have a few µF on the 3.3V line (for testing, my Teensy is powered by the micro-USB and there is the TFT fed through the 5V line of the Teensy). So I will try to put an additional electrolyte or tantalum cap to the 3.3V line of the audio board. I will have to look what I have in my box, bulky antique Elkos are surely there, but maybe also some fancy SMD tantalum ;-).

    @exFAT: I will think about that, maybe with the points above, I can improve the noise issue without having to use exFAT, but we will see . . .

    Will report about my experiments!

    Frank

  22. #22
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    just a short report of my experiments:

    1. an additional tantalum cap 47µF soldered to the 3.3V power supply line and Ground on the Teensy audio board does not change anything. The clicking remains there

    2. a dual buffer does not change anything, although I have to have a closer look into your code to confirm that I am doing everything the right way, so this deserves more attention from my side

    3. the clicking can be eliminated totally, if I switch off the FFT processing and the spectrum display drawing. That means,the realtime audio during recording and the recordings themselves are clean. Now, how comes that?

    And the result in 3 also indicates, that the clicks are not caused by a voltage drop of the ADC!?

    My interpretation would be, that the clicks are missing audio samples on the recording caused by the processor doing other things when it should be processing/pushing audio samples into the buffer (also indicated by temporary audio memory use of >50). But how can I care for that? (eg. do I have to split the spectrum display drawing into several parts with a state machine?)

    I´m a little puzzled now, maybe I should make a pause of one or two days to collect new ideas . . . Any hints?

    All the best, have fun,

    Frank
    Last edited by DD4WH; 11-08-2016 at 01:25 PM.

  23. #23
    Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    54
    Hi everyone, This is Edwin form the Netherlands. This is my first post here. I did manage to get the clicks out of the recording.
    On write operation there is quite a big load on the 3v3 regulator, this makes a slight voltage drop, the audioboard also uses this same 3v3.
    Just disconnect the 3v3 between Teensy and audioboard and make a seperate 3v3 supply on the audioboard. (use a low drop regulator to create 3v3 from 5v)

    I can't hear or see any weird noise, not even with the wordt SD card I have.

    Kind regards,

    Edwin

  24. #24
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    384
    Hi Edwin,

    thanks for your excellent information!

    I try to understand whats happening and why your setup does not have the clickings:

    * do you use the SD card slot on the Teensy or do you use the SD card slot on the audio board?
    * what kind of 3V3 regulator do you use? Does it deliver 1 Ampere of current or 250mA?
    * which sample rate do you use? 44k1 or higher?
    * what sketch are you using, what is the purpose of your setup?

    For my setups I use the Teensy 3.6 SD card slot. That would mean -in order to eliminate the clickings- I would have to cut the trace on the Teensy board to pin 4 of the SD card and setup a separate external voltage regulator to provide 3V3 to pin 4 of the SD card, is that right?

    Would be nice if you could provide some more information on your setup.

    All the best,

    Frank

  25. #25
    Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    54
    Hi Frank, I use the SD card in the Teensy.

    I just do not power the audio board form the teensy.

    I used and LF33CV regulator, just because I had one. Any low drop regulator should work I guess.
    The audioboard and microphone amplifier I connected will probably only use a few milliamps.

    My schematic is below, I use the code by Cor Berrevoets for the Teensy bat detector.
    I also noticed some display update noise, for reducing this I added resistors in the SPI lines. All modifications I used afterwards are in red.

    Click image for larger version. 

Name:	image (2).png 
Views:	34 
Size:	128.0 KB 
ID:	15169

    Edwin

Posting Permissions

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