Forum Rule: Always post complete source code & details to reproduce any issue!
Page 6 of 28 FirstFirst ... 4 5 6 7 8 16 ... LastLast
Results 126 to 150 of 683

Thread: Bat detector

  1. #126
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Hi,

    There are 3 displaymodes, one is a simple spectrum, one is a so called continous spectrum graph (scrolling) and one is without any graphs. When you select the display mode you can choose any of those three (no feedback on the mode is currently on the screen).

    heterodyne = old standard "radio-style" bat-detector. You tune to a frequency and get to hear all sounds that the microphone picks of about 5-6Khz above/below the tuned frequency. So for different bats you might have to tune. If you tune to 40Khz a sound of 45Khz will sound like 5Khz, but a sound of 35Khz will sound the same.

    divider: all incoming frequencies are divided by a default amount (currently I ahve this fixed at 10). So a sound of 60khz sounds like 6khz, no tuning needed but rather raw sounds.

    auto_heterodyne: this is like heterodyne but then automated tuning, it reacts to incoming sounds by adjusting the tuned centre-frequency at the peak of the incoming sound (max 1 change per second).

    auto_TE: this is the most experimental. TE is Time_expansion. In this mode when the detector picks up ultrasound it will replay it directly but slowed down (currenty 1/20 of the original speed). Its will only play short sounds like a blip from a bat. If that blip is 5 ms you will hear the slowed down version for 100ms. After that it will try to pick up another ultrasound. Continous blips of a bat will not be played all but you get to hear differences between bats very easy. If two species fly around you will directly realize that as soon as they get picked up. When walking through my garden I can hear many grasshoppers/crickets but when at the same moment a bat passes it directly is clearly heard. Needs more work to become even easier.

    passive: no processing at all. I want to have this mode to simple be able to hear outside sounds


    Message is a bit short, but I am still on a holiday
    cheers

    Cor

  2. #127
    Junior Member
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by CorBee View Post
    Message is a bit short, but I am still on a holiday
    Blimey, thanks for answering while on holiday! It makes sense and very helpful. I like the sound of the auto_het and auto_TE modes,,,

    Quote Originally Posted by CorBee View Post
    There are 3 displaymodes, one is a simple spectrum, one is a so called continous spectrum graph (scrolling) and one is without any graphs. When you select the display mode you can choose any of those three (no feedback on the mode is currently on the screen).
    Ok, I may add some indication of the display mode in some way so I can get a better handle on why I don't see anything some times.

    Now get back to relaxing

  3. #128
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    I am using Auto_TE most of the time, it allows me to wonder around and (also on a holiday) immediately hear if a sound is familiar or not. Its like listening to birdsongs (albeit shorter), you always recognize the species that you havent heard before rather easy. Determining which species is another thing.

    Back to the relaxed mode

  4. #129
    Junior Member
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by CorBee View Post
    I am using Auto_TE most of the time, it allows me to wonder around and (also on a holiday) immediately hear if a sound is familiar or not. Its like listening to birdsongs (albeit shorter), you always recognize the species that you havent heard before rather easy. Determining which species is another thing.
    I'm looking forward to trying it tonight although it's possibly a bit chilly today for bats.
    I've added a display mode indication after the divider mode indication and I can clearly see that the waterfall mode is simply is not working for me for some reason. Also noticed the second push button was not being used so I added some code to make that button change the display mode so I can quickly switch between them.

  5. #130
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Could be that the orientation of your ILI9341 is not the same ?

  6. #131
    Junior Member
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by CorBee View Post
    Could be that the orientation of your ILI9341 is not the same ?
    Not sure I understand what you're asking? I've attached a photo - does this look like you'd expect? - This is with me whistling while taking the photo.
    Click image for larger version. 

Name:	1.jpg 
Views:	78 
Size:	80.7 KB 
ID:	14877

  7. #132
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    For a spectrum thats quite OK !

  8. #133
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    When you turn to use waterfall be aware that its configured to only display and scroll when ultrasound comes in ... Try getting your apparatus close to the backside of a modern TV, that will probably send enough ultrasound

  9. #134
    Junior Member
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by CorBee View Post
    When you turn to use waterfall be aware that its configured to only display and scroll when ultrasound comes in ... Try getting your apparatus close to the backside of a modern TV, that will probably send enough ultrasound
    Ah! - that explains it then, I was expecting it to display something immediately

  10. #135
    Junior Member
    Join Date
    Apr 2018
    Posts
    6
    Hi there,
    finally the housing of my batdetector is ready :-)

    I use it with a LiIon-Accu and a Charger inside the housing.
    The 20kHz-Peak comes from the LED-Lamp at my writing desk.

    Everything works but how can i play the records (i.e. from my keyring).
    If i connect a speaker with Amplifer an select the file in the menu, i cant hear anything!

    Many, many thanks CorBee an DD4WH !!
    Click image for larger version. 

