Bat detector

there must be some missing includes
Can you tell me the program you are compiling?
which system are you using Arduino IDE or other?
I am using the code from here: https://github.com/CorBer/teensy_batdetector
I've added the uSDFS to my library from here: https://github.com/WMXZ-EU/uSDFS
Compiling in Arduino 1.8.7
I've added a blank .ino to make it compile, apart from that it is all as downloaded

I tried adding #include <stddef.h> just before #include "ff.h" but it didn't make any difference

I don't think it is related to the Time.h discussion above as that include is commented out in this code but maybe I'm misunderstanding that bit.
 
Hi nickjb,

I guess you are using Arduino 1.8.7 in windows just like me.
The code by corber should have an #include "Time.h"

I now have replaced that with TimeAlt.h, if you add this TimeAlt library and use the code as below I guess it should compile.
Your error output looked exactly like mine. I know this is not bet best way to fix but at least it should allow you to compile.

TimeAlt to be added to C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\ :
https://drive.google.com/open?id=1V4k3TSwuOZy65caC_H1j_Bbh78NpooRB

I hope someone will be able to fix this properly without the TimeAlt library which actually is just a renamed Time library.


Code:
/***********************************************************************
 *  TEENSY 3.6 BAT DETECTOR V0.1 20180814
 * 
 *  Copyright (c) 2018, Cor Berrevoets, registax@gmail.com
 *  
 *  TODO: use selectable presets
 * 
 *  HARDWARE USED:
 *     TEENSY 3.6
 *     TEENSY audio board
 *     Ultrasonic microphone with seperate preamplifier connected to mic/gnd on audioboard
 *       eg. Knowles MEMS SPU0410LR5H-QB 
 *     TFT based on ILI9341
 *     2 rotary encoders with pushbutton
 *     2 pushbuttons
 *     SDCard
 * 
*   IMPORTANT: uses the SD card slot of the Teensy, NOT the SD card slot of the audio board 
 * 
 *  4 operational modes: Heterodyne.
 *                       Frequency divider
 *                       Automatic heterodyne (1/10 implemented)
 *                       Automatic TimeExpansion (live)
 *
 *  Sample rates up to 352k
 *  
 *  User controlled parameters:
 *     Volume
 *     Gain
 *     Frequency
 *     Display (none, spectrum, waterfall)
 *     Samplerate
 *     
 *  Record raw data
 *  Play raw data (user selectable) on the SDcard using time_expansion (8, 11, 16,22,32,44k samplerate )
 * 
 * 
 *  Fixes compared to original base:
 *    - issue during recording due to not refilling part of the buffer (was repeating the original first 256 samples )
 *    - filenames have samplerate stored
 *    - RTC added (based on hardware)
 * 
 * **********************************************************************
 *   Based on code by DD4WH 
 * 
 *   https://forum.pjrc.com/threads/38988-Bat-detector
 *   
 *   https://github.com/DD4WH/Teensy-Bat-Detector
 *         
 *   made possible by the samplerate code by Frank Boesing, thanks Frank!
 *   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.
 * 
 **********************************************************************/

/* CORBEE */
/* TEENSY 3.6 PINSETUP (20180814)

                  GND                  Vin  - PREAMP V+
                   0                   Analog GND
                   1                   3.3V - MEMS MIC
                   2                   23 AUDIO -LRCLK
                   3                   22 AUDIO -TX
                   4                   21 TFT CS
                   5                   20 TFT DC
       AUDIO MEMCS 6                   19 AUDIO - SCL
       AUDIO MOSI  7                   18 AUDIO - SDA
                   8                   17
       AUDIO BCLK  9                   16
       AUDIO SDCS 10                  15 AUDIO -VOL
       AUDIO MCLK 11                  14 AUDIO -SCLK
       AUDIO MISO 12                  13 AUDIO -RX
                  3.3V                GND
                  24                  A22
                  25                  A21
                  26                  39  TFT MISO
        TFT SCLK  27                  38  MICROPUSH_L
        TFT MOSI  28                  37  MICROPUSH_R
     ENC_L-BUTTON 29                  36  ENC_R-BUTTON
     ENC_L A      30                  35  ENC_R A
     ENC_L B      31                  34  ENC_R B
                  32                  33

*/

//#define DEBUG

#define USETFT

//SD1 uses default SDcard Fat, TODO !! SD2 uses faster SDIO library
#define USESD1

#ifdef USESD1
  #define USESD
  #include <SD.h>
  #include "ff.h"       // uSDFS lib
  #include "ff_utils.h" // uSDFS lib
  File root;
  FRESULT rc;        /* Result code */
  FATFS fatfs;      /* File system object */
  FIL fil;        /* File object */
#endif

// TODO: try and see if using the SdFs library is able to write faster 
// started setup and included several #ifdefs inside the audio-library SDrelated files (play_raw play_wav)
#ifdef USESD2
#define USESD
//#include "SdFs.h"

#include "logger_setup.h"



#endif

//default SD related
#ifdef USESD
  #define MAX_FILES    50
  #define MAX_FILE_LENGTH  13   // 8 chars plus 4 for.RAW plus NULL
  char filelist[ MAX_FILES ][ MAX_FILE_LENGTH ];
  int filecounter=0;
  int fileselect=0;
  int referencefile=0;
  //File frec; // audio is recorded to this file first
  int file_number = 0;
#endif

#include <TimeAlt.h> 

#include "Audio.h"
//#include <Wire.h>
#include <SPI.h>
#include <Bounce.h>
//#include <Metro.h>

boolean SD_ACTIVE=false;
boolean continousPlay=false;
boolean batTrigger=false;//triggers when an ultrasonic signalpeak is found during FFT
boolean TE_ready=true; //when a TEcall is played this signals the end of the call

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}
int helpmin; // definitions for time and date adjust - Menu
int helphour;
int helpday;
int helpmonth;
int helpyear;
int helpsec;
uint8_t hour10_old;
uint8_t hour1_old;
uint8_t minute10_old;
uint8_t minute1_old;
uint8_t second10_old;
uint8_t second1_old;
bool timeflag = 0;


uint32_t lastmillis;

#ifdef USETFT

 #define ILI9341
 #ifdef ILI9341
  #include "ILI9341_t3.h"
  #include "font_Arial.h"
  
  #define BACKLIGHT_PIN 255
  #define TOP_OFFSET 90
  #define BOTTOM_OFFSET 20

  #define TFT_DC      20
  #define TFT_CS      21
  #define TFT_RST     255  // 255 = unused. connect to 3.3V

  #define TFT_MOSI    28
  #define TFT_SCLK    27 
  #define TFT_MISO    39
  //#define Touch_CS    8

  ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
  //XPT2046_Touchscreen ts = XPT2046_Touchscreen(Touch_CS);
//predefine menu background etc colors
  #define ENC_MENU_COLOR COLOR_YELLOW
  #define ENC_VALUE_COLOR COLOR_LIGHTGREY
  #define MENU_BCK_COLOR COLOR_DARKRED
  
 #endif

#endif


// this audio comes from the codec by I2S2
AudioInputI2S                    i2s_in; // MIC input
AudioRecordQueue                 recorder; 
AudioSynthWaveformSineHires      sine1; // local oscillator
//AudioSynthWaveformSineHires      sine2; // local oscillator
//
AudioEffectMultiply              heterodyne_multiplier; // multiply = mix
//AudioEffectMultiply              mult2; // multiply = mix

//AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
AudioAnalyzeFFT256               myFFT; // for spectrum display

AudioPlaySdRaw                   player; 

AudioEffectGranular              granular1;

AudioMixer4                      mixFFT;
AudioMixer4                      outputMixer; //selective output
AudioMixer4                      inputMixer; //selective input
AudioOutputI2S                   i2s_out; // headphone output          

AudioConnection mic_toinput         (i2s_in, 0, inputMixer, 0); //microphone signal
AudioConnection mic_torecorder      (i2s_in, 0, recorder, 0); //microphone signal
//AudioConnection mic_topeak (i2s_in, peakRMS);
//AudioConnection mic_topeak1 (i2s_in, peakVal);

AudioConnection player_toinput      (player, 0, inputMixer, 1); //player signal

AudioConnection input_toswitch      (inputMixer,0,  mixFFT,0);

AudioConnection input_todelay       (inputMixer,0, granular1, 0);

AudioConnection switch_toFFT        (mixFFT,0, myFFT,0 ); //raw recording channel 

AudioConnection input_toheterodyne1 (inputMixer, 0, heterodyne_multiplier, 0); //heterodyne 1 signal
AudioConnection sineheterodyne1    (sine1, 0, heterodyne_multiplier, 1);//heterodyne 1 mixerfreq

AudioConnection granular_toout (granular1,0, outputMixer,1);
//AudioConnection input_toheterodyne2 (granular1, 0, mult2, 0); //heterodyne 2
//AudioConnection sineheterodyne2     (sine2, 0, mult2, 1);//heterodyne 2 mixerfreq

AudioConnection heterodyne1_toout      (heterodyne_multiplier, 0, outputMixer, 0);  //heterodyne 1 output to outputmixer
//AudioConnection heterodyne2_toout      (mult2, 0, outputMixer, 1);  //heterodyne 2 output to outputmixer
AudioConnection player_toout           (inputMixer,0, outputMixer, 2);    //direct signal (use with player) to outputmixer

AudioConnection output_toheadphoneleft      (outputMixer, 0, i2s_out, 0); // output to headphone
AudioConnection output_toheadphoneright     (outputMixer, 0, i2s_out, 1);
//AudioConnection granular_toheadphone        (granular1,0,i2s_out,1);

AudioControlSGTL5000        sgtl5000;  

//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

#define GRANULAR_MEMORY_SIZE 30000  // enough for 100 ms at 281kHz
int16_t granularMemory[GRANULAR_MEMORY_SIZE];

// forward declaration Stop recording with message 
#ifdef DEBUGSERIAL
   void die(char *str, FRESULT rc);
#endif

extern "C" uint32_t usd_getError(void);

struct tm seconds2tm(uint32_t tt);

//continous timers
elapsedMillis since_bat_detection1; //start timing directly after FFT detects an ultrasound
elapsedMillis since_bat_detection2; //start timing directly after FFT detects the end of the ultrasound
//
elapsedMillis since_heterodyne=1000; //timing interval for auto_heterodyne frequency adjustments
uint16_t callLength=0;
//uint16_t clicker=0;

/************** RECORDING PLAYING SETTINGS *****************/

const int8_t    MODE_DETECT = 0;
const int8_t    MODE_REC = 1;
const int8_t    MODE_PLAY = 2;

int mode = MODE_DETECT; 

#if defined(__MK20DX256__)
  #define BUFFSIZE (8*1024) // size of buffer to be written
#elif defined(__MK66FX1M0__)
  #define BUFF 64
  #define BUFFSIZE (BUFF*1024) // size of buffer to be written

#endif

// buffer to store audiosamples during recording
uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
//uint8_t buffern2[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
uint wr;
uint32_t nj = 0;

#define waterfallgraph 1
#define spectrumgraph 2

int idx_t = 0;
int idx = 0;
int64_t sum;
float32_t mean;
int16_t FFT_bin [128]; 
int16_t FFT_max1 = 0;
uint32_t FFT_max_bin1 = 0;
int16_t FFT_mean1 = 0;
int16_t FFT_max2 = 0;
uint32_t FFT_max_bin2 = 0;
int16_t FFT_mean2 = 0;
//int16_t FFT_threshold = 0;
int16_t FFT_bat [3]; // max of 3 frequencies are being displayed
int16_t index_FFT;
int l_limit;
int u_limit;
int index_l_limit;
int index_u_limit;
//const uint16_t FFT_points = 1024;
const uint16_t FFT_points = 256;

int barm [512];

#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_234K              11
#define SAMPLE_RATE_281K              12
#define SAMPLE_RATE_352K              13
#define SAMPLE_RATE_MAX               13

typedef struct SR_Descriptor
{
    const int SR_n;
    const char* const txt; //display 
    
} SR_Desc;

// SRtext and position for the FFT spectrum display scale
const SR_Descriptor SR [SAMPLE_RATE_MAX + 1] =
{
    //   SR_n ,  f1
    {  SAMPLE_RATE_8K,  "8"}, 
    {  SAMPLE_RATE_11K,  "11"}, 
    {  SAMPLE_RATE_16K,  "16"}, 
    {  SAMPLE_RATE_22K,  "22"}, 
    {  SAMPLE_RATE_32K,  "32"}, 
    {  SAMPLE_RATE_44K,  "44"}, 
    {  SAMPLE_RATE_48K,  "48"},
    {  SAMPLE_RATE_88K,  "88"},
    {  SAMPLE_RATE_96K,  "96"},
    {  SAMPLE_RATE_176K,  "176"},
    {  SAMPLE_RATE_192K,  "192"}, 
    {  SAMPLE_RATE_234K,  "234"}, 
    {  SAMPLE_RATE_281K,  "281"}, 
    {  SAMPLE_RATE_352K,  "352"}
};    

// setup for FFTgraph denoising 
uint32_t FFTcount=0; //count the # of FFTs done 
uint16_t powerspectrumCounter=0;

float FFTavg[128];

float FFTpowerspectrum[128];
float powerspectrum_Max=0;

// defaults at startup functions
int displaychoice=waterfallgraph; //default display
int8_t mic_gain = 35; // start detecting with this MIC_GAIN in dB
int8_t volume=50;

int freq_real = 45000; // start heterodyne detecting at this frequency
int freq_real_backup=freq_real; //used to return to proper settingafter using the play_function

// initial sampling setup
int sample_rate = SAMPLE_RATE_281K;
int sample_rate_real = 281000;
char * SRtext="281";

int last_sample_rate=sample_rate;

float freq_Oscillator =50000;

/************************************************* MENU ********************************/
/***************************************************************************************/

typedef struct Menu_Descriptor
{
    const char* name;
    // ********preset variables below NOT USED YET
    const int len; // length of string to allow right-alignment
    const int def; //default settings
    const int low; // low threshold
    const int high; //high threshold
    
} Menu_Desc;


const int Leftchoices=10; //can have any value
const int Rightchoices=10;
const Menu_Descriptor MenuEntry [Leftchoices] =
{  {"Volume",6,60,0,100}, //divide by 100
   {"Gain",4,30,0,63},
   {"Frequency",9,45,20,90}, //multiply 1000
   {"Display",7,0,0,0},
   {"Denoise",7,0,0,0},
   {"SampleR",6,0,0,0},
   {"Record",6,0,0,0}, //functions where the LeftEncoder 
   {"Play",4,0,0,0},
   {"PlayD",5,0,0,0},
} ;

//TODO constants should be part of the menuentry, a single structure to hold the info
const int8_t  MENU_VOL = 0; //volume
const int8_t  MENU_MIC = 1; //mic_gain
const int8_t  MENU_FRQ = 2; //frequency
const int8_t  MENU_DSP = 3; //display
const int8_t  MENU_DNS = 4; //denoise
const int8_t  MENU_SR  = 5; //sample rate
const int8_t  MENU_REC = 6; //record
const int8_t  MENU_PLY = 7; //play 
const int8_t  MENU_PLD = 8; //play at original rate 

//available modes
const int detector_heterodyne=0;
const int detector_divider=1;
const int detector_Auto_heterodyne=2;
const int detector_Auto_TE=3;
const int detector_passive=4;

//default
int detector_mode=detector_heterodyne;  

//************************* ENCODER variables/constants
const int8_t enc_menu=0; //changing encoder sets menuchoice
const int8_t enc_value=1; //changing encoder sets value for a menuchoice

const int8_t enc_leftside=0; //encoder 
const int8_t enc_rightside=1; //encoder

const int8_t enc_up=1; //encoder goes up
const int8_t enc_nc=0;
const int8_t enc_dn=-1; //encoder goes down

int EncLeft_menu_idx=0; 
int EncRight_menu_idx=0;

int EncLeft_function=0; 
int EncRight_function=0;

/************************** */

// ******** LEFT AND RIGHT ENCODER CONNECTIONS/BUTTONS
#include <Encoder.h>
//try to avoid interrupts as they can (possibly ?) interfere during recording
#define ENCODER_DO_NOT_USE_INTERRUPTS

#define MICROPUSH_RIGHT  37 
Bounce micropushButton_R = Bounce(MICROPUSH_RIGHT, 50); 
#define encoderButton_RIGHT      36    
Bounce encoderButton_R = Bounce(encoderButton_RIGHT, 50); 
Encoder EncRight(34,35);
int EncRightPos=0;
int EncRightchange=0;

#define MICROPUSH_LEFT  38
Bounce micropushButton_L = Bounce(MICROPUSH_LEFT, 50); 
#define encoderButton_LEFT       29
Bounce encoderButton_L = Bounce(encoderButton_LEFT, 50); 
Encoder EncLeft(30,31);
int EncLeftPos=0;
int EncLeftchange=0;

// **END************ LEFT AND RIGHT ENCODER DEFINITIONS

#ifdef USESD1
void die(char *str, FRESULT rc) 
{ 
   #ifdef DEBUGSERIAL
   Serial.printf("%s: Failed with rc=%u.\n", str, rc); for (;;) delay(100); 
   #endif 
   }

//=========================================================================
#endif

#ifdef USESD1
//uint32_t count=0;
uint32_t ifn=0;
uint32_t isFileOpen=0;

TCHAR wfilename[80];
uint32_t t0=0;
uint32_t t1=0;
#endif

char filename[80];

void display_settings() {
  #ifdef USETFT
    
    tft.setTextColor(ENC_MENU_COLOR);
    
    tft.setFont(Arial_16);
    tft.fillRect(0,0,240,TOP_OFFSET-50,MENU_BCK_COLOR);
    tft.fillRect(0,TOP_OFFSET-10,240,10,COLOR_BLACK);
    tft.fillRect(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET,240,BOTTOM_OFFSET,MENU_BCK_COLOR);

    tft.setCursor(0,0);
    tft.print("g:"); tft.print(mic_gain);
    tft.print(" f:"); tft.print(freq_real);
    tft.print(" v:"); tft.print(volume);
    tft.print(" SR"); tft.print(SRtext);
    tft.setCursor(0,20);
    
    switch (detector_mode) {
       case detector_heterodyne:
         tft.print("HTD"); // 
       break;
       case detector_divider:
         tft.print("FD");
       break;
       case detector_Auto_heterodyne:
         tft.print("Auto_HTD");
       break;
       case detector_Auto_TE:
        tft.print("Auto_TE");
       break;
       case detector_passive:
        tft.print("PASS");
       break;
       default:
        tft.print("error");
       
     }
     // push the cursor to the lower part of the screen
     tft.setCursor(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);

     /****************** SHOW ENCODER SETTING ***********************/

     // set the colors according to the function of the encoders
     if (mode==MODE_DETECT ) 
     // show menu selection as menu-active of value-active
      {
       if (EncLeft_function==enc_value) 
        { tft.setTextColor(ENC_MENU_COLOR);
         }
        else
        { tft.setTextColor(ENC_VALUE_COLOR);}

       tft.print(MenuEntry[EncLeft_menu_idx].name);
       tft.print(" "); 

       if (EncRight_function==enc_value) 
         { tft.setTextColor(ENC_MENU_COLOR);} //value is active 
       else
         { tft.setTextColor(ENC_VALUE_COLOR);} //menu is active 
       
       //if MENU on the left-side is PLAY and selected than show the filename
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))     
           { //tft.print(fileselect); 
             tft.print(filelist[fileselect]);
           }
        else
         if (EncLeft_menu_idx==MENU_REC)      
          // show the filename that will be used for the next recording
           {  sprintf(filename, "B%u_%s.raw", file_number+1,SRtext);
              tft.print(filename );
            }
         else
         if (EncLeft_menu_idx==MENU_SR)
          { tft.print(SR[sample_rate].txt);

          }
          else
          { //tft.print(EncRightchange); 
            tft.setCursor(ILI9341_TFTWIDTH/2  ,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);
            tft.print(MenuEntry[EncRight_menu_idx].name);
          }
    }
    else
      { 
        if (mode==MODE_REC)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("REC:"); 
            tft.print(filename);
         }
        if (mode==MODE_PLAY) 
         {if (EncLeft_menu_idx==MENU_PLY)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("PLAY:"); 
            tft.print(filename);
          }
          else
           {tft.setTextColor(ENC_VALUE_COLOR);
            tft.print(MenuEntry[EncLeft_menu_idx].name);
            tft.print(" "); 
            tft.print(MenuEntry[EncRight_menu_idx].name);
      
           }

        }
      }

    //scale every 10kHz  
    float x_factor=10000/(0.5*(sample_rate_real / FFT_points)); 
    int curF=2*int(freq_real/(sample_rate_real / FFT_points));

    int maxScale=int(sample_rate_real/20000);
    for (int i=1; i<maxScale; i++) 
     { tft.drawFastVLine(i*x_factor, TOP_OFFSET-10, 9, ENC_MENU_COLOR);  
     }    
    tft.fillCircle(curF,TOP_OFFSET-4,3,ENC_MENU_COLOR);
    
   #endif
}


void       set_mic_gain(int8_t gain) {
    
    AudioNoInterrupts();
    //sgtl5000.micGainNew (24);
    sgtl5000.micGain (gain);
    //sgtl5000.lineInLevel(gain/4);
    AudioInterrupts();

    display_settings();
    powerspectrum_Max=0; // change the powerspectrum_Max for the FFTpowerspectrum
} // end function set_mic_gain

void       set_freq_Oscillator(int freq) {
    // audio lib thinks we are still in 44118sps sample rate
    // therefore we have to scale the frequency of the local oscillator
    // in accordance with the REAL sample rate
      
    freq_Oscillator = (freq) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    //float F_LO2= (freq+5000) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    // if we switch to LOWER samples rates, make sure the running LO 
    // frequency is allowed ( < 22k) ! If not, adjust consequently, so that
    // LO freq never goes up 22k, also adjust the variable freq_real  
    if(freq_Oscillator > 22000) {
      freq_Oscillator = 22000;
      freq_real = freq_Oscillator * (sample_rate_real / AUDIO_SAMPLE_RATE_EXACT) + 9;
    }
    AudioNoInterrupts();
    //setup multiplier SINE
    sine1.frequency(freq_Oscillator);
    //sine2.frequency(freq_Oscillator);
        
    AudioInterrupts();
    display_settings();
} // END of function set_freq_Oscillator

// set samplerate code by Frank Boesing 
void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;
//MCLD Divide sets the MCLK divide ratio such that: MCLK output = MCLK input * ( (FRACT + 1) / (DIVIDE + 1) ).
// FRACT must be set equal or less than the value in the DIVIDE field.
//(double)F_PLL * (double)clkArr[iFreq].mult / (256.0 * (double)clkArr[iFreq].div);
//ex 180000000* 1 /(256* 3 )=234375Hz  setting   {1,3} at 180Mhz

  
#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 int numfreqs = 17;
  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,  234000, 281000, 352800};
  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}, {1, 4},      {64, 255},     {219,802}, { 1,3 },  {2,5} , {1,2} };  //last value 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      set_sample_rate (int sr) {
  switch (sr) {
    case SAMPLE_RATE_8K:
    sample_rate_real = 8000;
    SRtext = " 8";
    break;
    case SAMPLE_RATE_11K:
    sample_rate_real = 11025;
    SRtext = "11";
    break;
    case SAMPLE_RATE_16K:
    sample_rate_real = 16000;
    SRtext = "16";
    break;
    case SAMPLE_RATE_22K:
    sample_rate_real = 22050;
    SRtext = "22";
    break;
    case SAMPLE_RATE_32K:
    sample_rate_real = 32000;
    SRtext = "32";
    break;
    case SAMPLE_RATE_44K:
    sample_rate_real = 44100;
    SRtext = "44";
    break;
    case SAMPLE_RATE_48K:
    sample_rate_real = 48000;
    SRtext = "48";
    break;
    case SAMPLE_RATE_88K:
    sample_rate_real = 88200;
    SRtext = "88";
    break;
    case SAMPLE_RATE_96K:
    sample_rate_real = 96000;
    SRtext = "96";
    break;
    case SAMPLE_RATE_176K:
    sample_rate_real = 176400;
    SRtext = "176";
    break;
    case SAMPLE_RATE_192K:
    sample_rate_real = 192000;
    SRtext = "192";
    break;
    case SAMPLE_RATE_234K:
    sample_rate_real = 234000;
    SRtext = "234";
    break;
    case SAMPLE_RATE_281K:
    sample_rate_real = 281000;
    SRtext = "281";
    break;
    case SAMPLE_RATE_352K:
    sample_rate_real = 352800;
    SRtext = "352";
    break;
  }
    
    AudioNoInterrupts();
    setI2SFreq (sample_rate_real); 
    delay(200); // this delay seems to be very essential !
    set_freq_Oscillator (freq_real);
    AudioInterrupts();
    delay(20);
    display_settings();
   
} // END function set_sample_rate



