Newest version of TeensyVariablePlayback added.

Fixes for performance data (only midi channel adaptions).
Added midi learning lowest/highest note for Dexed and EP.
dev
Holger Wirtz 1 year ago
parent acc4740ff8
commit 7d0a2a2922
  1. 131
      MicroDexed.ino
  2. 33
      UI.hpp
  3. 2
      addon/SD/PERFORMANCE/0/drmmap.json
  4. 4
      addon/SD/PERFORMANCE/0/epiano.json
  5. 4
      addon/SD/PERFORMANCE/0/voice1.json
  6. 4
      addon/SD/PERFORMANCE/0/voice2.json
  7. 20
      config.h
  8. 0
      third-party/TeensyVariablePlayback/build-linux.sh
  9. 0
      third-party/TeensyVariablePlayback/build-t41.sh
  10. 1
      third-party/TeensyVariablePlayback/src/IndexableFile.cpp
  11. 340
      third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp
  12. 392
      third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp
  13. 92
      third-party/TeensyVariablePlayback/src/playarrayresmp.cpp
  14. 4
      third-party/TeensyVariablePlayback/src/playresmp.h
  15. 83
      third-party/TeensyVariablePlayback/src/playsdresmp.cpp
  16. 0
      third-party/TeensyVariablePlayback/test.sh
  17. 0
      third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD

@ -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
#endif

@ -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
#endif //ENABLE_LCD_UI

@ -16,5 +16,5 @@
"transpose": 24,
"sound_intensity": 100,
"pan": 20,
"midi_channel": 1
}
"midi_channel": 4
}

@ -29,5 +29,5 @@
"portamento_glissando": 0,
"portamento_time": 0,
"op_enabled": 63,
"midi_channel": 6
}
"midi_channel": 2
}

@ -29,5 +29,5 @@
"portamento_glissando": 0,
"portamento_time": 0,
"op_enabled": 63,
"midi_channel": 5
}
"midi_channel": 3
}

@ -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
*/
*/

@ -1 +0,0 @@
#include "IndexableFile.h"

@ -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<signed int>(_remainder);
_remainder -= static_cast<double>(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();
}
}

@ -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<signed int>(_remainder);
_remainder -= static_cast<double>(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();
}

@ -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;
}

@ -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

@ -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();
}
Loading…
Cancel
Save