Name:	BatDetector2.jpg 
Views:	60 
Size:	64.7 KB 
ID:	14956

  11. #136
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Hi,

    Nice work ! Interested to hear which lion-battery and charger you used and how long it runs on batteries.
    Can you hear the sound when you attach a headphone to the audioboard ?

    Cor

  12. #137
    Junior Member
    Join Date
    Apr 2018
    Posts
    6
    Hi,
    the Problem is solved: I did use a Mono-Amplifer, with a Stereo-Earphone its ok.

    I use an 1000mAh-Accu. I didnt test it, but i think it runs 6-7hrs.
    The Charger is a Adafruit PowerBoost 500 + Charger. It has 3 LEDs to display charging,
    PowerOn ond LowPower. I can see the LEDs through the 3 Holes at the Front (closed with transparent Glue)
    Andreas
    Click image for larger version. 

Name:	IMG_6214.jpg 
Views:	102 
Size:	234.2 KB 
ID:	14968

  13. #138
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Hi,

    I wonder if the charger leds wont reduce batterytime a lot, I also see you use a booster to raise the voltage from 3.7V coming from the Lion to 5V at the Teensy. Does this not introduce a lot of "noise" ? The Teensy should be running fine when you directly use 3.7V without using a booster ?

    Cor

  14. #139
    Junior Member
    Join Date
    Apr 2018
    Posts
    6
    Hi,
    this are only small SMD-LEDs. I dont know the type but i think each needs not more than 2-3mA.

    Its a good question for the noise.
    I did recordings in a silence room, with and without Mic (only disconnected, open connector) and with and without charger.
    The cable from the audioboard to the Mic-Connector is 10cm long, ground at the shield, plus and signal inside.
    I did calculate the spectum with matlab, i have more experience with that.

    There are no important differences between with or without Charger/Booster.

    Andreas
    Click image for larger version. 

Name:	Spect_mitOhneBooster_mitMic.jpg 
Views:	53 
Size:	61.6 KB 
ID:	14977Click image for larger version. 

