diff --git a/third-party/LCDMenuLib2/src/LCDMenuLib2.h b/third-party/LCDMenuLib2/src/LCDMenuLib2.h index 56b61b0..8f43575 100644 --- a/third-party/LCDMenuLib2/src/LCDMenuLib2.h +++ b/third-party/LCDMenuLib2/src/LCDMenuLib2.h @@ -44,7 +44,7 @@ // ####################### // // you can change this parameters - //#define _LCDML_cfg_use_ram // enable this line when you want to use the ram mode + #define _LCDML_cfg_use_ram // enable this line when you want to use the ram mode // set the number of custom events (this could be a button ore something else) #define _LCDML_CE_cb_function_cnt 4 // this is the number of custom event callback functions which are supported diff --git a/third-party/LCDMenuLib2/src/LCDMenuLib2_macros.h b/third-party/LCDMenuLib2/src/LCDMenuLib2_macros.h index 4990e7d..9edca34 100644 --- a/third-party/LCDMenuLib2/src/LCDMenuLib2_macros.h +++ b/third-party/LCDMenuLib2/src/LCDMenuLib2_macros.h @@ -95,15 +95,12 @@ // stored in flash (Teensy-3.5/Teensy-3.6/Teensy-4.x) #define LCDML_langDef(name, lang, content) \ const char g_LCDML_DISP_lang_ ## lang ## _ ## name ##_var[] = {content} - #define LCDML_getCustomContent(lang, var, id) \ if(id < _LCDML_NO_FUNC) {\ strcpy(var, g_LCDML_DISP_lang_ ## lang ## _table[id]); \ } - #define LCDML_createCustomLang(N, lang) \ const char * g_LCDML_DISP_lang_ ## lang ## _table[] = { LCDML_DISP_lang_repeat(N, lang) } - #define LCDML_getCustomElementName(lang, var, element_id) \ if(element_id < _LCDML_NO_FUNC && (sizeof(g_LCDML_DISP_lang_ ## lang ## _table)-1) >= element_id) {\ strcpy(var, g_LCDML_DISP_lang_ ## lang ## _table[element_id]);\ @@ -112,15 +109,12 @@ // stored in flash (Arduino) #define LCDML_langDef(name, lang, content) \ const char g_LCDML_DISP_lang_ ## lang ## _ ## name ##_var[] PROGMEM = {content} - #define LCDML_getCustomContent(lang, var, id) \ if(id < _LCDML_NO_FUNC) {\ strcpy_P(var, (char*)pgm_read_word(&(g_LCDML_DISP_lang_ ## lang ## _table[id]))); \ } - #define LCDML_createCustomLang(N, lang) \ const char * const g_LCDML_DISP_lang_ ## lang ## _table[] PROGMEM = { LCDML_DISP_lang_repeat(N, lang) } - #define LCDML_getCustomElementName(lang, var, element_id) \ if(element_id < _LCDML_NO_FUNC && (sizeof(g_LCDML_DISP_lang_ ## lang ## _table)-1) >= element_id) {\ strcpy_P(var, (char*)pgm_read_word(&(g_LCDML_DISP_lang_ ## lang ## _table[element_id])));\ @@ -130,15 +124,12 @@ // stored in ram (esp, stm, other controllers) #define LCDML_langDef(name, lang, content) \ char g_LCDML_DISP_lang_ ## lang ## _ ## name ##_var[] = {content} - #define LCDML_getCustomContent(lang, var, id) \ if(id < _LCDML_NO_FUNC) {\ strcpy(var, g_LCDML_DISP_lang_ ## lang ## _table[id]); \ } - #define LCDML_createCustomLang(N, lang) \ char * g_LCDML_DISP_lang_ ## lang ## _table[] = { LCDML_DISP_lang_repeat(N, lang) } - #define LCDML_getCustomElementName(lang, var, element_id) \ if(element_id < _LCDML_NO_FUNC && (sizeof(g_LCDML_DISP_lang_ ## lang ## _table)-1) >= element_id) {\ strcpy(var, g_LCDML_DISP_lang_ ## lang ## _table[element_id]);\ diff --git a/third-party/MD_REncoder/ISSUE_TEMPLATE.md b/third-party/MD_REncoder/ISSUE_TEMPLATE.md old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/LICENSE b/third-party/MD_REncoder/LICENSE old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/PULL_REQUEST.md b/third-party/MD_REncoder/PULL_REQUEST.md old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/README.md b/third-party/MD_REncoder/README.md old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/examples/Polling/Polling.ino b/third-party/MD_REncoder/examples/Polling/Polling.ino old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/keywords.txt b/third-party/MD_REncoder/keywords.txt old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/library.properties b/third-party/MD_REncoder/library.properties old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/src/MD_REncoder.cpp b/third-party/MD_REncoder/src/MD_REncoder.cpp old mode 100644 new mode 100755 diff --git a/third-party/MD_REncoder/src/MD_REncoder.h b/third-party/MD_REncoder/src/MD_REncoder.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_Dexed/examples/synth_dexed.ino b/third-party/Synth_Dexed/examples/synth_dexed.ino new file mode 100755 index 0000000..e3132af --- /dev/null +++ b/third-party/Synth_Dexed/examples/synth_dexed.ino @@ -0,0 +1,69 @@ +#include +#include "synth_dexed.h" + +uint8_t fmpiano_sysex[156] = { + 95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune + 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5 + 95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4 + 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 07, // OP3 + 95, 50, 35, 78, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 07, 58, 00, 14, 00, 07, // OP2 + 96, 25, 25, 67, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 10, // OP1 + 94, 67, 95, 60, 50, 50, 50, 50, // 4 * pitch EG rates, 4 * pitch EG level + 04, 06, 00, // algorithm, feedback, osc sync + 34, 33, 00, 00, 00, 04, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform + 03, 24, // pitch_mod_sensitivity, transpose + 70, 77, 45, 80, 73, 65, 78, 79, 00, 00 // 10 * char for name ("DEFAULT ") +}; // FM-Piano + +AudioSynthDexed dexed(4,SAMPLE_RATE); // 4 voices max +AudioOutputI2S i2s1; +AudioControlSGTL5000 sgtl5000_1; +AudioConnection patchCord1(dexed, 0, i2s1, 0); +AudioConnection patchCord2(dexed, 0, i2s1, 1); + +void setup() +{ + AudioMemory(32); + + sgtl5000_1.enable(); + sgtl5000_1.lineOutLevel(29); + sgtl5000_1.dacVolumeRamp(); + sgtl5000_1.dacVolume(1.0); + sgtl5000_1.unmuteHeadphone(); + sgtl5000_1.unmuteLineout(); + sgtl5000_1.volume(0.8, 0.8); // Headphone volume +} + +void loop() +{ + static uint8_t count; + + if (count % 2 == 0) + { + dexed.loadInitVoice(); + } + else + { + dexed.loadVoiceParameters(fmpiano_sysex); + dexed.setTranspose(36); + } + + Serial.println("Key-Down"); + dexed.keydown(48, 100); + delay(100); + dexed.keydown(52, 100); + delay(100); + dexed.keydown(55, 100); + delay(100); + dexed.keydown(60, 100); + delay(2000); + + Serial.println("Key-Up"); + dexed.keyup(48); + dexed.keyup(52); + dexed.keyup(55); + dexed.keyup(60); + delay(2000); + + count++; +} diff --git a/third-party/Synth_Dexed/src/teensy_board_detection.h b/third-party/Synth_Dexed/src/teensy_board_detection.h new file mode 100755 index 0000000..83d55ef --- /dev/null +++ b/third-party/Synth_Dexed/src/teensy_board_detection.h @@ -0,0 +1,50 @@ +/* + MicroDexed + + MicroDexed is a port of the Dexed sound engine + (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield. + Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android + + (c)2018-2021 H. Wirtz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef TEENSY_BOARD_DETECTION_H_INCLUDED +#define TEENSY_BOARD_DETECTION_H_INCLUDED + + +// Teensy-4.x +#if defined(__IMXRT1062__) || defined (ARDUINO_TEENSY40) || defined (ARDUINO_TEENSY41) +#define TEENSY4 +#if defined (ARDUINO_TEENSY40) +#define TEENSY4_0 +#elif defined (ARDUINO_TEENSY41) +#define TEENSY4_1 +#endif +#endif + +// Teensy-3.6 +#if defined(__MK66FX1M0__) +# define TEENSY3_6 +#endif + +// Teensy-3.5 +#if defined (__MK64FX512__) +#define TEENSY3_5 +#endif + +#endif diff --git a/third-party/Synth_MDA_EPiano/.gitignore b/third-party/Synth_MDA_EPiano/.gitignore old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/LICENSE-GPL3.txt b/third-party/Synth_MDA_EPiano/LICENSE-GPL3.txt old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/README.md b/third-party/Synth_MDA_EPiano/README.md old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/examples/MDA_EP_SimplePlay/MDA_EP_SimplePlay.ino b/third-party/Synth_MDA_EPiano/examples/MDA_EP_SimplePlay/MDA_EP_SimplePlay.ino old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/library.properties b/third-party/Synth_MDA_EPiano/library.properties old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/src/mdaEPiano.cpp b/third-party/Synth_MDA_EPiano/src/mdaEPiano.cpp old mode 100644 new mode 100755 index e84c206..a57955e --- a/third-party/Synth_MDA_EPiano/src/mdaEPiano.cpp +++ b/third-party/Synth_MDA_EPiano/src/mdaEPiano.cpp @@ -38,14 +38,14 @@ mdaEPiano::mdaEPiano(uint8_t nvoices) // mdaEPiano::mdaEPiano(audioMasterCallbac uint8_t i=0; programs = new mdaEPianoProgram[NPROGS]; - fillpatch(i++, "Default", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); - fillpatch(i++, "Bright", 0.500f, 0.500f, 1.000f, 0.800f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.500f); - fillpatch(i++, "Mellow", 0.500f, 0.500f, 0.000f, 0.000f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); - fillpatch(i++, "Autopan", 0.500f, 0.500f, 0.500f, 0.500f, 0.250f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); - fillpatch(i++, "Tremolo", 0.500f, 0.500f, 0.500f, 0.500f, 0.750f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); - fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); - fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); - fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); + fillpatch(i++, (char *) "Default", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); + fillpatch(i++, (char *) "Bright", 0.500f, 0.500f, 1.000f, 0.800f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.500f); + fillpatch(i++, (char *) "Mellow", 0.500f, 0.500f, 0.000f, 0.000f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); + fillpatch(i++, (char *) "Autopan", 0.500f, 0.500f, 0.500f, 0.500f, 0.250f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); + fillpatch(i++, (char *) "Tremolo", 0.500f, 0.500f, 0.500f, 0.500f, 0.750f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); + fillpatch(i++, (char *) "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); + fillpatch(i++, (char *) "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); + fillpatch(i++, (char *) "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); setDecay(0.500f); setRelease(0.500f); diff --git a/third-party/Synth_MDA_EPiano/src/mdaEPiano.h b/third-party/Synth_MDA_EPiano/src/mdaEPiano.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/src/mdaEPianoData.h b/third-party/Synth_MDA_EPiano/src/mdaEPianoData.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/src/mdaEPianoDataXfade.h b/third-party/Synth_MDA_EPiano/src/mdaEPianoDataXfade.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/src/synth_epiano.h b/third-party/Synth_MDA_EPiano/src/synth_epiano.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/src/synth_mda_epiano.h b/third-party/Synth_MDA_EPiano/src/synth_mda_epiano.h old mode 100644 new mode 100755 diff --git a/third-party/Synth_MDA_EPiano/utility/xfade_generator.c b/third-party/Synth_MDA_EPiano/utility/xfade_generator.c old mode 100644 new mode 100755 diff --git a/third-party/TeensyTimerTool/library.json b/third-party/TeensyTimerTool/library.json index daafd1e..6852714 100644 --- a/third-party/TeensyTimerTool/library.json +++ b/third-party/TeensyTimerTool/library.json @@ -14,7 +14,7 @@ "maintainer": true }, "homepage": "https://github.com/luni64/TeensyTimerTool", - "version": "0.4.3", + "version": "0.4.4", "frameworks": "arduino", "platforms": "Teensy" } diff --git a/third-party/TeensyTimerTool/library.properties b/third-party/TeensyTimerTool/library.properties index 6110c25..c910ef7 100644 --- a/third-party/TeensyTimerTool/library.properties +++ b/third-party/TeensyTimerTool/library.properties @@ -1,5 +1,5 @@ name=TeensyTimerTool -version=0.4.3 +version=0.4.4 author=luni64 maintainer=luni64 sentence=Generic Interface to Teensy Timers diff --git a/third-party/TeensyTimerTool/src/API/baseTimer.h b/third-party/TeensyTimerTool/src/API/baseTimer.h index c8071f1..f33caf7 100644 --- a/third-party/TeensyTimerTool/src/API/baseTimer.h +++ b/third-party/TeensyTimerTool/src/API/baseTimer.h @@ -27,6 +27,7 @@ namespace TeensyTimerTool inline errorCode stop(); inline float getMaxPeriod() const; + inline float getRemainingTime() const; protected: BaseTimer(TimerGenerator *generator, bool periodic); @@ -102,4 +103,13 @@ namespace TeensyTimerTool postError(errorCode::notInitialized); return NAN; } + + float BaseTimer::getRemainingTime() const + { + if (timerChannel != nullptr) + return timerChannel->getRemainingTime(); + + postError(errorCode::notInitialized); + return NAN; + } } // namespace TeensyTimerTool \ No newline at end of file diff --git a/third-party/TeensyTimerTool/src/ITimerChannel.h b/third-party/TeensyTimerTool/src/ITimerChannel.h index e912008..95ac616 100644 --- a/third-party/TeensyTimerTool/src/ITimerChannel.h +++ b/third-party/TeensyTimerTool/src/ITimerChannel.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" +#include namespace TeensyTimerTool { @@ -20,6 +21,7 @@ namespace TeensyTimerTool virtual errorCode setPrescaler(int psc) { return postError(errorCode::notImplemented); } virtual float getMaxPeriod() const = 0; + virtual float getRemainingTime() const { postError(errorCode::notImplemented); return NAN; } virtual errorCode setPeriod(float microSeconds) { return postError(errorCode::notImplemented); }; virtual errorCode setNextPeriod(float microSeconds) { return postError(errorCode::notImplemented); }; virtual uint32_t getPeriod() { return 0; } diff --git a/third-party/TeensyTimerTool/src/TimerModules/TCK/TckChannel.h b/third-party/TeensyTimerTool/src/TimerModules/TCK/TckChannel.h index 0c6b7f7..89274fc 100644 --- a/third-party/TeensyTimerTool/src/TimerModules/TCK/TckChannel.h +++ b/third-party/TeensyTimerTool/src/TimerModules/TCK/TckChannel.h @@ -26,6 +26,7 @@ namespace TeensyTimerTool inline errorCode setPeriod(float us) override; float getMaxPeriod() const override { return tckCounter::getMaxMicros() / 1E6f; } // seconds + float getRemainingTime() const override { return (currentPeriod - (tckCounter::getCount() - startCnt)) / (float)TTT_F_CPU; } protected: inline bool tick(); diff --git a/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.cpp b/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.cpp index 61610d2..8802010 100644 --- a/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.cpp +++ b/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.cpp @@ -1,14 +1,9 @@ #include "tickCounters.h" #include "Arduino.h" +#include "types.h" #include "boardDef.h" -#if defined(TTT_TEENSY4X) -#define TTT_F_CPU F_CPU_ACTUAL -#else -#define TTT_F_CPU F_CPU -#endif - namespace TeensyTimerTool { //--------------------------------------------------------------------- diff --git a/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.h b/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.h index 7a8b8b4..225ea1b 100644 --- a/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.h +++ b/third-party/TeensyTimerTool/src/TimerModules/TCK/tickCounters.h @@ -41,7 +41,7 @@ namespace TeensyTimerTool // Software counter based on the 64bit period counter // of the built in real time clock (RTC). // Resolution: 1/32768s (30.5 µs) - // + // struct RtcCounter { using counter_t = uint64_t; diff --git a/third-party/TeensyTimerTool/src/types.h b/third-party/TeensyTimerTool/src/types.h index 9ee1f34..0cbe181 100644 --- a/third-party/TeensyTimerTool/src/types.h +++ b/third-party/TeensyTimerTool/src/types.h @@ -1,6 +1,7 @@ #pragma once #include "ErrorHandling/error_codes.h" #include "config.h" +#include "Arduino.h" #if not defined(PLAIN_VANILLA_CALLBACKS) @@ -27,3 +28,9 @@ namespace TeensyTimerTool extern errorCode postError(errorCode); } // namespace TeensyTimerTool #endif + +#if defined(TTT_TEENSY4X) +#define TTT_F_CPU F_CPU_ACTUAL +#else +#define TTT_F_CPU F_CPU +#endif diff --git a/third-party/TeensyVariablePlayback/CMakeLists.txt b/third-party/TeensyVariablePlayback/CMakeLists.txt index ac6d954..575c3f6 100644 --- a/third-party/TeensyVariablePlayback/CMakeLists.txt +++ b/third-party/TeensyVariablePlayback/CMakeLists.txt @@ -15,11 +15,13 @@ if (NOT DEFINED BUILD_FOR_LINUX) import_arduino_library(Wire ${DEPSPATH}/Wire utility) import_arduino_library(arm_math ${DEPSPATH}/arm_math/src) import_arduino_library(Audio ${DEPSPATH}/Audio utility) + import_arduino_library(LittleFS ${DEPSPATH}/LittleFS/src littlefs) add_subdirectory(src) add_subdirectory(examples) else() add_subdirectory(src) add_subdirectory(test) + #add_subdirectory(extras/soundio/playqueue) add_subdirectory(extras/soundio/save_raw) add_subdirectory(extras/soundio/save_raw_sd) add_subdirectory(extras/soundio/save_wav) diff --git a/third-party/TeensyVariablePlayback/README.md b/third-party/TeensyVariablePlayback/README.md index 034a4c1..04669e6 100644 --- a/third-party/TeensyVariablePlayback/README.md +++ b/third-party/TeensyVariablePlayback/README.md @@ -16,6 +16,11 @@ play 16-bit PCM raw or wav audio samples at variable playback rates on teensy * [sd classes on wikipedia](https://en.wikipedia.org/wiki/SD_card#cite_ref-93) ## updates +* 16/06/2022: v1.0.14: + * refactored code to generic classes + * improve memory leaks + * remove calls to StartUsingSPI(), StopUsingSPI(), __disable_irq(), __enable_irq() + * intergated with SerialFlash and LittleFS * 25/09/2021: v1.0.13: positionMillis() implemented for AudioPlaySdResmp * 25/08/2021: v1.0.12: Skip over RIFF tags in .wav header * 12/08/2021: v1.0.11: When playing a mono sample, transmit on both channels (credit to @atoktoto) @@ -103,10 +108,11 @@ graph G {
linux + You can run and test this code on your linux computer. You can write a teensy sketch, and with a few modifications, you can redirect the audio input and output to and from your soundcard. [Soundio](https://github.com/newdigate/teensy-audio-x86-stubs/tree/main/extras/soundio) bindings are optional, you can also run sketches and tests with no audio input or output. + You will need to install the following libraries. ```cmake``` ```gcc or llvm``` ```teensy-x86-stubs```[^](https://github.com/newdigate/teensy-x86-stubs) ```teensy-audio-x86-stubs```[^](https://github.com/newdigate/teensy-audio-x86-stubs) ```teensy-x86-sd-stubs```[^](https://github.com/newdigate/teensy-x86-sd-stubs) ```boost-test``` -By using stub libraries, we can compile teensy code to native device architecture. To a certain extent, this allows sketches and libraries to be developed, emulated, debugged and unit-tested using linux, on your local device or a build server. In this case I have a few basic tests for the ResamplingSdReader class. * install boost unit-test library: * linux: ```sudo apt-get install -yq libboost-test-dev``` * macos: ```brew install boost``` diff --git a/third-party/TeensyVariablePlayback/examples/CMakeLists.txt b/third-party/TeensyVariablePlayback/examples/CMakeLists.txt index b4d911d..8c0d1bc 100644 --- a/third-party/TeensyVariablePlayback/examples/CMakeLists.txt +++ b/third-party/TeensyVariablePlayback/examples/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.5) add_subdirectory(array) +add_subdirectory(LittleFS) add_subdirectory(sampleloader) add_subdirectory(sd_play_all) add_subdirectory(sd_raw) add_subdirectory(sd_wav) +add_subdirectory(SerialFlash) diff --git a/third-party/TeensyVariablePlayback/examples/LittleFS/CMakeLists.txt b/third-party/TeensyVariablePlayback/examples/LittleFS/CMakeLists.txt new file mode 100644 index 0000000..9951c6b --- /dev/null +++ b/third-party/TeensyVariablePlayback/examples/LittleFS/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(sd_raw) +set(CMAKE_CXX_STANDARD 14) +add_definitions(-DPROG_FLASH_SIZE=10000) +teensy_include_directories(../../src) +teensy_add_executable(littlefs_raw littlefs_raw.ino) +teensy_target_link_libraries(littlefs_raw teensy_variable_playback SD SdFat Audio LittleFS SPI SerialFlash cores Wire arm_math) diff --git a/third-party/TeensyVariablePlayback/examples/LittleFS/littlefs_raw.ino b/third-party/TeensyVariablePlayback/examples/LittleFS/littlefs_raw.ino new file mode 100644 index 0000000..4da8c36 --- /dev/null +++ b/third-party/TeensyVariablePlayback/examples/LittleFS/littlefs_raw.ino @@ -0,0 +1,95 @@ +// Plays a RAW (16-bit signed) PCM audio file at slower or faster rate +// this example requires an uSD-card inserted to teensy 3.6 with a file called DEMO.RAW +#include +#include +#include +#include + +LittleFS_Program myfs; +#define PROG_FLASH_SIZE 1024 * 1024 * 1 // Specify size to use of onboard Teensy Program Flash chip + +// GUItool: begin automatically generated code +AudioPlayLfsResmp playLfsRaw1(myfs); //xy=324,457 +AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906 +AudioConnection patchCord1(playLfsRaw1, 0, i2s2, 0); +AudioConnection patchCord2(playLfsRaw1, 0, i2s2, 1); +AudioControlSGTL5000 audioShield; +// GUItool: end automatically generated code + +#define A14 10 + +const char* _filename = "DEMO.RAW"; +const int analogInPin = A14; +unsigned long lastSamplePlayed = 0; +uint32_t diskSize; + +double getPlaybackRate(int16_t analog) { //analog: 0..1023 + return (analog - 512.0) / 512.0; +} + +void setup() { + analogReference(0); + pinMode(analogInPin, INPUT_DISABLE); // i.e. Analog + + // see if the Flash is present and can be initialized: + // lets check to see if the T4 is setup for security first + #if ARDUINO_TEENSY40 + if ((IOMUXC_GPR_GPR11 & 0x100) == 0x100) { + //if security is active max disk size is 960x1024 + if (PROG_FLASH_SIZE > 960 * 1024) { + diskSize = 960 * 1024; + Serial.printf("Security Enables defaulted to %u bytes\n", diskSize); + } else { + diskSize = PROG_FLASH_SIZE; + Serial.printf("Security Not Enabled using %u bytes\n", diskSize); + } + } + #else + diskSize = PROG_FLASH_SIZE; + #endif + + // checks that the LittFS program has started with the disk size specified + if (!myfs.begin(diskSize)) { + Serial.printf("Error starting %s\n", "PROGRAM FLASH DISK"); + while (1) { + // Error, so don't do anything more - stay stuck here + } + } + Serial.println("LittleFS initialized."); + + audioShield.enable(); + audioShield.volume(0.5); + + playLfsRaw1.enableInterpolation(true); + int newsensorValue = analogRead(analogInPin); + playLfsRaw1.setPlaybackRate(getPlaybackRate(newsensorValue)); + + AudioMemory(24); +} + +void loop() { + + int newsensorValue = analogRead(analogInPin); + playLfsRaw1.setPlaybackRate(getPlaybackRate(newsensorValue)); + + unsigned currentMillis = millis(); + if (currentMillis > lastSamplePlayed + 500) { + if (!playLfsRaw1.isPlaying()) { + playLfsRaw1.playRaw(_filename, 1); + lastSamplePlayed = currentMillis; + + Serial.print("Memory: "); + Serial.print(AudioMemoryUsage()); + Serial.print(","); + Serial.print(AudioMemoryUsageMax()); + Serial.println(); + } + } + delay(10); +} + + +namespace std { + void __throw_bad_function_call() {} + void __throw_length_error(char const*) {} +} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/examples/SerialFlash/CMakeLists.txt b/third-party/TeensyVariablePlayback/examples/SerialFlash/CMakeLists.txt new file mode 100644 index 0000000..f08e24c --- /dev/null +++ b/third-party/TeensyVariablePlayback/examples/SerialFlash/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(serialflash_raw) +set(CMAKE_CXX_STANDARD 14) +add_definitions(-DPROG_FLASH_SIZE=10000) +teensy_include_directories(../../src) +teensy_add_executable(serialflash serialflash.ino) +teensy_target_link_libraries(serialflash teensy_variable_playback Audio SerialFlash SPI cores Wire arm_math) diff --git a/third-party/TeensyVariablePlayback/examples/SerialFlash/serialflash.ino b/third-party/TeensyVariablePlayback/examples/SerialFlash/serialflash.ino new file mode 100644 index 0000000..ea4e8f0 --- /dev/null +++ b/third-party/TeensyVariablePlayback/examples/SerialFlash/serialflash.ino @@ -0,0 +1,79 @@ +// Plays a RAW (16-bit signed) PCM audio file at slower or faster rate +// this example requires an uSD-card inserted to teensy 3.6 with a file called DEMO.RAW +#include +#include +#include +#include + +#define CSPIN 6 + +SerialFlashChip myfs; + +// GUItool: begin automatically generated code +AudioPlaySerialFlashResmp playSerialFlash1(myfs); //xy=324,457 +AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906 +AudioConnection patchCord1(playSerialFlash1, 0, i2s2, 0); +AudioConnection patchCord2(playSerialFlash1, 0, i2s2, 1); +AudioControlSGTL5000 audioShield; +// GUItool: end automatically generated code + +#define A14 10 + +const char* _filename = "DEMO.RAW"; +const int analogInPin = A14; +unsigned long lastSamplePlayed = 0; +uint32_t diskSize; + +double getPlaybackRate(int16_t analog) { //analog: 0..1023 + return (analog - 512.0) / 512.0; +} + +void setup() { + analogReference(0); + pinMode(analogInPin, INPUT_DISABLE); // i.e. Analog + + + if (!SerialFlash.begin(CSPIN)) { + while (1) { + Serial.println(F("Unable to access SPI Flash chip")); + delay(1000); + } + } + Serial.println("SerialFlash initialized."); + + audioShield.enable(); + audioShield.volume(0.5); + + playSerialFlash1.enableInterpolation(true); + int newsensorValue = analogRead(analogInPin); + playSerialFlash1.setPlaybackRate(getPlaybackRate(newsensorValue)); + + AudioMemory(24); +} + +void loop() { + + int newsensorValue = analogRead(analogInPin); + playSerialFlash1.setPlaybackRate(getPlaybackRate(newsensorValue)); + + unsigned currentMillis = millis(); + if (currentMillis > lastSamplePlayed + 500) { + if (!playSerialFlash1.isPlaying()) { + playSerialFlash1.playRaw(_filename, 1); + lastSamplePlayed = currentMillis; + + Serial.print("Memory: "); + Serial.print(AudioMemoryUsage()); + Serial.print(","); + Serial.print(AudioMemoryUsageMax()); + Serial.println(); + } + } + delay(10); +} + + +namespace std { + void __throw_bad_function_call() {} + void __throw_length_error(char const*) {} +} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/library.json b/third-party/TeensyVariablePlayback/library.json index dc465b6..8c8b9c1 100644 --- a/third-party/TeensyVariablePlayback/library.json +++ b/third-party/TeensyVariablePlayback/library.json @@ -5,7 +5,7 @@ "keywords": "sound, audio, sample, resample, pitch, interpolation, legrange, sampler, playback, speed", "description": "Teensy Variable Playback", "url": "https://github.com/newdigate/teensy-variable-playback", - "version": "1.0.13", + "version": "1.0.14", "export": { "exclude": [ ".vscode", diff --git a/third-party/TeensyVariablePlayback/library.properties b/third-party/TeensyVariablePlayback/library.properties index c9df1cb..a75ee4c 100644 --- a/third-party/TeensyVariablePlayback/library.properties +++ b/third-party/TeensyVariablePlayback/library.properties @@ -1,5 +1,5 @@ name=TeensyVariablePlayback -version=1.0.13 +version=1.0.14 author=Nic Newdigate maintainer=Nic Newdigate sentence=Play samples at variable pitch using Teensy Audio Library diff --git a/third-party/TeensyVariablePlayback/src/CMakeLists.txt b/third-party/TeensyVariablePlayback/src/CMakeLists.txt index 365b7a3..cbb2657 100644 --- a/third-party/TeensyVariablePlayback/src/CMakeLists.txt +++ b/third-party/TeensyVariablePlayback/src/CMakeLists.txt @@ -3,12 +3,8 @@ project(teensy_variable_playback C CXX) set(teensy_variable_playback_VERSION 1.0.0) set(CMAKE_CXX_STANDARD 11) set(SOURCE_FILES - playsdresmp.cpp - ResamplingSdReader.cpp - ResamplingArrayReader.cpp - playarrayresmp.cpp interpolation.cpp - IndexableFile.cpp) + ) set(HEADER_FILES loop_type.h @@ -17,10 +13,19 @@ set(HEADER_FILES ResamplingSdReader.h waveheaderparser.h ResamplingArrayReader.h + ResamplingReader.h playarrayresmp.h interpolation.h TeensyVariablePlayback.h - IndexableFile.h) + IndexableFile.h + IndexableSerialFlashFile.h + IndexableLittleFSFile.h + IndexableSDFile.h + ResamplingLfsReader.h + ResamplingSerialFlashReader.h + playlfsresmp.h + playserialflashresmp.h + ) #set(CMAKE_VERBOSE_MAKEFILE 1) if (NOT DEFINED TEENSY_VERSION) diff --git a/third-party/TeensyVariablePlayback/src/IndexableFile.h b/third-party/TeensyVariablePlayback/src/IndexableFile.h index 30f423e..a4d4ea6 100644 --- a/third-party/TeensyVariablePlayback/src/IndexableFile.h +++ b/third-party/TeensyVariablePlayback/src/IndexableFile.h @@ -18,19 +18,25 @@ constexpr bool isPowerOf2(size_t value){ return !(value == 0) && !(value & (value - 1)); } -template // BUFFER_SIZE needs to be a power of two +template // BUFFER_SIZE needs to be a power of two class IndexableFile { public: static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); + + virtual TFile open(const char *filename) = 0; static constexpr size_t element_size = sizeof(int16_t); size_t buffer_to_index_shift; IndexableFile(const char *filename) : _buffers(), - buffer_to_index_shift(log2(BUFFER_SIZE)) { + buffer_to_index_shift(log2(BUFFER_SIZE)) + { _filename = new char[strlen(filename)+1] {0}; memcpy(_filename, filename, strlen(filename)); - _file = SD.open(_filename); + } + + virtual ~IndexableFile() { + close(); } int16_t &operator[](int i) { @@ -53,9 +59,7 @@ public: #ifndef TEENSYDUINO if (!_file.available()){ _file.close(); - __disable_irq(); - _file = SD.open(_filename); - __enable_irq(); + _file = open(_filename); } #endif next->buffer_size = bytesRead; @@ -66,15 +70,24 @@ public: } void close() { - if (_file) + if (_file.available()) { _file.close(); + } + + for (auto && x : _buffers){ + delete [] x->buffer; + delete x; + } + _buffers.clear(); - if (_filename) + if (_filename != nullptr) { delete [] _filename; - _filename = nullptr; + _filename = nullptr; + } } -private: - File _file; + +protected: + TFile _file; char *_filename; std::vector _buffers; @@ -90,4 +103,6 @@ private: } + + #endif diff --git a/third-party/TeensyVariablePlayback/src/IndexableLittleFSFile.h b/third-party/TeensyVariablePlayback/src/IndexableLittleFSFile.h new file mode 100644 index 0000000..e60ec3b --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/IndexableLittleFSFile.h @@ -0,0 +1,43 @@ +#ifndef TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H +#define TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H + +#include +#include "IndexableFile.h" +#include +#include + +namespace newdigate { + +template // BUFFER_SIZE needs to be a power of two +class IndexableLittleFSFile : public IndexableFile { +public: + static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); + + IndexableLittleFSFile(LittleFS &fs, const char *filename) : + IndexableFile(filename), + _myFS(fs) + { + IndexableFile::_file = _myFS.open(filename); + } + + File open(const char *filename) override { + return _myFS.open(filename); + } + + virtual ~IndexableLittleFSFile() { + IndexableFile::close(); + } + + int16_t &operator[](int i) { + return IndexableFile::operator[](i); + } + +private: + LittleFS &_myFS; +}; + +} + + + +#endif //TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H diff --git a/third-party/TeensyVariablePlayback/src/IndexableSDFile.h b/third-party/TeensyVariablePlayback/src/IndexableSDFile.h new file mode 100644 index 0000000..83eb218 --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/IndexableSDFile.h @@ -0,0 +1,38 @@ +#ifndef TEENSY_RESAMPLING_INDEXABLESD_FILE_H +#define TEENSY_RESAMPLING_INDEXABLESD_FILE_H + +#include +#include "IndexableFile.h" +#include +#include + +namespace newdigate { + +template // BUFFER_SIZE needs to be a power of two +class IndexableSDFile : public IndexableFile { +public: + static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); + + IndexableSDFile(const char *filename) : + IndexableFile(filename) { + IndexableFile::_file = open(filename); + } + + File open(const char *filename) override { + return SD.open(filename); + } + + virtual ~IndexableSDFile() { + IndexableFile::close(); + } + + int16_t &operator[](int i) { + return IndexableFile::operator[](i); + } +}; + +} + + + +#endif diff --git a/third-party/TeensyVariablePlayback/src/IndexableSerialFlashFile.h b/third-party/TeensyVariablePlayback/src/IndexableSerialFlashFile.h new file mode 100644 index 0000000..fc401aa --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/IndexableSerialFlashFile.h @@ -0,0 +1,43 @@ +#ifndef TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H +#define TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H + +#include +#include "IndexableFile.h" +#include +#include + +namespace newdigate { + +template // BUFFER_SIZE needs to be a power of two +class IndexableSerialFlashFile : public IndexableFile { +public: + static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); + + IndexableSerialFlashFile(SerialFlashChip &fs, const char *filename) : + IndexableFile(filename), + _myFS(fs) + { + IndexableFile::_file = _myFS.open(filename); + } + + SerialFlashFile open(const char *filename) override { + return _myFS.open(filename); + } + + virtual ~IndexableSerialFlashFile() { + IndexableFile::close(); + } + + int16_t &operator[](int i) { + return IndexableFile::operator[](i); + } + +private: + SerialFlashChip &_myFS; +}; + +} + + + +#endif //TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H diff --git a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h b/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h index bc2b5f5..5e87fb2 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h +++ b/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h @@ -6,115 +6,44 @@ #include #include "loop_type.h" #include "interpolation.h" +#include "ResamplingReader.h" -class ResamplingArrayReader { -public: - ResamplingArrayReader() { - - } - - void begin(void); - bool playRaw(int16_t *array, uint32_t length, uint16_t numChannels); - bool playWav(int16_t *array, uint32_t length); - bool play(); - void stop(void); - bool isPlaying(void) { return _playing; } - - unsigned int read(void **buf, uint16_t nbyte); - bool readNextValue(int16_t *value, uint16_t channelNumber); - - void setPlaybackRate(double f) { - _playbackRate = f; - if (f < 0.0 && _bufferPosition == 0) { - //_file.seek(_file_size); - _bufferPosition = _file_size/2 - _numChannels; - } - } - - float playbackRate() { - return _playbackRate; - } - - void loop(uint32_t numSamples) { - __disable_irq(); - _loop_start = _bufferPosition; - _loop_finish = _bufferPosition + numSamples * _numChannels; - _loopType = loop_type::looptype_repeat; - __enable_irq(); - } +namespace newdigate { - void setLoopType(loop_type loopType) - { - _loopType = loopType; - } - - loop_type getLoopType(){ - return _loopType; - } - - int available(void); - void reset(void); - void close(void); - - void setLoopStart(uint32_t loop_start) { - _loop_start = _header_offset + (loop_start * _numChannels); +class ResamplingArrayReader : public ResamplingReader { +public: + ResamplingArrayReader() : + ResamplingReader() { + } + + virtual ~ResamplingArrayReader() { } - void setLoopFinish(uint32_t loop_finish) { - // sample number, (NOT byte number) - _loop_finish = _header_offset + (loop_finish * _numChannels); + int16_t getSourceBufferValue(long index) override { + return _sourceBuffer[index]; } - void setInterpolationType(ResampleInterpolationType interpolationType) { - if (interpolationType != _interpolationType) { - _interpolationType = interpolationType; - initializeInterpolationPoints(); - } + int available(void) { + return _playing; } - int16_t getNumChannels() { - return _numChannels; + int16_t* createSourceBuffer() override { + return _sourceBuffer; } - void setNumChannels(uint16_t numChannels) { - if (numChannels != _numChannels) { - _numChannels = numChannels; - initializeInterpolationPoints(); + void close(void) override { + if (_playing) { + stop(); + deleteInterpolationPoints(); } } - void setHeaderSizeInBytes(uint32_t headerSizeInBytes) { - _header_offset = headerSizeInBytes / 2; - if (_bufferPosition < _header_offset) { - if (_playbackRate >= 0) { - _bufferPosition = _header_offset; - } else - _bufferPosition = _loop_finish - _numChannels; - } + File open(char *filename) override { + return File(); } - -private: - volatile bool _playing = false; - - int32_t _file_size; - int32_t _header_offset = 0; // == (header size in bytes ) / 2 - - double _playbackRate = 1.0; - double _remainder = 0.0; - loop_type _loopType = looptype_none; - int _bufferPosition = 0; - int32_t _loop_start = 0; - int32_t _loop_finish = 0; - int16_t _numChannels = -1; - uint16_t _numInterpolationPointsChannels = 0; - int16_t *_sourceBuffer = nullptr; - - ResampleInterpolationType _interpolationType = ResampleInterpolationType::resampleinterpolation_none; - unsigned int _numInterpolationPoints = 0; - InterpolationData **_interpolationPoints = nullptr; - void initializeInterpolationPoints(void); - void deleteInterpolationPoints(void); +protected: }; +} #endif //TEENSYAUDIOLIBRARY_RESAMPLINGARRAYREADER_H diff --git a/third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h b/third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h new file mode 100644 index 0000000..d2ce8dc --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h @@ -0,0 +1,78 @@ +// +// Created by Nicholas Newdigate on 10/02/2019. +// + +#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H +#define TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H + +#include +#include "IndexableLittleFSFile.h" +#include "ResamplingReader.h" +#include "LittleFS.h" + +#define RESAMPLE_BUFFER_SAMPLE_SIZE 128 + +#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 + +namespace newdigate { + +class ResamplingLfsReader : public ResamplingReader< IndexableLittleFSFile<128, 2>, File > { +public: + ResamplingLfsReader(LittleFS &fs) : + ResamplingReader(), + _myFS(fs) + { + } + virtual ~ResamplingLfsReader() { + } + + int16_t getSourceBufferValue(long index) override { + return (*_sourceBuffer)[index]; + } + + int available(void) + { + return _playing; + } + + File open(char *filename) override { + return _myFS.open(filename); + } + + void close(void) override + { + if (_playing) + stop(); + if (_sourceBuffer != nullptr) { + _sourceBuffer->close(); + delete _sourceBuffer; + _sourceBuffer = nullptr; + } + if (_filename != nullptr) { + delete [] _filename; + _filename = nullptr; + } + deleteInterpolationPoints(); + } + + IndexableLittleFSFile<128, 2>* createSourceBuffer() override { + return new IndexableLittleFSFile<128, 2>(_myFS, _filename); + } + + uint32_t positionMillis(void) { + if (_file_size == 0) return 0; + + return (uint32_t) (( (double)_bufferPosition * lengthMillis() ) / (double)(_file_size/2)); + } + + uint32_t lengthMillis(void) { + return ((uint64_t)_file_size * B2M) >> 32; + } + +protected: + LittleFS &_myFS; +}; + +} + +#endif //TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H diff --git a/third-party/TeensyVariablePlayback/src/ResamplingReader.h b/third-party/TeensyVariablePlayback/src/ResamplingReader.h new file mode 100644 index 0000000..742bc6f --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/ResamplingReader.h @@ -0,0 +1,500 @@ +#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H +#define TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H + +#include +#include +#include "loop_type.h" +#include "interpolation.h" +#include "waveheaderparser.h" + +namespace newdigate { + +template +class ResamplingReader { +public: + ResamplingReader() { + } + virtual ~ResamplingReader() { + } + + virtual TFile open(char *filename) = 0; + virtual TArray* createSourceBuffer() = 0; + virtual int16_t getSourceBufferValue(long index) = 0; + virtual void close(void) = 0; + + void begin(void) + { + if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { + initializeInterpolationPoints(); + } + _playing = false; + _bufferPosition = _header_offset; + _file_size = 0; + } + + bool playRaw(TArray *array, uint32_t length, uint16_t numChannels) + { + _sourceBuffer = array; + stop(); + + _header_offset = 0; + _file_size = length * 2; + _loop_start = 0; + _loop_finish = length; + setNumChannels(numChannels); + + reset(); + _playing = true; + return true; + } + + bool playRaw(TArray *array, uint16_t numChannels) { + return playRaw(array, false, numChannels); + } + + bool playWav(TArray *array, uint32_t length) { + return playRaw(array, true); + } + + bool play(const char *filename, bool isWave, uint16_t numChannelsIfRaw = 0) + { + close(); + + if (!isWave) // if raw file, then hardcode the numChannels as per the parameter + setNumChannels(numChannelsIfRaw); + + _filename = new char[strlen(filename)+1] {0}; + memcpy(_filename, filename, strlen(filename) + 1); + + TFile file = open(_filename); + if (!file) { + Serial.printf("Not able to open file: %s\n", _filename); + if (_filename) delete [] _filename; + _filename = nullptr; + return false; + } + + _file_size = file.size(); + if (isWave) { + wav_header wav_header; + wav_data_header data_header; + + WaveHeaderParser wavHeaderParser; + char buffer[36]; + size_t bytesRead = file.read(buffer, 36); + + wavHeaderParser.readWaveHeaderFromBuffer((const char *) buffer, wav_header); + if (wav_header.bit_depth != 16) { + Serial.printf("Needs 16 bit audio! Aborting.... (got %d)", wav_header.bit_depth); + return false; + } + setNumChannels(wav_header.num_channels); + + bytesRead = file.read(buffer, 8); + unsigned infoTagsSize; + if (!wavHeaderParser.readInfoTags((unsigned char *)buffer, 0, infoTagsSize)) + { + Serial.println("Not able to read header! Aborting..."); + return false; + } + + file.seek(36 + infoTagsSize); + bytesRead = file.read(buffer, 8); + + if (!wavHeaderParser.readDataHeader((unsigned char *)buffer, 0, data_header)) { + Serial.println("Not able to read header! Aborting..."); + return false; + } + + _header_offset = (44 + infoTagsSize) / 2; + _loop_finish = ((data_header.data_bytes) / 2) + _header_offset; + } else + _loop_finish = _file_size / 2; + + file.close(); + + if (_file_size <= _header_offset * sizeof(int16_t)) { + _playing = false; + if (_filename) delete [] _filename; + _filename = nullptr; + Serial.printf("Wave file contains no samples: %s\n", filename); + return false; + } + + _sourceBuffer = createSourceBuffer(); + _loop_start = _header_offset; + + reset(); + _playing = true; + return true; + } + + bool playRaw(const char *filename, uint16_t numChannelsIfRaw){ + return play(filename, false, numChannelsIfRaw); + } + + bool playWav(const char *filename){ + return play(filename, true); + } + + bool play() + { + stop(); + reset(); + _playing = true; + return true; + } + + void stop(void) + { + if (_playing) { + _playing = false; + } + } + + bool isPlaying(void) { return _playing; } + + unsigned int read(void **buf, uint16_t nsamples) { + if (!_playing) return 0; + + int16_t *index[_numChannels]; + unsigned int count = 0; + for (int channel=0; channel < _numChannels; channel++) { + index[channel] = (int16_t*)buf[channel]; + } + + while (count < nsamples) { + for (int channel=0; channel < _numChannels; channel++) { + if (readNextValue(index[channel], channel)) { + if (channel == _numChannels - 1) + count++; + index[channel]++; + } + else { + // we have reached the end of the file + + switch (_loopType) { + case looptype_repeat: + { + if (_playbackRate >= 0.0) + _bufferPosition = _loop_start; + else + _bufferPosition = _loop_finish - _numChannels; + + break; + } + + case looptype_pingpong: + { + if (_playbackRate >= 0.0) { + _bufferPosition = _loop_finish - _numChannels; + //printf("switching to reverse playback...\n"); + } + else { + _bufferPosition = _header_offset; + //printf("switching to forward playback...\n"); + } + _playbackRate = -_playbackRate; + break; + } + + case looptype_none: + default: + { + //Serial.printf("end of loop...\n"); + /* no looping - return the number of (resampled) bytes returned... */ + close(); + return count; + } + } + } + } + } + return count; + } + + // read the sample value for given channel and store it at the location pointed to by the pointer 'value' + bool readNextValue(int16_t *value, uint16_t channel) { + if (_playbackRate >= 0 ) { + //forward playback + if (_bufferPosition >= _loop_finish ) + return false; + + } else if (_playbackRate < 0) { + // reverse playback + if (_bufferPosition < _header_offset) + return false; + } + + + int16_t result = getSourceBufferValue(_bufferPosition + channel); + if (_interpolationType == ResampleInterpolationType::resampleinterpolation_linear) { + + double abs_remainder = abs(_remainder); + if (abs_remainder > 0.0) { + + if (_playbackRate > 0) { + if (_remainder - _playbackRate < 0.0){ + // we crossed over a whole number, make sure we update the samples for interpolation + + if (_playbackRate > 1.0) { + // need to update last sample + _interpolationPoints[channel][1].y = getSourceBufferValue(_bufferPosition-_numChannels); + } + + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = result; + if (_numInterpolationPoints < 2) + _numInterpolationPoints++; + } + } + else if (_playbackRate < 0) { + if (_remainder - _playbackRate > 0.0){ + // we crossed over a whole number, make sure we update the samples for interpolation + + if (_playbackRate < -1.0) { + // need to update last sample + _interpolationPoints[channel][1].y = getSourceBufferValue(_bufferPosition+_numChannels); + } + + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = result; + if (_numInterpolationPoints < 2) + _numInterpolationPoints++; + } + } + + if (_numInterpolationPoints > 1) { + result = abs_remainder * _interpolationPoints[channel][1].y + (1.0 - abs_remainder) * _interpolationPoints[channel][0].y; + //Serial.printf("[%f]\n", interpolation); + } + } else { + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = result; + if (_numInterpolationPoints < 2) + _numInterpolationPoints++; + + result =_interpolationPoints[channel][0].y; + //Serial.printf("%f\n", result); + } + } + else if (_interpolationType == ResampleInterpolationType::resampleinterpolation_quadratic) { + double abs_remainder = abs(_remainder); + if (abs_remainder > 0.0) { + if (_playbackRate > 0) { + if (_remainder - _playbackRate < 0.0){ + // we crossed over a whole number, make sure we update the samples for interpolation + int numberOfSamplesToUpdate = - floor(_remainder - _playbackRate); + if (numberOfSamplesToUpdate > 4) + numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples + for (int i=numberOfSamplesToUpdate; i > 0; i--) { + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; + _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; + _interpolationPoints[channel][3].y = getSourceBufferValue(_bufferPosition-(i*_numChannels)+1+channel); + if (_numInterpolationPoints < 4) _numInterpolationPoints++; + } + } + } + else if (_playbackRate < 0) { + if (_remainder - _playbackRate > 0.0){ + // we crossed over a whole number, make sure we update the samples for interpolation + int numberOfSamplesToUpdate = ceil(_remainder - _playbackRate); + if (numberOfSamplesToUpdate > 4) + numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples + for (int i=numberOfSamplesToUpdate; i > 0; i--) { + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; + _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; + _interpolationPoints[channel][3].y = getSourceBufferValue(_bufferPosition+(i*_numChannels)-1+channel); + if (_numInterpolationPoints < 4) _numInterpolationPoints++; + } + } + } + + if (_numInterpolationPoints >= 4) { + //int16_t interpolation = interpolate(_interpolationPoints, 1.0 + abs_remainder, 4); + int16_t interpolation + = fastinterpolate( + _interpolationPoints[channel][0].y, + _interpolationPoints[channel][1].y, + _interpolationPoints[channel][2].y, + _interpolationPoints[channel][3].y, + 1.0 + abs_remainder); + result = interpolation; + //Serial.printf("[%f]\n", interpolation); + } else + result = 0; + } else { + _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; + _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; + _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; + _interpolationPoints[channel][3].y = result; + if (_numInterpolationPoints < 4) { + _numInterpolationPoints++; + result = 0; + } else + result = _interpolationPoints[channel][1].y; + //Serial.printf("%f\n", result); + } + } + + if (channel == _numChannels - 1) { + _remainder += _playbackRate; + + auto delta = static_cast(_remainder); + _remainder -= static_cast(delta); + _bufferPosition += (delta * _numChannels); + } + + *value = result; + return true; + } + + void setPlaybackRate(double f) { + _playbackRate = f; + if (f < 0.0 && _bufferPosition == 0) { + //_file.seek(_file_size); + _bufferPosition = _file_size/2 - _numChannels; + } + } + + float playbackRate() { + return _playbackRate; + } + + void loop(uint32_t numSamples) { + _loop_start = _bufferPosition; + _loop_finish = _bufferPosition + numSamples * _numChannels; + _loopType = loop_type::looptype_repeat; + } + + void setLoopType(loop_type loopType) + { + _loopType = loopType; + } + + loop_type getLoopType() { + return _loopType; + } + + int available(void) { + return _playing; + } + + void reset(void) { + if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { + initializeInterpolationPoints(); + } + _numInterpolationPoints = 0; + if (_playbackRate > 0.0) { + // forward playabck - set _file_offset to first audio block in file + _bufferPosition = _header_offset; + } else { + // reverse playback - forward _file_offset to last audio block in file + _bufferPosition = _loop_finish - _numChannels; + } + } + + void setLoopStart(uint32_t loop_start) { + _loop_start = _header_offset + (loop_start * _numChannels); + } + + void setLoopFinish(uint32_t loop_finish) { + // sample number, (NOT byte number) + _loop_finish = _header_offset + (loop_finish * _numChannels); + } + + void setInterpolationType(ResampleInterpolationType interpolationType) { + if (interpolationType != _interpolationType) { + _interpolationType = interpolationType; + initializeInterpolationPoints(); + } + } + + int16_t getNumChannels() { + return _numChannels; + } + + void setNumChannels(uint16_t numChannels) { + if (numChannels != _numChannels) { + _numChannels = numChannels; + initializeInterpolationPoints(); + } + } + + void setHeaderSizeInBytes(uint32_t headerSizeInBytes) { + _header_offset = headerSizeInBytes / 2; + if (_bufferPosition < _header_offset) { + if (_playbackRate >= 0) { + _bufferPosition = _header_offset; + } else + _bufferPosition = _loop_finish - _numChannels; + } + } + + #define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 + uint32_t positionMillis() + { + return ((uint64_t)_file_size * B2M) >> 32; + } + + uint32_t lengthMillis() + { + return ((uint64_t)_file_size * B2M) >> 32; + } + +protected: + volatile bool _playing = false; + + int32_t _file_size; + int32_t _header_offset = 0; // == (header size in bytes ) / 2 + + double _playbackRate = 1.0; + double _remainder = 0.0; + loop_type _loopType = looptype_none; + int _bufferPosition = 0; + int32_t _loop_start = 0; + int32_t _loop_finish = 0; + int16_t _numChannels = -1; + uint16_t _numInterpolationPointsChannels = 0; + char *_filename = nullptr; + TArray *_sourceBuffer = nullptr; + + ResampleInterpolationType _interpolationType = ResampleInterpolationType::resampleinterpolation_none; + unsigned int _numInterpolationPoints = 0; + InterpolationData **_interpolationPoints = nullptr; + + void initializeInterpolationPoints(void) { + if (_numChannels < 0) + return; + + deleteInterpolationPoints(); + _interpolationPoints = new InterpolationData*[_numChannels]; + for (int channel=0; channel < _numChannels; channel++) { + InterpolationData *interpolation = new InterpolationData[4]; + interpolation[0].y = 0.0; + interpolation[1].y = 0.0; + interpolation[2].y = 0.0; + interpolation[3].y = 0.0; + _interpolationPoints[channel] = interpolation ; + } + _numInterpolationPointsChannels = _numChannels; + } + + void deleteInterpolationPoints(void) + { + if (!_interpolationPoints) return; + for (int i=0; i<_numInterpolationPointsChannels; i++) { + delete [] _interpolationPoints[i]; + } + delete [] _interpolationPoints; + _interpolationPoints = nullptr; + _numInterpolationPointsChannels = 0; + } + +}; + +} + +#endif //TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h index dbda611..77c955d 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h +++ b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h @@ -10,87 +10,56 @@ #include "spi_interrupt.h" #include "loop_type.h" #include "interpolation.h" -#include "IndexableFile.h" +#include "IndexableSDFile.h" +#include "ResamplingReader.h" #define RESAMPLE_BUFFER_SAMPLE_SIZE 128 #define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 -class ResamplingSdReader { +namespace newdigate { + +class ResamplingSdReader : public ResamplingReader< IndexableSDFile<128, 2>, File > { public: - ResamplingSdReader() { + ResamplingSdReader() : + ResamplingReader() + { } - - void begin(void); - bool playRaw(const char *filename, uint16_t numChannels); - bool playWav(const char *filename); - - bool play(); - void stop(void); - bool isPlaying(void) { return _playing; } - - unsigned int read(void **buf, uint16_t nbyte); - bool readNextValue(int16_t *value, uint16_t channelNumber); - - void setPlaybackRate(double f) { - _playbackRate = f; - if (f < 0.0 && _bufferPosition == 0) { - //_file.seek(_file_size); - _bufferPosition = _file_size / 2 - _numChannels; - } + + virtual ~ResamplingSdReader() { } - double playbackRate() { - return _playbackRate; + int16_t getSourceBufferValue(long index) override { + return (*_sourceBuffer)[index]; } - void setLoopType(loop_type loopType) + int available(void) { - _loopType = loopType; - } - - loop_type getLoopType(){ - return _loopType; - } - - int available(void); - void reset(void); - void close(void); - - void setLoopStart(uint32_t loop_start) { - _loop_start = _header_offset + (loop_start * _numChannels); + return _playing; } - void setLoopFinish(uint32_t loop_finish) { - // sample number, (NOT byte number) - _loop_finish = _header_offset + (loop_finish * _numChannels); + File open(char *filename) override { + return SD.open(filename); } - void setInterpolationType(ResampleInterpolationType interpolationType) { - if (interpolationType != _interpolationType) { - _interpolationType = interpolationType; - initializeInterpolationPoints(); + void close(void) override + { + if (_playing) + stop(); + if (_sourceBuffer != nullptr) { + _sourceBuffer->close(); + delete _sourceBuffer; + _sourceBuffer = nullptr; } - } - - int16_t getNumChannels() { - return _numChannels; - } - - void setNumChannels(uint16_t numChannels) { - if (numChannels != _numChannels) { - _numChannels = numChannels; - initializeInterpolationPoints(); + if (_filename != nullptr) { + delete [] _filename; + _filename = nullptr; } + deleteInterpolationPoints(); } - void setHeaderSize(uint32_t headerSizeInBytes) { - _header_offset = headerSizeInBytes / 2; - if (_bufferPosition < _header_offset) { - if (_playbackRate >= 0) { - _bufferPosition = _header_offset; - } - } + IndexableSDFile<128, 2>* createSourceBuffer() override { + return new IndexableSDFile<128, 2>(_filename); } uint32_t positionMillis(void) { @@ -102,54 +71,11 @@ public: uint32_t lengthMillis(void) { return ((uint64_t)_file_size * B2M) >> 32; } -private: - volatile bool _playing = false; - - int32_t _file_size; - int32_t _header_offset = 0; // == (header size in bytes ) / 2 - - double _playbackRate = 1.0; - double _remainder = 0.0; - loop_type _loopType = looptype_none; - int _bufferPosition = 0; - int32_t _loop_start = 0; - int32_t _loop_finish = 0; - int16_t _numChannels = -1; - uint16_t _numInterpolationPointsChannels = 0; - char *_filename = nullptr; - newdigate::IndexableFile<128, 2> *_sourceBuffer = nullptr; - - ResampleInterpolationType _interpolationType = ResampleInterpolationType::resampleinterpolation_none; - unsigned int _numInterpolationPoints = 0; - InterpolationData **_interpolationPoints = nullptr; - - static bool isUsingSPI; - void StartUsingSPI(){ - if (!isUsingSPI) { - isUsingSPI = true; -#if defined(HAS_KINETIS_SDHC) - if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStartUsingSPI(); -#else - AudioStartUsingSPI(); -#endif - } - } - - void StopUsingSPI() { - if (isUsingSPI) { - isUsingSPI = false; -#if defined(HAS_KINETIS_SDHC) - if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI(); -#else - AudioStopUsingSPI(); -#endif - } - } - - bool play(const char *filename, bool isWave, uint16_t numChannelsIfRaw = 0); - void initializeInterpolationPoints(void); - void deleteInterpolationPoints(void); + +protected: + }; +} #endif //TEENSYAUDIOLIBRARY_RESAMPLINGSDREADER_H diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h b/third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h new file mode 100644 index 0000000..ca67e58 --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h @@ -0,0 +1,82 @@ +// +// Created by Nicholas Newdigate on 10/02/2019. +// + +#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H +#define TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H + +#include +#include "spi_interrupt.h" +#include "loop_type.h" +#include "interpolation.h" +#include "IndexableSerialFlashFile.h" +#include "ResamplingReader.h" +#include "SerialFlash.h" + +#define RESAMPLE_BUFFER_SAMPLE_SIZE 128 + +#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 + +namespace newdigate { + +class ResamplingSerialFlashReader : public ResamplingReader< IndexableSerialFlashFile<128, 2>, SerialFlashFile > { +public: + ResamplingSerialFlashReader(SerialFlashChip &fs) : + ResamplingReader(), + _myFS(fs) + { + } + + virtual ~ResamplingSerialFlashReader() { + } + + int16_t getSourceBufferValue(long index) override { + return (*_sourceBuffer)[index]; + } + + int available(void) + { + return _playing; + } + + SerialFlashFile open(char *filename) override { + return _myFS.open(filename); + } + + void close(void) override + { + if (_playing) + stop(); + if (_sourceBuffer != nullptr) { + _sourceBuffer->close(); + delete _sourceBuffer; + _sourceBuffer = nullptr; + } + if (_filename != nullptr) { + delete [] _filename; + _filename = nullptr; + } + deleteInterpolationPoints(); + } + + IndexableSerialFlashFile<128, 2>* createSourceBuffer() override { + return new IndexableSerialFlashFile<128, 2>(_myFS, _filename); + } + + uint32_t positionMillis(void) { + if (_file_size == 0) return 0; + + return (uint32_t) (( (double)_bufferPosition * lengthMillis() ) / (double)(_file_size/2)); + } + + uint32_t lengthMillis(void) { + return ((uint64_t)_file_size * B2M) >> 32; + } + +protected: + SerialFlashChip &_myFS; +}; + +} + +#endif //TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H diff --git a/third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h b/third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h index 75c53a3..eec731d 100644 --- a/third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h +++ b/third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h @@ -8,7 +8,10 @@ #include "waveheaderparser.h" #include "ResamplingSdReader.h" #include "ResamplingArrayReader.h" +#include "ResamplingLfsReader.h" +#include "ResamplingSerialFlashReader.h" #include "playsdresmp.h" #include "playarrayresmp.h" - +#include "playlfsresmp.h" +#include "playserialflashresmp.h" #endif //TEENSY_RESAMPLING_ARDUINO_SAMPLER_H diff --git a/third-party/TeensyVariablePlayback/src/playarrayresmp.h b/third-party/TeensyVariablePlayback/src/playarrayresmp.h index 6a11c4c..4f37701 100644 --- a/third-party/TeensyVariablePlayback/src/playarrayresmp.h +++ b/third-party/TeensyVariablePlayback/src/playarrayresmp.h @@ -11,60 +11,19 @@ #include "ResamplingArrayReader.h" #include "playresmp.h" -class AudioPlayArrayResmp : public AudioPlayResmp +class AudioPlayArrayResmp : public AudioPlayResmp { public: AudioPlayArrayResmp(void) : - AudioPlayResmp(), - arrayReader() + AudioPlayResmp() { + reader = new newdigate::ResamplingArrayReader(); begin(); } - void begin(void); - bool playRaw(int16_t *data, uint32_t numSamples, uint16_t numChannels); - bool playRaw(const unsigned int *data, uint32_t numSamples, uint16_t numChannels); - - bool playWav(int16_t *data, uint32_t fileSize); - bool playWav(const unsigned int *data, uint32_t fileSize); - - void stop(void); - bool isPlaying(void) { return arrayReader.isPlaying(); } - uint32_t positionMillis(void); - uint32_t lengthMillis(void); - virtual void update(void); - - void setPlaybackRate(float f) { - arrayReader.setPlaybackRate(f); - } - - void setLoopType(loop_type t) { - arrayReader.setLoopType(t); - } - - void startLoop(uint32_t samples) { - arrayReader.loop(samples); - } - - void setLoopStart(uint32_t loop_start) { - arrayReader.setLoopStart(loop_start); + virtual ~AudioPlayArrayResmp() { + delete reader; } - - void setLoopFinish(uint32_t loop_finish) { - arrayReader.setLoopFinish(loop_finish); - } - - void enableInterpolation(bool enable) { - if (enable) - arrayReader.setInterpolationType(ResampleInterpolationType::resampleinterpolation_quadratic); - else - arrayReader.setInterpolationType(ResampleInterpolationType::resampleinterpolation_none); - } -private: - - uint32_t file_size; - ResamplingArrayReader arrayReader; - }; diff --git a/third-party/TeensyVariablePlayback/src/playlfsresmp.h b/third-party/TeensyVariablePlayback/src/playlfsresmp.h new file mode 100644 index 0000000..5774441 --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/playlfsresmp.h @@ -0,0 +1,26 @@ +// +// Created by Nicholas Newdigate on 18/07/2020. +// + +#ifndef TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H +#define TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H + +#include "ResamplingLfsReader.h" + +class AudioPlayLfsResmp : public AudioPlayResmp +{ +public: + AudioPlayLfsResmp(LittleFS &fs) : + AudioPlayResmp() + { + reader = new newdigate::ResamplingLfsReader(fs); + begin(); + } + + virtual ~AudioPlayLfsResmp() { + delete reader; + } +}; + + +#endif //TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H diff --git a/third-party/TeensyVariablePlayback/src/playresmp.h b/third-party/TeensyVariablePlayback/src/playresmp.h index 3d0ad3f..12df93f 100644 --- a/third-party/TeensyVariablePlayback/src/playresmp.h +++ b/third-party/TeensyVariablePlayback/src/playresmp.h @@ -5,20 +5,135 @@ #include "Audio.h" #include "loop_type.h" +template class AudioPlayResmp : public AudioStream { public: - AudioPlayResmp(void): AudioStream(0, NULL) {} - virtual ~AudioPlayResmp() {} - - virtual void setPlaybackRate(float f) = 0; - virtual void setLoopType(loop_type t) = 0; - virtual void setLoopStart(uint32_t loop_start) = 0; - virtual void setLoopFinish(uint32_t loop_finish) = 0; - virtual void begin() = 0; - virtual void enableInterpolation(bool enable) = 0; - virtual bool isPlaying(void) = 0; - virtual void stop() = 0; + AudioPlayResmp(): AudioStream(0, NULL), reader(nullptr) + { + } + + virtual ~AudioPlayResmp() { + } + + void begin(void) + { + reader->begin(); + } + + bool playRaw(const char *filename, uint16_t numChannels) + { + stop(); + return reader->play(filename, false, numChannels); + } + + bool playWav(const char *filename) + { + stop(); + return reader->play(filename, true, 0); + } + + bool playRaw(int16_t *data, uint32_t numSamples, uint16_t numChannels) + { + stop(); + return reader->playRaw(data, numSamples, numChannels); + } + + bool playRaw(const unsigned int *data, uint32_t numSamples, uint16_t numChannels) + { + return playRaw((int16_t *) data, numSamples, numChannels); + } + + bool playWav(int16_t *data, uint32_t fileSize) + { + stop(); + return reader->playWav(data, fileSize); + } + + bool playWav(const unsigned int *data, uint32_t fileSize) { + return playWav((int16_t *) data, fileSize); + } + + void setPlaybackRate(float f) { + reader->setPlaybackRate(f); + } + + void setLoopType(loop_type t) { + reader->setLoopType(t); + } + + void setLoopStart(uint32_t loop_start) { + reader->setLoopStart(loop_start); + } + + void setLoopFinish(uint32_t loop_finish) { + reader->setLoopFinish(loop_finish); + } + + void enableInterpolation(bool enable) { + if (enable) + reader->setInterpolationType(ResampleInterpolationType::resampleinterpolation_quadratic); + else + reader->setInterpolationType(ResampleInterpolationType::resampleinterpolation_none); + } + + bool isPlaying(void) { + return reader->isPlaying(); + }; + + void stop() { + reader->stop(); + } + + void update() + { + int _numChannels = reader->getNumChannels(); + if (_numChannels == -1) + return; + + unsigned int i, n; + audio_block_t *blocks[_numChannels]; + int16_t *data[_numChannels]; + // only update if we're playing + if (!reader->isPlaying()) return; + + // allocate the audio blocks to transmit + for (int i=0; i < _numChannels; i++) { + blocks[i] = allocate(); + if (blocks[i] == nullptr) return; + data[i] = blocks[i]->data; + } + + if (reader->available()) { + // we can read more data from the file... + n = reader->read((void**)data, AUDIO_BLOCK_SAMPLES); + for (int channel=0; channel < _numChannels; channel++) { + memset( &blocks[channel]->data[n], 0, (AUDIO_BLOCK_SAMPLES - n) * 2); + transmit(blocks[channel], channel); + } + + if(_numChannels == 1) { + transmit(blocks[0], 1); + } + } else { + reader->close(); + } + for (int channel=0; channel < _numChannels; channel++) { + release(blocks[channel]); + } + } + uint32_t positionMillis() + { + return reader->positionMillis(); + } + + uint32_t lengthMillis() + { + return reader->lengthMillis(); + } + + protected: + TResamplingReader *reader; }; #endif // TEENSY_RESAMPLING_SDREADER_PLAYRESMP_H \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/src/playsdresmp.h b/third-party/TeensyVariablePlayback/src/playsdresmp.h index d4ae427..fdc039d 100644 --- a/third-party/TeensyVariablePlayback/src/playsdresmp.h +++ b/third-party/TeensyVariablePlayback/src/playsdresmp.h @@ -13,52 +13,19 @@ #include "ResamplingSdReader.h" #include "playresmp.h" -class AudioPlaySdResmp : public AudioPlayResmp +class AudioPlaySdResmp : public AudioPlayResmp { public: AudioPlaySdResmp(void) : - AudioPlayResmp(), - sdReader() + AudioPlayResmp() { + reader = new newdigate::ResamplingSdReader(); begin(); } - void begin(void); - bool playRaw(const char *filename, uint16_t numChannels); - bool playWav(const char *filename); - void stop(void); - bool isPlaying(void) { return sdReader.isPlaying(); } - uint32_t positionMillis(void); - uint32_t lengthMillis(void); - virtual void update(void); - - void setPlaybackRate(float f) { - sdReader.setPlaybackRate(f); - } - - void setLoopType(loop_type t) { - sdReader.setLoopType(t); - } - - void setLoopStart(uint32_t loop_start) { - sdReader.setLoopStart(loop_start); + virtual ~AudioPlaySdResmp() { + delete reader; } - - void setLoopFinish(uint32_t loop_finish) { - sdReader.setLoopFinish(loop_finish); - } - - void enableInterpolation(bool enable) { - if (enable) - sdReader.setInterpolationType(ResampleInterpolationType::resampleinterpolation_quadratic); - else - sdReader.setInterpolationType(ResampleInterpolationType::resampleinterpolation_none); - } -private: - - uint32_t file_size; - ResamplingSdReader sdReader; - }; diff --git a/third-party/TeensyVariablePlayback/src/playserialflashresmp.h b/third-party/TeensyVariablePlayback/src/playserialflashresmp.h new file mode 100644 index 0000000..6dc56d5 --- /dev/null +++ b/third-party/TeensyVariablePlayback/src/playserialflashresmp.h @@ -0,0 +1,31 @@ +// +// Created by Nicholas Newdigate on 18/07/2020. +// + +#ifndef TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H +#define TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H + +#include "Arduino.h" +#include "AudioStream.h" +#include "SerialFlash.h" +#include "stdint.h" +#include "ResamplingSerialFlashReader.h" +#include "playresmp.h" + +class AudioPlaySerialFlashResmp : public AudioPlayResmp +{ +public: + AudioPlaySerialFlashResmp(SerialFlashChip &fs) : + AudioPlayResmp() + { + reader = new newdigate::ResamplingSerialFlashReader(fs); + begin(); + } + + virtual ~AudioPlaySerialFlashResmp() { + delete reader; + } +}; + + +#endif //TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H diff --git a/third-party/TeensyVariablePlayback/src/waveheaderparser.h b/third-party/TeensyVariablePlayback/src/waveheaderparser.h index f2678e1..e8d7a56 100644 --- a/third-party/TeensyVariablePlayback/src/waveheaderparser.h +++ b/third-party/TeensyVariablePlayback/src/waveheaderparser.h @@ -14,33 +14,31 @@ using namespace std; // from https://gist.github.com/Jon-Schneider/8b7c53d27a7a13346a643dac9c19d34f struct wav_header { // RIFF Header - char riff_header[4]; // 00 - 03 - Contains "RIFF" - int header_chunk_size; // 04 - 07 - Size of the wav portion of the file, which follows the first 8 bytes. File size - 8 - char wave_header[4]; // 08 - 11 - Contains "WAVE" + char riff_header[4] = {0,0,0,0}; // 00 - 03 - Contains "RIFF" + int header_chunk_size = 0; // 04 - 07 - Size of the wav portion of the file, which follows the first 8 bytes. File size - 8 + char wave_header[4] = {0,0,0,0}; // 08 - 11 - Contains "WAVE" // Format Header - char fmt_header[4]; // 12 - 15 - Contains "fmt " (includes trailing space) - int fmt_chunk_size; // 16 - 19 - Should be 16 for PCM - short audio_format; // 20 - 21 - Should be 1 for PCM. 3 for IEEE Float - short num_channels; // 22 - 23 - int sample_rate; // 24 - 27 - int byte_rate; // 28 - 31 - short sample_alignment; // 32 - 33 - short bit_depth; // 34 - 35 + char fmt_header[4] = {0,0,0,0}; // 12 - 15 - Contains "fmt " (includes trailing space) + int fmt_chunk_size = 0; // 16 - 19 - Should be 16 for PCM + short audio_format = 0; // 20 - 21 - Should be 1 for PCM. 3 for IEEE Float + short num_channels = 0; // 22 - 23 + int sample_rate = 0; // 24 - 27 + int byte_rate = 0; // 28 - 31 + short sample_alignment = 0; // 32 - 33 + short bit_depth = 0; // 34 - 35 }; struct wav_data_header { // Data - char data_header[4]; // 36 - 39 - unsigned int data_bytes;// 40 - 43 + char data_header[4] = {0,0,0,0}; // 36 - 39 + unsigned int data_bytes = 0;// 40 - 43 }; class WaveHeaderParser { public: bool readWaveHeader(const char *filename, wav_header &header, wav_data_header &wav_data_header) { - __disable_irq(); File wavFile = SD.open(filename); - __enable_irq(); if (!wavFile) { Serial.printf("Not able to open wave file... %s\n", filename); return false; @@ -77,9 +75,7 @@ public: bool readWaveHeader(const char *filename, wav_header &header, File &wavFile) { char buffer[36]; - __disable_irq(); int bytesRead = wavFile.read(buffer, 36); - __enable_irq(); if (bytesRead != 36) { Serial.printf("expected 36 bytes (was %d)\n", bytesRead); return false; diff --git a/third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h b/third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h index d6df109..0490daf 100644 --- a/third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h +++ b/third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h @@ -10,14 +10,14 @@ struct ResamplingArrayFixture { ResamplingArrayFixture() { - resamplingArrayReader = new ResamplingArrayReader(); + resamplingArrayReader = new newdigate::ResamplingArrayReader(); } ~ResamplingArrayFixture() { delete resamplingArrayReader; } - ResamplingArrayReader * resamplingArrayReader; + newdigate::ResamplingArrayReader * resamplingArrayReader; }; #endif //TEENSY_RESAMPLING_SDREADER_RESAMPLINGARRAYFIXTURE_H diff --git a/third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h b/third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h index 9db4e38..f954704 100644 --- a/third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h +++ b/third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h @@ -10,14 +10,14 @@ struct ResamplingArrayWavFixture { ResamplingArrayWavFixture() { - resamplingArrayReader = new ResamplingArrayReader(); + resamplingArrayReader = new newdigate::ResamplingArrayReader(); } ~ResamplingArrayWavFixture() { delete resamplingArrayReader; } - ResamplingArrayReader * resamplingArrayReader; + newdigate::ResamplingArrayReader * resamplingArrayReader; }; #endif //TEENSY_RESAMPLING_SDREADER_RESAMPLINGARRAYWAVFIXTURE_H diff --git a/third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp b/third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp index 35c416c..017d38c 100644 --- a/third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp +++ b/third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp @@ -2,7 +2,7 @@ #include "IndexedFileFixture.h" #include -#include "IndexableFile.h" +#include "IndexableSDFile.h" BOOST_AUTO_TEST_SUITE(test_indexablefile) @@ -15,7 +15,7 @@ BOOST_AUTO_TEST_SUITE(test_indexablefile) } SD.setSDCardFileData((char*)file_contents, sample_size * 2); - newdigate::IndexableFile<16, 2> indexable("blah.h"); // use max 2 buffers, with 16 elements each.... + newdigate::IndexableSDFile<16, 2> indexable("blah.h"); // use max 2 buffers, with 16 elements each.... /* for (int i=0; ibegin(); resamplingSdReader->setPlaybackRate(1.0); - resamplingSdReader->playRaw("test2.bin", 1); + resamplingSdReader->play("test2.bin", false, 1); resamplingSdReader->setLoopType(looptype_none); resamplingSdReader->setInterpolationType(ResampleInterpolationType::resampleinterpolation_quadratic); int16_t actual[1024];