void spectrum() { // spectrum analyser code by rheslip - modified
     #ifdef USETFT
     if (myFFT.available()) {
//     if (fft1024_1.available()) {
    int16_t peak=0; uint16_t avgF=0;
    
    // find the BIN corresponding to the current frequency-setting 
    int curF=int(freq_real/(sample_rate_real / FFT_points));

//    for (int i = 0; i < 240; i++) {
    //startup sequence to denoise the FFT
    FFTcount++;
    if (FFTcount==1)
     {for (int16_t i = 0; i < 128; i++) {
         FFTavg[i]=0; 
     }
     }

    if (FFTcount<1000)
     { 
       for (int i = 0; i < 128; i++) {
         FFTavg[i]=FFTavg[i]+abs(myFFT.output[i])*0.001; //0.1% of total values
         }
     }

for (int16_t x = 2; x < 128; x++) {
   avgF=avgF+FFT_bin[x];
   if (FFT_bin[x]>peak)
      {
        peak=FFT_bin[x];   
      }
}

/*
avgF=avgF/128;
//check if the peak is at least 2x higher than the average otherwise set the indicator low
if ((peak-avgF)<(avgF/3))
  { maxF=2;}
  */  
  for (int16_t x = 2; x < 128; x++) {
//  for (uint16_t x = 8; x < 512; x+=4) {
     FFT_bin[x] = (myFFT.output[x]);//-FFTavg[x]*0.9; 
     int colF=ENC_VALUE_COLOR;
     
//     FFT_bin[x/4] = abs(fft1024_1.output[x]); 
     int barnew = (FFT_bin[x])/2 ;
     
     // this is a very simple first order IIR filter to smooth the reaction of the bars
     int bar = 0.05 * barnew + 0.95 * barm[x]; 
     if (bar >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { bar=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     if (bar <0) bar=0;
     if (barnew >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { barnew=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     int g_x=x*2;
     int spectrumline=barm[x];
     int spectrumline_new=barnew;
          
     //tft.drawFastVLine(g_x,TOP_OFFSET,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET, COLOR_BLACK);
     tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline_new, COLOR_GREEN);
     //tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline, COLOR_RED);
     tft.drawFastVLine(g_x,TOP_OFFSET+spectrumline_new,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline_new, COLOR_BLACK);
     tft.drawFastVLine(g_x+1,TOP_OFFSET,spectrumline, COLOR_DARKGREEN);
     tft.drawFastVLine(g_x+1,TOP_OFFSET+spectrumline,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline, COLOR_BLACK);
    /* if (x==maxF)
       { colF=COLOR_ORANGE;
         tft.drawFastVLine(g_x,TOP_OFFSET,240-bar, colF);
         }
      */   
     
     //tft.drawPixel(g_x,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-bar,colF);

     barm[x] = bar;
  }
  
    // if (mode == MODE_DETECT)  search_bats();     
  } //end if
  if (mode==MODE_PLAY)
    {//float ww=( player.positionMillis()/player.lengthMillis()*240.0);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-5,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-5,5,ENC_MENU_COLOR);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-4,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-4,5,ENC_MENU_COLOR);
    }
  #endif
}
#ifdef DEBUGSERIAL 

/*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(")");
 
      AudioProcessorUsageMaxReset();
      AudioMemoryUsageMaxReset();
    }


}
*/ // END function check_processor
#endif



void waterfall(void) // thanks to Frank B !
{ 
  
#ifdef USETFT

// code for 256 point FFT 
     
 if (myFFT.available()) {
  const uint16_t Y_OFFSET = TOP_OFFSET;
  static int count = TOP_OFFSET;
  //int curF=int(freq_real/(sample_rate_real / FFT_points));

  // lowest frequencybin to detect as a batcall
  int batCall_LoF_bin= int(30000/(sample_rate_real / FFT_points));
  int batCall_HiF_bin= int(80000/(sample_rate_real / FFT_points));

  uint16_t FFT_pixels[240]; // maximum of 240 pixels, each one is the result of one FFT 
  FFT_pixels[0]=0; FFT_pixels[1]=0;  FFT_pixels[2]=0; FFT_pixels[3]=0;
  
    FFTcount++;

    //requested to start with a clean FFTavg array to denoise
    if (FFTcount==1)
       {for (int16_t i = 0; i < 128; i++) {
          FFTavg[i]=0; 
       }
     }

    // collect 1000 FFT samples for the denoise array
    if (FFTcount<100)
     { for (int i = 2; i < 128; i++) {
         //FFTavg[i]=FFTavg[i]+myFFT.read(i)*65536.0*5*0.001; //0.1% of total values
         FFTavg[i]=FFTavg[i]+myFFT.output[i]*10*0.001; //0.1% of total values
         }
     }
    

    int FFT_peakF_bin=0; 
    int peak=512;
    int avgFFTbin=0;
    // there are 128 FFT different bins only 120 are shown on the graphs  
    
    for (int i = 2; i < 120; i++) { 
      int val = myFFT.output[i]*10 -FFTavg[i]*0.9 + 10; //v1
      avgFFTbin+=val;
      //detect the peakfrequency
      if (val>peak)
       { peak=val; 
         FFT_peakF_bin=i;
        }
       if (val<5) 
           {val=5;}

       FFT_pixels[i*2] = tft.color565(
              min(255, val/2),
              (val/6>255)? 255 : val/6,
              //(val/4>255)? 255 : val/4
                            0
              //((255-val)>>1) <0? 0: (255-val)>>1 
             ); 
       
      FFT_pixels[i*2+1]=FFT_pixels[i*2];       
    }
    avgFFTbin=avgFFTbin/120;
    //mark the peak
    //FFT_pixels[FFT_peakF_bin*2]=COLOR_RED;
    //FFT_pixels[FFT_peakF_bin*2+1]=COLOR_RED;
   if ((peak/avgFFTbin)<1.2) //very low peakvalue so probably noise
     { FFT_peakF_bin=0; 
     }

  int powerSpectrum_Maxbin=0;
  // detected a peak in the bat frequencies
  if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin))
  {
    //collect data for the powerspectrum 
    for (int i = 2; i < 120; i++)
     { 
        //add new samples
        FFTpowerspectrum[i]+=myFFT.output[i];
        //keep track of the maximum
        if (FFTpowerspectrum[i]>powerspectrum_Max) 
           { powerspectrum_Max=FFTpowerspectrum[i];
             powerSpectrum_Maxbin=i;
           }

     }
     //keep track of the no of samples with bat-activity
     powerspectrumCounter++;
  }
    // update display after every 100th FFT sample with bat-activity
    if ((powerspectrumCounter>50)  )
       { powerspectrumCounter=0;
         //clear powerspectrumbox
         tft.fillRect(0,TOP_OFFSET-50,240,45, COLOR_BLACK);
         // keep a minimum maximumvalue to the powerspectrum
         int binLo=2; int binHi=0;

         for (int i=2; i<120; i++)
          {             
            int ypos=FFTpowerspectrum[i]/powerspectrum_Max*45; 

            // first encounter of 1/20 of maximum
            if (i<powerSpectrum_Maxbin)
              {if (FFTpowerspectrum[i]<(powerspectrum_Max*0.1))
                    {binLo=i;}}
            else
              {if (FFTpowerspectrum[i]>(powerspectrum_Max*0.1))
                    {binHi=i;}
              }

            tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            if (i==powerSpectrum_Maxbin)                        
              { tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,ENC_MENU_COLOR);
               }
            
            //tft.drawFastVLine(i*2+1,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            FFTpowerspectrum[i]=0;
          }
         
         //tft.setCursor(0,TOP_OFFSET-45);
         //tft.print(powerspectrum_Max);
         if (powerspectrum_Max==20000)
          {binLo=0; binHi=0;
          }
         float multiplier=(sample_rate_real / FFT_points)*0.001;
         powerspectrum_Max=powerspectrum_Max*0.5; //lower the max after a graphupdate
         tft.setCursor(140,TOP_OFFSET-45);
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binLo*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_MENU_COLOR);
         tft.print(int(powerSpectrum_Maxbin*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binHi*multiplier) );
        
       }
      
    
    if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin)) // we got a high-frequent signal peak
      { 
        // when a batcall is first discovered 
        if (not batTrigger) 
          { since_bat_detection1=0; //start of the call mark
            //clicker=0;
            FFT_pixels[5]=ENC_VALUE_COLOR; // mark the start on the screen
            FFT_pixels[6]=ENC_VALUE_COLOR;
            FFT_pixels[7]=ENC_VALUE_COLOR;
            
            if (detector_mode==detector_Auto_heterodyne)
               if (since_heterodyne>1000) //update the most every second
                {freq_real=int((FFT_peakF_bin*(sample_rate_real / FFT_points)/500))*500; //round to nearest 500hz
                 set_freq_Oscillator(freq_real); 
                 since_heterodyne=0;
                 //granular1.stop();
                }
            
            //restart the TimeExpansion only if the previous call was played
            if ((detector_mode==detector_Auto_TE) and (TE_ready) )
             { granular1.stop();
               granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
               granular1.setSpeed(0.05);
               TE_ready=false;
               
             }
                      
          }
         //clicker++; 
         batTrigger=true;
         
     }
   else // FFT_peakF_bin does not show a battcall  
        { 
          if (batTrigger) //previous sample was still a call
           { callLength=since_bat_detection1; // got a pause so store the time since the start of the call
             
             /*if (callLength>20) //call is too long 
              { TE_ready=true; // break the TE replay
                } 
              */  
              since_bat_detection2=0; //start timing the length of the replay
             }
          batTrigger=false;
        }    
    // restart TimeExpansion recording a bit after the call has finished completely
    if ((!TE_ready) and (since_bat_detection2>(callLength*10)))
      { //stop the time expansion
        TE_ready=true;
        granular1.stopTimeExpansion();
       
      }
    

    if (since_bat_detection2<50) //keep scrolling 100ms after the last bat-call
      {  tft.writeRect( 0,count, ILI9341_TFTWIDTH,1, (uint16_t*) &FFT_pixels); //show a line with spectrumdata
         tft.setScroll(count);
        count++;
        
      } 


    if (count >= ILI9341_TFTHEIGHT-BOTTOM_OFFSET) count = Y_OFFSET;
    

  }
#endif

}

