From 7d0a2a29222f3684018ec7f8b5c8121a101ab94f Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Fri, 9 Dec 2022 08:52:29 +0100 Subject: [PATCH] Newest version of TeensyVariablePlayback added. Fixes for performance data (only midi channel adaptions). Added midi learning lowest/highest note for Dexed and EP. --- MicroDexed.ino | 131 ++++-- UI.hpp | 33 +- addon/SD/PERFORMANCE/0/drmmap.json | 2 +- addon/SD/PERFORMANCE/0/epiano.json | 4 +- addon/SD/PERFORMANCE/0/voice1.json | 4 +- addon/SD/PERFORMANCE/0/voice2.json | 4 +- config.h | 20 +- .../TeensyVariablePlayback/build-linux.sh | 0 .../TeensyVariablePlayback/build-t41.sh | 0 .../src/IndexableFile.cpp | 1 - .../src/ResamplingArrayReader.cpp | 340 --------------- .../src/ResamplingSdReader.cpp | 392 ------------------ .../src/playarrayresmp.cpp | 92 ---- .../TeensyVariablePlayback/src/playresmp.h | 4 +- .../src/playsdresmp.cpp | 83 ---- third-party/TeensyVariablePlayback/test.sh | 0 .../test/low_level/sd/readme.MD | 0 17 files changed, 133 insertions(+), 977 deletions(-) mode change 100644 => 100755 third-party/TeensyVariablePlayback/build-linux.sh mode change 100644 => 100755 third-party/TeensyVariablePlayback/build-t41.sh delete mode 100644 third-party/TeensyVariablePlayback/src/IndexableFile.cpp delete mode 100644 third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp delete mode 100644 third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp delete mode 100644 third-party/TeensyVariablePlayback/src/playarrayresmp.cpp delete mode 100644 third-party/TeensyVariablePlayback/src/playsdresmp.cpp mode change 100644 => 100755 third-party/TeensyVariablePlayback/test.sh mode change 100644 => 100755 third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD diff --git a/MicroDexed.ino b/MicroDexed.ino index c76b7ff..0830aff 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -361,7 +361,7 @@ uint8_t midi_voices[NUM_DEXED]; #ifdef SHOW_CPU_LOAD_MSEC elapsedMillis cpu_mem_millis; #endif -uint8_t* midi_learn_var = NULL; +bool midi_learn_mode = false; uint32_t cpumax = 0; uint32_t peak_dexed = 0; float peak_dexed_value = 0.0; @@ -905,36 +905,27 @@ void init_MIDI_send_CC(void) { MIDI MESSAGE HANDLER ******************************************************************************/ void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) { - // check for MIDI learn mode - if (midi_learn_var != NULL) { - *midi_learn_var = inNumber; - if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_drum_midi_note) && inChannel == configuration.drums.drum_midi_channel) - LCDML.OTHER_jumpToFunc(UI_func_drum_midi_note); - else { - for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) { - if (inChannel == configuration.dexed[selected_instance_id].midi_channel) { - if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_lowest_note)) { - if (inNumber > configuration.dexed[selected_instance_id].highest_note) - configuration.dexed[selected_instance_id].highest_note = inNumber; - LCDML.OTHER_jumpToFunc(UI_func_lowest_note); - } else if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_highest_note)) { - if (inNumber < configuration.dexed[selected_instance_id].lowest_note) - configuration.dexed[selected_instance_id].lowest_note = inNumber; - LCDML.OTHER_jumpToFunc(UI_func_highest_note); - } - } - } - } + // + // MIDI learn mode + // + if (midi_learn_mode == true) { + int8_t tmp_channel = handle_midi_learn(inNumber); + if (tmp_channel >= 0) + inChannel = tmp_channel; } + // + // Play Notes + // + // Check for MicroDexed for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) { if (checkMidiChannel(inChannel, instance_id)) { if (inNumber >= configuration.dexed[instance_id].lowest_note && inNumber <= configuration.dexed[instance_id].highest_note) { - if (configuration.dexed[instance_id].polyphony > 0) + if (configuration.dexed[instance_id].polyphony > 0) { MicroDexed[instance_id]->keydown(inNumber, uint8_t(float(configuration.dexed[instance_id].velocity_level / 127.0) * inVelocity + 0.5)); - - midi_voices[instance_id]++; + midi_voices[instance_id]++; + } #if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_voice_select)) { midi_decay_timer = 0; @@ -984,6 +975,7 @@ void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) { #endif if (drum_config[d].drum_data != NULL && drum_config[d].len > 0) { //Drum[slot]->play(drum_config[d].drum_data); + if (drum_config[d].pitch != 0.0) { Drum[slot]->enableInterpolation(true); Drum[slot]->setPlaybackRate(drum_config[d].pitch); @@ -1013,10 +1005,10 @@ void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) { } #endif -// -// E-Piano -// #if defined(USE_EPIANO) + // + // E-Piano + // if (configuration.epiano.midi_channel == MIDI_CHANNEL_OMNI || configuration.epiano.midi_channel == inChannel) { if (inNumber >= configuration.epiano.lowest_note && inNumber <= configuration.epiano.highest_note) { ep.noteOn(inNumber + configuration.epiano.transpose - 24, inVelocity); @@ -1069,7 +1061,90 @@ uint8_t drum_get_slot(uint8_t dt) { } #endif +int8_t handle_midi_learn(int8_t note) { + int8_t ret_channel = -1; + +#ifdef DEBUG + Serial.print("MIDI learning for "); + Serial.println(note); +#endif + + if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_drum_midi_note)) { + ret_channel = configuration.drums.drum_midi_channel; + //LCDML.OTHER_jumpToFunc(UI_func_drum_midi_note); + } else if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_epiano_lowest_note)) { + if (note > configuration.epiano.highest_note) + configuration.epiano.lowest_note = configuration.epiano.highest_note; + else + configuration.epiano.lowest_note = note; + ret_channel = configuration.epiano.midi_channel; +#ifdef DEBUG + Serial.print("MIDI learned lowest note: "); + Serial.print(note); + Serial.print(" for EPiano, ghosting MIDI channel "); + Serial.println(ret_channel); +#endif + } else if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_epiano_highest_note)) { + if (note < configuration.epiano.lowest_note) + configuration.epiano.highest_note = configuration.epiano.lowest_note; + else + configuration.epiano.highest_note = note; + ret_channel = configuration.epiano.midi_channel; +#ifdef DEBUG + Serial.print("MIDI learned highest note: "); + Serial.print(note); + Serial.print(" for EPiano, ghosting MIDI channel "); + Serial.println(ret_channel); +#endif + } + + // Check for Dexed + for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) { + if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_lowest_note)) { + if (note > configuration.dexed[selected_instance_id].highest_note) + configuration.dexed[selected_instance_id].lowest_note = configuration.dexed[selected_instance_id].highest_note; + else + configuration.dexed[selected_instance_id].lowest_note = note; + ret_channel = configuration.dexed[selected_instance_id].midi_channel; +#ifdef DEBUG + Serial.print("MIDI learned lowest note: "); + Serial.print(note); + Serial.print(" for instance: "); + Serial.print(selected_instance_id); + Serial.print(", ghosting MIDI channel "); + Serial.println(ret_channel); +#endif + } else if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_highest_note)) { + if (note < configuration.dexed[selected_instance_id].lowest_note) + configuration.dexed[selected_instance_id].highest_note = configuration.dexed[selected_instance_id].lowest_note; + else + configuration.dexed[selected_instance_id].highest_note = note; + ret_channel = configuration.dexed[selected_instance_id].midi_channel; +#ifdef DEBUG + Serial.print("MIDI learned highest note: "); + Serial.print(note); + Serial.print(" for instance: "); + Serial.print(selected_instance_id); + Serial.print(", ghosting MIDI channel "); + Serial.println(ret_channel); +#endif + } + LCDML.OTHER_updateFunc(); + } + + return (ret_channel); +} + void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity) { + // + // MIDI learn mode + // + if (midi_learn_mode == true) { + int8_t tmp_channel = handle_midi_learn(inNumber); + if (tmp_channel >= 0) + inChannel = tmp_channel; + } + for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) { if (checkMidiChannel(inChannel, instance_id)) { if (inNumber >= configuration.dexed[instance_id].lowest_note && inNumber <= configuration.dexed[instance_id].highest_note) { @@ -2983,4 +3058,4 @@ int FreeMem(void) { return (char*)&_heap_end - __brkval; } #endif -#endif \ No newline at end of file +#endif diff --git a/UI.hpp b/UI.hpp index aa911b4..7df8538 100644 --- a/UI.hpp +++ b/UI.hpp @@ -55,7 +55,7 @@ #define _LCDML_DISP_cfg_scrollbar 1 // enable a scrollbar extern bool check_sd_performance_exists(uint8_t number); -extern uint8_t* midi_learn_var; +extern bool midi_learn_mode; extern config_t configuration; extern void set_volume(uint8_t v, uint8_t m); @@ -228,11 +228,14 @@ const uint8_t special_chars[22][8] = { enum { SCROLLBAR, BLOCKBAR, - METERBAR }; + METERBAR +}; enum { ENC_R, - ENC_L }; + ENC_L +}; enum { MENU_VOICE_BANK, - MENU_VOICE_SOUND }; + MENU_VOICE_SOUND +}; void lcdml_menu_display(void); void lcdml_menu_clear(void); @@ -1761,7 +1764,7 @@ void UI_func_lowest_note(uint8_t param) { lcd_active_instance_number(selected_instance_id); UI_update_instance_icons(); - midi_learn_var = &configuration.dexed[selected_instance_id].lowest_note; + midi_learn_mode = true; } if (LCDML.FUNC_loop()) // ****** LOOP ********* @@ -1789,7 +1792,7 @@ void UI_func_lowest_note(uint8_t param) { { lcd_special_chars(SCROLLBAR); encoderDir[ENC_R].reset(); - midi_learn_var = NULL; + midi_learn_mode = false; } } @@ -1810,7 +1813,7 @@ void UI_func_highest_note(uint8_t param) { lcd_active_instance_number(selected_instance_id); UI_update_instance_icons(); - midi_learn_var = &configuration.dexed[selected_instance_id].highest_note; + midi_learn_mode = true; } if (LCDML.FUNC_loop()) // ****** LOOP ********* @@ -1838,7 +1841,7 @@ void UI_func_highest_note(uint8_t param) { { lcd_special_chars(SCROLLBAR); encoderDir[ENC_R].reset(); - midi_learn_var = NULL; + midi_learn_mode = false; } } @@ -2035,6 +2038,7 @@ void UI_func_epiano_lowest_note(uint8_t param) { if (LCDML.FUNC_setup()) // ****** SETUP ********* { encoderDir[ENC_R].reset(); + midi_learn_mode = true; getNoteName(note_name, configuration.epiano.lowest_note); display.setCursor(0, 0); @@ -2063,6 +2067,7 @@ void UI_func_epiano_lowest_note(uint8_t param) { { lcd_special_chars(SCROLLBAR); encoderDir[ENC_R].reset(); + midi_learn_mode = false; } } @@ -2072,6 +2077,7 @@ void UI_func_epiano_highest_note(uint8_t param) { if (LCDML.FUNC_setup()) // ****** SETUP ********* { encoderDir[ENC_R].reset(); + midi_learn_mode = true; getNoteName(note_name, configuration.dexed[selected_instance_id].highest_note); display.setCursor(0, 0); @@ -2100,6 +2106,7 @@ void UI_func_epiano_highest_note(uint8_t param) { { lcd_special_chars(SCROLLBAR); encoderDir[ENC_R].reset(); + midi_learn_mode = false; } } @@ -4308,13 +4315,13 @@ void _UI_func_drum_midi_note_display(bool mode) { if (mode == false) { snprintf_P(temp1, sizeof(temp1), PSTR("[%02d]"), activesample + 1); - if (_get_midi_note == true) + if (_get_midi_note(configuration.drums.drum_midi_note[activesample]) == true) snprintf_P(temp2, sizeof(temp2), PSTR(" %-3s*"), note_name); else snprintf_P(temp2, sizeof(temp2), PSTR(" %-3s "), note_name); } else { snprintf_P(temp1, sizeof(temp1), PSTR(" %02d "), activesample + 1); - if (_get_midi_note == true) + if (_get_midi_note(configuration.drums.drum_midi_note[activesample]) == true) snprintf_P(temp2, sizeof(temp2), PSTR("<%-3s>"), note_name); else snprintf_P(temp2, sizeof(temp2), PSTR(" %-3s*"), note_name); @@ -4333,7 +4340,7 @@ void UI_func_drum_midi_note(uint8_t param) { display.setCursor(0, 0); display.print("Drum MIDI Note"); _UI_func_drum_midi_note_display(mode); - midi_learn_var = &configuration.drums.drum_midi_note[activesample]; + midi_learn_mode = true; //configuration.drums.drum_midi_note[activesample]; } if (LCDML.FUNC_loop()) // ****** LOOP ********* @@ -4364,7 +4371,7 @@ void UI_func_drum_midi_note(uint8_t param) { if (LCDML.FUNC_close()) // ****** STABLE END ********* { encoderDir[ENC_R].reset(); - midi_learn_var = NULL; + midi_learn_mode = false; } } @@ -6822,4 +6829,4 @@ char* basename(const char* filename) { return p ? p + 1 : (char*)filename; } -#endif //ENABLE_LCD_UI \ No newline at end of file +#endif //ENABLE_LCD_UI diff --git a/addon/SD/PERFORMANCE/0/drmmap.json b/addon/SD/PERFORMANCE/0/drmmap.json index 3017f56..7649ed2 100755 --- a/addon/SD/PERFORMANCE/0/drmmap.json +++ b/addon/SD/PERFORMANCE/0/drmmap.json @@ -103,4 +103,4 @@ 0, 0 ] -} \ No newline at end of file +} diff --git a/addon/SD/PERFORMANCE/0/epiano.json b/addon/SD/PERFORMANCE/0/epiano.json index b3064a3..21dab3e 100755 --- a/addon/SD/PERFORMANCE/0/epiano.json +++ b/addon/SD/PERFORMANCE/0/epiano.json @@ -16,5 +16,5 @@ "transpose": 24, "sound_intensity": 100, "pan": 20, - "midi_channel": 1 -} \ No newline at end of file + "midi_channel": 4 +} diff --git a/addon/SD/PERFORMANCE/0/voice1.json b/addon/SD/PERFORMANCE/0/voice1.json index 9514512..586e2e3 100755 --- a/addon/SD/PERFORMANCE/0/voice1.json +++ b/addon/SD/PERFORMANCE/0/voice1.json @@ -29,5 +29,5 @@ "portamento_glissando": 0, "portamento_time": 0, "op_enabled": 63, - "midi_channel": 6 -} \ No newline at end of file + "midi_channel": 2 +} diff --git a/addon/SD/PERFORMANCE/0/voice2.json b/addon/SD/PERFORMANCE/0/voice2.json index 9075b71..33cc661 100755 --- a/addon/SD/PERFORMANCE/0/voice2.json +++ b/addon/SD/PERFORMANCE/0/voice2.json @@ -29,5 +29,5 @@ "portamento_glissando": 0, "portamento_time": 0, "op_enabled": 63, - "midi_channel": 5 -} \ No newline at end of file + "midi_channel": 3 +} diff --git a/config.h b/config.h index 8a381ea..574160c 100644 --- a/config.h +++ b/config.h @@ -642,24 +642,6 @@ #define PERFORMANCE_NUM_MAX 99 #define PERFORMANCE_NUM_DEFAULT 0 -/* - #define VELOCITY_CONFIG_MIN 0 - #define VELOCITY_CONFIG_MAX 99 - #define VELOCITY_CONFIG_DEFAULT 0 - - #define VOICE_CONFIG_MIN 0 - #define VOICE_CONFIG_MAX 99 - #define VOICE_CONFIG_DEFAULT -1 - - #define DRUMS_CONFIG_MIN 0 - #define DRUMS_CONFIG_MAX 99 - #define DRUMS_CONFIG_DEFAULT 0 - - #define SEQUENCE_CONFIG_MIN 0 - #define SEQUENCE_CONFIG_MAX 99 - #define SEQUENCE_CONFIG_DEFAULT 0 -*/ - #define DRUMS_MAIN_VOL_MIN 0 #define DRUMS_MAIN_VOL_MAX 100 #define DRUMS_MAIN_VOL_DEFAULT 80 @@ -990,4 +972,4 @@ inline float mapfloat(float val, float in_min, float in_max, float out_min, floa ARDUINO_TEENSY40 - Teensy 4.0 ARDUINO_TEENSY41 - Teensy 4.1 ARDUINO_TEENSYMM - Teensy 4 MM -*/ \ No newline at end of file +*/ diff --git a/third-party/TeensyVariablePlayback/build-linux.sh b/third-party/TeensyVariablePlayback/build-linux.sh old mode 100644 new mode 100755 diff --git a/third-party/TeensyVariablePlayback/build-t41.sh b/third-party/TeensyVariablePlayback/build-t41.sh old mode 100644 new mode 100755 diff --git a/third-party/TeensyVariablePlayback/src/IndexableFile.cpp b/third-party/TeensyVariablePlayback/src/IndexableFile.cpp deleted file mode 100644 index 541c7af..0000000 --- a/third-party/TeensyVariablePlayback/src/IndexableFile.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "IndexableFile.h" diff --git a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp b/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp deleted file mode 100644 index c3386d3..0000000 --- a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include "ResamplingArrayReader.h" -#include "interpolation.h" -#include "waveheaderparser.h" - -// read n samples into each buffer (1 buffer per channel) -unsigned int ResamplingArrayReader::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... */ - _playing = false; - return count; - } - } - } - } - } - return count; -} - -// read the sample value for given channel and store it at the location pointed to by the pointer 'value' -bool ResamplingArrayReader::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 = _sourceBuffer[_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 = _sourceBuffer[_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 = _sourceBuffer[_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 = _sourceBuffer[_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 = _sourceBuffer[_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 ResamplingArrayReader::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 ResamplingArrayReader::deleteInterpolationPoints(void) { - if (!_interpolationPoints) return; - for (int i=0; i<_numInterpolationPointsChannels; i++) { - delete [] _interpolationPoints[i]; - } - delete [] _interpolationPoints; - _interpolationPoints = nullptr; - _numInterpolationPointsChannels = 0; -} - -void ResamplingArrayReader::begin(void) -{ - if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { - initializeInterpolationPoints(); - } - _playing = false; - _bufferPosition = _header_offset; - _file_size = 0; -} - -bool ResamplingArrayReader::playRaw(int16_t *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(); - //updateBuffers(); - _playing = true; - return true; -} - -bool ResamplingArrayReader::playWav(int16_t *array, uint32_t length) // length == total number of 16-bit samples for all channels, including header -{ - _sourceBuffer = array; - stop(); - - wav_header wav_header; - wav_data_header data_header; - - WaveHeaderParser wavHeaderParser; - if (!wavHeaderParser.readWaveHeaderFromBuffer((const char *) array, wav_header)) { - Serial.println("Not able to read header! Aborting.... "); - return false; - } - - if (wav_header.bit_depth != 16) { - Serial.printf("Needs 16 bit audio! Aborting.... (got %d)\n", wav_header.bit_depth); - return false; - } - setNumChannels(wav_header.num_channels); - unsigned infoTagsSize; - if (!wavHeaderParser.readInfoTags((unsigned char *)array, 36, infoTagsSize)) - { - Serial.println("Not able to read header! Aborting..."); - return false; - } - - if (!wavHeaderParser.readDataHeader((unsigned char *)array, 36 + infoTagsSize, data_header)) { - Serial.println("Not able to read header! Aborting..."); - return false; - } - - - _header_offset = (44 + infoTagsSize) / 2; - _file_size = data_header.data_bytes + 44 + infoTagsSize; //2 bytes per sample - if (_file_size > length * 2){ - Serial.printf("TeensyVariablePlayback: warning: length of array in bytes (%d) is smaller than the file data size in bytes (%d) according to the header - defaulting length to filesize...", length * 2, _file_size); - _loop_finish = length; - } else - _loop_finish = _file_size / 2; - _loop_start = _header_offset; - - reset(); - _playing = true; - return true; -} - -bool ResamplingArrayReader::play() -{ - stop(); - reset(); - _playing = true; - return true; -} - -void ResamplingArrayReader::reset(){ - _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 ResamplingArrayReader::stop() -{ - if (_playing) { - _playing = false; - } -} - -int ResamplingArrayReader::available(void) { - return _playing; -} - -void ResamplingArrayReader::close(void) { - if (_playing) { - stop(); - deleteInterpolationPoints(); - } -} - diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp deleted file mode 100644 index 5697c46..0000000 --- a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp +++ /dev/null @@ -1,392 +0,0 @@ -#include "ResamplingSdReader.h" -#include "interpolation.h" -#include "waveheaderparser.h" -bool ResamplingSdReader::isUsingSPI = false; - -// read n samples into each buffer (1 buffer per channel) -unsigned int ResamplingSdReader::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 - _numChannels; - - break; - } - - case looptype_pingpong: - { - if (_playbackRate >= 0.0) { - _bufferPosition = _loop_finish / _numChannels - _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... */ - _playing = false; - if (_sourceBuffer) _sourceBuffer->close(); - _sourceBuffer = nullptr; - StopUsingSPI(); - return count; - } - } - } - } - } - return count; -} - -// read the sample value for given channel and store it at the location pointed to by the pointer 'value' -bool ResamplingSdReader::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; - } - - newdigate::IndexableFile<128, 2> &sourceBuffer = (*_sourceBuffer); - - int16_t result = sourceBuffer[_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 = sourceBuffer[_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 = sourceBuffer[_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 = sourceBuffer[_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 = sourceBuffer[_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 ResamplingSdReader::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 ResamplingSdReader::deleteInterpolationPoints(void) { - if (!_interpolationPoints) return; - for (int i=0; i<_numInterpolationPointsChannels; i++) { - delete [] _interpolationPoints[i]; - } - delete [] _interpolationPoints; - _interpolationPoints = nullptr; - _numInterpolationPointsChannels = 0; -} - -void ResamplingSdReader::begin(void) -{ - if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { - initializeInterpolationPoints(); - } - _playing = false; - _bufferPosition = _header_offset; - _file_size = 0; -} - -bool ResamplingSdReader::playRaw(const char *filename, uint16_t numChannels) { - return play(filename, false, numChannels); -} - -bool ResamplingSdReader::playWav(const char *filename) { - return play(filename, true); -} - -bool ResamplingSdReader::play(const char *filename, bool isWave, uint16_t numChannelsIfRaw) -{ - stop(); - //system("echo ${PWD}"); - if (!isWave) // if raw file, then hardcode the numChannels as per the parameter - setNumChannels(numChannelsIfRaw); - - if (_sourceBuffer) { - //Serial.printf("closing %s\n", _filename); - __disable_irq(); - _sourceBuffer->close(); - __enable_irq(); - } - if (_sourceBuffer) delete _sourceBuffer; - if (_filename) delete [] _filename; - _filename = new char[strlen(filename)+1] {0}; - memcpy(_filename, filename, strlen(filename) + 1); - StartUsingSPI(); - - __disable_irq(); - File file = SD.open(_filename); - __enable_irq(); - - if (!file) { - StopUsingSPI(); - Serial.printf("Not able to open file: %s\n", _filename); - if (_filename) delete [] _filename; - _filename = nullptr; - return false; - } - - __disable_irq(); - _file_size = file.size(); - __enable_irq(); - - if (isWave) { - wav_header wav_header; - wav_data_header data_header; - - WaveHeaderParser wavHeaderParser; - char buffer[36]; - __disable_irq(); - size_t bytesRead = file.read(buffer, 36); - - __enable_irq(); - 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; - - __disable_irq(); - file.close(); - __enable_irq(); - - _sourceBuffer = new newdigate::IndexableFile<128, 2>(_filename); - _loop_start = _header_offset; - if (_file_size <= _header_offset * newdigate::IndexableFile<128, 2>::element_size) { - _playing = false; - if (_filename) delete [] _filename; - _filename = nullptr; - Serial.printf("Wave file contains no samples: %s\n", filename); - return false; - } - - reset(); - _playing = true; - return true; -} - -bool ResamplingSdReader::play() -{ - stop(); - reset(); - _playing = true; - return true; -} - -void ResamplingSdReader::reset(){ - initializeInterpolationPoints(); - 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 - _numChannels; - } -} - -void ResamplingSdReader::stop() -{ - if (_playing) { - __disable_irq(); - _playing = false; - //_file.close(); - __enable_irq(); - //StopUsingSPI(); - } -} - -int ResamplingSdReader::available(void) { - return _playing; -} - -void ResamplingSdReader::close(void) { - if (_playing) - stop(); - if (_sourceBuffer) { - _sourceBuffer->close(); - delete _sourceBuffer; - } - StopUsingSPI(); - _sourceBuffer = nullptr; - //TODO: dispose _sourceBuffer properly - deleteInterpolationPoints(); -} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/src/playarrayresmp.cpp b/third-party/TeensyVariablePlayback/src/playarrayresmp.cpp deleted file mode 100644 index a214e61..0000000 --- a/third-party/TeensyVariablePlayback/src/playarrayresmp.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// -// Created by Nicholas Newdigate on 18/07/2020. -// - -#include "playarrayresmp.h" - -void AudioPlayArrayResmp::begin() -{ - file_size = 0; - arrayReader.begin(); -} - -bool AudioPlayArrayResmp::playRaw(int16_t *data, uint32_t numSamples, uint16_t numChannels) -{ - stop(); - bool playing = arrayReader.playRaw(data, numSamples, numChannels); - return playing; -} - -bool AudioPlayArrayResmp::playRaw(const unsigned int *data, uint32_t numSamples, uint16_t numChannels) -{ - return playRaw((int16_t *) data, numSamples, numChannels); -} - -bool AudioPlayArrayResmp::playWav(int16_t *data, uint32_t fileSize) -{ - stop(); - bool playing = arrayReader.playWav(data, fileSize); - - return playing; -} - -bool AudioPlayArrayResmp::playWav(const unsigned int *data, uint32_t fileSize) { - return playWav((int16_t *) data, fileSize); -} - -void AudioPlayArrayResmp::stop() -{ - arrayReader.stop(); -} - -void AudioPlayArrayResmp::update() -{ - int _numChannels = arrayReader.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 (!arrayReader.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 (arrayReader.available()) { - // we can read more data from the file... - n = arrayReader.read((void**)data, AUDIO_BLOCK_SAMPLES); - for (int channel=0; channel < _numChannels; channel++) { - for (i=n; i < AUDIO_BLOCK_SAMPLES; i++) { - blocks[channel]->data[i] = 0; - } - transmit(blocks[channel], channel); - } - - if(_numChannels == 1) { - transmit(blocks[0], 1); - } - } else { - arrayReader.close(); - } - for (int channel=0; channel < _numChannels; channel++) { - release(blocks[channel]); - } -} - -#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 - -uint32_t AudioPlayArrayResmp::positionMillis() -{ - return ((uint64_t)file_size * B2M) >> 32; -} - -uint32_t AudioPlayArrayResmp::lengthMillis() -{ - return ((uint64_t)file_size * B2M) >> 32; -} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/src/playresmp.h b/third-party/TeensyVariablePlayback/src/playresmp.h index 984c5c8..12df93f 100644 --- a/third-party/TeensyVariablePlayback/src/playresmp.h +++ b/third-party/TeensyVariablePlayback/src/playresmp.h @@ -91,7 +91,7 @@ class AudioPlayResmp : public AudioStream if (_numChannels == -1) return; - unsigned int n; + unsigned int i, n; audio_block_t *blocks[_numChannels]; int16_t *data[_numChannels]; // only update if we're playing @@ -136,4 +136,4 @@ class AudioPlayResmp : public AudioStream TResamplingReader *reader; }; -#endif // TEENSY_RESAMPLING_SDREADER_PLAYRESMP_H +#endif // TEENSY_RESAMPLING_SDREADER_PLAYRESMP_H \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/src/playsdresmp.cpp b/third-party/TeensyVariablePlayback/src/playsdresmp.cpp deleted file mode 100644 index d2b0b00..0000000 --- a/third-party/TeensyVariablePlayback/src/playsdresmp.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// Created by Nicholas Newdigate on 18/07/2020. -// - -#include "playsdresmp.h" -#include "spi_interrupt.h" -#include "waveheaderparser.h" - -void AudioPlaySdResmp::begin() -{ - file_size = 0; - sdReader.begin(); -} - -bool AudioPlaySdResmp::playRaw(const char *filename, uint16_t numChannels) -{ - stop(); - bool playing = sdReader.playRaw(filename, numChannels); - return playing; -} - -bool AudioPlaySdResmp::playWav(const char *filename) -{ - stop(); - bool playing = sdReader.playWav(filename); - return playing; - -} - -void AudioPlaySdResmp::stop() -{ - sdReader.stop(); -} - -void AudioPlaySdResmp::update() -{ - int _numChannels = sdReader.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 (!sdReader.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 (sdReader.available()) { - // we can read more data from the file... - n = sdReader.read((void**)data, AUDIO_BLOCK_SAMPLES); - for (int channel=0; channel < _numChannels; channel++) { - for (i=n; i < AUDIO_BLOCK_SAMPLES; i++) { - blocks[channel]->data[i] = 0; - } - transmit(blocks[channel], channel); - } - - if(_numChannels == 1) { - transmit(blocks[0], 1); - } - } else { - sdReader.close(); - } - for (int channel=0; channel < _numChannels; channel++) { - release(blocks[channel]); - } -} - -uint32_t AudioPlaySdResmp::positionMillis() -{ - return sdReader.positionMillis(); -} - -uint32_t AudioPlaySdResmp::lengthMillis() -{ - return sdReader.lengthMillis(); -} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/test.sh b/third-party/TeensyVariablePlayback/test.sh old mode 100644 new mode 100755 diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD b/third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD old mode 100644 new mode 100755