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

Thread: audio guestbook Teensy 4.1 Please help.

  1. #1
    Junior Member
    Join Date
    Nov 2022
    Posts
    5

    audio guestbook Teensy 4.1 Please help.

    Please help. I have a project made from the link https://github.com/DD4WH/audio-guestbook
    One error pops up during programming:[B] C: \ User \ iJackService \ Documents \ Arduino \ libraries \ src \ MTP_Teensy.cpp: In member function "uint32_t MTP_class:: GetObjectInfo (MTP_class:: MTPContainers)":
    After connecting the power, the phone sounds for recording after a while and nothing else happens. It seems to me that it does not respond to putting the handset off. Rather, it doesn't detect the SD card. Formatted in exFat. What should I check?

    Code
    **
    * Audio Guestbook, Copyright (c) 2022 Playful Technology
    *
    * Tested using a Teensy 4.0 with Teensy Audio Shield, although should work
    * with minor modifications on other similar hardware
    *
    * When handset is lifted, a pre-recorded greeting message is played, followed by a tone.
    * Then, recording starts, and continues until the handset is replaced.
    * Playback button allows all messages currently saved on SD card through earpiece
    *
    * follow the detailed instructions here:
    * https://github.com/DD4WH/audio-guest...main/README.md
    *
    *
    * the sketch only works with the latest Teensyduino 1.57 version, so please update your Arduino IDE AND your Teensyduino to Arduino version 1.8.19 and the latest Teensyduino version 1.57
    * download the following library, unzip it and put it into your local Arduino folder (on my computer, the local Arduino folder is: "C:/Users/DD4WH/Documents/Arduino/libraries/"): https://github.com/KurtE/MTP_Teensy
    * compile with option: "Serial + MTP Disk (Experimental)"" and with option "CPU speed: 150MHz" (this can save about 70% of battery power)
    *
    * Modifications by Frank DD4WH, August 2022
    * - now uses a Teensy 4.1 with built-in SD card (faster via SDIO), if you want to use a Teensy 4.0, uncomment in the USER CONFIGURATION below
    * - Files are saved on SD card as 44.1kHz, 16-bit mono WAV audio files
    * - if you plug in the telephones´ USB cable into your computer, the telephone is mounted as a drive and you can acess the recordings
    * - if there is no "greeting.wav" message on the SD card, the telephone automatically plays an invitation to record this message and then you can record the greeting message
    * - if you want to record the greeting message again, just delete it from the telephone and lift the handheld again to record the greeting message
    * --> if your handheld contact switch opens on lifting, simply uncomment in the USER CONFIGURATION below, everything else is done by the software
    *
    * GNU GPL v3.0 license
    *
    */

    #include <Bounce.h>
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <TimeLib.h>
    #include <MTP_Teensy.h> // this library has to be downloaded separately (https://github.com/KurtE/MTP_Teensy)
    // unzip the downloaded file and its content into your local Arduino folder (on my computer, the local Arduino folder is: "C:/Users/DD4WH/Documents/Arduino/libraries/")

    /************************************************** ************************************************** ***********************************************/
    /**USER CONFIGURATION ************************************************** ************************************************** **************************/
    /**COMMENT / UNCOMMENT ACCORDING TO YOUR HARDWARE ************************************************** ************************************************/
    /************************************************** ************************************************** ***********************************************/

    // comment this out, if your handheld OPENS the contact on lift
    // use a digital voltmeter to find out
    #define HANDHELD_CLOSES_ON_LIFT

    // comment this out, if you want to record your greeting message with an external recorder
    // leave as-is if you want to have the telephone automatically switch to recording the greeting message, in case there is no "greeting.wav" on the SD card
    #define AUTO_GREETING_MESSAGE

    // comment out, if you use a Teensy 4.0 (and thus the SD card slot on the audio board)
    // if you leave this as-is, you have to use the built-in SD card slot on the Teensy 4.1, NOT the SD card slot on the audio board
    #define TEENSY_41

    /************************************************** ************************************************** ***********************************************/
    /**END OF USER CONFIGURATION ************************************************** ************************************************** *******************/
    /************************************************** ************************************************** ***********************************************/
    /************************************************** ************************************************** ***********************************************/

    // Define pins used by Teensy Audio Shield
    #ifdef TEENSY_41
    #define SDCARD_CS_PIN BUILTIN_SDCARD
    #else
    #define SDCARD_CS_PIN 10
    #endif
    #define SDCARD_MOSI_PIN 7
    #define SDCARD_SCK_PIN 14
    // And those used for inputs
    // You can choose the pins you use here:
    #define HOOK_PIN 40
    #define PLAYBACK_BUTTON_PIN 41
    //#define HOOK_PIN 0 // this is the default
    //#define PLAYBACK_BUTTON_PIN 1 // this is the default


    // GLOBALS
    // Audio initialisation code can be generated using the GUI interface at https://www.pjrc.com/teensy/gui/
    // Inputs
    AudioSynthWaveform waveform1; // To create the "beep" sfx
    AudioInputI2S i2s2; // I2S input from microphone on audio shield
    AudioPlaySdWav playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
    AudioRecordQueue queue1; // Creating an audio buffer in memory before saving to SD
    AudioMixer4 mixer; // Allows merging several inputs to same output
    AudioOutputI2S i2s1; // I2S interface to Speaker/Line Out on Audio shield
    AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer
    AudioConnection patchCord3(playWav1, 0, mixer, 1); // wav file playback mixer
    AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
    AudioConnection patchCord6(mixer, 0, i2s1, 1); // mixer output to speaker (R)
    AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
    AudioControlSGTL5000 sgtl5000_1;

    // Filename to save audio recording on SD card
    char filename[15];
    // The file object itself
    File frec;

    // Use long 40ms debounce time on both switches
    Bounce buttonRecord = Bounce(HOOK_PIN, 40);
    Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 40);

    // Keep track of current state of the device
    enum Mode {Initialising, Ready, Prompting, Recording, Playing, Recording_Greeting};
    Mode mode = Mode::Initialising;

    float beep_volume = 0.04f; // not too loud :-)

    // variables for writing to WAV file
    unsigned long ChunkSize = 0L;
    unsigned long Subchunk1Size = 16;
    unsigned int AudioFormat = 1;
    unsigned int numChannels = 1;
    unsigned long sampleRate = 44100;
    unsigned int bitsPerSample = 16;
    unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8)
    unsigned int blockAlign = numChannels*bitsPerSample/8;
    unsigned long Subchunk2Size = 0L;
    unsigned long recByteSaved = 0L;
    unsigned long NumSamples = 0L;
    byte byte1, byte2, byte3, byte4;


    void setup() {

    Serial.begin(9600);
    while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
    }
    Serial.println("Serial set up correctly");
    print_mode();
    // Configure the input pins
    pinMode(HOOK_PIN, INPUT_PULLUP);
    pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);

    // Audio connections require memory, and the record queue
    // uses this memory to buffer incoming audio.
    AudioMemory(60);

    // Enable the audio shield, select input, and enable output
    sgtl5000_1.enable();
    // Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
    sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
    sgtl5000_1.adcHighPassFilterDisable(); //
    sgtl5000_1.volume(0.85);

    mixer.gain(0, 1.0f);
    mixer.gain(1, 1.0f);

    // Play a beep to indicate system is online
    waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
    wait(1000);
    waveform1.amplitude(0);
    delay(1000);

    // Initialize the SD card
    #ifndef TEENSY_41 // assumes that you are using the SD card slot of the AUDIO BOARD
    SPI.setMOSI(SDCARD_MOSI_PIN);
    SPI.setSCK(SDCARD_SCK_PIN);
    #endif
    if (!(SD.begin(SDCARD_CS_PIN)))
    {
    // stop here if no SD card, but print a message
    while (1) {
    Serial.println("Unable to access the SD card");
    delay(500);
    }
    }
    else Serial.println("SD card correctly initialized");

    // mandatory to begin the MTP session.
    MTP.begin();

    // Add SD Card
    MTP.addFilesystem(SD, "Gruenkohls Audio guestbook"); // choose a nice name for the SD card volume to appear in your file explorer
    Serial.println("Added SD card via MTP");

    // Value in dB
    // sgtl5000_1.micGain(15);
    sgtl5000_1.micGain(8); // much lower gain is required for the AOM5024 electret capsule

    // Synchronise the Time object used in the program code with the RTC time provider.
    // See https://github.com/PaulStoffregen/Time
    setSyncProvider(getTeensy3Time);

    // Define a callback that will assign the correct datetime for any file system operations
    // (i.e. saving a new audio recording onto the SD card)
    FsDateTime::setCallback(dateTime);

    mode = Mode::Ready; print_mode();
    }

    void loop() { //1
    // First, read the buttons
    buttonRecord.update();
    buttonPlay.update();

    switch(mode)
    { //2
    case Mode::Ready:
    // Falling edge occurs when the handset is lifted --> 611 telephone
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if (buttonRecord.fallingEdge())
    #else
    if (buttonRecord.risingEdge())
    #endif
    {
    Serial.println("Handset lifted");
    mode = Mode::Prompting; print_mode();
    } //3
    else if(buttonPlay.fallingEdge())
    { //4
    //playAllRecordings();
    playLastRecording();
    } //4
    break;

    case Mode::Prompting:
    // Wait a second for users to put the handset to their ear
    wait(1000);

    #if defined(AUTO_GREETING_MESSAGE)

    if (!SD.exists("greeting.wav"))
    { //5
    mode = Mode::Recording_Greeting;
    break;
    } //5
    #endif

    // Play the greeting inviting them to record their message
    playWav1.play("greeting.wav");
    // Wait until the message has finished playing
    // while (playWav1.isPlaying()) {
    while (!playWav1.isStopped())
    { //6
    // Check whether the handset is replaced
    buttonRecord.update();
    buttonPlay.update();
    // Handset is replaced
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if (buttonRecord.risingEdge())
    #else
    if (buttonRecord.fallingEdge())
    #endif
    {
    playWav1.stop();
    mode = Mode::Ready; print_mode();
    return;
    } //7
    if(buttonPlay.fallingEdge())
    { //8
    playWav1.stop();
    //playAllRecordings();
    playLastRecording();
    return;
    } //8

    } //

    // Debug message
    Serial.println("Starting Recording");
    // Play the tone sound effect
    waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
    wait(1250);
    waveform1.amplitude(0);
    // Start the recording function
    startRecording();
    break;

    case Mode::Recording:
    // Handset is replaced
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if (buttonRecord.risingEdge())
    #else
    if (buttonRecord.fallingEdge())
    #endif
    { //9
    // Debug log
    Serial.println("Stopping Recording");
    // Stop recording
    stopRecording();
    // Play audio tone to confirm recording has ended
    end_Beep();
    } //9
    else
    { //10
    continueRecording();
    } // 10
    break;

    case Mode::Playing: // to make compiler happy
    break;

    case Mode::Initialising: // to make compiler happy
    break;

    case Mode::Recording_Greeting: // to make compiler happy
    startRecordingGreeting();
    mode = Mode::Recording;
    break;
    } // 2 end switch
    MTP.loop(); //This is mandatory to be placed in the loop code.
    } // 1 end loop

    void startRecordingGreeting() {
    if (SD.exists("greeting.wav")) {
    return;
    }
    // play message "Please record Greeting message now !"
    playWav1.play("invitation_greeting.wav");
    while (!playWav1.isStopped()) { // this works for playWav
    buttonPlay.update();
    buttonRecord.update();
    // Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    {
    playWav1.stop();
    mode = Mode::Ready; print_mode();
    return;
    }
    }
    // play beep
    two_tone_Beep();
    frec = SD.open("greeting.wav", FILE_WRITE);
    Serial.println("Opened Greeting file !");
    if(frec) {
    Serial.print("Recording to greeting.wav");
    queue1.begin();
    mode = Mode::Recording; print_mode();
    recByteSaved = 0L;
    }
    else {
    Serial.println("Couldn't open file to record!");
    }
    }

    void startRecording() {
    // Find the first available file number
    // for (uint8_t i=0; i<9999; i++) { // BUGFIX uint8_t overflows if it reaches 255
    for (uint16_t i=0; i<9999; i++) {
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // Create if does not exist, do not open existing, write, sync after write
    if (!SD.exists(filename)) {
    break;
    }
    }
    frec = SD.open(filename, FILE_WRITE);
    Serial.println("Opened file !");
    if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording; print_mode();
    recByteSaved = 0L;
    }
    else {
    Serial.println("Couldn't open file to record!");
    }
    }

    void continueRecording() {
    // Check if there is data in the queue
    if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer. The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // Write all 512 bytes to the SD card
    frec.write(buffer, 512);
    recByteSaved += 512;
    }
    }

    void stopRecording() {
    // Stop adding any new data to the queue
    queue1.end();
    // Flush all existing remaining data from the queue
    while (queue1.available() > 0) {
    // Save to open file
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
    recByteSaved += 256;
    }
    writeOutHeader();
    // Close the file
    frec.close();
    Serial.println("Closed file");
    mode = Mode::Ready; print_mode();
    }


    void playAllRecordings() {
    // Recording files are saved in the root directory
    File dir = SD.open("/");

    while (true) {
    File entry = dir.openNextFile();
    if (strstr(entry.name(), "greeting"))
    {
    entry = dir.openNextFile();
    }
    if (!entry) {
    // no more files
    entry.close();
    end_Beep();
    break;
    }
    //int8_t len = strlen(entry.name()) - 4;
    // if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
    // if (strstr(strlwr(entry.name() + (len - 4)), ".wav")) {
    // the lines above throw a warning, so I replace them with this (which is also easier to read):
    if (strstr(entry.name(), ".wav") || strstr(entry.name(), ".WAV")) {
    Serial.print("Now playing ");
    Serial.println(entry.name());
    // Play a short beep before each message
    waveform1.amplitude(beep_volume);
    wait(750);
    waveform1.amplitude(0);
    // Play the file
    playWav1.play(entry.name());
    mode = Mode::Playing; print_mode();
    }
    entry.close();

    // while (playWav1.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    while (!playWav1.isStopped()) { // this works for playWav
    buttonPlay.update();
    buttonRecord.update();
    // Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    {
    playWav1.stop();
    mode = Mode::Ready; print_mode();
    return;
    }
    }
    }
    // All files have been played
    mode = Mode::Ready; print_mode();
    }

    void playLastRecording() { // 1
    // Find the first available file number
    uint16_t idx = 0;
    for (uint16_t i=0; i<9999; i++) { // 2
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // check, if file with index i exists
    if (!SD.exists(filename)) { // 3
    idx = i - 1;
    break;
    } // 3
    } // 2
    // now play file with index idx == last recorded file
    snprintf(filename, 11, " %05d.wav", idx);
    Serial.println(filename);
    playWav1.play(filename);
    mode = Mode::Playing; print_mode();
    while (!playWav1.isStopped())
    { // 5 // this works for playWav
    buttonPlay.update();
    buttonRecord.update();
    // Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    {
    playWav1.stop();
    mode = Mode::Ready; print_mode();
    return;
    } //4
    } // 5 end while
    // file has been played
    mode = Mode::Ready; print_mode();
    end_Beep();
    } // 1 end playLastRecording


    // Retrieve the current time from Teensy built-in RTC
    time_t getTeensy3Time(){
    return Teensy3Clock.get();
    }

    // Callback to assign timestamps for file system operations
    void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

    // Return date using FS_DATE macro to format fields.
    *date = FS_DATE(year(), month(), day());

    // Return time using FS_TIME macro to format fields.
    *time = FS_TIME(hour(), minute(), second());

    // Return low time bits in units of 10 ms.
    *ms10 = second() & 1 ? 100 : 0;
    }

    // Non-blocking delay, which pauses execution of main program logic,
    // but while still listening for input
    void wait(unsigned int milliseconds) {
    elapsedMillis msec=0;

    while (msec <= milliseconds) {
    buttonRecord.update();
    buttonPlay.update();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
    }
    }


    void writeOutHeader() { // update WAV header with final filesize/datasize

    // NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
    // Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
    Subchunk2Size = recByteSaved;
    ChunkSize = Subchunk2Size + 36;
    frec.seek(0);
    frec.write("RIFF");
    byte1 = ChunkSize & 0xff;
    byte2 = (ChunkSize >> 8) & 0xff;
    byte3 = (ChunkSize >> 16) & 0xff;
    byte4 = (ChunkSize >> 24) & 0xff;
    frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    frec.write("WAVE");
    frec.write("fmt ");
    byte1 = Subchunk1Size & 0xff;
    byte2 = (Subchunk1Size >> 8) & 0xff;
    byte3 = (Subchunk1Size >> 16) & 0xff;
    byte4 = (Subchunk1Size >> 24) & 0xff;
    frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    byte1 = AudioFormat & 0xff;
    byte2 = (AudioFormat >> 8) & 0xff;
    frec.write(byte1); frec.write(byte2);
    byte1 = numChannels & 0xff;
    byte2 = (numChannels >> 8) & 0xff;
    frec.write(byte1); frec.write(byte2);
    byte1 = sampleRate & 0xff;
    byte2 = (sampleRate >> 8) & 0xff;
    byte3 = (sampleRate >> 16) & 0xff;
    byte4 = (sampleRate >> 24) & 0xff;
    frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    byte1 = byteRate & 0xff;
    byte2 = (byteRate >> 8) & 0xff;
    byte3 = (byteRate >> 16) & 0xff;
    byte4 = (byteRate >> 24) & 0xff;
    frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    byte1 = blockAlign & 0xff;
    byte2 = (blockAlign >> 8) & 0xff;
    frec.write(byte1); frec.write(byte2);
    byte1 = bitsPerSample & 0xff;
    byte2 = (bitsPerSample >> 8) & 0xff;
    frec.write(byte1); frec.write(byte2);
    frec.write("data");
    byte1 = Subchunk2Size & 0xff;
    byte2 = (Subchunk2Size >> 8) & 0xff;
    byte3 = (Subchunk2Size >> 16) & 0xff;
    byte4 = (Subchunk2Size >> 24) & 0xff;
    frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    frec.close();
    Serial.println("header written");
    Serial.print("Subchunk2: ");
    Serial.println(Subchunk2Size);
    }

    void end_Beep(void) {
    waveform1.frequency(523.25);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    }

    void two_tone_Beep(void) {
    waveform1.frequency(523.25);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    waveform1.frequency(375.0);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    waveform1.frequency(523.25);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    waveform1.frequency(375.0);
    wait(250);
    waveform1.amplitude(beep_volume);
    wait(250);
    waveform1.amplitude(0);
    }

    void print_mode(void) { // only for debugging
    Serial.print("Mode switched to: ");
    // Initialising, Ready, Prompting, Recording, Playing
    if(mode == Mode::Ready) Serial.println(" Ready");
    else if(mode == Mode::Prompting) Serial.println(" Prompting");
    else if(mode == Mode::Recording) Serial.println(" Recording");
    else if(mode == Mode::Playing) Serial.println(" Playing");
    else if(mode == Mode::Initialising) Serial.println(" Initialising");
    else if(mode == Mode::Recording_Greeting) Serial.println(" Recording Greeting");
    else Serial.println(" Undefined");
    }

  2. #2
    Junior Member
    Join Date
    Nov 2022
    Posts
    5
    Alternatively, please describe which version fits Teensy 4.1 and which files exactly where to copy them.

  3. #3
    Senior Member BriComp's Avatar
    Join Date
    Apr 2014
    Location
    Cheltenham, UK
    Posts
    949
    Code:
    /**
    * Audio Guestbook, Copyright(c) 2022 Playful Technology
    *
    *Tested using a Teensy 4.0 with Teensy Audio Shield, although should work
    * with minor modifications on other similar hardware
    *
    * When handset is lifted, a pre - recorded greeting message is played, followed by a tone.
    * Then, recording starts, and continues until the handset is replaced.
    * Playback button allows all messages currently saved on SD card through earpiece
    *
    * follow the detailed instructions here :
    *https ://github.com/DD4WH/audio-guest...main/README.md
    	*
    	*
    	*the sketch only works with the latest Teensyduino 1.57 version, so please update your Arduino IDE AND your Teensyduino to Arduino version 1.8.19 and the latest Teensyduino version 1.57
    	* download the following library, unzip itand put it into your local Arduino folder(on my computer, the local Arduino folder is : "C:/Users/DD4WH/Documents/Arduino/libraries/") : https ://github.com/KurtE/MTP_Teensy
    	*compile with option : "Serial + MTP Disk (Experimental)"" and with option "CPU speed : 150MHz" (this can save about 70% of battery power)
    	*
    	*Modifications by Frank DD4WH, August 2022
    	* -now uses a Teensy 4.1 with built - in SD card(faster via SDIO), if you want to use a Teensy 4.0, uncomment in the USER CONFIGURATION below
    	* -Files are saved on SD card as 44.1kHz, 16 - bit mono WAV audio files
    	* -if you plug in the telephones´ USB cable into your computer, the telephone is mounted as a driveand you can acess the recordings
    	* -if there is no "greeting.wav" message on the SD card, the telephone automatically plays an invitation to record this messageand then you can record the greeting message
    	* -if you want to record the greeting message again, just delete it from the telephoneand lift the handheld again to record the greeting message
    	* -- > if your handheld contact switch opens on lifting, simply uncomment in the USER CONFIGURATION below, everything else is done by the software
    	*
    	* GNU GPL v3.0 license
    	*
    	*/
    
    #include <Bounce.h>
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <TimeLib.h>
    #include <MTP_Teensy.h> // this library has to be downloaded separately (https://github.com/KurtE/MTP_Teensy)
    	// unzip the downloaded file and its content into your local Arduino folder (on my computer, the local Arduino folder is: "C:/Users/DD4WH/Documents/Arduino/libraries/")
    
    	/************************************************** ************************************************** ***********************************************/
    	/**USER CONFIGURATION ************************************************** ************************************************** **************************/
    	/**COMMENT / UNCOMMENT ACCORDING TO YOUR HARDWARE ************************************************** ************************************************/
    	/************************************************** ************************************************** ***********************************************/
    
    	// comment this out, if your handheld OPENS the contact on lift
    	// use a digital voltmeter to find out
    #define HANDHELD_CLOSES_ON_LIFT
    
    // comment this out, if you want to record your greeting message with an external recorder
    // leave as-is if you want to have the telephone automatically switch to recording the greeting message, in case there is no "greeting.wav" on the SD card
    #define AUTO_GREETING_MESSAGE
    
    // comment out, if you use a Teensy 4.0 (and thus the SD card slot on the audio board)
    // if you leave this as-is, you have to use the built-in SD card slot on the Teensy 4.1, NOT the SD card slot on the audio board
    #define TEENSY_41
    
    /************************************************** ************************************************** ***********************************************/
    /**END OF USER CONFIGURATION ************************************************** ************************************************** *******************/
    /************************************************** ************************************************** ***********************************************/
    /************************************************** ************************************************** ***********************************************/
    
    // Define pins used by Teensy Audio Shield
    #ifdef TEENSY_41
    #define SDCARD_CS_PIN BUILTIN_SDCARD
    #else
    #define SDCARD_CS_PIN 10
    #endif
    #define SDCARD_MOSI_PIN 7
    #define SDCARD_SCK_PIN 14
    // And those used for inputs
    // You can choose the pins you use here:
    #define HOOK_PIN 40
    #define PLAYBACK_BUTTON_PIN 41
    //#define HOOK_PIN 0 // this is the default
    //#define PLAYBACK_BUTTON_PIN 1 // this is the default
    
    
    // GLOBALS
    // Audio initialisation code can be generated using the GUI interface at https://www.pjrc.com/teensy/gui/
    // Inputs
    AudioSynthWaveform waveform1; // To create the "beep" sfx
    AudioInputI2S i2s2; // I2S input from microphone on audio shield
    AudioPlaySdWav playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
    AudioRecordQueue queue1; // Creating an audio buffer in memory before saving to SD
    AudioMixer4 mixer; // Allows merging several inputs to same output
    AudioOutputI2S i2s1; // I2S interface to Speaker/Line Out on Audio shield
    AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer
    AudioConnection patchCord3(playWav1, 0, mixer, 1); // wav file playback mixer
    AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
    AudioConnection patchCord6(mixer, 0, i2s1, 1); // mixer output to speaker (R)
    AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
    AudioControlSGTL5000 sgtl5000_1;
    
    // Filename to save audio recording on SD card
    char filename[15];
    // The file object itself
    File frec;
    
    // Use long 40ms debounce time on both switches
    Bounce buttonRecord = Bounce(HOOK_PIN, 40);
    Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 40);
    
    // Keep track of current state of the device
    enum Mode { Initialising, Ready, Prompting, Recording, Playing, Recording_Greeting };
    Mode mode = Mode::Initialising;
    
    float beep_volume = 0.04f; // not too loud :-)
    
    // variables for writing to WAV file
    unsigned long ChunkSize = 0L;
    unsigned long Subchunk1Size = 16;
    unsigned int AudioFormat = 1;
    unsigned int numChannels = 1;
    unsigned long sampleRate = 44100;
    unsigned int bitsPerSample = 16;
    unsigned long byteRate = sampleRate * numChannels * (bitsPerSample / 8);// samplerate x channels x (bitspersample / 8)
    unsigned int blockAlign = numChannels * bitsPerSample / 8;
    unsigned long Subchunk2Size = 0L;
    unsigned long recByteSaved = 0L;
    unsigned long NumSamples = 0L;
    byte byte1, byte2, byte3, byte4;
    
    
    void setup() {
    
    	Serial.begin(9600);
    	while (!Serial && millis() < 5000) {
    		// wait for serial port to connect.
    	}
    	Serial.println("Serial set up correctly");
    	print_mode();
    	// Configure the input pins
    	pinMode(HOOK_PIN, INPUT_PULLUP);
    	pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);
    
    	// Audio connections require memory, and the record queue
    	// uses this memory to buffer incoming audio.
    	AudioMemory(60);
    
    	// Enable the audio shield, select input, and enable output
    	sgtl5000_1.enable();
    	// Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
    	sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
    	sgtl5000_1.adcHighPassFilterDisable(); //
    	sgtl5000_1.volume(0.85);
    
    	mixer.gain(0, 1.0f);
    	mixer.gain(1, 1.0f);
    
    	// Play a beep to indicate system is online
    	waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
    	wait(1000);
    	waveform1.amplitude(0);
    	delay(1000);
    
    	// Initialize the SD card
    #ifndef TEENSY_41 // assumes that you are using the SD card slot of the AUDIO BOARD
    	SPI.setMOSI(SDCARD_MOSI_PIN);
    	SPI.setSCK(SDCARD_SCK_PIN);
    #endif
    	if (!(SD.begin(SDCARD_CS_PIN)))
    	{
    		// stop here if no SD card, but print a message
    		while (1) {
    			Serial.println("Unable to access the SD card");
    			delay(500);
    		}
    	}
    	else Serial.println("SD card correctly initialized");
    
    	// mandatory to begin the MTP session.
    	MTP.begin();
    
    	// Add SD Card
    	MTP.addFilesystem(SD, "Gruenkohls Audio guestbook"); // choose a nice name for the SD card volume to appear in your file explorer
    	Serial.println("Added SD card via MTP");
    
    	// Value in dB
    	// sgtl5000_1.micGain(15);
    	sgtl5000_1.micGain(8); // much lower gain is required for the AOM5024 electret capsule
    
    	// Synchronise the Time object used in the program code with the RTC time provider.
    	// See https://github.com/PaulStoffregen/Time
    	setSyncProvider(getTeensy3Time);
    
    	// Define a callback that will assign the correct datetime for any file system operations
    	// (i.e. saving a new audio recording onto the SD card)
    	FsDateTime::setCallback(dateTime);
    
    	mode = Mode::Ready; print_mode();
    }
    
    void loop() { //1
    	// First, read the buttons
    	buttonRecord.update();
    	buttonPlay.update();
    
    	switch (mode)
    	{ //2
    	case Mode::Ready:
    		// Falling edge occurs when the handset is lifted --> 611 telephone
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    		if (buttonRecord.fallingEdge())
    #else
    		if (buttonRecord.risingEdge())
    #endif
    		{
    			Serial.println("Handset lifted");
    			mode = Mode::Prompting; print_mode();
    		} //3
    		else if (buttonPlay.fallingEdge())
    		{ //4
    		//playAllRecordings();
    			playLastRecording();
    		} //4
    		break;
    
    	case Mode::Prompting:
    		// Wait a second for users to put the handset to their ear
    		wait(1000);
    
    #if defined(AUTO_GREETING_MESSAGE)
    
    		if (!SD.exists("greeting.wav"))
    		{ //5
    			mode = Mode::Recording_Greeting;
    			break;
    		} //5
    #endif
    
    // Play the greeting inviting them to record their message
    		playWav1.play("greeting.wav");
    		// Wait until the message has finished playing
    		// while (playWav1.isPlaying()) {
    		while (!playWav1.isStopped())
    		{ //6
    		// Check whether the handset is replaced
    			buttonRecord.update();
    			buttonPlay.update();
    			// Handset is replaced
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    			if (buttonRecord.risingEdge())
    #else
    			if (buttonRecord.fallingEdge())
    #endif
    			{
    				playWav1.stop();
    				mode = Mode::Ready; print_mode();
    				return;
    			} //7
    			if (buttonPlay.fallingEdge())
    			{ //8
    				playWav1.stop();
    				//playAllRecordings();
    				playLastRecording();
    				return;
    			} //8
    
    		} //
    
    		// Debug message
    		Serial.println("Starting Recording");
    		// Play the tone sound effect
    		waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
    		wait(1250);
    		waveform1.amplitude(0);
    		// Start the recording function
    		startRecording();
    		break;
    
    	case Mode::Recording:
    		// Handset is replaced
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    		if (buttonRecord.risingEdge())
    #else
    		if (buttonRecord.fallingEdge())
    #endif
    		{ //9
    		// Debug log
    			Serial.println("Stopping Recording");
    			// Stop recording
    			stopRecording();
    			// Play audio tone to confirm recording has ended
    			end_Beep();
    		} //9
    		else
    		{ //10
    			continueRecording();
    		} // 10
    		break;
    
    	case Mode::Playing: // to make compiler happy
    		break;
    
    	case Mode::Initialising: // to make compiler happy
    		break;
    
    	case Mode::Recording_Greeting: // to make compiler happy
    		startRecordingGreeting();
    		mode = Mode::Recording;
    		break;
    	} // 2 end switch
    	MTP.loop(); //This is mandatory to be placed in the loop code.
    } // 1 end loop
    
    void startRecordingGreeting() {
    	if (SD.exists("greeting.wav")) {
    		return;
    	}
    	// play message "Please record Greeting message now !"
    	playWav1.play("invitation_greeting.wav");
    	while (!playWav1.isStopped()) { // this works for playWav
    		buttonPlay.update();
    		buttonRecord.update();
    		// Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    		if (buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    		if (buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    		{
    			playWav1.stop();
    			mode = Mode::Ready; print_mode();
    			return;
    		}
    	}
    	// play beep
    	two_tone_Beep();
    	frec = SD.open("greeting.wav", FILE_WRITE);
    	Serial.println("Opened Greeting file !");
    	if (frec) {
    		Serial.print("Recording to greeting.wav");
    		queue1.begin();
    		mode = Mode::Recording; print_mode();
    		recByteSaved = 0L;
    	}
    	else {
    		Serial.println("Couldn't open file to record!");
    	}
    }
    
    void startRecording() {
    	// Find the first available file number
    	// for (uint8_t i=0; i<9999; i++) { // BUGFIX uint8_t overflows if it reaches 255
    	for (uint16_t i = 0; i < 9999; i++) {
    		// Format the counter as a five-digit number with leading zeroes, followed by file extension
    		snprintf(filename, 11, " %05d.wav", i);
    		// Create if does not exist, do not open existing, write, sync after write
    		if (!SD.exists(filename)) {
    			break;
    		}
    	}
    	frec = SD.open(filename, FILE_WRITE);
    	Serial.println("Opened file !");
    	if (frec) {
    		Serial.print("Recording to ");
    		Serial.println(filename);
    		queue1.begin();
    		mode = Mode::Recording; print_mode();
    		recByteSaved = 0L;
    	}
    	else {
    		Serial.println("Couldn't open file to record!");
    	}
    }
    
    void continueRecording() {
    	// Check if there is data in the queue
    	if (queue1.available() >= 2) {
    		byte buffer[512];
    		// Fetch 2 blocks from the audio library and copy
    		// into a 512 byte buffer. The Arduino SD library
    		// is most efficient when full 512 byte sector size
    		// writes are used.
    		memcpy(buffer, queue1.readBuffer(), 256);
    		queue1.freeBuffer();
    		memcpy(buffer + 256, queue1.readBuffer(), 256);
    		queue1.freeBuffer();
    		// Write all 512 bytes to the SD card
    		frec.write(buffer, 512);
    		recByteSaved += 512;
    	}
    }
    
    void stopRecording() {
    	// Stop adding any new data to the queue
    	queue1.end();
    	// Flush all existing remaining data from the queue
    	while (queue1.available() > 0) {
    		// Save to open file
    		frec.write((byte*)queue1.readBuffer(), 256);
    		queue1.freeBuffer();
    		recByteSaved += 256;
    	}
    	writeOutHeader();
    	// Close the file
    	frec.close();
    	Serial.println("Closed file");
    	mode = Mode::Ready; print_mode();
    }
    
    
    void playAllRecordings() {
    	// Recording files are saved in the root directory
    	File dir = SD.open("/");
    
    	while (true) {
    		File entry = dir.openNextFile();
    		if (strstr(entry.name(), "greeting"))
    		{
    			entry = dir.openNextFile();
    		}
    		if (!entry) {
    			// no more files
    			entry.close();
    			end_Beep();
    			break;
    		}
    		//int8_t len = strlen(entry.name()) - 4;
    		// if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
    		// if (strstr(strlwr(entry.name() + (len - 4)), ".wav")) {
    		// the lines above throw a warning, so I replace them with this (which is also easier to read):
    		if (strstr(entry.name(), ".wav") || strstr(entry.name(), ".WAV")) {
    			Serial.print("Now playing ");
    			Serial.println(entry.name());
    			// Play a short beep before each message
    			waveform1.amplitude(beep_volume);
    			wait(750);
    			waveform1.amplitude(0);
    			// Play the file
    			playWav1.play(entry.name());
    			mode = Mode::Playing; print_mode();
    		}
    		entry.close();
    
    		// while (playWav1.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    		while (!playWav1.isStopped()) { // this works for playWav
    			buttonPlay.update();
    			buttonRecord.update();
    			// Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    			if (buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    			if (buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    			{
    				playWav1.stop();
    				mode = Mode::Ready; print_mode();
    				return;
    			}
    		}
    	}
    	// All files have been played
    	mode = Mode::Ready; print_mode();
    }
    
    void playLastRecording() { // 1
    	// Find the first available file number
    	uint16_t idx = 0;
    	for (uint16_t i = 0; i < 9999; i++) { // 2
    		// Format the counter as a five-digit number with leading zeroes, followed by file extension
    		snprintf(filename, 11, " %05d.wav", i);
    		// check, if file with index i exists
    		if (!SD.exists(filename)) { // 3
    			idx = i - 1;
    			break;
    		} // 3
    	} // 2
    	// now play file with index idx == last recorded file
    	snprintf(filename, 11, " %05d.wav", idx);
    	Serial.println(filename);
    	playWav1.play(filename);
    	mode = Mode::Playing; print_mode();
    	while (!playWav1.isStopped())
    	{ // 5 // this works for playWav
    		buttonPlay.update();
    		buttonRecord.update();
    		// Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
    		if (buttonPlay.fallingEdge() || buttonRecord.risingEdge())
    #else
    		if (buttonPlay.fallingEdge() || buttonRecord.fallingEdge())
    #endif
    		{
    			playWav1.stop();
    			mode = Mode::Ready; print_mode();
    			return;
    		} //4
    	} // 5 end while
    	// file has been played
    	mode = Mode::Ready; print_mode();
    	end_Beep();
    } // 1 end playLastRecording
    
    
    // Retrieve the current time from Teensy built-in RTC
    time_t getTeensy3Time() {
    	return Teensy3Clock.get();
    }
    
    // Callback to assign timestamps for file system operations
    void dateTime(uint16_t * date, uint16_t * time, uint8_t * ms10) {
    
    	// Return date using FS_DATE macro to format fields.
    	*date = FS_DATE(year(), month(), day());
    
    	// Return time using FS_TIME macro to format fields.
    	*time = FS_TIME(hour(), minute(), second());
    
    	// Return low time bits in units of 10 ms.
    	*ms10 = second() & 1 ? 100 : 0;
    }
    
    // Non-blocking delay, which pauses execution of main program logic,
    // but while still listening for input
    void wait(unsigned int milliseconds) {
    	elapsedMillis msec = 0;
    
    	while (msec <= milliseconds) {
    		buttonRecord.update();
    		buttonPlay.update();
    		if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    		if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    		if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    		if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
    	}
    }
    
    
    void writeOutHeader() { // update WAV header with final filesize/datasize
    
    	// NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
    	// Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
    	Subchunk2Size = recByteSaved;
    	ChunkSize = Subchunk2Size + 36;
    	frec.seek(0);
    	frec.write("RIFF");
    	byte1 = ChunkSize & 0xff;
    	byte2 = (ChunkSize >> 8) & 0xff;
    	byte3 = (ChunkSize >> 16) & 0xff;
    	byte4 = (ChunkSize >> 24) & 0xff;
    	frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    	frec.write("WAVE");
    	frec.write("fmt ");
    	byte1 = Subchunk1Size & 0xff;
    	byte2 = (Subchunk1Size >> 8) & 0xff;
    	byte3 = (Subchunk1Size >> 16) & 0xff;
    	byte4 = (Subchunk1Size >> 24) & 0xff;
    	frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    	byte1 = AudioFormat & 0xff;
    	byte2 = (AudioFormat >> 8) & 0xff;
    	frec.write(byte1); frec.write(byte2);
    	byte1 = numChannels & 0xff;
    	byte2 = (numChannels >> 8) & 0xff;
    	frec.write(byte1); frec.write(byte2);
    	byte1 = sampleRate & 0xff;
    	byte2 = (sampleRate >> 8) & 0xff;
    	byte3 = (sampleRate >> 16) & 0xff;
    	byte4 = (sampleRate >> 24) & 0xff;
    	frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    	byte1 = byteRate & 0xff;
    	byte2 = (byteRate >> 8) & 0xff;
    	byte3 = (byteRate >> 16) & 0xff;
    	byte4 = (byteRate >> 24) & 0xff;
    	frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    	byte1 = blockAlign & 0xff;
    	byte2 = (blockAlign >> 8) & 0xff;
    	frec.write(byte1); frec.write(byte2);
    	byte1 = bitsPerSample & 0xff;
    	byte2 = (bitsPerSample >> 8) & 0xff;
    	frec.write(byte1); frec.write(byte2);
    	frec.write("data");
    	byte1 = Subchunk2Size & 0xff;
    	byte2 = (Subchunk2Size >> 8) & 0xff;
    	byte3 = (Subchunk2Size >> 16) & 0xff;
    	byte4 = (Subchunk2Size >> 24) & 0xff;
    	frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
    	frec.close();
    	Serial.println("header written");
    	Serial.print("Subchunk2: ");
    	Serial.println(Subchunk2Size);
    }
    
    void end_Beep(void) {
    	waveform1.frequency(523.25);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    }
    
    void two_tone_Beep(void) {
    	waveform1.frequency(523.25);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	waveform1.frequency(375.0);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	waveform1.frequency(523.25);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    	waveform1.frequency(375.0);
    	wait(250);
    	waveform1.amplitude(beep_volume);
    	wait(250);
    	waveform1.amplitude(0);
    }
    
    void print_mode(void) { // only for debugging
    	Serial.print("Mode switched to: ");
    	// Initialising, Ready, Prompting, Recording, Playing
    	if (mode == Mode::Ready) Serial.println(" Ready");
    	else if (mode == Mode::Prompting) Serial.println(" Prompting");
    	else if (mode == Mode::Recording) Serial.println(" Recording");
    	else if (mode == Mode::Playing) Serial.println(" Playing");
    	else if (mode == Mode::Initialising) Serial.println(" Initialising");
    	else if (mode == Mode::Recording_Greeting) Serial.println(" Recording Greeting");
    	else Serial.println(" Undefined");
    }
    Please post your code between code tags using the # button.
    I am sure you will agree that it looks better and is much easier for someone to understand and possibly help you.

  4. #4
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    11,151
    Sorry, I don't have any idea here on:
    Please help. I have a project made from the link https://github.com/DD4WH/audio-guestbook
    One error pops up during programming:[B] C: \ User \ iJackService \ Documents \ Arduino \ libraries \ src \ MTP_Teensy.cpp: In member function "uint32_t MTP_class:: GetObjectInfo (MTP_class:: MTPContainers)":
    There should be other lines with this, like what line, what is the error message?

    I copied your sources to my machine, (did reformat them ctrl+t in ide 2), As it uses MTP, had to choose USB type that includes MTP and then it built.

  5. #5
    Junior Member
    Join Date
    Nov 2022
    Posts
    5
    Everything department. define it was better. breaks off pieces of the recording while recording. It skips. Sandisk extreme u4 a2 v3 memory

Posting Permissions

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