void startRecording() {
  mode = MODE_REC;
  #ifdef USESD1
  
    #ifdef DEBUGSERIAL
      Serial.print("startRecording");
    #endif  
    
    // close file
    if(isFileOpen)
    {
      //close file
      rc = f_close(&fil);
      if (rc) die("close", rc);
      isFileOpen=0;
    }
  
  if(!isFileOpen)
  {
  file_number++;
  //automated filename BA_S.raw where A=file_number and S shows samplerate. Has to fit 8 chars
  // so max is B999_192.raw
  sprintf(filename, "B%u_%s.raw", file_number, SRtext);
    #ifdef DEBUGSERIAL
    Serial.println(filename);
    #endif  
  char2tchar(filename, 13, wfilename);
  filecounter++;
  strcpy(filelist[filecounter],filename );

  rc = f_stat (wfilename, 0);
  #ifdef DEBUGSERIAL
    Serial.printf("stat %d %x\n",rc,fil.obj.sclust);
 #endif   
  rc = f_open (&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
#ifdef DEBUGSERIAL
    Serial.printf(" opened %d %x\n\r",rc,fil.obj.sclust);
#endif 
    // check if file has errors
    if(rc == FR_INT_ERR)
    { // only option then is to close file
        rc = f_close(&fil);
        if(rc == FR_INVALID_OBJECT)
        { 
          #ifdef DEBUGSERIAL
          Serial.println("unlinking file");
          #endif
          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;
  }

  #endif

  //clear the screen completely
  tft.fillRect(0,0,ILI9341_TFTWIDTH,ILI9341_TFTHEIGHT,COLOR_BLACK);
  tft.setTextColor(ENC_VALUE_COLOR);
  tft.setFont(Arial_28);
  tft.setCursor(0,100);
  tft.print("RECORDING");
  tft.setFont(Arial_16);
  
  display_settings();
  
  granular1.stop(); //stop granular

  //switch off several circuits
  mixFFT.gain(0,0);
  
  outputMixer.gain(1,0);  //shutdown granular output      
  
  detector_mode=detector_heterodyne;

  outputMixer.gain(0,1); 
  
  nj=0;
  recorder.begin();
    
}

void continueRecording() {
  #ifdef USESD1
  const uint32_t N_BUFFER = 2;
  const uint32_t N_LOOPS = BUFF*N_BUFFER; // !!! NLOOPS and BUFFSIZE ARE DEPENDENT !!! NLOOPS = BUFFSIZE/N_BUFFER
  // 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
    //read N_BUFFER sample-blocks into memory
    for (int i = 0; i < N_BUFFER; i++) {
       //copy a new bufferblock from the audiorecorder into memory
       memcpy(buffern + i*256 + nj * 256 * N_BUFFER, recorder.readBuffer(), 256);
       //free the last buffer that was read
       recorder.freeBuffer();
       } 

    nj++; 

    if (nj >  (N_LOOPS-1)) 
    {
      nj = 0;
      //old code used to copy into a 2nd buffer, not needed since the writing to SD of the buffer seems faster than the filling 
      //this allows larger buffers to be used
      //memcpy(buffern2,buffern,BUFFSIZE);  
      //push to SDcard  
      rc =  f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
      }
  }
  #endif
}

void stopRecording() {
#ifdef USESD1
  #ifdef DEBUGSERIAL  
    Serial.print("stopRecording");
  #endif  
    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_DETECT;
  //  clearname();
  #ifdef DEBUGSERIAL  
    Serial.println (" Recording stopped!");
  #endif 
#endif
  //switch on FFT
  tft.fillScreen(COLOR_BLACK);
  mixFFT.gain(0,1); 
      
}

void startPlaying(int SR) {
//      String NAME = "Bat_"+String(file_number)+".raw";
//      char fi[15];
//      NAME.toCharArray(fi, sizeof(NAME));

inputMixer.gain(0,0); //switch off the mic-line as input
inputMixer.gain(1,1); //switch on the playerline as input

if (EncLeft_menu_idx==MENU_PLY) 
  {
      outputMixer.gain(2,1);  //player to output 
      outputMixer.gain(1,0);  //shutdown granular output      
      outputMixer.gain(0,0);  //shutdown heterodyne output
      EncRight_menu_idx=MENU_SR;
      EncRight_function=enc_value;
      freq_real_backup=freq_real; //keep track of heterodyne setting
  }
  //direct play is used to test functionalty based on previous recorded data
  //this will play a previous recorded raw file through the system as if it were live data coming from the microphone
if (EncLeft_menu_idx==MENU_PLD) 
  {
      outputMixer.gain(2,0);  //shutdown direct audio from player to output 
      outputMixer.gain(0,1);  //default mode will be heterodyne based output 
      if (detector_mode==detector_Auto_TE)
         { outputMixer.gain(1,1);  //start granular output processing      
           outputMixer.gain(0,0);
         }
  }

//allow settling
  delay(100);
  
  //keep track of the sample_rate 
  last_sample_rate=sample_rate;
  SR=constrain(SR,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);
  set_sample_rate(SR);
  
  fileselect=constrain(fileselect,0,filecounter);
  strncpy(filename, filelist[fileselect],  13);
  
  //default display is waterfall
  displaychoice=waterfallgraph;
  display_settings();

  player.play(filename);
  mode = MODE_PLAY;

}
  

void stopPlaying() {
  
#ifdef DEBUGSERIAL      
  Serial.print("stopPlaying");
#endif  
  if (mode == MODE_PLAY) player.stop();
  mode = MODE_DETECT;
#ifdef DEBUGSERIAL      
  Serial.println (" Playing stopped");
#endif  
  
  //restore last sample_rate setting
  set_sample_rate(last_sample_rate);
if (EncLeft_menu_idx==MENU_PLY)
{
  freq_real=freq_real_backup;
  //restore heterodyne frequency
  set_freq_Oscillator (freq_real);
}
  outputMixer.gain(2,0); //stop the direct line output
  outputMixer.gain(1,1); // open granular output
  outputMixer.gain(0,1); // open heterodyne output  

  inputMixer.gain(0,1); //switch on the mic-line
  inputMixer.gain(1,0); //switch off the playerline

}


void continuePlaying() {
  //the end of file was reached
  if (!player.isPlaying()) {
    stopPlaying();
    if (continousPlay) //keep playing until stopped by the user
      { startPlaying(SAMPLE_RATE_176K);
      }
  }
}

void changeDetector_mode()
{
  if (detector_mode==detector_heterodyne)
         { granular1.stop(); //stop other detecting routines
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
          //switch menu to volume/frequency
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_FRQ;
           EncRight_function=enc_value;
          
         } 
      if (detector_mode==detector_divider)
         { granular1.beginDivider(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  

      if (detector_mode==detector_Auto_TE)
         { granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           granular1.setSpeed(0.06); //default TE is 1/0.06 ~ 1/16 :TODO, switch from 1/x floats to divider value x
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  
      if (detector_mode==detector_Auto_heterodyne)
         { granular1.stop(); 
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

           
         }  

      if (detector_mode==detector_passive)
         { granular1.stop(); //stop all other detecting routines
           outputMixer.gain(2,1);  //direct line to output 
           outputMixer.gain(1,0);  //shutdown granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }
       else //all other options use the heterodyne and granular output line
        {  outputMixer.gain(2,0);  //shut down direct line to output 
           //outputMixer.gain(1,1);  //granular   output      
           //outputMixer.gain(0,1);  //heterodyne output
 
        } 

}

//*****************************************************update encoders
void updateEncoder(uint8_t Encoderside )
 {
   
  /************************setup vars*************************/
   int encodermode=-1; // menu=0 value =1;
   int change=0;
   int menu_idx=0;
   int choices=0;

    //get encodermode 
   if (Encoderside==enc_leftside)
    { encodermode=EncLeft_function;
      change=EncLeftchange;
      menu_idx=EncLeft_menu_idx;
      choices=Leftchoices; //available menu options
    }

   if (Encoderside==enc_rightside)
    { encodermode=EncRight_function;
      change=EncRightchange;
      menu_idx=EncRight_menu_idx;
      choices=Rightchoices; //available menu options
    } 


  /************************proces changes*************************/
  //encoder is in menumode
  if (encodermode==enc_menu)
    { menu_idx=menu_idx+change;
            
      //allow revolving choices
      if (menu_idx<0)
        {menu_idx=choices-1;}
      if (menu_idx>=choices)
        {menu_idx=0;}
      //remove functionality when SD is not active, so no SDCARD mounted or SDCARD is unreadable  
      if (!SD_ACTIVE)
        { if ((menu_idx==MENU_PLD) or (menu_idx==MENU_PLY) or (menu_idx==MENU_REC))
           { // move menu to volume
             menu_idx=MENU_VOL;
           }
        }

      if (Encoderside==enc_leftside)
          { EncLeft_menu_idx=menu_idx; //limit the menu 
               }
     
     //limit the changes of the rightside encoder for specific functions
      if ((EncLeft_menu_idx!=MENU_SR) )      
        if (Encoderside==enc_rightside)
          { EncRight_menu_idx=menu_idx; //limit the menu 
               }
    }
        
  //encoder is in valuemode and has changed position
  if ((encodermode==enc_value) and (change!=0))
    { 
      /******************************VOLUME  ***************/
      if (menu_idx==MENU_VOL)
        { volume+=change;
          volume=constrain(volume,0,90);
          float V=volume*0.01;
          AudioNoInterrupts();
          sgtl5000.volume(V);
          AudioInterrupts();
        }
      /******************************MIC_GAIN  ***************/
      if (menu_idx==MENU_MIC)
        {
         mic_gain+=change;
         mic_gain=constrain(mic_gain,0,63);
         set_mic_gain(mic_gain);
         FFTcount=0; //start denoise after changing gain
         //reset FFTdenoise array
         {for (int16_t i = 0; i < 128; i++) {
           FFTavg[i]=0; 
         }}  
        }
      /******************************FREQUENCY  ***************/
      if (menu_idx==MENU_FRQ)
         { int delta=500;
           uint32_t currentmillis=millis();
           //when turning the encoder fast make the change larger
           if ((currentmillis-lastmillis)<500)
              { delta=1000;}
           if ((currentmillis-lastmillis)<250)
              { delta=2000;}
           if ((currentmillis-lastmillis)<100)
              { delta=5000;}

          freq_real=freq_real+delta*change;
          // limit the frequencies to 500hz steps
          freq_real=constrain(freq_real,7000,int(sample_rate_real/2000)*1000-1000);
          set_freq_Oscillator (freq_real);
          lastmillis=millis();
         }
      /******************************DENOISE  ***************/
      if (menu_idx==MENU_DNS)
        { // setting FFTcount to 0 activates a 1000 sample denoise
          FFTcount=0;
        }
      
      /******************************DISPLAY  ***************/
    
      if (menu_idx==MENU_DSP)
         { 
           displaychoice+=change;
           displaychoice=displaychoice%3; //limit to 0(none),1(spectrum),2(waterfall)
           if (displaychoice==waterfallgraph) 
              {
               tft.setRotation( 0 );
            }
           if (displaychoice==spectrumgraph) 
             { 
            tft.setScroll(0);
            tft.setRotation( 0 );
              }
            tft.fillScreen(COLOR_BLACK);
        }

      

/************** SPECIAL MODES WHERE THE LEFTENCODER SETS A FUNCTION AND THE RIGHT ENCODER SELECTS */
      
      /******************************SAMPLE_RATE  ***************/
      if (EncLeft_menu_idx==MENU_SR)  //only selects a possible sample_rate, user needs to press a button to SET sample_rate
        { sample_rate+=EncRightchange;
          sample_rate=constrain(sample_rate,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);

          
        }   

      
      /******************************SELECT A FILE  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_PLY) and (EncRight_function==enc_value))//menu play selected on the left and right 
         {  
           if (mode!=MODE_PLAY)
            {
             fileselect+=EncRightchange; 
             fileselect=constrain(fileselect,0,filecounter-1);
            
            }   
         }  

      /******************************CHANGE SR during PLAY  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_value))//menu play selected on the left and right    
          {
           if (mode==MODE_PLAY)
              {
                 sample_rate+=EncRightchange;
                 sample_rate=constrain(sample_rate,SAMPLE_RATE_8K,SAMPLE_RATE_44K);
                 set_sample_rate(sample_rate);
                 
              }

        }

    }

 }

void updateEncoders()
{
//only react to changes large enough (depending on the steps of the encoder for one rotation)
 long EncRightnewPos = EncRight.read()/4;
 if (EncRightnewPos>EncRightPos)
   { EncRightchange=enc_up; }
   else
   if (EncRightnewPos<EncRightPos)
   { EncRightchange=enc_dn; }
   else
   { EncRightchange=enc_nc; }

 if (EncRightchange!=0)
    {updateEncoder(enc_rightside);
     } 

 EncRightPos=EncRightnewPos;
 
 long EncLeftnewPos = EncLeft.read()/4;
 if (EncLeftnewPos>EncLeftPos)
   { EncLeftchange=enc_up; }
   else
   if (EncLeftnewPos<EncLeftPos)
   { EncLeftchange=enc_dn; }
   else
   { EncLeftchange=enc_nc; }

 if (EncLeftchange!=0)
    {updateEncoder(enc_leftside);
    } 
 
 EncLeftPos=EncLeftnewPos;
 //update display only if a change has happened
 if ((EncRightchange!=0) or (EncLeftchange!=0))
      display_settings();

}

void updateButtons()
{
 // Respond to button presses
   
 // try to make the interrupts as short as possible when recording
 if (mode==MODE_REC) 
   {
     encoderButton_L.update(); //check the left encoderbutton
      if ((encoderButton_L.risingEdge())  )
       { stopRecording();
          EncLeft_function=enc_menu; //force into active-menu
          display_settings();      
       }
   }
 else // not MODE_REC
  {
  // Respond to button presses
 
  encoderButton_L.update();
  encoderButton_R.update();

  micropushButton_L.update();
  micropushButton_R.update();
    
   /*RIGHT MICROPUSH */
  if (micropushButton_R.risingEdge()) {
        detector_mode++;
        if (detector_mode>detector_passive)
          {detector_mode=0;}
        changeDetector_mode();
        display_settings();      
    }
/*LEFT MICROPUSH */
  if (micropushButton_L.risingEdge()) {
      //no function yet
    }



  /************  LEFT ENCODER BUTTON *******************/
  if (encoderButton_L.risingEdge()) {

      EncLeft_function=!EncLeft_function;
        
      //*SPECIAL MODES that change rightside Encoder based on leftside Encoder menusetting
      //****************************************SAMPLERATE
      if ((EncLeft_menu_idx==MENU_SR) and (EncLeft_function==enc_value))
       { EncRight_menu_idx=MENU_SR;
         EncRight_function=enc_value; // set the rightcontroller to select
       }
      
     if (SD_ACTIVE)
     {
        /*if (mode==MODE_REC)
                 { stopRecording();
                   EncLeft_function=enc_menu; //force into active-menu
                 }*/
        if ((mode==MODE_PLAY) and ((EncLeft_menu_idx==MENU_PLY) or (EncLeft_menu_idx==MENU_PLD)))
                 { stopPlaying();
                   EncLeft_menu_idx=MENU_PLY;
                   EncLeft_function=enc_menu; //force into active-menu
                   continousPlay=false;
                 }
               
        //Direct play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLD) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_FRQ ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }

        //play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_PLY ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }
       
     
     } //END SD_ACTIVE
     display_settings();      
  }

/************  RIGHT ENCODER BUTTON *******************/

 if (encoderButton_R.risingEdge()) {
    
    EncRight_function=!EncRight_function; //switch between menu/value control
        
    //when EncLeftoder is SR menu than EncRightoder can directly be setting the samplerate at a press 
    //the press should be a transfer from enc_value to enc_menu before it gets activated
    if ( (EncLeft_menu_idx==MENU_SR) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_menu))
    { set_sample_rate(sample_rate);
      last_sample_rate=sample_rate;

    }
    
    //recording and playing are only possible with an active SD card
    if (SD_ACTIVE)
     {
      /*if (mode==MODE_REC)
             { stopRecording();
               EncRight_function=enc_menu; //force into active-menu
              }
          else*/
           if (EncLeft_menu_idx==MENU_REC)
            {
              if (mode == MODE_DETECT) 
                 startRecording();
              }  
      else
      if (mode==MODE_PLAY)
             {
              if (not continousPlay) 
                { stopPlaying();
                  EncLeft_menu_idx=MENU_PLY;
                  EncLeft_function=enc_menu;
                }
             }     
      else       
      if (mode==MODE_DETECT)   
       { if (EncLeft_menu_idx==MENU_PLY)
            { last_sample_rate=sample_rate; 
              startPlaying(SAMPLE_RATE_8K);
             }
          
          if (EncLeft_menu_idx==MENU_PLD)
              {  fileselect=referencefile;
                 continousPlay=true;
                 last_sample_rate=sample_rate;
                 startPlaying(SAMPLE_RATE_176K);
              }  
       }
     }
      
      display_settings();
  }

  }
}



/********************************************************* */

void setup() {
 #ifdef DEBUGSERIAL
  Serial.begin(115200);
 #endif  
  delay(200);
 
//setup Encoder Buttonpins with pullups
  pinMode(encoderButton_RIGHT,INPUT_PULLUP);
  pinMode(encoderButton_LEFT,INPUT_PULLUP);

  pinMode(MICROPUSH_RIGHT,INPUT_PULLUP);
  pinMode(MICROPUSH_LEFT,INPUT_PULLUP);
 
 // EncLeft.write(10000); //set default far away from 0 to avoid negative transitions
  //EncRight.write(10000); //set default far away from 0 to avoid negative transitions

//startup menu
  EncLeft_menu_idx=MENU_VOL;
  EncRight_menu_idx=MENU_FRQ;
  EncLeft_function=enc_menu;
  EncRight_function=enc_menu;

  // Audio connections require memory. 
  AudioMemory(300);

 setSyncProvider(getTeensy3Time);

// Enable the audio shield. select input. and enable output
  sgtl5000.enable();
  sgtl5000.inputSelect(myInput);
  
  sgtl5000.volume(0.45);
  sgtl5000.micGain (mic_gain);
  //sgtl5000.adcHighPassFilterDisable(); // does not help too much!
  sgtl5000.lineInLevel(0);
  mixFFT.gain(0,1);

// Init TFT display  
#ifdef USETFT
  tft.begin();
  //ts.begin();
  tft.setRotation( 0 );
  tft.fillScreen(COLOR_BLACK);

  tft.setCursor(0, 0);
  tft.setScrollarea(TOP_OFFSET,BOTTOM_OFFSET);
  display_settings();
  tft.setCursor(80,50);
  tft.setFont(Arial_24);
  char tstr[9];
  snprintf(tstr,9, "%02d:%02d:%02d",  hour(), minute(), second() );
  tft.print(tstr);
  delay(2000); //wait a second to clearly show the time 
#endif

   //Init SD card use
// uses the SD card slot of the Teensy, NOT that of the audio boards
// this init only for playback
#ifdef USESD1
  if(!(SD.begin(BUILTIN_SDCARD))) 
  {
      #ifdef DEBUGSERIAL
          Serial.println("Unable to access the SD card");
          delay(500);  
      #endif     
      
    SD_ACTIVE=false;
    tft.fillCircle(70,50,5,COLOR_RED);
     
  }
  else
  { 
    SD_ACTIVE=true;
    tft.fillCircle(70,50,5,COLOR_GREEN);
    filecounter=0;
    root = SD.open("/");


    //TODO: check if file is a RAW file and also read the SAMPLERATE
    while (true) {
        
        File entry =  root.openNextFile();
        if (! entry) {
          // no more files
          tft.setCursor(0,50);
          tft.print(filecounter);
          break;
        }
        
        if (entry.isDirectory()) {
          // do nothing, only look for raw files in the root
        } else 
        {
          
        strcpy(filelist[filecounter],entry.name() );
      
        if (String(entry.name())=="TEST_176.RAW")
           {referencefile=filecounter;
            }
        filecounter++;  
          
        }
        entry.close();
       }

    }


if (SD_ACTIVE)
// Recording on SD card by uSDFS library
  {f_mount (&fatfs, (TCHAR *)_T("0:/"), 0);      /* Mount/Unmount a logical drive */
   for (int i=0; i<filecounter; i++)
     {   
       #ifdef DEBUGSERIAL
          #ifdef USETFT
            tft.setCursor(0,50+i*20);
            tft.print(filelist[i]);
          #endif
      #endif    

     }
     file_number=filecounter+1;

  }

#endif

#ifdef USESD2
 uSD.init();

#endif

set_sample_rate (sample_rate);
set_freq_Oscillator (freq_real);
inputMixer.gain(0,1); //microphone active
inputMixer.gain(1,0); //player off

outputMixer.gain(0,1); // heterodyne1 to output 
outputMixer.gain(1,0); // granular to output off
outputMixer.gain(2,0); // player to output off

// the Granular effect requires memory to operate
granular1.begin(granularMemory, GRANULAR_MEMORY_SIZE);

// reset the FFT denoising array at startup
for (int16_t i = 0; i < 128; i++) {
  FFTavg[i]=0; 
    }
} // END SETUP


void loop() {
// If we're playing or recording, carry on...
  if (mode == MODE_REC) {
    continueRecording();
    
  } 
  
  if (mode == MODE_PLAY) {
    continuePlaying();
  }

updateButtons();   
// during recording only the left encoders button is used and screens are not updated
if (mode!=MODE_REC)
{
 updateEncoders();
 #ifdef USETFT
   if (displaychoice==waterfallgraph) 
    { waterfall();
      
     }
   else
    if (displaychoice==spectrumgraph)
    {  spectrum();
     }
 #endif
 }   

}
 
OK, that code seems to compile "better". Thanks. It still doesn't complete but I'm not really sure why. The only things in red are the first line, c:\\user... etc and the very last line
Code:
Arduino: 1.8.7 (Windows 10), TD: 1.44, Board: "Teensy 3.6, Serial, 180 MHz, Faster, US English"

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp: In member function 'void AudioEffectGranular::begin(int16_t*, int16_t)':

effect_granular.cpp:39: warning: unused variable 'subsample' 
  int subsample=0;

      ^

main.cpp:398: warning: ISO C++ forbids converting a string constant to 'char*' 
 char * SRtext="281";

               ^

main.cpp: In function 'void set_sample_rate(int)':
main.cpp:725: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = " 8";

            ^

main.cpp:729: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "11";

            ^

main.cpp:733: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "16";

            ^

main.cpp:737: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "22";

            ^

main.cpp:741: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "32";

            ^

main.cpp:745: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "44";

            ^

main.cpp:749: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "48";

            ^

main.cpp:753: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "88";

            ^

main.cpp:757: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "96";

            ^

main.cpp:761: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "176";

            ^

main.cpp:765: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "192";

            ^

main.cpp:769: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "234";

            ^

main.cpp:773: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "281";

            ^

main.cpp:777: warning: ISO C++ forbids converting a string constant to 'char*' 
     SRtext = "352";

            ^

main.cpp: In function 'void spectrum()':
main.cpp:835: warning: unused variable 'colF' 
      int colF=ENC_VALUE_COLOR;

          ^

main.cpp:800: warning: unused variable 'curF' 
     int curF=int(freq_real/(sample_rate_real / FFT_points));

         ^

main.cpp: In function 'void startRecording()':
main.cpp:1129: warning: ISO C++ forbids converting a string constant to 'char*' 
       if (rc) die("close", rc);

                              ^

main.cpp:1165: warning: ISO C++ forbids converting a string constant to 'char*' 
             die("unlink", rc);

                             ^

main.cpp:1170: warning: ISO C++ forbids converting a string constant to 'char*' 
           die("close", rc);

                          ^

main.cpp:1176: warning: ISO C++ forbids converting a string constant to 'char*' 
       die("open", rc);

                     ^

main.cpp: In function 'void continueRecording()':
main.cpp:1218: warning: comparison between signed and unsigned integer expressions 
   if (recorder.available() >= N_BUFFER  )

                            ^

main.cpp:1221: warning: comparison between signed and unsigned integer expressions 
     for (int i = 0; i < N_BUFFER; i++) {

                       ^

main.cpp: In function 'void stopRecording()':
main.cpp:1257: warning: ISO C++ forbids converting a string constant to 'char*' 
         if (rc) die("close", rc);

                                ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c: In function 'SDHC_InitCard':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c:194:12: warning: variable 'd' set but not used [-Wunused-but-set-variable]

  { uint8_t d[16];

            ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c: At top level:

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c:523:16: warning: 'sdhc_ReadBlock' defined but not used [-Wunused-function]

 static DRESULT sdhc_ReadBlock(LWord* pData, LWord Count, LWord Size)

                ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c:554:16: warning: 'sdhc_WriteBlock' defined but not used [-Wunused-function]

 static DRESULT sdhc_WriteBlock(const LWord* pData, LWord Count, LWord Size)

                ^

In file included from C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio.c:56:0:

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdio_prv.h:223:17: warning: 'sdhc_WaitStatus' declared 'static' but never defined [-Wunused-function]

 static uint16_t sdhc_WaitStatus(LWord mask);

                 ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c: In function 'SDReadBlock':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:382:11: warning: pointer targets in passing argument 1 of 'xferBulk' differ in signedness [-Wpointer-sign]

  xferBulk(buff,0,512);

           ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:382:11: note: expected 'char *' but argument is of type 'uint8_t * {aka unsigned char *}'

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c: In function 'SDWriteBlock':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:563:13: warning: pointer targets in passing argument 2 of 'xferBulk' differ in signedness [-Wpointer-sign]

  xferBulk(0,buff,512);

             ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:563:13: note: expected 'char *' but argument is of type 'uint8_t * {aka unsigned char *}'

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c: In function 'usd_readData':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:859:11: warning: pointer targets in passing argument 1 of 'xferBulk' differ in signedness [-Wpointer-sign]

  xferBulk(dst, 0, count);

           ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdcard.c:859:11: note: expected 'char *' but argument is of type 'uint8_t * {aka unsigned char *}'

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdspi.c: In function 'SPIInit':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\sdspi.c:98:12: warning: unused variable 'brscaler' [-Wunused-variable]

  uint32_t  brscaler;

            ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp: In member function 'TCHAR* uSDClass::char2tchar(const char*, size_t, TCHAR*)':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp:37:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

   for(ii = 0; ii<nn; ii++) tcharString[ii] = (TCHAR) charString[ii];

                 ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp: In member function 'char* uSDClass::tchar2char(TCHAR*, size_t, char*)':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp:43:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

   for(ii = 0; ii<nn; ii++) charString[ii] = (char) tcharString[ii];

                 ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp: In member function 'boolean uSDClass::exists(const char*)':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSD.cpp:77:8: warning: unused variable 'drive' [-Wunused-variable]

  TCHAR drive[80]; //(TCHAR *)_T("0:/")

        ^

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSDif.cpp: In function 'void sdspi_setup(uint16_t)':

C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\uSDif.cpp:76:19: warning: variable 'finalclk' set but not used [-Wunused-but-set-variable]

   uint32_t        finalclk;

                   ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio\effect_granular.cpp: In member function 'void AudioEffectGranular::begin(int16_t*, int16_t)':

effect_granular.cpp:39: warning: unused variable 'subsample' 
  int subsample=0;

      ^

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::update()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::begin(short*, short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::beginFreeze_int(int)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::beginPitchShift_int(int)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::beginTimeExpansion_int(int)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::beginDivider_int(int)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `AudioEffectGranular::stop()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\Audio\effect_granular.cpp.o: In function `AudioEffectGranular::update()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio/effect_granular.cpp:119: multiple definition of `vtable for AudioEffectGranular'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\effect_granular.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/effect_granular.cpp:119: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `ILI9341_t3::ILI9341_t3(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char)':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\ILI9341_t3/ILI9341_t3.cpp:63: multiple definition of `ILI9341_t3::ILI9341_t3(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch/ILI9341_t3.cpp:63: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::ILI9341_t3(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setAddrWindow(unsigned short, unsigned short, unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::pushColor(unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawPixel(short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawFastVLine(short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawFastHLine(short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillRect(short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillScreen(unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillRectVGradient(short, short, short, short, unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillRectHGradient(short, short, short, short, unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillScreenVGradient(unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillScreenHGradient(unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setRotation(unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setScroll(unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::invertDisplay(bool)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::readcommand8(unsigned char, unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::readPixel(short, short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::readRect(short, short, short, short, unsigned short*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::writeRect(short, short, short, short, unsigned short const*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::writeRect8BPP(short, short, short, short, unsigned char const*, unsigned short const*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::writeRect4BPP(short, short, short, short, unsigned char const*, unsigned short const*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::writeRect2BPP(short, short, short, short, unsigned char const*, unsigned short const*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::writeRect1BPP(short, short, short, short, unsigned char const*, unsigned short const*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::begin()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawCircle(short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawCircleHelper(short, short, short, unsigned char, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillCircleHelper(short, short, short, unsigned char, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillCircle(short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawLine(short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawRect(short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawRoundRect(short, short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillRoundRect(short, short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawTriangle(short, short, short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::fillTriangle(short, short, short, short, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawBitmap(short, short, unsigned char const*, short, short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawChar(short, short, unsigned char, unsigned short, unsigned short, unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawFontChar(unsigned int)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::write(unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::strPixelLen(char*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::drawFontBits(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setCursor(short, short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::getCursor(short*, short*)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setTextSize(unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::getTextSize()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setTextColor(unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setTextColor(unsigned short, unsigned short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::setTextWrap(bool)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::getTextWrap()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::getRotation()'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `ILI9341_t3::sleep(bool)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `Adafruit_GFX_Button::initButton(ILI9341_t3*, short, short, unsigned char, unsigned char, unsigned short, unsigned short, unsigned short, char const*, unsigned char)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `Adafruit_GFX_Button::drawButton(bool)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `Adafruit_GFX_Button::contains(short, short)'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\libraries\ILI9341_t3\ILI9341_t3.cpp.o: In function `SPIClass::port()':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI/SPI.h:488: multiple definition of `vtable for ILI9341_t3'

C:\Users\NICKUS~1\AppData\Local\Temp\arduino_build_756457\sketch\ILI9341_t3.cpp.o:C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/Print.h:61: first defined here

collect2.exe: error: ld returned 1 exit status

Error compiling for board Teensy 3.6.
 
I hope someone will be able to fix this properly without the TimeAlt library which actually is just a renamed Time library.


pe1pwf: could you please comment the include of TimeAlt.h and all known time function until it compiles
which lines do you have to comment?
If it is only the two function, then solution is easy
 
pe1pwf: could you please comment the include of TimeAlt.h and all known time function until it compiles
which lines do you have to comment?
If it is only the two function, then solution is easy




These seem to be the only lines that seem to be related....

Ihe number is the line number in the code (+/- 3 lines)


147 //#include <Time.h>

1812 // setSyncProvider(getTeensy3Time);

1837 // snprintf(tstr,9, "%02d:%02d:%02d", hour(), minute(), second() );
 
147 //#include <Time.h>

1812 // setSyncProvider(getTeensy3Time);

1837 // snprintf(tstr,9, "%02d:%02d:%02d", hour(), minute(), second() );

let the line commented (to be able to go back)

comment also lines 160 to 163 (function time_t getTeensy3Time())
after (commented) line 1837 add
Code:
struct tm tx = seconds2tm(RTC_TSR); 
snprintf(tstr,9, "%02d:%02d:%02d", tx.tm_hour, tx.tm_min, tx.tm_sec);
 
Thanks WMXZ

It compiles just fine. I have not checked how well everything works.

I just got my PCB's so I will mount the parts first on one of these new PCB's.
My experimental PCB does not have a RTC battery mounted yet.

teensybatpcb.jpg
 
Just threw some parts together on the board.

I know I did not gave the right transistors for the microphone amplifier, but compiled and uploaded the sketch.

I just made some silent recordings and the right timestamp is used, the RTC seems to function just fine.

Thanks a lot WMXZ, this functions without the struggle with Time.h.
I hope I will get the enclosure and some transistors quickly so I can finish this project and do a full and complete test.

Kind regards,

Edwin


teenybatparts.jpg
 
I just had some experiments with the detector with the code alterations as suggested by WMXZ. As far as I can tel thinks look good without Time.h

I pasted the code below for the people that missed the lines that needed to be altered.
I hope Cor also find the time to check this after all this is his baby, well together with Frank DD4WH that is....

I hope nickjb get things compiled and maybe misterixx could try as well. As i understand he also was struggling to het things working in windows and used some virtual machine running linux to compile.

The PCB's you have seen pictures off work well enough, I did not even hear the updating of the display as I did with my forst experimental board. Apparently the shielding works well enough and decoupling of noises is enough, the only modifiction needed is the seperate 3.3V for the audioboard and microphone amplifier.

I had some boards made and I only need one myself so if anyone would like to try one.... It can change owner for about $5 usd and shipping takes about $2 usd WW.


Kind regards,

Edwin





Code:
/***********************************************************************
 *  TEENSY 3.6 BAT DETECTOR V0.1 20180814
 * 
 *  Copyright (c) 2018, Cor Berrevoets, registax@gmail.com
 *  
 *  TODO: use selectable presets
 * 
 *  HARDWARE USED:
 *     TEENSY 3.6
 *     TEENSY audio board
 *     Ultrasonic microphone with seperate preamplifier connected to mic/gnd on audioboard
 *       eg. Knowles MEMS SPU0410LR5H-QB 
 *     TFT based on ILI9341
 *     2 rotary encoders with pushbutton
 *     2 pushbuttons
 *     SDCard
 * 
*   IMPORTANT: uses the SD card slot of the Teensy, NOT the SD card slot of the audio board 
 * 
 *  4 operational modes: Heterodyne.
 *                       Frequency divider
 *                       Automatic heterodyne (1/10 implemented)
 *                       Automatic TimeExpansion (live)
 *
 *  Sample rates up to 352k
 *  
 *  User controlled parameters:
 *     Volume
 *     Gain
 *     Frequency
 *     Display (none, spectrum, waterfall)
 *     Samplerate
 *     
 *  Record raw data
 *  Play raw data (user selectable) on the SDcard using time_expansion (8, 11, 16,22,32,44k samplerate )
 * 
 * 
 *  Fixes compared to original base:
 *    - issue during recording due to not refilling part of the buffer (was repeating the original first 256 samples )
 *    - filenames have samplerate stored
 *    - RTC added (based on hardware)
 * 
 * **********************************************************************
 *   Based on code by DD4WH 
 * 
 *   https://forum.pjrc.com/threads/38988-Bat-detector
 *   
 *   https://github.com/DD4WH/Teensy-Bat-Detector
 *         
 *   made possible by the samplerate code by Frank Boesing, thanks Frank!
 *   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.
 * 
 **********************************************************************/

/* CORBEE */
/* TEENSY 3.6 PINSETUP (20180814)

                  GND                  Vin  - PREAMP V+
                   0                   Analog GND
                   1                   3.3V - MEMS MIC
                   2                   23 AUDIO -LRCLK
                   3                   22 AUDIO -TX
                   4                   21 TFT CS
                   5                   20 TFT DC
       AUDIO MEMCS 6                   19 AUDIO - SCL
       AUDIO MOSI  7                   18 AUDIO - SDA
                   8                   17
       AUDIO BCLK  9                   16
       AUDIO SDCS 10                  15 AUDIO -VOL
       AUDIO MCLK 11                  14 AUDIO -SCLK
       AUDIO MISO 12                  13 AUDIO -RX
                  3.3V                GND
                  24                  A22
                  25                  A21
                  26                  39  TFT MISO
        TFT SCLK  27                  38  MICROPUSH_L
        TFT MOSI  28                  37  MICROPUSH_R
     ENC_L-BUTTON 29                  36  ENC_R-BUTTON
     ENC_L A      30                  35  ENC_R A
     ENC_L B      31                  34  ENC_R B
                  32                  33

*/

//#define DEBUG

#define USETFT

//SD1 uses default SDcard Fat, TODO !! SD2 uses faster SDIO library
#define USESD1

#ifdef USESD1
  #define USESD
  #include <SD.h>
  #include "ff.h"       // uSDFS lib
  #include "ff_utils.h" // uSDFS lib
  File root;
  FRESULT rc;        /* Result code */
  FATFS fatfs;      /* File system object */
  FIL fil;        /* File object */
#endif

// TODO: try and see if using the SdFs library is able to write faster 
// started setup and included several #ifdefs inside the audio-library SDrelated files (play_raw play_wav)
#ifdef USESD2
#define USESD
//#include "SdFs.h"

#include "logger_setup.h"



#endif

//default SD related
#ifdef USESD
  #define MAX_FILES    50
  #define MAX_FILE_LENGTH  13   // 8 chars plus 4 for.RAW plus NULL
  char filelist[ MAX_FILES ][ MAX_FILE_LENGTH ];
  int filecounter=0;
  int fileselect=0;
  int referencefile=0;
  //File frec; // audio is recorded to this file first
  int file_number = 0;
#endif

//#include "Time.h"

#include "Audio.h"
//#include <Wire.h>
#include <SPI.h>
#include <Bounce.h>
#include <Metro.h>

boolean SD_ACTIVE=false;
boolean continousPlay=false;
boolean batTrigger=false;//triggers when an ultrasonic signalpeak is found during FFT
boolean TE_ready=true; //when a TEcall is played this signals the end of the call

//time_t getTeensy3Time()
//{
//  return Teensy3Clock.get();
//}
int helpmin; // definitions for time and date adjust - Menu
int helphour;
int helpday;
int helpmonth;
int helpyear;
int helpsec;
uint8_t hour10_old;
uint8_t hour1_old;
uint8_t minute10_old;
uint8_t minute1_old;
uint8_t second10_old;
uint8_t second1_old;
bool timeflag = 0;


uint32_t lastmillis;

#ifdef USETFT

 #define ILI9341
 #ifdef ILI9341
  #include "ILI9341_t3.h"
  #include "font_Arial.h"
  
  #define BACKLIGHT_PIN 255
  #define TOP_OFFSET 90
  #define BOTTOM_OFFSET 20

  #define TFT_DC      20
  #define TFT_CS      21
  #define TFT_RST     255  // 255 = unused. connect to 3.3V

  #define TFT_MOSI    28
  #define TFT_SCLK    27 
  #define TFT_MISO    39
  //#define Touch_CS    8

  ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
  //XPT2046_Touchscreen ts = XPT2046_Touchscreen(Touch_CS);
//predefine menu background etc colors
  #define ENC_MENU_COLOR COLOR_YELLOW
  #define ENC_VALUE_COLOR COLOR_LIGHTGREY
  #define MENU_BCK_COLOR COLOR_DARKRED
  
 #endif

#endif


// this audio comes from the codec by I2S2
AudioInputI2S                    i2s_in; // MIC input
AudioRecordQueue                 recorder; 
AudioSynthWaveformSineHires      sine1; // local oscillator
//AudioSynthWaveformSineHires      sine2; // local oscillator
//
AudioEffectMultiply              heterodyne_multiplier; // multiply = mix
//AudioEffectMultiply              mult2; // multiply = mix

//AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
AudioAnalyzeFFT256               myFFT; // for spectrum display

AudioPlaySdRaw                   player; 

AudioEffectGranular              granular1;

AudioMixer4                      mixFFT;
AudioMixer4                      outputMixer; //selective output
AudioMixer4                      inputMixer; //selective input
AudioOutputI2S                   i2s_out; // headphone output          

AudioConnection mic_toinput         (i2s_in, 0, inputMixer, 0); //microphone signal
AudioConnection mic_torecorder      (i2s_in, 0, recorder, 0); //microphone signal
//AudioConnection mic_topeak (i2s_in, peakRMS);
//AudioConnection mic_topeak1 (i2s_in, peakVal);

AudioConnection player_toinput      (player, 0, inputMixer, 1); //player signal

AudioConnection input_toswitch      (inputMixer,0,  mixFFT,0);

AudioConnection input_todelay       (inputMixer,0, granular1, 0);

AudioConnection switch_toFFT        (mixFFT,0, myFFT,0 ); //raw recording channel 

AudioConnection input_toheterodyne1 (inputMixer, 0, heterodyne_multiplier, 0); //heterodyne 1 signal
AudioConnection sineheterodyne1    (sine1, 0, heterodyne_multiplier, 1);//heterodyne 1 mixerfreq

AudioConnection granular_toout (granular1,0, outputMixer,1);
//AudioConnection input_toheterodyne2 (granular1, 0, mult2, 0); //heterodyne 2
//AudioConnection sineheterodyne2     (sine2, 0, mult2, 1);//heterodyne 2 mixerfreq

AudioConnection heterodyne1_toout      (heterodyne_multiplier, 0, outputMixer, 0);  //heterodyne 1 output to outputmixer
//AudioConnection heterodyne2_toout      (mult2, 0, outputMixer, 1);  //heterodyne 2 output to outputmixer
AudioConnection player_toout           (inputMixer,0, outputMixer, 2);    //direct signal (use with player) to outputmixer

AudioConnection output_toheadphoneleft      (outputMixer, 0, i2s_out, 0); // output to headphone
AudioConnection output_toheadphoneright     (outputMixer, 0, i2s_out, 1);
//AudioConnection granular_toheadphone        (granular1,0,i2s_out,1);

AudioControlSGTL5000        sgtl5000;  

//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

#define GRANULAR_MEMORY_SIZE 30000  // enough for 100 ms at 281kHz
int16_t granularMemory[GRANULAR_MEMORY_SIZE];

// forward declaration Stop recording with message 
#ifdef DEBUGSERIAL
   void die(char *str, FRESULT rc);
#endif

extern "C" uint32_t usd_getError(void);

struct tm seconds2tm(uint32_t tt);

//continous timers
elapsedMillis since_bat_detection1; //start timing directly after FFT detects an ultrasound
elapsedMillis since_bat_detection2; //start timing directly after FFT detects the end of the ultrasound
//
elapsedMillis since_heterodyne=1000; //timing interval for auto_heterodyne frequency adjustments
uint16_t callLength=0;
//uint16_t clicker=0;

/************** RECORDING PLAYING SETTINGS *****************/

const int8_t    MODE_DETECT = 0;
const int8_t    MODE_REC = 1;
const int8_t    MODE_PLAY = 2;

int mode = MODE_DETECT; 

#if defined(__MK20DX256__)
  #define BUFFSIZE (8*1024) // size of buffer to be written
#elif defined(__MK66FX1M0__)
  #define BUFF 64
  #define BUFFSIZE (BUFF*1024) // size of buffer to be written

#endif

// buffer to store audiosamples during recording
uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
//uint8_t buffern2[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
uint wr;
uint32_t nj = 0;

#define waterfallgraph 1
#define spectrumgraph 2

int idx_t = 0;
int idx = 0;
int64_t sum;
float32_t mean;
int16_t FFT_bin [128]; 
int16_t FFT_max1 = 0;
uint32_t FFT_max_bin1 = 0;
int16_t FFT_mean1 = 0;
int16_t FFT_max2 = 0;
uint32_t FFT_max_bin2 = 0;
int16_t FFT_mean2 = 0;
//int16_t FFT_threshold = 0;
int16_t FFT_bat [3]; // max of 3 frequencies are being displayed
int16_t index_FFT;
int l_limit;
int u_limit;
int index_l_limit;
int index_u_limit;
//const uint16_t FFT_points = 1024;
const uint16_t FFT_points = 256;

int barm [512];

#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_234K              11
#define SAMPLE_RATE_281K              12
#define SAMPLE_RATE_352K              13
#define SAMPLE_RATE_MAX               13

typedef struct SR_Descriptor
{
    const int SR_n;
    const char* const txt; //display 
    
} SR_Desc;

// SRtext and position for the FFT spectrum display scale
const SR_Descriptor SR [SAMPLE_RATE_MAX + 1] =
{
    //   SR_n ,  f1
    {  SAMPLE_RATE_8K,  "8"}, 
    {  SAMPLE_RATE_11K,  "11"}, 
    {  SAMPLE_RATE_16K,  "16"}, 
    {  SAMPLE_RATE_22K,  "22"}, 
    {  SAMPLE_RATE_32K,  "32"}, 
    {  SAMPLE_RATE_44K,  "44"}, 
    {  SAMPLE_RATE_48K,  "48"},
    {  SAMPLE_RATE_88K,  "88"},
    {  SAMPLE_RATE_96K,  "96"},
    {  SAMPLE_RATE_176K,  "176"},
    {  SAMPLE_RATE_192K,  "192"}, 
    {  SAMPLE_RATE_234K,  "234"}, 
    {  SAMPLE_RATE_281K,  "281"}, 
    {  SAMPLE_RATE_352K,  "352"}
};    

// setup for FFTgraph denoising 
uint32_t FFTcount=0; //count the # of FFTs done 
uint16_t powerspectrumCounter=0;

float FFTavg[128];

float FFTpowerspectrum[128];
float powerspectrum_Max=0;

// defaults at startup functions
int displaychoice=waterfallgraph; //default display
int8_t mic_gain = 35; // start detecting with this MIC_GAIN in dB
int8_t volume=50;

int freq_real = 45000; // start heterodyne detecting at this frequency
int freq_real_backup=freq_real; //used to return to proper settingafter using the play_function

// initial sampling setup
int sample_rate = SAMPLE_RATE_281K;
int sample_rate_real = 281000;
char * SRtext="281";

int last_sample_rate=sample_rate;

float freq_Oscillator =50000;

/************************************************* MENU ********************************/
/***************************************************************************************/

typedef struct Menu_Descriptor
{
    const char* name;
    // ********preset variables below NOT USED YET
    const int len; // length of string to allow right-alignment
    const int def; //default settings
    const int low; // low threshold
    const int high; //high threshold
    
} Menu_Desc;


const int Leftchoices=10; //can have any value
const int Rightchoices=10;
const Menu_Descriptor MenuEntry [Leftchoices] =
{  {"Volume",6,60,0,100}, //divide by 100
   {"Gain",4,30,0,63},
   {"Frequency",9,45,20,90}, //multiply 1000
   {"Display",7,0,0,0},
   {"Denoise",7,0,0,0},
   {"SampleR",6,0,0,0},
   {"Record",6,0,0,0}, //functions where the LeftEncoder 
   {"Play",4,0,0,0},
   {"PlayD",5,0,0,0},
} ;

//TODO constants should be part of the menuentry, a single structure to hold the info
const int8_t  MENU_VOL = 0; //volume
const int8_t  MENU_MIC = 1; //mic_gain
const int8_t  MENU_FRQ = 2; //frequency
const int8_t  MENU_DSP = 3; //display
const int8_t  MENU_DNS = 4; //denoise
const int8_t  MENU_SR  = 5; //sample rate
const int8_t  MENU_REC = 6; //record
const int8_t  MENU_PLY = 7; //play 
const int8_t  MENU_PLD = 8; //play at original rate 

//available modes
const int detector_heterodyne=0;
const int detector_divider=1;
const int detector_Auto_heterodyne=2;
const int detector_Auto_TE=3;
const int detector_passive=4;

//default
int detector_mode=detector_heterodyne;  

//************************* ENCODER variables/constants
const int8_t enc_menu=0; //changing encoder sets menuchoice
const int8_t enc_value=1; //changing encoder sets value for a menuchoice

const int8_t enc_leftside=0; //encoder 
const int8_t enc_rightside=1; //encoder

const int8_t enc_up=1; //encoder goes up
const int8_t enc_nc=0;
const int8_t enc_dn=-1; //encoder goes down

int EncLeft_menu_idx=0; 
int EncRight_menu_idx=0;

int EncLeft_function=0; 
int EncRight_function=0;

/************************** */

// ******** LEFT AND RIGHT ENCODER CONNECTIONS/BUTTONS
#include <Encoder.h>
//try to avoid interrupts as they can (possibly ?) interfere during recording
#define ENCODER_DO_NOT_USE_INTERRUPTS

#define MICROPUSH_RIGHT  37 
Bounce micropushButton_R = Bounce(MICROPUSH_RIGHT, 50); 
#define encoderButton_RIGHT      36    
Bounce encoderButton_R = Bounce(encoderButton_RIGHT, 50); 
Encoder EncRight(34,35);
int EncRightPos=0;
int EncRightchange=0;

#define MICROPUSH_LEFT  38
Bounce micropushButton_L = Bounce(MICROPUSH_LEFT, 50); 
#define encoderButton_LEFT       29
Bounce encoderButton_L = Bounce(encoderButton_LEFT, 50); 
Encoder EncLeft(30,31);
int EncLeftPos=0;
int EncLeftchange=0;

// **END************ LEFT AND RIGHT ENCODER DEFINITIONS

#ifdef USESD1
void die(char *str, FRESULT rc) 
{ 
   #ifdef DEBUGSERIAL
   Serial.printf("%s: Failed with rc=%u.\n", str, rc); for (;;) delay(100); 
   #endif 
   }

//=========================================================================
#endif
#ifdef USESD1
//uint32_t count=0;
uint32_t ifn=0;
uint32_t isFileOpen=0;

TCHAR wfilename[80];
uint32_t t0=0;
uint32_t t1=0;
#endif

char filename[80];

void display_settings() {
  #ifdef USETFT
    
    tft.setTextColor(ENC_MENU_COLOR);
    
    tft.setFont(Arial_16);
    tft.fillRect(0,0,240,TOP_OFFSET-50,MENU_BCK_COLOR);
    tft.fillRect(0,TOP_OFFSET-10,240,10,COLOR_BLACK);
    tft.fillRect(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET,240,BOTTOM_OFFSET,MENU_BCK_COLOR);

    tft.setCursor(0,0);
    tft.print("g:"); tft.print(mic_gain);
    tft.print(" f:"); tft.print(freq_real);
    tft.print(" v:"); tft.print(volume);
    tft.print(" SR"); tft.print(SRtext);
    tft.setCursor(0,20);
    
    switch (detector_mode) {
       case detector_heterodyne:
         tft.print("HTD"); // 
       break;
       case detector_divider:
         tft.print("FD");
       break;
       case detector_Auto_heterodyne:
         tft.print("Auto_HTD");
       break;
       case detector_Auto_TE:
        tft.print("Auto_TE");
       break;
       case detector_passive:
        tft.print("PASS");
       break;
       default:
        tft.print("error");
       
     }
     // push the cursor to the lower part of the screen
     tft.setCursor(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);

     /****************** SHOW ENCODER SETTING ***********************/

     // set the colors according to the function of the encoders
     if (mode==MODE_DETECT ) 
     // show menu selection as menu-active of value-active
      {
       if (EncLeft_function==enc_value) 
        { tft.setTextColor(ENC_MENU_COLOR);
         }
        else
        { tft.setTextColor(ENC_VALUE_COLOR);}

       tft.print(MenuEntry[EncLeft_menu_idx].name);
       tft.print(" "); 

       if (EncRight_function==enc_value) 
         { tft.setTextColor(ENC_MENU_COLOR);} //value is active 
       else
         { tft.setTextColor(ENC_VALUE_COLOR);} //menu is active 
       
       //if MENU on the left-side is PLAY and selected than show the filename
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))     
           { //tft.print(fileselect); 
             tft.print(filelist[fileselect]);
           }
        else
         if (EncLeft_menu_idx==MENU_REC)      
          // show the filename that will be used for the next recording
           {  sprintf(filename, "B%u_%s.raw", file_number+1,SRtext);
              tft.print(filename );
            }
         else
         if (EncLeft_menu_idx==MENU_SR)
          { tft.print(SR[sample_rate].txt);

          }
          else
          { //tft.print(EncRightchange); 
            tft.setCursor(ILI9341_TFTWIDTH/2  ,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);
            tft.print(MenuEntry[EncRight_menu_idx].name);
          }
    }
    else
      { 
        if (mode==MODE_REC)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("REC:"); 
            tft.print(filename);
         }
        if (mode==MODE_PLAY) 
         {if (EncLeft_menu_idx==MENU_PLY)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("PLAY:"); 
            tft.print(filename);
          }
          else
           {tft.setTextColor(ENC_VALUE_COLOR);
            tft.print(MenuEntry[EncLeft_menu_idx].name);
            tft.print(" "); 
            tft.print(MenuEntry[EncRight_menu_idx].name);
      
           }

        }
      }

    //scale every 10kHz  
    float x_factor=10000/(0.5*(sample_rate_real / FFT_points)); 
    int curF=2*int(freq_real/(sample_rate_real / FFT_points));

    int maxScale=int(sample_rate_real/20000);
    for (int i=1; i<maxScale; i++) 
     { tft.drawFastVLine(i*x_factor, TOP_OFFSET-10, 9, ENC_MENU_COLOR);  
     }    
    tft.fillCircle(curF,TOP_OFFSET-4,3,ENC_MENU_COLOR);
    
   #endif
}


void       set_mic_gain(int8_t gain) {
    
    AudioNoInterrupts();
    //sgtl5000.micGainNew (24);
    sgtl5000.micGain (gain);
    //sgtl5000.lineInLevel(gain/4);
    AudioInterrupts();

    display_settings();
    powerspectrum_Max=0; // change the powerspectrum_Max for the FFTpowerspectrum
} // end function set_mic_gain

void       set_freq_Oscillator(int freq) {
    // audio lib thinks we are still in 44118sps sample rate
    // therefore we have to scale the frequency of the local oscillator
    // in accordance with the REAL sample rate
      
    freq_Oscillator = (freq) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    //float F_LO2= (freq+5000) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    // if we switch to LOWER samples rates, make sure the running LO 
    // frequency is allowed ( < 22k) ! If not, adjust consequently, so that
    // LO freq never goes up 22k, also adjust the variable freq_real  
    if(freq_Oscillator > 22000) {
      freq_Oscillator = 22000;
      freq_real = freq_Oscillator * (sample_rate_real / AUDIO_SAMPLE_RATE_EXACT) + 9;
    }
    AudioNoInterrupts();
    //setup multiplier SINE
    sine1.frequency(freq_Oscillator);
    //sine2.frequency(freq_Oscillator);
        
    AudioInterrupts();
    display_settings();
} // END of function set_freq_Oscillator

// set samplerate code by Frank Boesing 
void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;
//MCLD Divide sets the MCLK divide ratio such that: MCLK output = MCLK input * ( (FRACT + 1) / (DIVIDE + 1) ).
// FRACT must be set equal or less than the value in the DIVIDE field.
//(double)F_PLL * (double)clkArr[iFreq].mult / (256.0 * (double)clkArr[iFreq].div);
//ex 180000000* 1 /(256* 3 )=234375Hz  setting   {1,3} at 180Mhz

  
#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 int numfreqs = 17;
  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,  234000, 281000, 352800};
  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}, {1, 4},      {64, 255},     {219,802}, { 1,3 },  {2,5} , {1,2} };  //last value 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      set_sample_rate (int sr) {
  switch (sr) {
    case SAMPLE_RATE_8K:
    sample_rate_real = 8000;
    SRtext = " 8";
    break;
    case SAMPLE_RATE_11K:
    sample_rate_real = 11025;
    SRtext = "11";
    break;
    case SAMPLE_RATE_16K:
    sample_rate_real = 16000;
    SRtext = "16";
    break;
    case SAMPLE_RATE_22K:
    sample_rate_real = 22050;
    SRtext = "22";
    break;
    case SAMPLE_RATE_32K:
    sample_rate_real = 32000;
    SRtext = "32";
    break;
    case SAMPLE_RATE_44K:
    sample_rate_real = 44100;
    SRtext = "44";
    break;
    case SAMPLE_RATE_48K:
    sample_rate_real = 48000;
    SRtext = "48";
    break;
    case SAMPLE_RATE_88K:
    sample_rate_real = 88200;
    SRtext = "88";
    break;
    case SAMPLE_RATE_96K:
    sample_rate_real = 96000;
    SRtext = "96";
    break;
    case SAMPLE_RATE_176K:
    sample_rate_real = 176400;
    SRtext = "176";
    break;
    case SAMPLE_RATE_192K:
    sample_rate_real = 192000;
    SRtext = "192";
    break;
    case SAMPLE_RATE_234K:
    sample_rate_real = 234000;
    SRtext = "234";
    break;
    case SAMPLE_RATE_281K:
    sample_rate_real = 281000;
    SRtext = "281";
    break;
    case SAMPLE_RATE_352K:
    sample_rate_real = 352800;
    SRtext = "352";
    break;
  }
    
    AudioNoInterrupts();
    setI2SFreq (sample_rate_real); 
    delay(200); // this delay seems to be very essential !
    set_freq_Oscillator (freq_real);
    AudioInterrupts();
    delay(20);
    display_settings();
   
} // END function set_sample_rate



void spectrum() { // spectrum analyser code by rheslip - modified
     #ifdef USETFT
     if (myFFT.available()) {
//     if (fft1024_1.available()) {
    int16_t peak=0; uint16_t avgF=0;
    
    // find the BIN corresponding to the current frequency-setting 
    int curF=int(freq_real/(sample_rate_real / FFT_points));

//    for (int i = 0; i < 240; i++) {
    //startup sequence to denoise the FFT
    FFTcount++;
    if (FFTcount==1)
     {for (int16_t i = 0; i < 128; i++) {
         FFTavg[i]=0; 
     }
     }

    if (FFTcount<1000)
     { 
       for (int i = 0; i < 128; i++) {
         FFTavg[i]=FFTavg[i]+abs(myFFT.output[i])*0.001; //0.1% of total values
         }
     }

for (int16_t x = 2; x < 128; x++) {
   avgF=avgF+FFT_bin[x];
   if (FFT_bin[x]>peak)
      {
        peak=FFT_bin[x];   
      }
}

/*
avgF=avgF/128;
//check if the peak is at least 2x higher than the average otherwise set the indicator low
if ((peak-avgF)<(avgF/3))
  { maxF=2;}
  */  
  for (int16_t x = 2; x < 128; x++) {
//  for (uint16_t x = 8; x < 512; x+=4) {
     FFT_bin[x] = (myFFT.output[x]);//-FFTavg[x]*0.9; 
     int colF=ENC_VALUE_COLOR;
     
//     FFT_bin[x/4] = abs(fft1024_1.output[x]); 
     int barnew = (FFT_bin[x])/2 ;
     
     // this is a very simple first order IIR filter to smooth the reaction of the bars
     int bar = 0.05 * barnew + 0.95 * barm[x]; 
     if (bar >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { bar=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     if (bar <0) bar=0;
     if (barnew >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { barnew=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     int g_x=x*2;
     int spectrumline=barm[x];
     int spectrumline_new=barnew;
          
     //tft.drawFastVLine(g_x,TOP_OFFSET,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET, COLOR_BLACK);
     tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline_new, COLOR_GREEN);
     //tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline, COLOR_RED);
     tft.drawFastVLine(g_x,TOP_OFFSET+spectrumline_new,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline_new, COLOR_BLACK);
     tft.drawFastVLine(g_x+1,TOP_OFFSET,spectrumline, COLOR_DARKGREEN);
     tft.drawFastVLine(g_x+1,TOP_OFFSET+spectrumline,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline, COLOR_BLACK);
    /* if (x==maxF)
       { colF=COLOR_ORANGE;
         tft.drawFastVLine(g_x,TOP_OFFSET,240-bar, colF);
         }
      */   
     
     //tft.drawPixel(g_x,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-bar,colF);

     barm[x] = bar;
  }
  
    // if (mode == MODE_DETECT)  search_bats();     
  } //end if
  if (mode==MODE_PLAY)
    {//float ww=( player.positionMillis()/player.lengthMillis()*240.0);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-5,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-5,5,ENC_MENU_COLOR);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-4,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-4,5,ENC_MENU_COLOR);
    }
  #endif
}
#ifdef DEBUGSERIAL 

/*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(")");
 
      AudioProcessorUsageMaxReset();
      AudioMemoryUsageMaxReset();
    }


}
*/ // END function check_processor
#endif



void waterfall(void) // thanks to Frank B !
{ 
  
#ifdef USETFT

// code for 256 point FFT 
     
 if (myFFT.available()) {
  const uint16_t Y_OFFSET = TOP_OFFSET;
  static int count = TOP_OFFSET;
  //int curF=int(freq_real/(sample_rate_real / FFT_points));

  // lowest frequencybin to detect as a batcall
  int batCall_LoF_bin= int(30000/(sample_rate_real / FFT_points));
  int batCall_HiF_bin= int(80000/(sample_rate_real / FFT_points));

  uint16_t FFT_pixels[240]; // maximum of 240 pixels, each one is the result of one FFT 
  FFT_pixels[0]=0; FFT_pixels[1]=0;  FFT_pixels[2]=0; FFT_pixels[3]=0;
  
    FFTcount++;

    //requested to start with a clean FFTavg array to denoise
    if (FFTcount==1)
       {for (int16_t i = 0; i < 128; i++) {
          FFTavg[i]=0; 
       }
     }

    // collect 1000 FFT samples for the denoise array
    if (FFTcount<100)
     { for (int i = 2; i < 128; i++) {
         //FFTavg[i]=FFTavg[i]+myFFT.read(i)*65536.0*5*0.001; //0.1% of total values
         FFTavg[i]=FFTavg[i]+myFFT.output[i]*10*0.001; //0.1% of total values
         }
     }
    

    int FFT_peakF_bin=0; 
    int peak=512;
    int avgFFTbin=0;
    // there are 128 FFT different bins only 120 are shown on the graphs  
    
    for (int i = 2; i < 120; i++) { 
      int val = myFFT.output[i]*10 -FFTavg[i]*0.9 + 10; //v1
      avgFFTbin+=val;
      //detect the peakfrequency
      if (val>peak)
       { peak=val; 
         FFT_peakF_bin=i;
        }
       if (val<5) 
           {val=5;}

       FFT_pixels[i*2] = tft.color565(
              min(255, val/2),
              (val/6>255)? 255 : val/6,
              //(val/4>255)? 255 : val/4
                            0
              //((255-val)>>1) <0? 0: (255-val)>>1 
             ); 
       
      FFT_pixels[i*2+1]=FFT_pixels[i*2];       
    }
    avgFFTbin=avgFFTbin/120;
    //mark the peak
    //FFT_pixels[FFT_peakF_bin*2]=COLOR_RED;
    //FFT_pixels[FFT_peakF_bin*2+1]=COLOR_RED;
   if ((peak/avgFFTbin)<1.2) //very low peakvalue so probably noise
     { FFT_peakF_bin=0; 
     }

  int powerSpectrum_Maxbin=0;
  // detected a peak in the bat frequencies
  if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin))
  {
    //collect data for the powerspectrum 
    for (int i = 2; i < 120; i++)
     { 
        //add new samples
        FFTpowerspectrum[i]+=myFFT.output[i];
        //keep track of the maximum
        if (FFTpowerspectrum[i]>powerspectrum_Max) 
           { powerspectrum_Max=FFTpowerspectrum[i];
             powerSpectrum_Maxbin=i;
           }

     }
     //keep track of the no of samples with bat-activity
     powerspectrumCounter++;
  }
    // update display after every 100th FFT sample with bat-activity
    if ((powerspectrumCounter>50)  )
       { powerspectrumCounter=0;
         //clear powerspectrumbox
         tft.fillRect(0,TOP_OFFSET-50,240,45, COLOR_BLACK);
         // keep a minimum maximumvalue to the powerspectrum
         int binLo=2; int binHi=0;

         for (int i=2; i<120; i++)
          {             
            int ypos=FFTpowerspectrum[i]/powerspectrum_Max*45; 

            // first encounter of 1/20 of maximum
            if (i<powerSpectrum_Maxbin)
              {if (FFTpowerspectrum[i]<(powerspectrum_Max*0.1))
                    {binLo=i;}}
            else
              {if (FFTpowerspectrum[i]>(powerspectrum_Max*0.1))
                    {binHi=i;}
              }

            tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            if (i==powerSpectrum_Maxbin)                        
              { tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,ENC_MENU_COLOR);
               }
            
            //tft.drawFastVLine(i*2+1,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            FFTpowerspectrum[i]=0;
          }
         
         //tft.setCursor(0,TOP_OFFSET-45);
         //tft.print(powerspectrum_Max);
         if (powerspectrum_Max==20000)
          {binLo=0; binHi=0;
          }
         float multiplier=(sample_rate_real / FFT_points)*0.001;
         powerspectrum_Max=powerspectrum_Max*0.5; //lower the max after a graphupdate
         tft.setCursor(140,TOP_OFFSET-45);
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binLo*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_MENU_COLOR);
         tft.print(int(powerSpectrum_Maxbin*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binHi*multiplier) );
        
       }
      
    
    if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin)) // we got a high-frequent signal peak
      { 
        // when a batcall is first discovered 
        if (not batTrigger) 
          { since_bat_detection1=0; //start of the call mark
            //clicker=0;
            FFT_pixels[5]=ENC_VALUE_COLOR; // mark the start on the screen
            FFT_pixels[6]=ENC_VALUE_COLOR;
            FFT_pixels[7]=ENC_VALUE_COLOR;
            
            if (detector_mode==detector_Auto_heterodyne)
               if (since_heterodyne>1000) //update the most every second
                {freq_real=int((FFT_peakF_bin*(sample_rate_real / FFT_points)/500))*500; //round to nearest 500hz
                 set_freq_Oscillator(freq_real); 
                 since_heterodyne=0;
                 //granular1.stop();
                }
            
            //restart the TimeExpansion only if the previous call was played
            if ((detector_mode==detector_Auto_TE) and (TE_ready) )
             { granular1.stop();
               granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
               granular1.setSpeed(0.05);
               TE_ready=false;
               
             }
                      
          }
         //clicker++; 
         batTrigger=true;
         
     }
   else // FFT_peakF_bin does not show a battcall  
        { 
          if (batTrigger) //previous sample was still a call
           { callLength=since_bat_detection1; // got a pause so store the time since the start of the call
             
             /*if (callLength>20) //call is too long 
              { TE_ready=true; // break the TE replay
                } 
              */  
              since_bat_detection2=0; //start timing the length of the replay
             }
          batTrigger=false;
        }    
    // restart TimeExpansion recording a bit after the call has finished completely
    if ((!TE_ready) and (since_bat_detection2>(callLength*10)))
      { //stop the time expansion
        TE_ready=true;
        granular1.stopTimeExpansion();
       
      }
    

    if (since_bat_detection2<50) //keep scrolling 100ms after the last bat-call
      {  tft.writeRect( 0,count, ILI9341_TFTWIDTH,1, (uint16_t*) &FFT_pixels); //show a line with spectrumdata
         tft.setScroll(count);
        count++;
        
      } 


    if (count >= ILI9341_TFTHEIGHT-BOTTOM_OFFSET) count = Y_OFFSET;
    

  }
#endif

}

void startRecording() {
  mode = MODE_REC;
  #ifdef USESD1
  
    #ifdef DEBUGSERIAL
      Serial.print("startRecording");
    #endif  
    
    // close file
    if(isFileOpen)
    {
      //close file
      rc = f_close(&fil);
      if (rc) die("close", rc);
      isFileOpen=0;
    }
  
  if(!isFileOpen)
  {
  file_number++;
  //automated filename BA_S.raw where A=file_number and S shows samplerate. Has to fit 8 chars
  // so max is B999_192.raw
  sprintf(filename, "B%u_%s.raw", file_number, SRtext);
    #ifdef DEBUGSERIAL
    Serial.println(filename);
    #endif  
  char2tchar(filename, 13, wfilename);
  filecounter++;
  strcpy(filelist[filecounter],filename );

  rc = f_stat (wfilename, 0);
  #ifdef DEBUGSERIAL
    Serial.printf("stat %d %x\n",rc,fil.obj.sclust);
 #endif   
  rc = f_open (&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
#ifdef DEBUGSERIAL
    Serial.printf(" opened %d %x\n\r",rc,fil.obj.sclust);
#endif 
    // check if file has errors
    if(rc == FR_INT_ERR)
    { // only option then is to close file
        rc = f_close(&fil);
        if(rc == FR_INVALID_OBJECT)
        { 
          #ifdef DEBUGSERIAL
          Serial.println("unlinking file");
          #endif
          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;
  }

  #endif

  //clear the screen completely
  tft.fillRect(0,0,ILI9341_TFTWIDTH,ILI9341_TFTHEIGHT,COLOR_BLACK);
  tft.setTextColor(ENC_VALUE_COLOR);
  tft.setFont(Arial_28);
  tft.setCursor(0,100);
  tft.print("RECORDING");
  tft.setFont(Arial_16);
  
  display_settings();
  
  granular1.stop(); //stop granular

  //switch off several circuits
  mixFFT.gain(0,0);
  
  outputMixer.gain(1,0);  //shutdown granular output      
  
  detector_mode=detector_heterodyne;

  outputMixer.gain(0,1); 
  
  nj=0;
  recorder.begin();
    
}

void continueRecording() {
  #ifdef USESD1
  const uint32_t N_BUFFER = 2;
  const uint32_t N_LOOPS = BUFF*N_BUFFER; // !!! NLOOPS and BUFFSIZE ARE DEPENDENT !!! NLOOPS = BUFFSIZE/N_BUFFER
  // 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
    //read N_BUFFER sample-blocks into memory
    for (int i = 0; i < N_BUFFER; i++) {
       //copy a new bufferblock from the audiorecorder into memory
       memcpy(buffern + i*256 + nj * 256 * N_BUFFER, recorder.readBuffer(), 256);
       //free the last buffer that was read
       recorder.freeBuffer();
       } 

    nj++; 

    if (nj >  (N_LOOPS-1)) 
    {
      nj = 0;
      //old code used to copy into a 2nd buffer, not needed since the writing to SD of the buffer seems faster than the filling 
      //this allows larger buffers to be used
      //memcpy(buffern2,buffern,BUFFSIZE);  
      //push to SDcard  
      rc =  f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
      }
  }
  #endif
}

void stopRecording() {
#ifdef USESD1
  #ifdef DEBUGSERIAL  
    Serial.print("stopRecording");
  #endif  
    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_DETECT;
  //  clearname();
  #ifdef DEBUGSERIAL  
    Serial.println (" Recording stopped!");
  #endif 
#endif
  //switch on FFT
  tft.fillScreen(COLOR_BLACK);
  mixFFT.gain(0,1); 
      
}

void startPlaying(int SR) {
//      String NAME = "Bat_"+String(file_number)+".raw";
//      char fi[15];
//      NAME.toCharArray(fi, sizeof(NAME));

inputMixer.gain(0,0); //switch off the mic-line as input
inputMixer.gain(1,1); //switch on the playerline as input

if (EncLeft_menu_idx==MENU_PLY) 
  {
      outputMixer.gain(2,1);  //player to output 
      outputMixer.gain(1,0);  //shutdown granular output      
      outputMixer.gain(0,0);  //shutdown heterodyne output
      EncRight_menu_idx=MENU_SR;
      EncRight_function=enc_value;
      freq_real_backup=freq_real; //keep track of heterodyne setting
  }
  //direct play is used to test functionalty based on previous recorded data
  //this will play a previous recorded raw file through the system as if it were live data coming from the microphone
if (EncLeft_menu_idx==MENU_PLD) 
  {
      outputMixer.gain(2,0);  //shutdown direct audio from player to output 
      outputMixer.gain(0,1);  //default mode will be heterodyne based output 
      if (detector_mode==detector_Auto_TE)
         { outputMixer.gain(1,1);  //start granular output processing      
           outputMixer.gain(0,0);
         }
  }

//allow settling
  delay(100);
  
  //keep track of the sample_rate 
  last_sample_rate=sample_rate;
  SR=constrain(SR,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);
  set_sample_rate(SR);
  
  fileselect=constrain(fileselect,0,filecounter);
  strncpy(filename, filelist[fileselect],  13);
  
  //default display is waterfall
  displaychoice=waterfallgraph;
  display_settings();

  player.play(filename);
  mode = MODE_PLAY;

}
  

void stopPlaying() {
  
#ifdef DEBUGSERIAL      
  Serial.print("stopPlaying");
#endif  
  if (mode == MODE_PLAY) player.stop();
  mode = MODE_DETECT;
#ifdef DEBUGSERIAL      
  Serial.println (" Playing stopped");
#endif  
  
  //restore last sample_rate setting
  set_sample_rate(last_sample_rate);
if (EncLeft_menu_idx==MENU_PLY)
{
  freq_real=freq_real_backup;
  //restore heterodyne frequency
  set_freq_Oscillator (freq_real);
}
  outputMixer.gain(2,0); //stop the direct line output
  outputMixer.gain(1,1); // open granular output
  outputMixer.gain(0,1); // open heterodyne output  

  inputMixer.gain(0,1); //switch on the mic-line
  inputMixer.gain(1,0); //switch off the playerline

}


void continuePlaying() {
  //the end of file was reached
  if (!player.isPlaying()) {
    stopPlaying();
    if (continousPlay) //keep playing until stopped by the user
      { startPlaying(SAMPLE_RATE_176K);
      }
  }
}

void changeDetector_mode()
{
  if (detector_mode==detector_heterodyne)
         { granular1.stop(); //stop other detecting routines
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
          //switch menu to volume/frequency
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_FRQ;
           EncRight_function=enc_value;
          
         } 
      if (detector_mode==detector_divider)
         { granular1.beginDivider(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  

      if (detector_mode==detector_Auto_TE)
         { granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           granular1.setSpeed(0.06); //default TE is 1/0.06 ~ 1/16 :TODO, switch from 1/x floats to divider value x
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  
      if (detector_mode==detector_Auto_heterodyne)
         { granular1.stop(); 
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

           
         }  

      if (detector_mode==detector_passive)
         { granular1.stop(); //stop all other detecting routines
           outputMixer.gain(2,1);  //direct line to output 
           outputMixer.gain(1,0);  //shutdown granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }
       else //all other options use the heterodyne and granular output line
        {  outputMixer.gain(2,0);  //shut down direct line to output 
           //outputMixer.gain(1,1);  //granular   output      
           //outputMixer.gain(0,1);  //heterodyne output
 
        } 

}

//*****************************************************update encoders
void updateEncoder(uint8_t Encoderside )
 {
   
  /************************setup vars*************************/
   int encodermode=-1; // menu=0 value =1;
   int change=0;
   int menu_idx=0;
   int choices=0;

    //get encodermode 
   if (Encoderside==enc_leftside)
    { encodermode=EncLeft_function;
      change=EncLeftchange;
      menu_idx=EncLeft_menu_idx;
      choices=Leftchoices; //available menu options
    }

   if (Encoderside==enc_rightside)
    { encodermode=EncRight_function;
      change=EncRightchange;
      menu_idx=EncRight_menu_idx;
      choices=Rightchoices; //available menu options
    } 


  /************************proces changes*************************/
  //encoder is in menumode
  if (encodermode==enc_menu)
    { menu_idx=menu_idx+change;
            
      //allow revolving choices
      if (menu_idx<0)
        {menu_idx=choices-1;}
      if (menu_idx>=choices)
        {menu_idx=0;}
      //remove functionality when SD is not active, so no SDCARD mounted or SDCARD is unreadable  
      if (!SD_ACTIVE)
        { if ((menu_idx==MENU_PLD) or (menu_idx==MENU_PLY) or (menu_idx==MENU_REC))
           { // move menu to volume
             menu_idx=MENU_VOL;
           }
        }

      if (Encoderside==enc_leftside)
          { EncLeft_menu_idx=menu_idx; //limit the menu 
               }
     
     //limit the changes of the rightside encoder for specific functions
      if ((EncLeft_menu_idx!=MENU_SR) )      
        if (Encoderside==enc_rightside)
          { EncRight_menu_idx=menu_idx; //limit the menu 
               }
    }
        
  //encoder is in valuemode and has changed position
  if ((encodermode==enc_value) and (change!=0))
    { 
      /******************************VOLUME  ***************/
      if (menu_idx==MENU_VOL)
        { volume+=change;
          volume=constrain(volume,0,90);
          float V=volume*0.01;
          AudioNoInterrupts();
          sgtl5000.volume(V);
          AudioInterrupts();
        }
      /******************************MIC_GAIN  ***************/
      if (menu_idx==MENU_MIC)
        {
         mic_gain+=change;
         mic_gain=constrain(mic_gain,0,63);
         set_mic_gain(mic_gain);
         FFTcount=0; //start denoise after changing gain
         //reset FFTdenoise array
         {for (int16_t i = 0; i < 128; i++) {
           FFTavg[i]=0; 
         }}  
        }
      /******************************FREQUENCY  ***************/
      if (menu_idx==MENU_FRQ)
         { int delta=500;
           uint32_t currentmillis=millis();
           //when turning the encoder fast make the change larger
           if ((currentmillis-lastmillis)<500)
              { delta=1000;}
           if ((currentmillis-lastmillis)<250)
              { delta=2000;}
           if ((currentmillis-lastmillis)<100)
              { delta=5000;}

          freq_real=freq_real+delta*change;
          // limit the frequencies to 500hz steps
          freq_real=constrain(freq_real,7000,int(sample_rate_real/2000)*1000-1000);
          set_freq_Oscillator (freq_real);
          lastmillis=millis();
         }
      /******************************DENOISE  ***************/
      if (menu_idx==MENU_DNS)
        { // setting FFTcount to 0 activates a 1000 sample denoise
          FFTcount=0;
        }
      
      /******************************DISPLAY  ***************/
    
      if (menu_idx==MENU_DSP)
         { 
           displaychoice+=change;
           displaychoice=displaychoice%3; //limit to 0(none),1(spectrum),2(waterfall)
           if (displaychoice==waterfallgraph) 
              {
               tft.setRotation( 0 );
            }
           if (displaychoice==spectrumgraph) 
             { 
            tft.setScroll(0);
            tft.setRotation( 0 );
              }
            tft.fillScreen(COLOR_BLACK);
        }

      

/************** SPECIAL MODES WHERE THE LEFTENCODER SETS A FUNCTION AND THE RIGHT ENCODER SELECTS */
      
      /******************************SAMPLE_RATE  ***************/
      if (EncLeft_menu_idx==MENU_SR)  //only selects a possible sample_rate, user needs to press a button to SET sample_rate
        { sample_rate+=EncRightchange;
          sample_rate=constrain(sample_rate,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);

          
        }   

      
      /******************************SELECT A FILE  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_PLY) and (EncRight_function==enc_value))//menu play selected on the left and right 
         {  
           if (mode!=MODE_PLAY)
            {
             fileselect+=EncRightchange; 
             fileselect=constrain(fileselect,0,filecounter-1);
            
            }   
         }  

      /******************************CHANGE SR during PLAY  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_value))//menu play selected on the left and right    
          {
           if (mode==MODE_PLAY)
              {
                 sample_rate+=EncRightchange;
                 sample_rate=constrain(sample_rate,SAMPLE_RATE_8K,SAMPLE_RATE_44K);
                 set_sample_rate(sample_rate);
                 
              }

        }

    }

 }

void updateEncoders()
{
//only react to changes large enough (depending on the steps of the encoder for one rotation)
 long EncRightnewPos = EncRight.read()/4;
 if (EncRightnewPos>EncRightPos)
   { EncRightchange=enc_up; }
   else
   if (EncRightnewPos<EncRightPos)
   { EncRightchange=enc_dn; }
   else
   { EncRightchange=enc_nc; }

 if (EncRightchange!=0)
    {updateEncoder(enc_rightside);
     } 

 EncRightPos=EncRightnewPos;
 
 long EncLeftnewPos = EncLeft.read()/4;
 if (EncLeftnewPos>EncLeftPos)
   { EncLeftchange=enc_up; }
   else
   if (EncLeftnewPos<EncLeftPos)
   { EncLeftchange=enc_dn; }
   else
   { EncLeftchange=enc_nc; }

 if (EncLeftchange!=0)
    {updateEncoder(enc_leftside);
    } 
 
 EncLeftPos=EncLeftnewPos;
 //update display only if a change has happened
 if ((EncRightchange!=0) or (EncLeftchange!=0))
      display_settings();

}

void updateButtons()
{
 // Respond to button presses
   
 // try to make the interrupts as short as possible when recording
 if (mode==MODE_REC) 
   {
     encoderButton_L.update(); //check the left encoderbutton
      if ((encoderButton_L.risingEdge())  )
       { stopRecording();
          EncLeft_function=enc_menu; //force into active-menu
          display_settings();      
       }
   }
 else // not MODE_REC
  {
  // Respond to button presses
 
  encoderButton_L.update();
  encoderButton_R.update();

  micropushButton_L.update();
  micropushButton_R.update();
    
   /*RIGHT MICROPUSH */
  if (micropushButton_R.risingEdge()) {
        detector_mode++;
        if (detector_mode>detector_passive)
          {detector_mode=0;}
        changeDetector_mode();
        display_settings();      
    }
/*LEFT MICROPUSH */
  if (micropushButton_L.risingEdge()) {
      //no function yet
    }



  /************  LEFT ENCODER BUTTON *******************/
  if (encoderButton_L.risingEdge()) {

      EncLeft_function=!EncLeft_function;
        
      //*SPECIAL MODES that change rightside Encoder based on leftside Encoder menusetting
      //****************************************SAMPLERATE
      if ((EncLeft_menu_idx==MENU_SR) and (EncLeft_function==enc_value))
       { EncRight_menu_idx=MENU_SR;
         EncRight_function=enc_value; // set the rightcontroller to select
       }
      
     if (SD_ACTIVE)
     {
        /*if (mode==MODE_REC)
                 { stopRecording();
                   EncLeft_function=enc_menu; //force into active-menu
                 }*/
        if ((mode==MODE_PLAY) and ((EncLeft_menu_idx==MENU_PLY) or (EncLeft_menu_idx==MENU_PLD)))
                 { stopPlaying();
                   EncLeft_menu_idx=MENU_PLY;
                   EncLeft_function=enc_menu; //force into active-menu
                   continousPlay=false;
                 }
               
        //Direct play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLD) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_FRQ ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }

        //play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_PLY ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }
       
     
     } //END SD_ACTIVE
     display_settings();      
  }

/************  RIGHT ENCODER BUTTON *******************/

 if (encoderButton_R.risingEdge()) {
    
    EncRight_function=!EncRight_function; //switch between menu/value control
        
    //when EncLeftoder is SR menu than EncRightoder can directly be setting the samplerate at a press 
    //the press should be a transfer from enc_value to enc_menu before it gets activated
    if ( (EncLeft_menu_idx==MENU_SR) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_menu))
    { set_sample_rate(sample_rate);
      last_sample_rate=sample_rate;

    }
    
    //recording and playing are only possible with an active SD card
    if (SD_ACTIVE)
     {
      /*if (mode==MODE_REC)
             { stopRecording();
               EncRight_function=enc_menu; //force into active-menu
              }
          else*/
           if (EncLeft_menu_idx==MENU_REC)
            {
              if (mode == MODE_DETECT) 
                 startRecording();
              }  
      else
      if (mode==MODE_PLAY)
             {
              if (not continousPlay) 
                { stopPlaying();
                  EncLeft_menu_idx=MENU_PLY;
                  EncLeft_function=enc_menu;
                }
             }     
      else       
      if (mode==MODE_DETECT)   
       { if (EncLeft_menu_idx==MENU_PLY)
            { last_sample_rate=sample_rate; 
              startPlaying(SAMPLE_RATE_8K);
             }
          
          if (EncLeft_menu_idx==MENU_PLD)
              {  fileselect=referencefile;
                 continousPlay=true;
                 last_sample_rate=sample_rate;
                 startPlaying(SAMPLE_RATE_176K);
              }  
       }
     }
      
      display_settings();
  }

  }
}



/********************************************************* */

void setup() {
 #ifdef DEBUGSERIAL
  Serial.begin(115200);
 #endif  
  delay(200);
 
//setup Encoder Buttonpins with pullups
  pinMode(encoderButton_RIGHT,INPUT_PULLUP);
  pinMode(encoderButton_LEFT,INPUT_PULLUP);

  pinMode(MICROPUSH_RIGHT,INPUT_PULLUP);
  pinMode(MICROPUSH_LEFT,INPUT_PULLUP);
 
 // EncLeft.write(10000); //set default far away from 0 to avoid negative transitions
  //EncRight.write(10000); //set default far away from 0 to avoid negative transitions

//startup menu
  EncLeft_menu_idx=MENU_VOL;
  EncRight_menu_idx=MENU_FRQ;
  EncLeft_function=enc_menu;
  EncRight_function=enc_menu;

  // Audio connections require memory. 
  AudioMemory(300);

// setSyncProvider(getTeensy3Time);

// Enable the audio shield. select input. and enable output
  sgtl5000.enable();
  sgtl5000.inputSelect(myInput);
  
  sgtl5000.volume(0.45);
  sgtl5000.micGain (mic_gain);
  //sgtl5000.adcHighPassFilterDisable(); // does not help too much!
  sgtl5000.lineInLevel(0);
  mixFFT.gain(0,1);

// Init TFT display  
#ifdef USETFT
  tft.begin();
  //ts.begin();
  tft.setRotation( 0 );
  tft.fillScreen(COLOR_BLACK);

  tft.setCursor(0, 0);
  tft.setScrollarea(TOP_OFFSET,BOTTOM_OFFSET);
  display_settings();
  tft.setCursor(80,50);
  tft.setFont(Arial_24);
  char tstr[9];
//  snprintf(tstr,9, "%02d:%02d:%02d",  hour(), minute(), second() );
struct tm tx = seconds2tm(RTC_TSR); 
snprintf(tstr,9, "%02d:%02d:%02d", tx.tm_hour, tx.tm_min, tx.tm_sec);
tft.print(tstr);
  delay(2000); //wait a second to clearly show the time 
#endif

   //Init SD card use
// uses the SD card slot of the Teensy, NOT that of the audio boards
// this init only for playback
#ifdef USESD1
  if(!(SD.begin(BUILTIN_SDCARD))) 
  {
      #ifdef DEBUGSERIAL
          Serial.println("Unable to access the SD card");
          delay(500);  
      #endif     
      
    SD_ACTIVE=false;
    tft.fillCircle(70,50,5,COLOR_RED);
     
  }
  else
  { 
    SD_ACTIVE=true;
    tft.fillCircle(70,50,5,COLOR_GREEN);
    filecounter=0;
    root = SD.open("/");


    //TODO: check if file is a RAW file and also read the SAMPLERATE
    while (true) {
        
        File entry =  root.openNextFile();
        if (! entry) {
          // no more files
          tft.setCursor(0,50);
          tft.print(filecounter);
          break;
        }
        
        if (entry.isDirectory()) {
          // do nothing, only look for raw files in the root
        } else 
        {
          
        strcpy(filelist[filecounter],entry.name() );
      
        if (String(entry.name())=="TEST_176.RAW")
           {referencefile=filecounter;
            }
        filecounter++;  
          
        }
        entry.close();
       }

    }


if (SD_ACTIVE)
// Recording on SD card by uSDFS library
  {f_mount (&fatfs, (TCHAR *)_T("0:/"), 0);      /* Mount/Unmount a logical drive */
   for (int i=0; i<filecounter; i++)
     {   
       #ifdef DEBUGSERIAL
          #ifdef USETFT
            tft.setCursor(0,50+i*20);
            tft.print(filelist[i]);
          #endif
      #endif    

     }
     file_number=filecounter+1;

  }

#endif

#ifdef USESD2
 uSD.init();

#endif

set_sample_rate (sample_rate);
set_freq_Oscillator (freq_real);
inputMixer.gain(0,1); //microphone active
inputMixer.gain(1,0); //player off

outputMixer.gain(0,1); // heterodyne1 to output 
outputMixer.gain(1,0); // granular to output off
outputMixer.gain(2,0); // player to output off

// the Granular effect requires memory to operate
granular1.begin(granularMemory, GRANULAR_MEMORY_SIZE);

// reset the FFT denoising array at startup
for (int16_t i = 0; i < 128; i++) {
  FFTavg[i]=0; 
    }
} // END SETUP


void loop() {
// If we're playing or recording, carry on...
  if (mode == MODE_REC) {
    continueRecording();
    
  } 
  
  if (mode == MODE_PLAY) {
    continuePlaying();
  }

updateButtons();   
// during recording only the left encoders button is used and screens are not updated
if (mode!=MODE_REC)
{
 updateEncoders();
 #ifdef USETFT
   if (displaychoice==waterfallgraph) 
    { waterfall();
      
     }
   else
    if (displaychoice==spectrumgraph)
    {  spectrum();
     }
 #endif
 }   

}
 
Hi,

Just tested the changes WMXZ has proposed, code compiles OK on my side too. Have not updated the detector itself (just check the code) but I am not doubting that this is a good change, the advantage is that this makes the code less dependent on particular libraries. Especially the differences between coding environments (windows, linux, platformIO, arduinoIDE ...) are sometimes causing issues that are difficult to assess.

Thanks to WMXZ for chiming in and improving the code !
Thanks to Edwin for persisting to work on both this issue with the code and your work on decreasing the backgroud-noise causes. I will try to see if I can also change the way my current setup is powered in the way you have suggested.

Furthermore, I am currently not actively developing the code. Its winter and the bats overhere are allready at rest, but I do have some ideas to improve/extend the code further especially the AUTO_TE mode which is according to me the most attractive mode to listen to ultrasound. Sure its not scientifically as sound since it can skip parts of the incoming signal. But to wander around in a garden and hear different crickets/grasshoppers and at the same time hear bats all having distinct calls (like birds) is in my view very entertaining.

regards
Cor
 
Last edited:
Last edited:
hello I could not get the screen ili9341 that you use with this program code, I already buy different screens but I could not get the spectrum analysis visualized, could you advise me how do I make the connections with any of these screens or is it necessary get the same one that you use

Do you get any image at all?

As long as the screen uses the right river it should work, I use this display 2.8" 240x320 SPI TFT LCD ILI9341 5V/3.3V (red pcb, 14+4pin on ebay)

In post 147 you can see the schematic I use, reset is tied high with a delay 10k/100nF

Pleas keep in mind the code by DD4WH and Corber do not use the exact same pins!

Edwin
 
For the first link I can not se what chip it uses, the screen resolution seem the same, the second one, raspberry pi display will almost cirtainly use and other chip.

Be sure your display uses the ili9341 otherwise it will not work without changing the code to the right display driver chip.

Edwin
 
already verify the first screen and have the ili9341 mention this in the product description:
ITDB02-3.2S V2 display module is a 3.2 "TFT LCD with 65K color 320 x 240 resolutions for Arduino The controller of this Arduino LCD module is ILI9341.It supports 16bit data interface with control interface of four wires.
https://www.itead.cc/itdb02-3-2s-v2.html
 
CandelaRDZ

Did you get anything usefull on your display?

Do you use the Corber code and do you use the pins as described in the code?

Did you connect the reset pin to the powersupply? (or like I did with a 10k resistor and 100nF capacitor)

Edwin
 
Looking at the pinout of this module I think it will be using SPI for touch and the SDcard and a 16bit databus instead of SPI for the screen itself. I could be wrong but the sheer number of pins on the module look like its a 16bit bus. And the current code is made to work with SPI, also I dont see how a 16bit bus could be controlled by a Teensy, I have no experience with them.
 
CandelaRDZ, I just took some time to look at the datasheet of the ili chip and your display.

As it seem the ili chip can be configured for ome of 12 different interface modes. One of these is SPI, the display you have does not use SPI for the display interface. (the choice of interface mode is set in the hardware by the manufacturer)

It looks like you will have to use these displays for some other project and buy the right one for this project.

Edwin
 
Last edited:
Well I've tried Edwin's code in ArduinoIDE and it still won't compile. No clue as to why from the error message. I've now installed PlatformIO on windows. After failing to compile again as it couldn't find include files I put the full path in for the includes which helped but it still won't compile. To be honest the error message look much the same to me and I still can't see what the issue is. Only the last two lines are red. Everything else is yellow, which I'm taking as meaning not ideal but ok

here is my code:
Code:
/***********************************************************************
 *  TEENSY 3.6 BAT DETECTOR V0.1 20180814
 * 
 *  Copyright (c) 2018, Cor Berrevoets, registax@gmail.com
 *  
 *  TODO: use selectable presets
 * 
 *  HARDWARE USED:
 *     TEENSY 3.6
 *     TEENSY audio board
 *     Ultrasonic microphone with seperate preamplifier connected to mic/gnd on audioboard
 *       eg. Knowles MEMS SPU0410LR5H-QB 
 *     TFT based on ILI9341
 *     2 rotary encoders with pushbutton
 *     2 pushbuttons
 *     SDCard
 * 
*   IMPORTANT: uses the SD card slot of the Teensy, NOT the SD card slot of the audio board 
 * 
 *  4 operational modes: Heterodyne.
 *                       Frequency divider
 *                       Automatic heterodyne (1/10 implemented)
 *                       Automatic TimeExpansion (live)
 *
 *  Sample rates up to 352k
 *  
 *  User controlled parameters:
 *     Volume
 *     Gain
 *     Frequency
 *     Display (none, spectrum, waterfall)
 *     Samplerate
 *     
 *  Record raw data
 *  Play raw data (user selectable) on the SDcard using time_expansion (8, 11, 16,22,32,44k samplerate )
 * 
 * 
 *  Fixes compared to original base:
 *    - issue during recording due to not refilling part of the buffer (was repeating the original first 256 samples )
 *    - filenames have samplerate stored
 *    - RTC added (based on hardware)
 * 
 * **********************************************************************
 *   Based on code by DD4WH 
 * 
 *   https://forum.pjrc.com/threads/38988-Bat-detector
 *   
 *   https://github.com/DD4WH/Teensy-Bat-Detector
 *         
 *   made possible by the samplerate code by Frank Boesing, thanks Frank!
 *   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.
 * 
 **********************************************************************/

/* CORBEE */
/* TEENSY 3.6 PINSETUP (20180814)

                  GND                  Vin  - PREAMP V+
                   0                   Analog GND
                   1                   3.3V - MEMS MIC
                   2                   23 AUDIO -LRCLK
                   3                   22 AUDIO -TX
                   4                   21 TFT CS
                   5                   20 TFT DC
       AUDIO MEMCS 6                   19 AUDIO - SCL
       AUDIO MOSI  7                   18 AUDIO - SDA
                   8                   17
       AUDIO BCLK  9                   16
       AUDIO SDCS 10                  15 AUDIO -VOL
       AUDIO MCLK 11                  14 AUDIO -SCLK
       AUDIO MISO 12                  13 AUDIO -RX
                  3.3V                GND
                  24                  A22
                  25                  A21
                  26                  39  TFT MISO
        TFT SCLK  27                  38  MICROPUSH_L
        TFT MOSI  28                  37  MICROPUSH_R
     ENC_L-BUTTON 29                  36  ENC_R-BUTTON
     ENC_L A      30                  35  ENC_R A
     ENC_L B      31                  34  ENC_R B
                  32                  33

*/
//#include "C:/Program Files (x86)/Arduino/hardware/tools/avr/avr/include/stdlib.h"

//#define DEBUG

#define USETFT

//SD1 uses default SDcard Fat, TODO !! SD2 uses faster SDIO library
#define USESD1

#ifdef USESD1
  #define USESD
  #include "C:/Program Files (x86)/Arduino/hardware/teensy/avr/libraries/SD/SD.h"
  #include "C:/Program Files (x86)/Arduino/libraries/uSDFS-master/src/ff.h"       // uSDFS lib
  #include "C:/Program Files (x86)/Arduino/libraries/uSDFS-master/src/ff_utils.h" // uSDFS lib
  File root;
  FRESULT rc;        /* Result code */
  FATFS fatfs;      /* File system object */
  FIL fil;        /* File object */
#endif

// TODO: try and see if using the SdFs library is able to write faster 
// started setup and included several #ifdefs inside the audio-library SDrelated files (play_raw play_wav)
#ifdef USESD2
#define USESD
//#include "SdFs.h"

#include "logger_setup.h"



#endif

//default SD related
#ifdef USESD
  #define MAX_FILES    50
  #define MAX_FILE_LENGTH  13   // 8 chars plus 4 for.RAW plus NULL
  char filelist[ MAX_FILES ][ MAX_FILE_LENGTH ];
  int filecounter=0;
  int fileselect=0;
  int referencefile=0;
  //File frec; // audio is recorded to this file first
  int file_number = 0;
#endif

//#include "Time.h"

#include "C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio\Audio.h"
//#include <Wire.h>
#include "C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI\SPI.h"
#include <Bounce.h>
#include <Metro.h>

boolean SD_ACTIVE=false;
boolean continousPlay=false;
boolean batTrigger=false;//triggers when an ultrasonic signalpeak is found during FFT
boolean TE_ready=true; //when a TEcall is played this signals the end of the call

//time_t getTeensy3Time()
//{
//  return Teensy3Clock.get();
//}
int helpmin; // definitions for time and date adjust - Menu
int helphour;
int helpday;
int helpmonth;
int helpyear;
int helpsec;
uint8_t hour10_old;
uint8_t hour1_old;
uint8_t minute10_old;
uint8_t minute1_old;
uint8_t second10_old;
uint8_t second1_old;
bool timeflag = 0;


uint32_t lastmillis;

#ifdef USETFT

 #define ILI9341
 #ifdef ILI9341
  #include "C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\ILI9341_t3\ILI9341_t3.h"
  #include "font_Arial.h"
  
  #define BACKLIGHT_PIN 255
  #define TOP_OFFSET 90
  #define BOTTOM_OFFSET 20

  #define TFT_DC      20
  #define TFT_CS      21
  #define TFT_RST     255  // 255 = unused. connect to 3.3V
  
  #define TFT_MOSI    7
  #define TFT_SCLK    14 
  #define TFT_MISO    12
  //#define Touch_CS    8

  ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
  //XPT2046_Touchscreen ts = XPT2046_Touchscreen(Touch_CS);
//predefine menu background etc colors
  #define ENC_MENU_COLOR COLOR_YELLOW
  #define ENC_VALUE_COLOR COLOR_LIGHTGREY
  #define MENU_BCK_COLOR COLOR_DARKRED
  
 #endif

#endif


// this audio comes from the codec by I2S2
AudioInputI2S                    i2s_in; // MIC input
AudioRecordQueue                 recorder; 
AudioSynthWaveformSineHires      sine1; // local oscillator
//AudioSynthWaveformSineHires      sine2; // local oscillator
//
AudioEffectMultiply              heterodyne_multiplier; // multiply = mix
//AudioEffectMultiply              mult2; // multiply = mix

//AudioAnalyzeFFT1024         fft1024_1; // for waterfall display
AudioAnalyzeFFT256               myFFT; // for spectrum display

AudioPlaySdRaw                   player; 

AudioEffectGranular              granular1;

AudioMixer4                      mixFFT;
AudioMixer4                      outputMixer; //selective output
AudioMixer4                      inputMixer; //selective input
AudioOutputI2S                   i2s_out; // headphone output          

AudioConnection mic_toinput         (i2s_in, 0, inputMixer, 0); //microphone signal
AudioConnection mic_torecorder      (i2s_in, 0, recorder, 0); //microphone signal
//AudioConnection mic_topeak (i2s_in, peakRMS);
//AudioConnection mic_topeak1 (i2s_in, peakVal);

AudioConnection player_toinput      (player, 0, inputMixer, 1); //player signal

AudioConnection input_toswitch      (inputMixer,0,  mixFFT,0);

AudioConnection input_todelay       (inputMixer,0, granular1, 0);

AudioConnection switch_toFFT        (mixFFT,0, myFFT,0 ); //raw recording channel 

AudioConnection input_toheterodyne1 (inputMixer, 0, heterodyne_multiplier, 0); //heterodyne 1 signal
AudioConnection sineheterodyne1    (sine1, 0, heterodyne_multiplier, 1);//heterodyne 1 mixerfreq

AudioConnection granular_toout (granular1,0, outputMixer,1);
//AudioConnection input_toheterodyne2 (granular1, 0, mult2, 0); //heterodyne 2
//AudioConnection sineheterodyne2     (sine2, 0, mult2, 1);//heterodyne 2 mixerfreq

AudioConnection heterodyne1_toout      (heterodyne_multiplier, 0, outputMixer, 0);  //heterodyne 1 output to outputmixer
//AudioConnection heterodyne2_toout      (mult2, 0, outputMixer, 1);  //heterodyne 2 output to outputmixer
AudioConnection player_toout           (inputMixer,0, outputMixer, 2);    //direct signal (use with player) to outputmixer

AudioConnection output_toheadphoneleft      (outputMixer, 0, i2s_out, 0); // output to headphone
AudioConnection output_toheadphoneright     (outputMixer, 0, i2s_out, 1);
//AudioConnection granular_toheadphone        (granular1,0,i2s_out,1);

AudioControlSGTL5000        sgtl5000;  

//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

#define GRANULAR_MEMORY_SIZE 30000  // enough for 100 ms at 281kHz
int16_t granularMemory[GRANULAR_MEMORY_SIZE];

// forward declaration Stop recording with message 
#ifdef DEBUGSERIAL
   void die(char *str, FRESULT rc);
#endif

extern "C" uint32_t usd_getError(void);

struct tm seconds2tm(uint32_t tt);

//continous timers
elapsedMillis since_bat_detection1; //start timing directly after FFT detects an ultrasound
elapsedMillis since_bat_detection2; //start timing directly after FFT detects the end of the ultrasound
//
elapsedMillis since_heterodyne=1000; //timing interval for auto_heterodyne frequency adjustments
uint16_t callLength=0;
//uint16_t clicker=0;

/************** RECORDING PLAYING SETTINGS *****************/

const int8_t    MODE_DETECT = 0;
const int8_t    MODE_REC = 1;
const int8_t    MODE_PLAY = 2;

int mode = MODE_DETECT; 

#if defined(__MK20DX256__)
  #define BUFFSIZE (8*1024) // size of buffer to be written
#elif defined(__MK66FX1M0__)
  #define BUFF 64
  #define BUFFSIZE (BUFF*1024) // size of buffer to be written

#endif

// buffer to store audiosamples during recording
uint8_t buffern[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
//uint8_t buffern2[BUFFSIZE] __attribute__( ( aligned ( 16 ) ) );
uint wr;
uint32_t nj = 0;

#define waterfallgraph 1
#define spectrumgraph 2

int idx_t = 0;
int idx = 0;
int64_t sum;
float32_t mean;
int16_t FFT_bin [128]; 
int16_t FFT_max1 = 0;
uint32_t FFT_max_bin1 = 0;
int16_t FFT_mean1 = 0;
int16_t FFT_max2 = 0;
uint32_t FFT_max_bin2 = 0;
int16_t FFT_mean2 = 0;
//int16_t FFT_threshold = 0;
int16_t FFT_bat [3]; // max of 3 frequencies are being displayed
int16_t index_FFT;
int l_limit;
int u_limit;
int index_l_limit;
int index_u_limit;
//const uint16_t FFT_points = 1024;
const uint16_t FFT_points = 256;

int barm [512];

#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_234K              11
#define SAMPLE_RATE_281K              12
#define SAMPLE_RATE_352K              13
#define SAMPLE_RATE_MAX               13

typedef struct SR_Descriptor
{
    const int SR_n;
    const char* const txt; //display 
    
} SR_Desc;

// SRtext and position for the FFT spectrum display scale
const SR_Descriptor SR [SAMPLE_RATE_MAX + 1] =
{
    //   SR_n ,  f1
    {  SAMPLE_RATE_8K,  "8"}, 
    {  SAMPLE_RATE_11K,  "11"}, 
    {  SAMPLE_RATE_16K,  "16"}, 
    {  SAMPLE_RATE_22K,  "22"}, 
    {  SAMPLE_RATE_32K,  "32"}, 
    {  SAMPLE_RATE_44K,  "44"}, 
    {  SAMPLE_RATE_48K,  "48"},
    {  SAMPLE_RATE_88K,  "88"},
    {  SAMPLE_RATE_96K,  "96"},
    {  SAMPLE_RATE_176K,  "176"},
    {  SAMPLE_RATE_192K,  "192"}, 
    {  SAMPLE_RATE_234K,  "234"}, 
    {  SAMPLE_RATE_281K,  "281"}, 
    {  SAMPLE_RATE_352K,  "352"}
};    

// setup for FFTgraph denoising 
uint32_t FFTcount=0; //count the # of FFTs done 
uint16_t powerspectrumCounter=0;

float FFTavg[128];

float FFTpowerspectrum[128];
float powerspectrum_Max=0;

// defaults at startup functions
int displaychoice=waterfallgraph; //default display
int8_t mic_gain = 35; // start detecting with this MIC_GAIN in dB
int8_t volume=50;

int freq_real = 45000; // start heterodyne detecting at this frequency
int freq_real_backup=freq_real; //used to return to proper settingafter using the play_function

// initial sampling setup
int sample_rate = SAMPLE_RATE_281K;
int sample_rate_real = 281000;
char * SRtext="281";

int last_sample_rate=sample_rate;

float freq_Oscillator =50000;

/************************************************* MENU ********************************/
/***************************************************************************************/

typedef struct Menu_Descriptor
{
    const char* name;
    // ********preset variables below NOT USED YET
    const int len; // length of string to allow right-alignment
    const int def; //default settings
    const int low; // low threshold
    const int high; //high threshold
    
} Menu_Desc;


const int Leftchoices=10; //can have any value
const int Rightchoices=10;
const Menu_Descriptor MenuEntry [Leftchoices] =
{  {"Volume",6,60,0,100}, //divide by 100
   {"Gain",4,30,0,63},
   {"Frequency",9,45,20,90}, //multiply 1000
   {"Display",7,0,0,0},
   {"Denoise",7,0,0,0},
   {"SampleR",6,0,0,0},
   {"Record",6,0,0,0}, //functions where the LeftEncoder 
   {"Play",4,0,0,0},
   {"PlayD",5,0,0,0},
} ;

//TODO constants should be part of the menuentry, a single structure to hold the info
const int8_t  MENU_VOL = 0; //volume
const int8_t  MENU_MIC = 1; //mic_gain
const int8_t  MENU_FRQ = 2; //frequency
const int8_t  MENU_DSP = 3; //display
const int8_t  MENU_DNS = 4; //denoise
const int8_t  MENU_SR  = 5; //sample rate
const int8_t  MENU_REC = 6; //record
const int8_t  MENU_PLY = 7; //play 
const int8_t  MENU_PLD = 8; //play at original rate 

//available modes
const int detector_heterodyne=0;
const int detector_divider=1;
const int detector_Auto_heterodyne=2;
const int detector_Auto_TE=3;
const int detector_passive=4;

//default
int detector_mode=detector_heterodyne;  

//************************* ENCODER variables/constants
const int8_t enc_menu=0; //changing encoder sets menuchoice
const int8_t enc_value=1; //changing encoder sets value for a menuchoice

const int8_t enc_leftside=0; //encoder 
const int8_t enc_rightside=1; //encoder

const int8_t enc_up=1; //encoder goes up
const int8_t enc_nc=0;
const int8_t enc_dn=-1; //encoder goes down

int EncLeft_menu_idx=0; 
int EncRight_menu_idx=0;

int EncLeft_function=0; 
int EncRight_function=0;

/************************** */

// ******** LEFT AND RIGHT ENCODER CONNECTIONS/BUTTONS
#include <Encoder.h>
//try to avoid interrupts as they can (possibly ?) interfere during recording
#define ENCODER_DO_NOT_USE_INTERRUPTS

#define MICROPUSH_RIGHT  37 
Bounce micropushButton_R = Bounce(MICROPUSH_RIGHT, 50); 
#define encoderButton_RIGHT      36    
Bounce encoderButton_R = Bounce(encoderButton_RIGHT, 50); 
Encoder EncRight(34,35);
int EncRightPos=0;
int EncRightchange=0;

#define MICROPUSH_LEFT  38
Bounce micropushButton_L = Bounce(MICROPUSH_LEFT, 50); 
#define encoderButton_LEFT       29
Bounce encoderButton_L = Bounce(encoderButton_LEFT, 50); 
Encoder EncLeft(30,31);
int EncLeftPos=0;
int EncLeftchange=0;

// **END************ LEFT AND RIGHT ENCODER DEFINITIONS

#ifdef USESD1
void die(char *str, FRESULT rc) 
{ 
   #ifdef DEBUGSERIAL
   Serial.printf("%s: Failed with rc=%u.\n", str, rc); for (;;) delay(100); 
   #endif 
   }

//=========================================================================
#endif
#ifdef USESD1
//uint32_t count=0;
uint32_t ifn=0;
uint32_t isFileOpen=0;

TCHAR wfilename[80];
uint32_t t0=0;
uint32_t t1=0;
#endif

char filename[80];

void display_settings() {
  #ifdef USETFT
    
    tft.setTextColor(ENC_MENU_COLOR);
    
    tft.setFont(Arial_16);
    tft.fillRect(0,0,240,TOP_OFFSET-50,MENU_BCK_COLOR);
    tft.fillRect(0,TOP_OFFSET-10,240,10,COLOR_BLACK);
    tft.fillRect(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET,240,BOTTOM_OFFSET,MENU_BCK_COLOR);

    tft.setCursor(0,0);
    tft.print("g:"); tft.print(mic_gain);
    tft.print(" f:"); tft.print(freq_real);
    tft.print(" v:"); tft.print(volume);
    tft.print(" SR"); tft.print(SRtext);
    tft.setCursor(0,20);
    
    switch (detector_mode) {
       case detector_heterodyne:
         tft.print("HTD"); // 
       break;
       case detector_divider:
         tft.print("FD");
       break;
       case detector_Auto_heterodyne:
         tft.print("Auto_HTD");
       break;
       case detector_Auto_TE:
        tft.print("Auto_TE");
       break;
       case detector_passive:
        tft.print("PASS");
       break;
       default:
        tft.print("error");
       
     }
     // push the cursor to the lower part of the screen
     tft.setCursor(0,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);

     /****************** SHOW ENCODER SETTING ***********************/

     // set the colors according to the function of the encoders
     if (mode==MODE_DETECT ) 
     // show menu selection as menu-active of value-active
      {
       if (EncLeft_function==enc_value) 
        { tft.setTextColor(ENC_MENU_COLOR);
         }
        else
        { tft.setTextColor(ENC_VALUE_COLOR);}

       tft.print(MenuEntry[EncLeft_menu_idx].name);
       tft.print(" "); 

       if (EncRight_function==enc_value) 
         { tft.setTextColor(ENC_MENU_COLOR);} //value is active 
       else
         { tft.setTextColor(ENC_VALUE_COLOR);} //menu is active 
       
       //if MENU on the left-side is PLAY and selected than show the filename
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))     
           { //tft.print(fileselect); 
             tft.print(filelist[fileselect]);
           }
        else
         if (EncLeft_menu_idx==MENU_REC)      
          // show the filename that will be used for the next recording
           {  sprintf(filename, "B%u_%s.raw", file_number+1,SRtext);
              tft.print(filename );
            }
         else
         if (EncLeft_menu_idx==MENU_SR)
          { tft.print(SR[sample_rate].txt);

          }
          else
          { //tft.print(EncRightchange); 
            tft.setCursor(ILI9341_TFTWIDTH/2  ,ILI9341_TFTHEIGHT-BOTTOM_OFFSET);
            tft.print(MenuEntry[EncRight_menu_idx].name);
          }
    }
    else
      { 
        if (mode==MODE_REC)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("REC:"); 
            tft.print(filename);
         }
        if (mode==MODE_PLAY) 
         {if (EncLeft_menu_idx==MENU_PLY)
          { tft.setTextColor(ENC_VALUE_COLOR);
            tft.print("PLAY:"); 
            tft.print(filename);
          }
          else
           {tft.setTextColor(ENC_VALUE_COLOR);
            tft.print(MenuEntry[EncLeft_menu_idx].name);
            tft.print(" "); 
            tft.print(MenuEntry[EncRight_menu_idx].name);
      
           }

        }
      }

    //scale every 10kHz  
    float x_factor=10000/(0.5*(sample_rate_real / FFT_points)); 
    int curF=2*int(freq_real/(sample_rate_real / FFT_points));

    int maxScale=int(sample_rate_real/20000);
    for (int i=1; i<maxScale; i++) 
     { tft.drawFastVLine(i*x_factor, TOP_OFFSET-10, 9, ENC_MENU_COLOR);  
     }    
    tft.fillCircle(curF,TOP_OFFSET-4,3,ENC_MENU_COLOR);
    
   #endif
}


void       set_mic_gain(int8_t gain) {
    
    AudioNoInterrupts();
    //sgtl5000.micGainNew (24);
    sgtl5000.micGain (gain);
    //sgtl5000.lineInLevel(gain/4);
    AudioInterrupts();

    display_settings();
    powerspectrum_Max=0; // change the powerspectrum_Max for the FFTpowerspectrum
} // end function set_mic_gain

void       set_freq_Oscillator(int freq) {
    // audio lib thinks we are still in 44118sps sample rate
    // therefore we have to scale the frequency of the local oscillator
    // in accordance with the REAL sample rate
      
    freq_Oscillator = (freq) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    //float F_LO2= (freq+5000) * (AUDIO_SAMPLE_RATE_EXACT / sample_rate_real); 
    // if we switch to LOWER samples rates, make sure the running LO 
    // frequency is allowed ( < 22k) ! If not, adjust consequently, so that
    // LO freq never goes up 22k, also adjust the variable freq_real  
    if(freq_Oscillator > 22000) {
      freq_Oscillator = 22000;
      freq_real = freq_Oscillator * (sample_rate_real / AUDIO_SAMPLE_RATE_EXACT) + 9;
    }
    AudioNoInterrupts();
    //setup multiplier SINE
    sine1.frequency(freq_Oscillator);
    //sine2.frequency(freq_Oscillator);
        
    AudioInterrupts();
    display_settings();
} // END of function set_freq_Oscillator

// set samplerate code by Frank Boesing 
void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;
//MCLD Divide sets the MCLK divide ratio such that: MCLK output = MCLK input * ( (FRACT + 1) / (DIVIDE + 1) ).
// FRACT must be set equal or less than the value in the DIVIDE field.
//(double)F_PLL * (double)clkArr[iFreq].mult / (256.0 * (double)clkArr[iFreq].div);
//ex 180000000* 1 /(256* 3 )=234375Hz  setting   {1,3} at 180Mhz

  
#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 int numfreqs = 17;
  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,  234000, 281000, 352800};
  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}, {1, 4},      {64, 255},     {219,802}, { 1,3 },  {2,5} , {1,2} };  //last value 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      set_sample_rate (int sr) {
  switch (sr) {
    case SAMPLE_RATE_8K:
    sample_rate_real = 8000;
    SRtext = " 8";
    break;
    case SAMPLE_RATE_11K:
    sample_rate_real = 11025;
    SRtext = "11";
    break;
    case SAMPLE_RATE_16K:
    sample_rate_real = 16000;
    SRtext = "16";
    break;
    case SAMPLE_RATE_22K:
    sample_rate_real = 22050;
    SRtext = "22";
    break;
    case SAMPLE_RATE_32K:
    sample_rate_real = 32000;
    SRtext = "32";
    break;
    case SAMPLE_RATE_44K:
    sample_rate_real = 44100;
    SRtext = "44";
    break;
    case SAMPLE_RATE_48K:
    sample_rate_real = 48000;
    SRtext = "48";
    break;
    case SAMPLE_RATE_88K:
    sample_rate_real = 88200;
    SRtext = "88";
    break;
    case SAMPLE_RATE_96K:
    sample_rate_real = 96000;
    SRtext = "96";
    break;
    case SAMPLE_RATE_176K:
    sample_rate_real = 176400;
    SRtext = "176";
    break;
    case SAMPLE_RATE_192K:
    sample_rate_real = 192000;
    SRtext = "192";
    break;
    case SAMPLE_RATE_234K:
    sample_rate_real = 234000;
    SRtext = "234";
    break;
    case SAMPLE_RATE_281K:
    sample_rate_real = 281000;
    SRtext = "281";
    break;
    case SAMPLE_RATE_352K:
    sample_rate_real = 352800;
    SRtext = "352";
    break;
  }
    
    AudioNoInterrupts();
    setI2SFreq (sample_rate_real); 
    delay(200); // this delay seems to be very essential !
    set_freq_Oscillator (freq_real);
    AudioInterrupts();
    delay(20);
    display_settings();
   
} // END function set_sample_rate



void spectrum() { // spectrum analyser code by rheslip - modified
     #ifdef USETFT
     if (myFFT.available()) {
//     if (fft1024_1.available()) {
    int16_t peak=0; uint16_t avgF=0;
    
    // find the BIN corresponding to the current frequency-setting 
    int curF=int(freq_real/(sample_rate_real / FFT_points));

//    for (int i = 0; i < 240; i++) {
    //startup sequence to denoise the FFT
    FFTcount++;
    if (FFTcount==1)
     {for (int16_t i = 0; i < 128; i++) {
         FFTavg[i]=0; 
     }
     }

    if (FFTcount<1000)
     { 
       for (int i = 0; i < 128; i++) {
         FFTavg[i]=FFTavg[i]+abs(myFFT.output[i])*0.001; //0.1% of total values
         }
     }

for (int16_t x = 2; x < 128; x++) {
   avgF=avgF+FFT_bin[x];
   if (FFT_bin[x]>peak)
      {
        peak=FFT_bin[x];   
      }
}

/*
avgF=avgF/128;
//check if the peak is at least 2x higher than the average otherwise set the indicator low
if ((peak-avgF)<(avgF/3))
  { maxF=2;}
  */  
  for (int16_t x = 2; x < 128; x++) {
//  for (uint16_t x = 8; x < 512; x+=4) {
     FFT_bin[x] = (myFFT.output[x]);//-FFTavg[x]*0.9; 
     int colF=ENC_VALUE_COLOR;
     
//     FFT_bin[x/4] = abs(fft1024_1.output[x]); 
     int barnew = (FFT_bin[x])/2 ;
     
     // this is a very simple first order IIR filter to smooth the reaction of the bars
     int bar = 0.05 * barnew + 0.95 * barm[x]; 
     if (bar >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { bar=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     if (bar <0) bar=0;
     if (barnew >(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET)) 
        { barnew=(ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET);
        }
     int g_x=x*2;
     int spectrumline=barm[x];
     int spectrumline_new=barnew;
          
     //tft.drawFastVLine(g_x,TOP_OFFSET,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET, COLOR_BLACK);
     tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline_new, COLOR_GREEN);
     //tft.drawFastVLine(g_x,TOP_OFFSET,spectrumline, COLOR_RED);
     tft.drawFastVLine(g_x,TOP_OFFSET+spectrumline_new,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline_new, COLOR_BLACK);
     tft.drawFastVLine(g_x+1,TOP_OFFSET,spectrumline, COLOR_DARKGREEN);
     tft.drawFastVLine(g_x+1,TOP_OFFSET+spectrumline,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-TOP_OFFSET-spectrumline, COLOR_BLACK);
    /* if (x==maxF)
       { colF=COLOR_ORANGE;
         tft.drawFastVLine(g_x,TOP_OFFSET,240-bar, colF);
         }
      */   
     
     //tft.drawPixel(g_x,ILI9341_TFTHEIGHT-BOTTOM_OFFSET-bar,colF);

     barm[x] = bar;
  }
  
    // if (mode == MODE_DETECT)  search_bats();     
  } //end if
  if (mode==MODE_PLAY)
    {//float ww=( player.positionMillis()/player.lengthMillis()*240.0);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-5,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-5,5,ENC_MENU_COLOR);
     tft.drawFastHLine(0,320-BOTTOM_OFFSET-4,240*player.positionMillis()/player.lengthMillis()-10,COLOR_BLACK);
     tft.drawFastHLine(240*player.positionMillis()/player.lengthMillis()-9,320-BOTTOM_OFFSET-4,5,ENC_MENU_COLOR);
    }
  #endif
}
#ifdef DEBUGSERIAL 

/*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(")");
 
      AudioProcessorUsageMaxReset();
      AudioMemoryUsageMaxReset();
    }


}
*/ // END function check_processor
#endif



void waterfall(void) // thanks to Frank B !
{ 
  
#ifdef USETFT

// code for 256 point FFT 
     
 if (myFFT.available()) {
  const uint16_t Y_OFFSET = TOP_OFFSET;
  static int count = TOP_OFFSET;
  //int curF=int(freq_real/(sample_rate_real / FFT_points));

  // lowest frequencybin to detect as a batcall
  int batCall_LoF_bin= int(30000/(sample_rate_real / FFT_points));
  int batCall_HiF_bin= int(80000/(sample_rate_real / FFT_points));

  uint16_t FFT_pixels[240]; // maximum of 240 pixels, each one is the result of one FFT 
  FFT_pixels[0]=0; FFT_pixels[1]=0;  FFT_pixels[2]=0; FFT_pixels[3]=0;
  
    FFTcount++;

    //requested to start with a clean FFTavg array to denoise
    if (FFTcount==1)
       {for (int16_t i = 0; i < 128; i++) {
          FFTavg[i]=0; 
       }
     }

    // collect 1000 FFT samples for the denoise array
    if (FFTcount<100)
     { for (int i = 2; i < 128; i++) {
         //FFTavg[i]=FFTavg[i]+myFFT.read(i)*65536.0*5*0.001; //0.1% of total values
         FFTavg[i]=FFTavg[i]+myFFT.output[i]*10*0.001; //0.1% of total values
         }
     }
    

    int FFT_peakF_bin=0; 
    int peak=512;
    int avgFFTbin=0;
    // there are 128 FFT different bins only 120 are shown on the graphs  
    
    for (int i = 2; i < 120; i++) { 
      int val = myFFT.output[i]*10 -FFTavg[i]*0.9 + 10; //v1
      avgFFTbin+=val;
      //detect the peakfrequency
      if (val>peak)
       { peak=val; 
         FFT_peakF_bin=i;
        }
       if (val<5) 
           {val=5;}

       FFT_pixels[i*2] = tft.color565(
              min(255, val/2),
              (val/6>255)? 255 : val/6,
              //(val/4>255)? 255 : val/4
                            0
              //((255-val)>>1) <0? 0: (255-val)>>1 
             ); 
       
      FFT_pixels[i*2+1]=FFT_pixels[i*2];       
    }
    avgFFTbin=avgFFTbin/120;
    //mark the peak
    //FFT_pixels[FFT_peakF_bin*2]=COLOR_RED;
    //FFT_pixels[FFT_peakF_bin*2+1]=COLOR_RED;
   if ((peak/avgFFTbin)<1.2) //very low peakvalue so probably noise
     { FFT_peakF_bin=0; 
     }

  int powerSpectrum_Maxbin=0;
  // detected a peak in the bat frequencies
  if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin))
  {
    //collect data for the powerspectrum 
    for (int i = 2; i < 120; i++)
     { 
        //add new samples
        FFTpowerspectrum[i]+=myFFT.output[i];
        //keep track of the maximum
        if (FFTpowerspectrum[i]>powerspectrum_Max) 
           { powerspectrum_Max=FFTpowerspectrum[i];
             powerSpectrum_Maxbin=i;
           }

     }
     //keep track of the no of samples with bat-activity
     powerspectrumCounter++;
  }
    // update display after every 100th FFT sample with bat-activity
    if ((powerspectrumCounter>50)  )
       { powerspectrumCounter=0;
         //clear powerspectrumbox
         tft.fillRect(0,TOP_OFFSET-50,240,45, COLOR_BLACK);
         // keep a minimum maximumvalue to the powerspectrum
         int binLo=2; int binHi=0;

         for (int i=2; i<120; i++)
          {             
            int ypos=FFTpowerspectrum[i]/powerspectrum_Max*45; 

            // first encounter of 1/20 of maximum
            if (i<powerSpectrum_Maxbin)
              {if (FFTpowerspectrum[i]<(powerspectrum_Max*0.1))
                    {binLo=i;}}
            else
              {if (FFTpowerspectrum[i]>(powerspectrum_Max*0.1))
                    {binHi=i;}
              }

            tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            if (i==powerSpectrum_Maxbin)                        
              { tft.drawFastVLine(i*2,TOP_OFFSET-ypos-6,ypos,ENC_MENU_COLOR);
               }
            
            //tft.drawFastVLine(i*2+1,TOP_OFFSET-ypos-6,ypos,COLOR_RED);
            FFTpowerspectrum[i]=0;
          }
         
         //tft.setCursor(0,TOP_OFFSET-45);
         //tft.print(powerspectrum_Max);
         if (powerspectrum_Max==20000)
          {binLo=0; binHi=0;
          }
         float multiplier=(sample_rate_real / FFT_points)*0.001;
         powerspectrum_Max=powerspectrum_Max*0.5; //lower the max after a graphupdate
         tft.setCursor(140,TOP_OFFSET-45);
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binLo*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_MENU_COLOR);
         tft.print(int(powerSpectrum_Maxbin*multiplier) );
         tft.print(" ");
         tft.setTextColor(ENC_VALUE_COLOR);
         tft.print(int(binHi*multiplier) );
        
       }
      
    
    if ((FFT_peakF_bin>batCall_LoF_bin) and (FFT_peakF_bin<batCall_HiF_bin)) // we got a high-frequent signal peak
      { 
        // when a batcall is first discovered 
        if (not batTrigger) 
          { since_bat_detection1=0; //start of the call mark
            //clicker=0;
            FFT_pixels[5]=ENC_VALUE_COLOR; // mark the start on the screen
            FFT_pixels[6]=ENC_VALUE_COLOR;
            FFT_pixels[7]=ENC_VALUE_COLOR;
            
            if (detector_mode==detector_Auto_heterodyne)
               if (since_heterodyne>1000) //update the most every second
                {freq_real=int((FFT_peakF_bin*(sample_rate_real / FFT_points)/500))*500; //round to nearest 500hz
                 set_freq_Oscillator(freq_real); 
                 since_heterodyne=0;
                 //granular1.stop();
                }
            
            //restart the TimeExpansion only if the previous call was played
            if ((detector_mode==detector_Auto_TE) and (TE_ready) )
             { granular1.stop();
               granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
               granular1.setSpeed(0.05);
               TE_ready=false;
               
             }
                      
          }
         //clicker++; 
         batTrigger=true;
         
     }
   else // FFT_peakF_bin does not show a battcall  
        { 
          if (batTrigger) //previous sample was still a call
           { callLength=since_bat_detection1; // got a pause so store the time since the start of the call
             
             /*if (callLength>20) //call is too long 
              { TE_ready=true; // break the TE replay
                } 
              */  
              since_bat_detection2=0; //start timing the length of the replay
             }
          batTrigger=false;
        }    
    // restart TimeExpansion recording a bit after the call has finished completely
    if ((!TE_ready) and (since_bat_detection2>(callLength*10)))
      { //stop the time expansion
        TE_ready=true;
        granular1.stopTimeExpansion();
       
      }
    

    if (since_bat_detection2<50) //keep scrolling 100ms after the last bat-call
      {  tft.writeRect( 0,count, ILI9341_TFTWIDTH,1, (uint16_t*) &FFT_pixels); //show a line with spectrumdata
         tft.setScroll(count);
        count++;
        
      } 


    if (count >= ILI9341_TFTHEIGHT-BOTTOM_OFFSET) count = Y_OFFSET;
    

  }
#endif

}

void startRecording() {
  mode = MODE_REC;
  #ifdef USESD1
  
    #ifdef DEBUGSERIAL
      Serial.print("startRecording");
    #endif  
    
    // close file
    if(isFileOpen)
    {
      //close file
      rc = f_close(&fil);
      if (rc) die("close", rc);
      isFileOpen=0;
    }
  
  if(!isFileOpen)
  {
  file_number++;
  //automated filename BA_S.raw where A=file_number and S shows samplerate. Has to fit 8 chars
  // so max is B999_192.raw
  sprintf(filename, "B%u_%s.raw", file_number, SRtext);
    #ifdef DEBUGSERIAL
    Serial.println(filename);
    #endif  
  char2tchar(filename, 13, wfilename);
  filecounter++;
  strcpy(filelist[filecounter],filename );

  rc = f_stat (wfilename, 0);
  #ifdef DEBUGSERIAL
    Serial.printf("stat %d %x\n",rc,fil.obj.sclust);
 #endif   
  rc = f_open (&fil, wfilename, FA_WRITE | FA_CREATE_ALWAYS);
#ifdef DEBUGSERIAL
    Serial.printf(" opened %d %x\n\r",rc,fil.obj.sclust);
#endif 
    // check if file has errors
    if(rc == FR_INT_ERR)
    { // only option then is to close file
        rc = f_close(&fil);
        if(rc == FR_INVALID_OBJECT)
        { 
          #ifdef DEBUGSERIAL
          Serial.println("unlinking file");
          #endif
          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;
  }

  #endif

  //clear the screen completely
  tft.fillRect(0,0,ILI9341_TFTWIDTH,ILI9341_TFTHEIGHT,COLOR_BLACK);
  tft.setTextColor(ENC_VALUE_COLOR);
  tft.setFont(Arial_28);
  tft.setCursor(0,100);
  tft.print("RECORDING");
  tft.setFont(Arial_16);
  
  display_settings();
  
  granular1.stop(); //stop granular

  //switch off several circuits
  mixFFT.gain(0,0);
  
  outputMixer.gain(1,0);  //shutdown granular output      
  
  detector_mode=detector_heterodyne;

  outputMixer.gain(0,1); 
  
  nj=0;
  recorder.begin();
    
}

void continueRecording() {
  #ifdef USESD1
  const uint32_t N_BUFFER = 2;
  const uint32_t N_LOOPS = BUFF*N_BUFFER; // !!! NLOOPS and BUFFSIZE ARE DEPENDENT !!! NLOOPS = BUFFSIZE/N_BUFFER
  // 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
    //read N_BUFFER sample-blocks into memory
    for (int i = 0; i < N_BUFFER; i++) {
       //copy a new bufferblock from the audiorecorder into memory
       memcpy(buffern + i*256 + nj * 256 * N_BUFFER, recorder.readBuffer(), 256);
       //free the last buffer that was read
       recorder.freeBuffer();
       } 

    nj++; 

    if (nj >  (N_LOOPS-1)) 
    {
      nj = 0;
      //old code used to copy into a 2nd buffer, not needed since the writing to SD of the buffer seems faster than the filling 
      //this allows larger buffers to be used
      //memcpy(buffern2,buffern,BUFFSIZE);  
      //push to SDcard  
      rc =  f_write (&fil, buffern, N_BUFFER * 256 * N_LOOPS, &wr);
      }
  }
  #endif
}

void stopRecording() {
#ifdef USESD1
  #ifdef DEBUGSERIAL  
    Serial.print("stopRecording");
  #endif  
    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_DETECT;
  //  clearname();
  #ifdef DEBUGSERIAL  
    Serial.println (" Recording stopped!");
  #endif 
#endif
  //switch on FFT
  tft.fillScreen(COLOR_BLACK);
  mixFFT.gain(0,1); 
      
}

void startPlaying(int SR) {
//      String NAME = "Bat_"+String(file_number)+".raw";
//      char fi[15];
//      NAME.toCharArray(fi, sizeof(NAME));

inputMixer.gain(0,0); //switch off the mic-line as input
inputMixer.gain(1,1); //switch on the playerline as input

if (EncLeft_menu_idx==MENU_PLY) 
  {
      outputMixer.gain(2,1);  //player to output 
      outputMixer.gain(1,0);  //shutdown granular output      
      outputMixer.gain(0,0);  //shutdown heterodyne output
      EncRight_menu_idx=MENU_SR;
      EncRight_function=enc_value;
      freq_real_backup=freq_real; //keep track of heterodyne setting
  }
  //direct play is used to test functionalty based on previous recorded data
  //this will play a previous recorded raw file through the system as if it were live data coming from the microphone
if (EncLeft_menu_idx==MENU_PLD) 
  {
      outputMixer.gain(2,0);  //shutdown direct audio from player to output 
      outputMixer.gain(0,1);  //default mode will be heterodyne based output 
      if (detector_mode==detector_Auto_TE)
         { outputMixer.gain(1,1);  //start granular output processing      
           outputMixer.gain(0,0);
         }
  }

//allow settling
  delay(100);
  
  //keep track of the sample_rate 
  last_sample_rate=sample_rate;
  SR=constrain(SR,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);
  set_sample_rate(SR);
  
  fileselect=constrain(fileselect,0,filecounter);
  strncpy(filename, filelist[fileselect],  13);
  
  //default display is waterfall
  displaychoice=waterfallgraph;
  display_settings();

  player.play(filename);
  mode = MODE_PLAY;

}
  

void stopPlaying() {
  
#ifdef DEBUGSERIAL      
  Serial.print("stopPlaying");
#endif  
  if (mode == MODE_PLAY) player.stop();
  mode = MODE_DETECT;
#ifdef DEBUGSERIAL      
  Serial.println (" Playing stopped");
#endif  
  
  //restore last sample_rate setting
  set_sample_rate(last_sample_rate);
if (EncLeft_menu_idx==MENU_PLY)
{
  freq_real=freq_real_backup;
  //restore heterodyne frequency
  set_freq_Oscillator (freq_real);
}
  outputMixer.gain(2,0); //stop the direct line output
  outputMixer.gain(1,1); // open granular output
  outputMixer.gain(0,1); // open heterodyne output  

  inputMixer.gain(0,1); //switch on the mic-line
  inputMixer.gain(1,0); //switch off the playerline

}


void continuePlaying() {
  //the end of file was reached
  if (!player.isPlaying()) {
    stopPlaying();
    if (continousPlay) //keep playing until stopped by the user
      { startPlaying(SAMPLE_RATE_176K);
      }
  }
}

void changeDetector_mode()
{
  if (detector_mode==detector_heterodyne)
         { granular1.stop(); //stop other detecting routines
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
          //switch menu to volume/frequency
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_FRQ;
           EncRight_function=enc_value;
          
         } 
      if (detector_mode==detector_divider)
         { granular1.beginDivider(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  

      if (detector_mode==detector_Auto_TE)
         { granular1.beginTimeExpansion(GRANULAR_MEMORY_SIZE);
           outputMixer.gain(1,1);  //start granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           granular1.setSpeed(0.06); //default TE is 1/0.06 ~ 1/16 :TODO, switch from 1/x floats to divider value x
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }  
      if (detector_mode==detector_Auto_heterodyne)
         { granular1.stop(); 
           outputMixer.gain(1,0);  //stop granular output      
           outputMixer.gain(0,1);  //start heterodyne output
      
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

           
         }  

      if (detector_mode==detector_passive)
         { granular1.stop(); //stop all other detecting routines
           outputMixer.gain(2,1);  //direct line to output 
           outputMixer.gain(1,0);  //shutdown granular output      
           outputMixer.gain(0,0);  //shutdown heterodyne output
           //switch menu to volume/gain
           EncLeft_menu_idx=MENU_VOL;
           EncLeft_function=enc_value;
           EncRight_menu_idx=MENU_MIC;
           EncRight_function=enc_value;

         }
       else //all other options use the heterodyne and granular output line
        {  outputMixer.gain(2,0);  //shut down direct line to output 
           //outputMixer.gain(1,1);  //granular   output      
           //outputMixer.gain(0,1);  //heterodyne output
 
        } 

}

//*****************************************************update encoders
void updateEncoder(uint8_t Encoderside )
 {
   
  /************************setup vars*************************/
   int encodermode=-1; // menu=0 value =1;
   int change=0;
   int menu_idx=0;
   int choices=0;

    //get encodermode 
   if (Encoderside==enc_leftside)
    { encodermode=EncLeft_function;
      change=EncLeftchange;
      menu_idx=EncLeft_menu_idx;
      choices=Leftchoices; //available menu options
    }

   if (Encoderside==enc_rightside)
    { encodermode=EncRight_function;
      change=EncRightchange;
      menu_idx=EncRight_menu_idx;
      choices=Rightchoices; //available menu options
    } 


  /************************proces changes*************************/
  //encoder is in menumode
  if (encodermode==enc_menu)
    { menu_idx=menu_idx+change;
            
      //allow revolving choices
      if (menu_idx<0)
        {menu_idx=choices-1;}
      if (menu_idx>=choices)
        {menu_idx=0;}
      //remove functionality when SD is not active, so no SDCARD mounted or SDCARD is unreadable  
      if (!SD_ACTIVE)
        { if ((menu_idx==MENU_PLD) or (menu_idx==MENU_PLY) or (menu_idx==MENU_REC))
           { // move menu to volume
             menu_idx=MENU_VOL;
           }
        }

      if (Encoderside==enc_leftside)
          { EncLeft_menu_idx=menu_idx; //limit the menu 
               }
     
     //limit the changes of the rightside encoder for specific functions
      if ((EncLeft_menu_idx!=MENU_SR) )      
        if (Encoderside==enc_rightside)
          { EncRight_menu_idx=menu_idx; //limit the menu 
               }
    }
        
  //encoder is in valuemode and has changed position
  if ((encodermode==enc_value) and (change!=0))
    { 
      /******************************VOLUME  ***************/
      if (menu_idx==MENU_VOL)
        { volume+=change;
          volume=constrain(volume,0,90);
          float V=volume*0.01;
          AudioNoInterrupts();
          sgtl5000.volume(V);
          AudioInterrupts();
        }
      /******************************MIC_GAIN  ***************/
      if (menu_idx==MENU_MIC)
        {
         mic_gain+=change;
         mic_gain=constrain(mic_gain,0,63);
         set_mic_gain(mic_gain);
         FFTcount=0; //start denoise after changing gain
         //reset FFTdenoise array
         {for (int16_t i = 0; i < 128; i++) {
           FFTavg[i]=0; 
         }}  
        }
      /******************************FREQUENCY  ***************/
      if (menu_idx==MENU_FRQ)
         { int delta=500;
           uint32_t currentmillis=millis();
           //when turning the encoder fast make the change larger
           if ((currentmillis-lastmillis)<500)
              { delta=1000;}
           if ((currentmillis-lastmillis)<250)
              { delta=2000;}
           if ((currentmillis-lastmillis)<100)
              { delta=5000;}

          freq_real=freq_real+delta*change;
          // limit the frequencies to 500hz steps
          freq_real=constrain(freq_real,7000,int(sample_rate_real/2000)*1000-1000);
          set_freq_Oscillator (freq_real);
          lastmillis=millis();
         }
      /******************************DENOISE  ***************/
      if (menu_idx==MENU_DNS)
        { // setting FFTcount to 0 activates a 1000 sample denoise
          FFTcount=0;
        }
      
      /******************************DISPLAY  ***************/
    
      if (menu_idx==MENU_DSP)
         { 
           displaychoice+=change;
           displaychoice=displaychoice%3; //limit to 0(none),1(spectrum),2(waterfall)
           if (displaychoice==waterfallgraph) 
              {
               tft.setRotation( 0 );
            }
           if (displaychoice==spectrumgraph) 
             { 
            tft.setScroll(0);
            tft.setRotation( 0 );
              }
            tft.fillScreen(COLOR_BLACK);
        }

      

/************** SPECIAL MODES WHERE THE LEFTENCODER SETS A FUNCTION AND THE RIGHT ENCODER SELECTS */
      
      /******************************SAMPLE_RATE  ***************/
      if (EncLeft_menu_idx==MENU_SR)  //only selects a possible sample_rate, user needs to press a button to SET sample_rate
        { sample_rate+=EncRightchange;
          sample_rate=constrain(sample_rate,SAMPLE_RATE_MIN,SAMPLE_RATE_MAX);

          
        }   

      
      /******************************SELECT A FILE  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_PLY) and (EncRight_function==enc_value))//menu play selected on the left and right 
         {  
           if (mode!=MODE_PLAY)
            {
             fileselect+=EncRightchange; 
             fileselect=constrain(fileselect,0,filecounter-1);
            
            }   
         }  

      /******************************CHANGE SR during PLAY  ***************/
      if ((EncLeft_menu_idx==MENU_PLY) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_value))//menu play selected on the left and right    
          {
           if (mode==MODE_PLAY)
              {
                 sample_rate+=EncRightchange;
                 sample_rate=constrain(sample_rate,SAMPLE_RATE_8K,SAMPLE_RATE_44K);
                 set_sample_rate(sample_rate);
                 
              }

        }

    }

 }

void updateEncoders()
{
//only react to changes large enough (depending on the steps of the encoder for one rotation)
 long EncRightnewPos = EncRight.read()/4;
 if (EncRightnewPos>EncRightPos)
   { EncRightchange=enc_up; }
   else
   if (EncRightnewPos<EncRightPos)
   { EncRightchange=enc_dn; }
   else
   { EncRightchange=enc_nc; }

 if (EncRightchange!=0)
    {updateEncoder(enc_rightside);
     } 

 EncRightPos=EncRightnewPos;
 
 long EncLeftnewPos = EncLeft.read()/4;
 if (EncLeftnewPos>EncLeftPos)
   { EncLeftchange=enc_up; }
   else
   if (EncLeftnewPos<EncLeftPos)
   { EncLeftchange=enc_dn; }
   else
   { EncLeftchange=enc_nc; }

 if (EncLeftchange!=0)
    {updateEncoder(enc_leftside);
    } 
 
 EncLeftPos=EncLeftnewPos;
 //update display only if a change has happened
 if ((EncRightchange!=0) or (EncLeftchange!=0))
      display_settings();

}

void updateButtons()
{
 // Respond to button presses
   
 // try to make the interrupts as short as possible when recording
 if (mode==MODE_REC) 
   {
     encoderButton_L.update(); //check the left encoderbutton
      if ((encoderButton_L.risingEdge())  )
       { stopRecording();
          EncLeft_function=enc_menu; //force into active-menu
          display_settings();      
       }
   }
 else // not MODE_REC
  {
  // Respond to button presses
 
  encoderButton_L.update();
  encoderButton_R.update();

  micropushButton_L.update();
  micropushButton_R.update();
    
   /*RIGHT MICROPUSH */
  if (micropushButton_R.risingEdge()) {
        detector_mode++;
        if (detector_mode>detector_passive)
          {detector_mode=0;}
        changeDetector_mode();
        display_settings();      
    }
/*LEFT MICROPUSH */
  if (micropushButton_L.risingEdge()) {
      //no function yet
    }



  /************  LEFT ENCODER BUTTON *******************/
  if (encoderButton_L.risingEdge()) {

      EncLeft_function=!EncLeft_function;
        
      //*SPECIAL MODES that change rightside Encoder based on leftside Encoder menusetting
      //****************************************SAMPLERATE
      if ((EncLeft_menu_idx==MENU_SR) and (EncLeft_function==enc_value))
       { EncRight_menu_idx=MENU_SR;
         EncRight_function=enc_value; // set the rightcontroller to select
       }
      
     if (SD_ACTIVE)
     {
        /*if (mode==MODE_REC)
                 { stopRecording();
                   EncLeft_function=enc_menu; //force into active-menu
                 }*/
        if ((mode==MODE_PLAY) and ((EncLeft_menu_idx==MENU_PLY) or (EncLeft_menu_idx==MENU_PLD)))
                 { stopPlaying();
                   EncLeft_menu_idx=MENU_PLY;
                   EncLeft_function=enc_menu; //force into active-menu
                   continousPlay=false;
                 }
               
        //Direct play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLD) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_FRQ ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }

        //play menu got choosen, now change the rightencoder to choose a file
        if ((EncLeft_menu_idx==MENU_PLY) and (EncLeft_function==enc_value))
         { 
           EncRight_menu_idx=MENU_PLY ;
           EncRight_function=enc_value; // set the rightcontroller to select
           
         }
       
     
     } //END SD_ACTIVE
     display_settings();      
  }

/************  RIGHT ENCODER BUTTON *******************/

 if (encoderButton_R.risingEdge()) {
    
    EncRight_function=!EncRight_function; //switch between menu/value control
        
    //when EncLeftoder is SR menu than EncRightoder can directly be setting the samplerate at a press 
    //the press should be a transfer from enc_value to enc_menu before it gets activated
    if ( (EncLeft_menu_idx==MENU_SR) and (EncRight_menu_idx==MENU_SR) and (EncRight_function==enc_menu))
    { set_sample_rate(sample_rate);
      last_sample_rate=sample_rate;

    }
    
    //recording and playing are only possible with an active SD card
    if (SD_ACTIVE)
     {
      /*if (mode==MODE_REC)
             { stopRecording();
               EncRight_function=enc_menu; //force into active-menu
              }
          else*/
           if (EncLeft_menu_idx==MENU_REC)
            {
              if (mode == MODE_DETECT) 
                 startRecording();
              }  
      else
      if (mode==MODE_PLAY)
             {
              if (not continousPlay) 
                { stopPlaying();
                  EncLeft_menu_idx=MENU_PLY;
                  EncLeft_function=enc_menu;
                }
             }     
      else       
      if (mode==MODE_DETECT)   
       { if (EncLeft_menu_idx==MENU_PLY)
            { last_sample_rate=sample_rate; 
              startPlaying(SAMPLE_RATE_8K);
             }
          
          if (EncLeft_menu_idx==MENU_PLD)
              {  fileselect=referencefile;
                 continousPlay=true;
                 last_sample_rate=sample_rate;
                 startPlaying(SAMPLE_RATE_176K);
              }  
       }
     }
      
      display_settings();
  }

  }
}



/********************************************************* */

void setup() {
 #ifdef DEBUGSERIAL
  Serial.begin(115200);
 #endif  
  delay(200);
 
//setup Encoder Buttonpins with pullups
  pinMode(encoderButton_RIGHT,INPUT_PULLUP);
  pinMode(encoderButton_LEFT,INPUT_PULLUP);

  pinMode(MICROPUSH_RIGHT,INPUT_PULLUP);
  pinMode(MICROPUSH_LEFT,INPUT_PULLUP);
 
 // EncLeft.write(10000); //set default far away from 0 to avoid negative transitions
  //EncRight.write(10000); //set default far away from 0 to avoid negative transitions

//startup menu
  EncLeft_menu_idx=MENU_VOL;
  EncRight_menu_idx=MENU_FRQ;
  EncLeft_function=enc_menu;
  EncRight_function=enc_menu;

  // Audio connections require memory. 
  AudioMemory(300);

// setSyncProvider(getTeensy3Time);

// Enable the audio shield. select input. and enable output
  sgtl5000.enable();
  sgtl5000.inputSelect(myInput);
  
  sgtl5000.volume(0.45);
  sgtl5000.micGain (mic_gain);
  //sgtl5000.adcHighPassFilterDisable(); // does not help too much!
  sgtl5000.lineInLevel(0);
  mixFFT.gain(0,1);

// Init TFT display  
#ifdef USETFT
  tft.begin();
  //ts.begin();
  tft.setRotation( 0 );
  tft.fillScreen(COLOR_BLACK);

  tft.setCursor(0, 0);
  tft.setScrollarea(TOP_OFFSET,BOTTOM_OFFSET);
  display_settings();
  tft.setCursor(80,50);
  tft.setFont(Arial_24);
  char tstr[9];
//  snprintf(tstr,9, "%02d:%02d:%02d",  hour(), minute(), second() );
struct tm tx = seconds2tm(RTC_TSR); 
snprintf(tstr,9, "%02d:%02d:%02d", tx.tm_hour, tx.tm_min, tx.tm_sec);
tft.print(tstr);
  delay(2000); //wait a second to clearly show the time 
#endif

   //Init SD card use
// uses the SD card slot of the Teensy, NOT that of the audio boards
// this init only for playback
#ifdef USESD1
  if(!(SD.begin(BUILTIN_SDCARD))) 
  {
      #ifdef DEBUGSERIAL
          Serial.println("Unable to access the SD card");
          delay(500);  
      #endif     
      
    SD_ACTIVE=false;
    tft.fillCircle(70,50,5,COLOR_RED);
     
  }
  else
  { 
    SD_ACTIVE=true;
    tft.fillCircle(70,50,5,COLOR_GREEN);
    filecounter=0;
    root = SD.open("/");


    //TODO: check if file is a RAW file and also read the SAMPLERATE
    while (true) {
        
        File entry =  root.openNextFile();
        if (! entry) {
          // no more files
          tft.setCursor(0,50);
          tft.print(filecounter);
          break;
        }
        
        if (entry.isDirectory()) {
          // do nothing, only look for raw files in the root
        } else 
        {
          
        strcpy(filelist[filecounter],entry.name() );
      
        if (String(entry.name())=="TEST_176.RAW")
           {referencefile=filecounter;
            }
        filecounter++;  
          
        }
        entry.close();
       }

    }


if (SD_ACTIVE)
// Recording on SD card by uSDFS library
  {f_mount (&fatfs, (TCHAR *)_T("0:/"), 0);      /* Mount/Unmount a logical drive */
   for (int i=0; i<filecounter; i++)
     {   
       #ifdef DEBUGSERIAL
          #ifdef USETFT
            tft.setCursor(0,50+i*20);
            tft.print(filelist[i]);
          #endif
      #endif    

     }
     file_number=filecounter+1;

  }

#endif

#ifdef USESD2
 uSD.init();

#endif

set_sample_rate (sample_rate);
set_freq_Oscillator (freq_real);
inputMixer.gain(0,1); //microphone active
inputMixer.gain(1,0); //player off

outputMixer.gain(0,1); // heterodyne1 to output 
outputMixer.gain(1,0); // granular to output off
outputMixer.gain(2,0); // player to output off

// the Granular effect requires memory to operate
granular1.begin(granularMemory, GRANULAR_MEMORY_SIZE);

// reset the FFT denoising array at startup
for (int16_t i = 0; i < 128; i++) {
  FFTavg[i]=0; 
    }
} // END SETUP


void loop() {
// If we're playing or recording, carry on...
  if (mode == MODE_REC) {
    continueRecording();
    
  } 
  
  if (mode == MODE_PLAY) {
    continuePlaying();
  }

updateButtons();   
// during recording only the left encoders button is used and screens are not updated
if (mode!=MODE_REC)
{
 updateEncoders();
 #ifdef USETFT
   if (displaychoice==waterfallgraph) 
    { waterfall();
      
     }
   else
    if (displaychoice==spectrumgraph)
    {  spectrum();
     }
 #endif
 }   

}

and the error message:
Code:
> Executing task: C:\Users\nick user\.platformio\penv\Scripts\platformio.exe run --verbose <

Processing teensy36 (platform: teensy; board: teensy36; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CONFIGURATION: https://docs.platformio.org/page/boards/teensy/teensy36.html
PLATFORM: Teensy > Teensy 3.6
HARDWARE: MK66FX1M0 180MHz 256KB RAM (1MB Flash)
DEBUG: CURRENT(jlink) EXTERNAL(jlink)
Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF MODES: FINDER(chain) COMPATIBILITY(soft)
Collected 93 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <Metro> (C:\Users\nick user\.platformio\packages\framework-arduinoteensy\libraries\Metro)
|-- <SPI> 1.0 (C:\Users\nick user\.platformio\packages\framework-arduinoteensy\libraries\SPI)
|-- <ILI9341_t3> (C:\Users\nick user\.platformio\packages\framework-arduinoteensy\libraries\ILI9341_t3)
|   |-- <SPI> 1.0 (C:\Users\nick user\.platformio\packages\framework-arduinoteensy\libraries\SPI)
|-- <Encoder> (C:\Users\nick user\.platformio\packages\framework-arduinoteensy\libraries\Encoder)
arm-none-eabi-g++ -o .pioenvs\teensy36\firmware.elf -T mk66fx1m0.ld -Os -Wl,--gc-sections,--relax -mthumb -mcpu=cortex-m4 -Wl,--defsym=__rtc_localtime=1543263450 -fsingle-precision-constant -mfloat-abi=hard -mfpu=fpv4-sp-d16 .pioenvs\teensy36\src\ILI9341_t3.cpp.o .pioenvs\teensy36\src\effect_granular.cpp.o .pioenvs\teensy36\src\main.cpp.o "-LC:\Users\nick user\.platformio\packages\framework-arduinoteensy\cores\teensy3" -L.pioenvs\teensy36 -Wl,--start-group .pioenvs\teensy36\lib332\libMetro.a .pioenvs\teensy36\liba54\libSPI.a .pioenvs\teensy36\lib038\libILI9341_t3.a .pioenvs\teensy36\lib2eb\libEncoder.a .pioenvs\teensy36\libFrameworkArduino.a -larm_cortexM4lf_math -lm -lstdc++ -Wl,--end-group
.pioenvs\teensy36\src\main.cpp.o: In function `AudioPlaySdRaw::~AudioPlaySdRaw()':
main.cpp:(.text._ZN14AudioPlaySdRawD2Ev[_ZN14AudioPlaySdRawD5Ev]+0xa): undefined reference to `File::~File()'
main.cpp:(.text._ZN14AudioPlaySdRawD2Ev[_ZN14AudioPlaySdRawD5Ev]+0x14): undefined reference to `vtable for AudioPlaySdRaw'
.pioenvs\teensy36\src\main.cpp.o: In function `AudioMixer4::AudioMixer4()':
main.cpp:(.text._ZN11AudioMixer4C2Ev[_ZN11AudioMixer4C5Ev]+0x24): undefined reference to `vtable for AudioMixer4'
.pioenvs\teensy36\src\main.cpp.o: In function `set_mic_gain(signed char)':
main.cpp:(.text._Z12set_mic_gaina+0xc): undefined reference to `AudioControlSGTL5000::micGain(unsigned int)'
.pioenvs\teensy36\src\main.cpp.o: In function `spectrum()':
main.cpp:(.text._Z8spectrumv+0x22): undefined reference to `AudioPlaySdRaw::positionMillis()'
main.cpp:(.text._Z8spectrumv+0x2a): undefined reference to `AudioPlaySdRaw::lengthMillis()'
main.cpp:(.text._Z8spectrumv+0x4e): undefined reference to `AudioPlaySdRaw::positionMillis()'
main.cpp:(.text._Z8spectrumv+0x56): undefined reference to `AudioPlaySdRaw::lengthMillis()'
main.cpp:(.text._Z8spectrumv+0x7a): undefined reference to `AudioPlaySdRaw::positionMillis()'
main.cpp:(.text._Z8spectrumv+0x82): undefined reference to `AudioPlaySdRaw::lengthMillis()'
main.cpp:(.text._Z8spectrumv+0xa2): undefined reference to `AudioPlaySdRaw::positionMillis()'
main.cpp:(.text._Z8spectrumv+0xaa): undefined reference to `AudioPlaySdRaw::lengthMillis()'
.pioenvs\teensy36\src\main.cpp.o: In function `startRecording()':
main.cpp:(.text._Z14startRecordingv+0x10): undefined reference to `f_close'
main.cpp:(.text._Z14startRecordingv+0x3c): undefined reference to `char2tchar'
main.cpp:(.text._Z14startRecordingv+0x5c): undefined reference to `f_stat'
main.cpp:(.text._Z14startRecordingv+0x68): undefined reference to `f_open'
main.cpp:(.text._Z14startRecordingv+0x74): undefined reference to `f_close'
main.cpp:(.text._Z14startRecordingv+0x80): undefined reference to `f_unlink'
main.cpp:(.text._Z14startRecordingv+0x8c): undefined reference to `f_open'
main.cpp:(.text._Z14startRecordingv+0xf6): undefined reference to `AudioRecordQueue::clear()'
.pioenvs\teensy36\src\main.cpp.o: In function `continueRecording()':
main.cpp:(.text._Z17continueRecordingv+0x4): undefined reference to `AudioRecordQueue::available()'
main.cpp:(.text._Z17continueRecordingv+0x18): undefined reference to `AudioRecordQueue::readBuffer()'
main.cpp:(.text._Z17continueRecordingv+0x2a): undefined reference to `AudioRecordQueue::freeBuffer()'
main.cpp:(.text._Z17continueRecordingv+0x3a): undefined reference to `AudioRecordQueue::readBuffer()'
main.cpp:(.text._Z17continueRecordingv+0x4c): undefined reference to `AudioRecordQueue::freeBuffer()'
main.cpp:(.text._Z17continueRecordingv+0x6a): undefined reference to `f_write'
.pioenvs\teensy36\src\main.cpp.o: In function `stopRecording()':
main.cpp:(.text._Z13stopRecordingv+0x18): undefined reference to `AudioRecordQueue::available()'
main.cpp:(.text._Z13stopRecordingv+0x22): undefined reference to `AudioRecordQueue::readBuffer()'
main.cpp:(.text._Z13stopRecordingv+0x30): undefined reference to `f_write'
main.cpp:(.text._Z13stopRecordingv+0x38): undefined reference to `AudioRecordQueue::freeBuffer()'
main.cpp:(.text._Z13stopRecordingv+0x40): undefined reference to `f_close'
.pioenvs\teensy36\src\main.cpp.o: In function `startPlaying(int)':
main.cpp:(.text._Z12startPlayingi+0x9e): undefined reference to `AudioPlaySdRaw::play(char const*)'
.pioenvs\teensy36\src\main.cpp.o: In function `stopPlaying()':
main.cpp:(.text._Z11stopPlayingv+0xe): undefined reference to `AudioPlaySdRaw::stop()'
.pioenvs\teensy36\src\main.cpp.o: In function `updateEncoder(unsigned char)':
main.cpp:(.text._Z13updateEncoderh+0xaa): undefined reference to `AudioControlSGTL5000::volumeInteger(unsigned int)'
.pioenvs\teensy36\src\main.cpp.o: In function `updateButtons()':
main.cpp:(.text._Z13updateButtonsv+0xc): undefined reference to `Bounce::update()'
main.cpp:(.text._Z13updateButtonsv+0x12): undefined reference to `Bounce::risingEdge()'
main.cpp:(.text._Z13updateButtonsv+0x28): undefined reference to `Bounce::update()'
main.cpp:(.text._Z13updateButtonsv+0x2e): undefined reference to `Bounce::update()'
main.cpp:(.text._Z13updateButtonsv+0x34): undefined reference to `Bounce::update()'
main.cpp:(.text._Z13updateButtonsv+0x3a): undefined reference to `Bounce::update()'
main.cpp:(.text._Z13updateButtonsv+0x40): undefined reference to `Bounce::risingEdge()'
main.cpp:(.text._Z13updateButtonsv+0x5e): undefined reference to `Bounce::risingEdge()'
main.cpp:(.text._Z13updateButtonsv+0x64): undefined reference to `Bounce::risingEdge()'
main.cpp:(.text._Z13updateButtonsv+0xde): undefined reference to `Bounce::risingEdge()'
.pioenvs\teensy36\src\main.cpp.o: In function `setup':
main.cpp:(.text.setup+0x4e): undefined reference to `AudioControlSGTL5000::enable()'
main.cpp:(.text.setup+0x5a): undefined reference to `AudioControlSGTL5000::write(unsigned int, unsigned int)'
main.cpp:(.text.setup+0x66): undefined reference to `AudioControlSGTL5000::write(unsigned int, unsigned int)'
main.cpp:(.text.setup+0x76): undefined reference to `AudioControlSGTL5000::write(unsigned int, unsigned int)'
main.cpp:(.text.setup+0x82): undefined reference to `AudioControlSGTL5000::volumeInteger(unsigned int)'
main.cpp:(.text.setup+0x8e): undefined reference to `AudioControlSGTL5000::micGain(unsigned int)'
main.cpp:(.text.setup+0x98): undefined reference to `AudioControlSGTL5000::lineInLevel(unsigned char, unsigned char)'
main.cpp:(.text.setup+0xe6): undefined reference to `seconds2tm'
main.cpp:(.text.setup+0x112): undefined reference to `SDClass::begin(unsigned char)'
main.cpp:(.text.setup+0x152): undefined reference to `SDClass::open(char const*, unsigned char)'
main.cpp:(.text.setup+0x184): undefined reference to `File::~File()'
main.cpp:(.text.setup+0x194): undefined reference to `File::openNextFile(unsigned char)'
main.cpp:(.text.setup+0x19a): undefined reference to `File::operator bool()'
main.cpp:(.text.setup+0x1b4): undefined reference to `File::~File()'
main.cpp:(.text.setup+0x1bc): undefined reference to `File::isDirectory()'
main.cpp:(.text.setup+0x1cc): undefined reference to `File::name()'
main.cpp:(.text.setup+0x1da): undefined reference to `File::name()'
main.cpp:(.text.setup+0x208): undefined reference to `File::close()'
main.cpp:(.text.setup+0x20e): undefined reference to `File::~File()'
main.cpp:(.text.setup+0x21e): undefined reference to `f_mount'
main.cpp:(.text.setup+0x2a8): undefined reference to `SD'
.pioenvs\teensy36\src\main.cpp.o: In function `_GLOBAL__sub_I_root':
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x20): undefined reference to `File::File()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x58): undefined reference to `AudioInputI2S::begin()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0xf2): undefined reference to `File::File()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0xf8): undefined reference to `AudioPlaySdRaw::begin()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x140): undefined reference to `AudioOutputI2S::begin()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x360): undefined reference to `File::~File()'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x36c): undefined reference to `vtable for AudioInputI2S'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x370): undefined reference to `vtable for AudioRecordQueue'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x378): undefined reference to `vtable for AudioSynthWaveformSineHires'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x380): undefined reference to `vtable for AudioEffectMultiply'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x384): undefined reference to `vtable for AudioAnalyzeFFT256'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x388): undefined reference to `AudioWindowHanning256'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x38c): undefined reference to `vtable for AudioPlaySdRaw'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x3a4): undefined reference to `vtable for AudioOutputI2S'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x3d0): undefined reference to `vtable for AudioControlSGTL5000'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x42c): undefined reference to `Bounce::Bounce(unsigned char, unsigned long)'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x436): undefined reference to `Bounce::Bounce(unsigned char, unsigned long)'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x44a): undefined reference to `Bounce::Bounce(unsigned char, unsigned long)'
main.cpp:(.text.startup._GLOBAL__sub_I_root+0x454): undefined reference to `Bounce::Bounce(unsigned char, unsigned long)'
collect2.exe: error: ld returned 1 exit status
*** [.pioenvs\teensy36\firmware.elf] Error 1
=============================================================================================== [ERROR] Took 4.24 seconds ===============================================================================================
The terminal process terminated with exit code: 1

If anyone can give a hint I'd be grateful.
 
Last edited:
So I just hijacked my wifes computer to test.




Installed Arduino 1.8.7

Installed teensyduino

Downloaded https://github.com/WMXZ-EU/uSDFS
Extracted the library to C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries

Downloaded the zip here https://github.com/CorBer/teensy_batdetector
Extracted both lil.... files here C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\ILI9341_t3
Extracted both granular..... files here C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio

Chose the Teensy 3.6 board in Arduino IDE

Cut and paste the code from post #159 and compiled



I do not know what is special about the ili library, for audio I can imagine the custom bitrates.
I just replaced the files to prevent conflicts.
 
ILI9341_t3: agreed, if there are changed/edited libraries in the wild, they *really* should have a new, different name.. it's annoying that there are copies of well-known libraries with different features, but having the same name.
 
Ok. A quick update. I had a tidy up of old copies of files and deleted everything not useful, rather than archiving it. Started a new Arduino session created a new project and it complied! Just uploaded it and it seems to working (more or less). The clock is frozen, I can only use spectrum and I have a cheap mic and a dodgy screen but it works with the keyring test. Not much time to play with it now but I need to get a better hardware set up first (I might try one of those boards, Edwin)

hQzhtY3.jpg
 
Glad to see you finally got something compiled.


If you want a board, just contact me by email.

Displays are a real problem, more then half the displays I ordered arrived broken.
Somehow the sellers refuse to pack these properly.
 
Hi Edwin, hi CorBee,

thanks again for your efforts to improve the software and providing a nice PCB!

I managed to build the detector, but I have problems with using it :). I probably need a user manual ;-).