Name:	Spect_mitOhneBooster_ohneMic.jpg 
Views:	49 
Size:	71.6 KB 
ID:	14978

  15. #140
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Hi,

    Looks good !

    Cor

  16. #141
    Junior Member
    Join Date
    Nov 2018
    Posts
    5
    Quote Originally Posted by WMXZ View Post
    You can ignore the warnings, they only refer to different language standards.
    The errors, which complain about effect-granular, as CorBee explained means you are using different library.
    So, you have to find I CorBee's GitHub his modified effect-granular library and copy that into your local Arduino library.
    I think I may be having similar issues. I'm using the Arduino IDE. I have a blank sketch which when compiled gives a long string of errors. I've deleted the original effects_granular in my local arduino folder and replaced it with the modified one from CorBee's github. I'm getting red error messages for uSDFS

    EDITTED: I've deleted everything and started a new blank file and the granular related errors are gone so maybe it was using a cached version somehow. Still getting the uSDFS errors
    I got the uSDFS from here https://github.com/WMXZ-EU/uSDFS

    I'm way beyond my understanding of the code so any pointers would be appreciated.

    Here is the error message:

    Code:
    Arduino: 1.8.7 (Windows 10), TD: 1.44, Board: "Teensy 3.6, Serial, 180 MHz, Faster, US English"
    
    In file included from C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:30:0:
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.h:55:40: error: unknown type name 'size_t'
    
     TCHAR * char2tchar( char * charString, size_t nn, TCHAR * tcharString);
    
                                            ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.h:56:42: error: unknown type name 'size_t'
    
     char * tchar2char(  TCHAR * tcharString, size_t nn, char * charString);
    
                                              ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:46:11: error: return type is an incomplete type
    
     struct tm seconds2tm(uint32_t tt)
    
               ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:46:11: error: conflicting types for 'seconds2tm'
    
    In file included from C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:30:0:
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.h:50:11: note: previous declaration of 'seconds2tm' was here
    
     struct tm seconds2tm(uint32_t tt);
    
               ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c: In function 'seconds2tm':
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:47:13: error: storage size of 'tm' isn't known
    
     { struct tm tm;
    
                 ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:78:10: warning: 'return' with a value, in function returning void
    
       return tm;
    
              ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:47:13: warning: unused variable 'tm' [-Wunused-variable]
    
     { struct tm tm;
    
                 ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c: In function 'get_fattime':
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:83:10: error: variable 'tm' has initializer but incomplete type
    
       struct tm tm=seconds2tm(RTC_TSR);
    
              ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:83:13: error: storage size of 'tm' isn't known
    
       struct tm tm=seconds2tm(RTC_TSR);
    
                 ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:83:13: warning: unused variable 'tm' [-Wunused-variable]
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c: At top level:
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:95:40: error: unknown type name 'size_t'
    
     TCHAR * char2tchar( char * charString, size_t nn, TCHAR * tcharString)
    
                                            ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:101:42: error: unknown type name 'size_t'
    
     char * tchar2char(  TCHAR * tcharString, size_t nn, char * charString)
    
                                              ^
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c: In function 'get_fattime':
    
    C:\Program Files (x86)\Arduino\libraries\uSDFS-master\src\ff_utils.c:92:1: warning: control reaches end of non-void function [-Wreturn-type]
    
     }
    
     ^
    
    Error compiling for board Teensy 3.6.
    Last edited by nickjb; 11-13-2018 at 08:47 AM.

  17. #142
    Senior Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    117
    Hi guys Edwin here,
    Just new here on the forum.
    I have been working on this bat detector for some weeks now. Designed a nice PCB and stumbled into quite some issues.

    The issue with Arduino not compiling I had seemed to be around Time.h or Timelib.h there seem to be two versions that create issues when using Windows.
    I tried to let it compile finding it via the complete path but it seems some other part could also be looking for the other time.h or timelib.h


    Around line 147 I used this workaround.
    Code:
    // Workaround for "problems" with Time.h/Timelib.h used in the code by "Cor Berrevoets" 
    //#include <Time.h> This just seems to include Timelib.h so in the two lines below we use Timelib
    #include <C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Time\TimeLib.h> // compile in two steps by first using this line and wait for exit error.
    //#include <TimeLib.h> //After first compile error we uncomment this line and comment out the line above an compile again.
    A proper coder will probably come up with a better solution.

    I also Addressed Audio issues.
    I disconnected 3v3 from the Teensy to the audioboard. To power the audioboard an microphone amplifier I put in an additional 3v3 regulator. This eliminates the clicks that one can hear in the recordings.
    As it seemed the micro SD cards use quite a lot of current and must is used during writing making a tiny short voltage drop on the 3v3 circuit. One does not want the audio circuit on this same 3v3.


    I could also hear the display updating in my headphones. The biggest noise seemed to come from the clock line to the display. Near the Teensy I placed a 100 ohm resistor in the clock line. After this I also added resistors in the other SPI lines of the display. (I see others using 47-56 ohms in SPI busses but I had a lot of 100 Ohms and these work fine).


    Click image for larger version. 

Name:	image (2).png 
Views:	109 
Size:	128.0 KB 
ID:	15168

    Kind regards,

    Edwin

  18. #143
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,898
    Quote Originally Posted by nickjb View Post
    Still getting the uSDFS errors
    I got the uSDFS from here https://github.com/WMXZ-EU/uSDFS
    there must be some missing includes
    Can you tell me the program you are compiling?
    which system are you using Arduino IDE or other?

  19. #144
    Senior Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    117
    It should be this code by Cor Berrevoets I guess.

    Cor had this working in Linux, Visual Studio Code with PlatformIO but somehow in Windows it fails to compile.
    I tried VSC with PlatformIO and also Arduino but in windows it just refuses to compile in Linux it works fine.

    I created this workaround by looking at the right time.h with the complete path added.
    Somehow it still did not compile, but gave me a larger than 0 exitstatus.
    After removing the full path it suddenly compiled.
    I use a weird two step workaround.




    The code below is the code with my workaround commented out.

    I think it uses some changes in the effect_granular ligrary so you might need to replace these. https://github.com/CorBer/teensy_batdetector is the loacation on github.


    It looks like I had very much the same error's as nickjb

    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
    
    // Workaround for problems with Time.h/Timelib.h used in the code by "corber" 
    #include <Time.h> This just seems to include Timelib.h so in the two lines below we use Timelib
    //#include <C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Time\TimeLib.h> // compile in two steps by first using this line and wae for exit error.
    //#include <TimeLib.h> //After first compile error we uncomment this line and comment out the line above an compile again.
    
    #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
     }   
    
    }

  20. #145
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,898
    Quote Originally Posted by pe1pwf View Post
    Code:
    //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
    I do not know if my observation is relevant, but there are three SD libraries
    -SD (TD library)
    -uSDFS (ELM CHaN-WMXZ)
    -SdFs (Bill Greiman)

    I see that under USESD1 the first two libraries are included. that is definitely a call for conflicts.
    Also, if code is compiled outside Arduino, one may need to include <stddef.h> or <stdlib.h> before including <ff.h> to get the definition of size_t
    or other defines that Arduino automatically adds. sometimes I need to include <stdint.h>. So if standard definitions result in compiler errors, than non-Arduino compilations should add the necessary includes.

  21. #146
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    346
    Hi,

    The odd thing is that the declaration of the SD libs under USESD1 (which is the only mode that is currently working, USESD2 is work in progress) was taken from the original code by Frank (DD4WH). see https://github.com/DD4WH/Teensy-Bat-...ector_v1_2.ino

    Thusfar I have had no problems myself using this setup, most of the issues I am aware seem to originate to the time libraries, again since I have not had any of them in my particular setup I have not focussed on solving them (I am on Linux Mint, running Visual Code with PlatformIO for Teensy).

    regards
    Cor

  22. #147
    Senior Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    117
    I suddenly realized my reply was not tin the tread... It seems i walked away from it yesterday, fortunetaly it was still hete when my computer woke up.....




    The code by corber is based on Frank DD4WH's code for the bat detector.

    Somehow he also includes SD.h and ff.h and ff_utils.h which does not seem to create these problems.
    Just kicking out the SD.h is not the solution since the line File root; is dependent on SD.h although I am not sure what that line actually is used for.




    If I do take out all time related stuff like these lines.

    147 //#include <Time.h>

    1812 // setSyncProvider(getTeensy3Time);

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

    The code compiles.


    Unfortunately I am no coder, the code is mad by Cor, I hope he could join this tread again, but I know he is quite busy with some other project.
    The teensy uses the RTC so stored files get the right timestamp. I guess that is the main reason for using Time.h or Timelib.h

    Maybe the SD library is not used properly, but I am quite sure there is a problem with the Time.h


    ff_utils also includes a time.h which apparently is an other time.h than Time.h
    That to my opinion has the be part of the error that we find here.

    Edwin

  23. #148
    Senior Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    117
    To prove the problem is somewhere between Time.h and time.h I dis something silly.

    I just copied C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Time to
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimeAl t and renamed some files and changed names of dependancies like this

    In stead of #inlude "Time.h" I now use the #include "TimeAlt.h"


    Now that I use this alternative Time library the code will compile without any problems.

    It still looks to me like the Time.h included in this code and the time.h which is included by the ff_utils.h library somehow bite each other.


    Kind regards,

    Edwin

  24. #149
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,898
    Quote Originally Posted by pe1pwf View Post
    It still looks to me like the Time.h included in this code and the time.h which is included by the ff_utils.h library somehow bite each other.
    Not sure if windows OS is case sensitive.
    It seems that if using Time library, the system time.h include (used by ff_util.h) is not reachable anymore.
    If so, it is IMO bad programming style to have a custom library/include file with the same name as a system include file
    work around is indeed to change Time.h to TimaAlt.h and copy to local Arduino/library

    I never use the Time library (that is why there are time functions in ff_utils.h)

  25. #150
    Senior Member
    Join Date
    Nov 2018
    Location
    Netherlands
    Posts
    117
    Thank you for the Reply WMXZ,

    It looks like the case sensitivity is just the problem.

    Time.h and time.h are different but Windows does not know that.


    While the code is dependent on Time.h, ff_utils calls for time.h.

    As I am failry new to programming I have no idea how one can get the code to work without Time.h.
    (I only had a 4 evening intruduction workshop in spring this year at the local hamradiogroup to get a bit interested in programing)

    There are only two lines in the code that seem to depend on the Time.h I think it might be in there for RTC functions.


    1812 setSyncProvider(getTeensy3Time);

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

    There probably is a way to be able to use this without Time.h but I really have no clue how to do that.
    I know it is silly to use a "new" library that is just a renamed version so one does not run into problems with case sensitivity problems.


    Anyway, all help is appreciated.

    Edwin

Posting Permissions

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