I do not understand the difference between the functions of the two encoders and the meaning of the white and yellow colour of the menu options . . .

When I try to record (by setting the menu option to record with the left encoder, then pressing the left encoder then pressing again the right encoder), a sign appears "RECORDING", but nothing happens when I try to leave this mode. The detector remains entirely locked (heterodyne sound is still hearable).

I am totally lost now and I do not know how to further proceed.

Maybe you can help me to understand how to use the menu?

All the best,

Frank DD4WH
 
Hi Frank,

You got it all together quite quickly.

I guess this question can best be answered by Cor but I did play around with it so I think I know how it works.
I also made a video and put it on Youtube hoping to make more people enthusiastic.

I guess you will probably see me struggling pressing the wrong button.

First of all the left push button does not have a function assigned yet.

Both encoders seem to have almost the same functions, you can set either for volume, gain, frequency or other function.
The white text means turning the encoder makes you choose the function.
If you press it it turns yellow and you can change the value of that function bu turning the same encoder.

If you have set the left encoder to record you need to push the richt for record, the display will show recording.
Seems a bit weid, pushing the left will change color between white and yellow but probably does nothing.

I did notice at some point my recordings did not show in the files list when I selected Play. Somehow hey appeared after cycling the power. I was not able to reproduce that but I also had some strange ,raw files in my list at the time.
The working of the buttons could use some fine-tuning I guess but maybe Cor ad an explenation and a manual.

I am sure you will get things going, maybe the youtube video will help, around 6:30 you can see me recording.

Kind regards,

Edwin
 
Back
Top