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 18010ea..04669e6 100644 --- a/third-party/TeensyVariablePlayback/README.md +++ b/third-party/TeensyVariablePlayback/README.md @@ -9,12 +9,20 @@ [![Commits](https://img.shields.io/github/commit-activity/m/newdigate/teensy-variable-playback)](https://github.com/newdigate/teensy-variable-playback/graphs/contributors) ![s](https://img.shields.io/badge/dynamic/json?color=%23e85b46&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F4105381) -play 16-bit audio samples at variable playback rates on teensy +play 16-bit PCM raw or wav audio samples at variable playback rates on teensy +* **Note** : this library only works with signed 16-bit integer samples. Floating point samples will not play. * for best performance, use SDXC UHS 30MB/sec Application Performance Class 2 (A2) class micro sd-card. * [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) * 28/07/2021: v1.0.10: Fix issues when starting playback in reverse * 23/07/2021: v1.0.9: Fix issue which crashes teensy when playing multiple files from SD card using array of filenames @@ -100,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/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/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/sd_play_all/sd_play_all.ino b/third-party/TeensyVariablePlayback/examples/sd_play_all/sd_play_all.ino index 560e593..41d190d 100644 --- a/third-party/TeensyVariablePlayback/examples/sd_play_all/sd_play_all.ino +++ b/third-party/TeensyVariablePlayback/examples/sd_play_all/sd_play_all.ino @@ -133,6 +133,7 @@ void populateFilenames(char *directory, char **filenames) { if ((m > 0 || a > 0) && (underscore != 0)) { filenames[index] = new char[curfile.length()+1] {0}; memcpy(filenames[index], curfile.c_str(), curfile.length()); + index++; } files.close(); } @@ -143,4 +144,4 @@ void populateFilenames(char *directory, char **filenames) { 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 2fa62d0..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.11", + "version": "1.0.14", "export": { "exclude": [ ".vscode", @@ -26,11 +26,10 @@ "type": "git", "url": "https://github.com/newdigate/teensy-variable-playback" }, - "dependencies": - { - "name": "Audio", - "frameworks": "arduino" - }, + "dependencies":[{ + "name": "Audio", + "frameworks": "arduino" + }], "examples": [ "examples/*/*.ino", "examples/*/*/*.ino" diff --git a/third-party/TeensyVariablePlayback/library.properties b/third-party/TeensyVariablePlayback/library.properties index c0c49c8..a75ee4c 100644 --- a/third-party/TeensyVariablePlayback/library.properties +++ b/third-party/TeensyVariablePlayback/library.properties @@ -1,5 +1,5 @@ name=TeensyVariablePlayback -version=1.0.11 +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.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/IndexableFile.h b/third-party/TeensyVariablePlayback/src/IndexableFile.h index 9920f23..a4d4ea6 100644 --- a/third-party/TeensyVariablePlayback/src/IndexableFile.h +++ b/third-party/TeensyVariablePlayback/src/IndexableFile.h @@ -18,19 +18,24 @@ 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(File file) : + IndexableFile(const char *filename) : _buffers(), - buffer_to_index_shift(log2(BUFFER_SIZE)) { - _file = file; + buffer_to_index_shift(log2(BUFFER_SIZE)) + { + _filename = new char[strlen(filename)+1] {0}; + memcpy(_filename, filename, strlen(filename)); } - ~IndexableFile() { + + virtual ~IndexableFile() { close(); } @@ -49,12 +54,14 @@ public: next->buffer = new int16_t[BUFFER_SIZE]; size_t basePos = indexFor_i << buffer_to_index_shift; size_t seekPos = basePos * element_size; - - __disable_irq(); _file.seek(seekPos); int16_t bytesRead = _file.read(next->buffer, BUFFER_SIZE * element_size); - __enable_irq(); - + #ifndef TEENSYDUINO + if (!_file.available()){ + _file.close(); + _file = open(_filename); + } + #endif next->buffer_size = bytesRead; _buffers.push_back(next); match = next; @@ -63,20 +70,25 @@ public: } void close() { - if (_file.available()) { - __disable_irq(); + if (_file.available()) { _file.close(); - __enable_irq(); } - for (auto && x : _buffers){ + for (auto && x : _buffers){ delete [] x->buffer; delete x; } _buffers.clear(); + + if (_filename != nullptr) { + delete [] _filename; + _filename = nullptr; + } } -private: - File _file; + +protected: + TFile _file; + char *_filename; std::vector _buffers; indexedbuffer* find_with_index(uint32_t i) { @@ -91,4 +103,6 @@ private: } + + #endif diff --git a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp b/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp deleted file mode 100644 index 9edc8c0..0000000 --- a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.cpp +++ /dev/null @@ -1,321 +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; - WaveHeaderParser wavHeaderParser; - wavHeaderParser.readWaveHeaderFromBuffer((const char *) array, 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); - _header_offset = 22; - _file_size = wav_header.data_bytes + 44; //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/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/ResamplingSdReader.cpp b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp deleted file mode 100644 index b79b431..0000000 --- a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.cpp +++ /dev/null @@ -1,352 +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(int16_t **buf) { - 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 < AUDIO_BLOCK_SAMPLES) { - - 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: - { - /* 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 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) -{ - close(); - - if (!isWave) // if raw file, then hardcode the numChannels as per the parameter - setNumChannels(numChannelsIfRaw); - - __disable_irq(); - File file = SD.open(filename); - __enable_irq(); - - if (!file) { -// StopUsingSPI(); - Serial.print(F("Not able to open file: ")); - Serial.println(filename); - return false; - } - - __disable_irq(); - _file_size = file.size(); - __enable_irq(); - - wav_header wav_header; - WaveHeaderParser wavHeaderParser; - - wavHeaderParser.readWaveHeader(wav_header, file); - if (wav_header.bit_depth != 16) { - Serial.print(F("Needs 16 bit audio! Aborting.... (got ")); - Serial.print(wav_header.bit_depth); - Serial.println(F(")")); - __disable_irq(); - file.close(); - __enable_irq(); - return false; - } - setNumChannels(wav_header.num_channels); - _header_offset = 22; - _loop_finish = (wav_header.data_bytes / 2) + _header_offset; - - if (_file_size <= _header_offset * newdigate::IndexableFile<128, 2>::element_size) { - _playing = false; - Serial.print(F("Wave file contains no samples: ")); - Serial.println(filename); -// StopUsingSPI(); - __disable_irq(); - file.close(); - __enable_irq(); - return false; - } - - _sourceBuffer = new newdigate::IndexableFile<128, 2>(file); - _loop_start = _header_offset; - - 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) { - _playing = false; - } -} - -int ResamplingSdReader::available(void) { - return _playing; -} - -void ResamplingSdReader::close(void) { - - Serial.printf("sdreader close\n"); - if (_playing) - stop(); - - if (_sourceBuffer != nullptr) { - _sourceBuffer->close(); - delete _sourceBuffer; - _sourceBuffer = nullptr; - } - deleteInterpolationPoints(); -} \ No newline at end of file diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h index 49ff25f..77c955d 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h +++ b/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h @@ -10,143 +10,72 @@ #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(int16_t **buf); - 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; + return _playing; } - loop_type getLoopType(){ - return _loopType; + File open(char *filename) override { + return SD.open(filename); } - int available(void); - void reset(void); - void close(void); - - 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(); + 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) { - return ((uint64_t) _file_size * B2M) >> 32; - } + if (_file_size == 0) return 0; - uint32_t lengthMillis(void) { - return ((uint64_t) _file_size * B2M) >> 32; - } -private: - volatile bool _playing = false; - - uint32_t _file_size; - uint32_t _header_offset = 0; // == (header size in bytes ) / 2 - - double _playbackRate = 1.0; - double _remainder = 0.0; - loop_type _loopType = looptype_none; - unsigned int _bufferPosition = 0; - uint32_t _loop_start = 0; - uint32_t _loop_finish = 0; - int16_t _numChannels = -1; - uint16_t _numInterpolationPointsChannels = 0; - 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 - } + return (uint32_t) (( (double)_bufferPosition * lengthMillis() ) / (double)(_file_size/2)); } - void StopUsingSPI() { - if (isUsingSPI) { - isUsingSPI = false; -#if defined(HAS_KINETIS_SDHC) - if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI(); -#else - AudioStopUsingSPI(); -#endif - } + uint32_t lengthMillis(void) { + return ((uint64_t)_file_size * B2M) >> 32; } - - 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/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.cpp b/third-party/TeensyVariablePlayback/src/playarrayresmp.cpp deleted file mode 100644 index 1b180e9..0000000 --- a/third-party/TeensyVariablePlayback/src/playarrayresmp.cpp +++ /dev/null @@ -1,97 +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::close() -{ - arrayReader.close(); -} - -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/playarrayresmp.h b/third-party/TeensyVariablePlayback/src/playarrayresmp.h index ff8e21a..4f37701 100644 --- a/third-party/TeensyVariablePlayback/src/playarrayresmp.h +++ b/third-party/TeensyVariablePlayback/src/playarrayresmp.h @@ -11,61 +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); - void close(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/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.cpp b/third-party/TeensyVariablePlayback/src/playsdresmp.cpp deleted file mode 100644 index 8a6f040..0000000 --- a/third-party/TeensyVariablePlayback/src/playsdresmp.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -// Created by Nicholas Newdigate on 18/07/2020. -// - -#include "playsdresmp.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 n; - audio_block_t *blocks[_numChannels]; - int16_t *data[_numChannels]; - - // only update if we're playing - if (!sdReader.isPlaying()) return; - - if (sdReader.available()) { - // 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; - } - - // we can read more data from the file... - n = sdReader.read(data); - - 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); - } - - if (n < AUDIO_BLOCK_SAMPLES) { - sdReader.close(); - } - } else { - sdReader.close(); - } - for (int channel=0; channel < _numChannels; channel++) { - release(blocks[channel]); - blocks[channel] = NULL; - } -} - -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/src/playsdresmp.h b/third-party/TeensyVariablePlayback/src/playsdresmp.h index faa2dfa..fdc039d 100644 --- a/third-party/TeensyVariablePlayback/src/playsdresmp.h +++ b/third-party/TeensyVariablePlayback/src/playsdresmp.h @@ -8,56 +8,24 @@ #include "Arduino.h" #include "AudioStream.h" +#include "SD.h" #include "stdint.h" #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/waveheaderparser.h b/third-party/TeensyVariablePlayback/src/waveheaderparser.h index b5f23d7..e8d7a56 100644 --- a/third-party/TeensyVariablePlayback/src/waveheaderparser.h +++ b/third-party/TeensyVariablePlayback/src/waveheaderparser.h @@ -14,47 +14,70 @@ 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_header &wav_header) { - __disable_irq(); + bool readWaveHeader(const char *filename, wav_header &header, wav_data_header &wav_data_header) { File wavFile = SD.open(filename); - __enable_irq(); if (!wavFile) { Serial.printf("Not able to open wave file... %s\n", filename); return false; } - bool result = readWaveHeader(header, wavFile); + bool result = readWaveHeader(filename, header, wavFile); + if (result) { + wavFile.seek(36); + unsigned char buffer[8]; + size_t bytesRead = wavFile.read(buffer, 8); + if (bytesRead != 8) { + Serial.printf("Not able to read header... %s\n", filename); + result = false; + } + + if (result) { + unsigned infoTagsSize; + result = readInfoTags(buffer, 0, infoTagsSize); + + if (result) { + wavFile.seek(36 + infoTagsSize); + bytesRead = wavFile.read(buffer, 8); + if (bytesRead != 8) { + Serial.printf("Not able to read header... %s\n", filename); + return false; + } + + result = readDataHeader(buffer, 0, wav_data_header); + } + } + } wavFile.close(); return result; } - bool readWaveHeader(wav_header &header, File wavFile) { - char buffer[44]; - __disable_irq(); - int bytesRead = wavFile.read(buffer, 44); - __enable_irq(); - if (bytesRead != 44) { - Serial.printf("expected 44 bytes (was %d)\n", bytesRead); + bool readWaveHeader(const char *filename, wav_header &header, File &wavFile) { + char buffer[36]; + int bytesRead = wavFile.read(buffer, 36); + if (bytesRead != 36) { + Serial.printf("expected 36 bytes (was %d)\n", bytesRead); return false; } return readWaveHeaderFromBuffer(buffer, header); @@ -112,18 +135,45 @@ public: auto bit_depth = static_cast(b[35] << 8 | b[34]); header.bit_depth = bit_depth; - for (int i=0; i < 4; i++) - header.data_header[i] = buffer[i+36]; - if (buffer[36] != 'd' || buffer[37] != 'a' || buffer[38] != 't' || buffer[39] != 'a') { + return true; + } + + bool readInfoTags(unsigned char *buffer, size_t offset, unsigned &infoTagsSize) { + if ( buffer[offset+0] == 'L' + && buffer[offset+1] == 'I' + && buffer[offset+2] == 'S' + && buffer[offset+3] == 'T') { + infoTagsSize = static_cast(buffer[offset+7] << 24 | buffer[offset+6] << 16 | buffer[offset+5] << 8 | buffer[offset+4]); + infoTagsSize += 8; + return true; + } + + if ( buffer[offset+0] == 'd' + && buffer[offset+1] == 'a' + && buffer[offset+2] == 't' + && buffer[offset+3] == 'a') { + infoTagsSize = 0; + return true; + } + + Serial.println("expected 'data' or 'LIST'..."); + return false; + } + + bool readDataHeader(unsigned char *buffer, size_t offset, wav_data_header &data_header) { + + for (int i=0; i < 4; i++) + data_header.data_header[i] = buffer[i+offset]; + + if (buffer[offset+0] != 'd' || buffer[offset+1] != 'a' || buffer[offset+2] != 't' || buffer[offset+3] != 'a') { Serial.printf("expected data... (was %d)\n", buffer); return false; } - auto data_bytes = static_cast(b[43] << 24 | b[42] << 16 | b[41] << 8 | b[40]); - header.data_bytes = data_bytes; + auto data_bytes = static_cast(buffer[offset+7] << 24 | buffer[offset+6] << 16 | buffer[offset+5] << 8 | buffer[offset+4]); + data_header.data_bytes = data_bytes; return true; } - private: }; 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/CMakeLists.txt b/third-party/TeensyVariablePlayback/test/CMakeLists.txt index b6116c3..74b6efc 100644 --- a/third-party/TeensyVariablePlayback/test/CMakeLists.txt +++ b/third-party/TeensyVariablePlayback/test/CMakeLists.txt @@ -57,6 +57,7 @@ if (DEFINED BUILD_FOR_LINUX) audio/wav/AudioWavFixture.h audio/wav/test_wav_mono_loop_forward_playback.cpp audio/wav/test_wav_stereo_loop_forward_playback.cpp + audio/wav/test_wav_tags.cpp low_level/sd/ResamplingReaderFixture.h low_level/sd/test_raw_mono_noloop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp b/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp index 5a5424a..0428821 100644 --- a/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp +++ b/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp @@ -14,6 +14,11 @@ extern unsigned int kick_raw_len; // in bytes, divide by 2 to get samples BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const uint16_t numberOfChannels = 1; + + const std::string referencePath = "test/resources/reference/"; + const std::string inputPath = "test/resources/input/"; + const std::string outputPath = "output/"; + BOOST_FIXTURE_TEST_CASE(Array_fwd_1_0000_quadratic_mono_noloop, AudioArrayFixture) { // GUItool: begin automatically generated code @@ -25,10 +30,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 1.0; const std::string testName = "Array_fwd_1_0000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -59,10 +64,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 0.5; const std::string testName = "Array_fwd_0_5000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -94,10 +99,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 2.0; const std::string testName = "Array_fwd_2_0000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -129,10 +134,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 0.7437; const std::string testName = "Array_fwd_0_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -165,10 +170,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 1.7437; const std::string testName = "Array_fwd_1_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -200,10 +205,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_mono_loop_forward_playback) const double playbackRate = 8.7437; const std::string testName = "Array_fwd_8_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp b/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp index ad0c1e5..f0e83fc 100644 --- a/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp +++ b/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp @@ -14,6 +14,10 @@ extern unsigned int stereo_souljah_raw_len; BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const uint16_t numberOfChannels = 2; + const std::string referencePath = "test/resources/reference/"; + const std::string inputPath = "test/resources/input/"; + const std::string outputPath = "output/"; + BOOST_FIXTURE_TEST_CASE(Array_fwd_1_0000_quadratic_stereo_noloop, AudioArrayFixture) { // GUItool: begin automatically generated code @@ -26,10 +30,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 1.0; const std::string testName = "Array_fwd_1_0000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; - - testout.saveOutputFile(outputFile.c_str()); + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; + + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -61,10 +65,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 0.5; const std::string testName = "Array_fwd_0_5000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -97,10 +101,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 2.0; const std::string testName = "Array_fwd_2_0000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -133,10 +137,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 0.7437; const std::string testName = "Array_fwd_0_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -170,10 +174,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 1.7437; const std::string testName = "Array_fwd_1_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); @@ -206,10 +210,10 @@ BOOST_AUTO_TEST_SUITE(test_audio_array_stereo_loop_forward_playback) const double playbackRate = 8.7437; const std::string testName = "Array_fwd_8_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath+testName+".wav"; - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); memory.begin(); memory.enableInterpolation(true); memory.setPlaybackRate(playbackRate); diff --git a/third-party/TeensyVariablePlayback/test/audio/output_test.h b/third-party/TeensyVariablePlayback/test/audio/output_test.h index 5da06c3..f6f9745 100644 --- a/third-party/TeensyVariablePlayback/test/audio/output_test.h +++ b/third-party/TeensyVariablePlayback/test/audio/output_test.h @@ -21,33 +21,34 @@ public: virtual void update(void); void begin(void); static void isr(void); - void saveOutputFile(const char* filename){ - if (num_inputs == 0) return; + bool saveOutputFile(const char * path, const char* filename){ + if (num_inputs == 0) return false; char cwd[500]; if (getcwd(cwd, sizeof(cwd)) != NULL) { printf("Current working dir: %s\n", cwd); } else { perror("getcwd() error"); } - string outputPath = string(cwd) + "/output/"; + string outputPath = string(cwd) + "/" + string(path); __filesystem::path p(outputPath); if (! __filesystem::exists(p) ) __filesystem::create_directories(outputPath); - string filePath = outputPath + string(filename); - std::cout << "saving output audio .wav file to " << filePath << std::endl; - _outputFile.open(filePath); + _filePath = outputPath + string(filename); + std::cout << "saving output audio .wav file to " << _filePath << std::endl; + _outputFile.open(_filePath, ios_base::trunc | ios_base::out); if (!_outputFile.is_open()) { - Serial.println("couldn't open file for recording..."); + Serial.printf("couldn't open file for recording...%s\n", _filePath.c_str()); + return false; } else { _filename = filename; _outputFile.write((char*)test_output_wav_header, 44); - _saveToFile = true; + _saveToFile = true; + return true; } } void closeOutputfile(uint16_t numChannels) { if (!_saveToFile) return; - if (_outputFile.is_open()) { _saveToFile = false; char buf[4]; @@ -82,6 +83,7 @@ public: } protected: std::ofstream _outputFile; + std::string _filePath; static audio_block_t *block_left_1st; static audio_block_t *block_right_1st; static bool update_responsibility; diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp b/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp index b26c2df..a771dd5 100644 --- a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp +++ b/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp @@ -11,6 +11,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const uint16_t numberOfChannels = 1; + + const std::string referencePath = "test/resources/reference/"; + const std::string inputPath = "test/resources/input/"; + const std::string outputPath = "output/"; + BOOST_FIXTURE_TEST_CASE(Wav_fwd_1_0000_quadratic_mono_noloop, AudioWavFixture) { // GUItool: begin automatically generated code @@ -18,15 +23,15 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) TestAudioOutput testout; //xy=612,224 AudioConnection patchCord1(wave, 0, testout, 0); // GUItool: end automatically generated code - + const double playbackRate = 1.0; const std::string testName = "Wav_fwd_1_0000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referencePath = "test/resources/reference/"; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); - testout.saveOutputFile(outputFile.c_str()); + SD.setSDCardFolderPath(inputPath); + + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); @@ -58,12 +63,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const double playbackRate = 0.5; const std::string testName = "Wav_fwd_0_5000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referencePath = "test/resources/reference/"; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -95,12 +99,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const double playbackRate = 2.0; const std::string testName = "Wav_fwd_2_0000_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referencePath = "test/resources/reference/"; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -132,12 +135,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const double playbackRate = 0.7437; const std::string testName = "Wav_fwd_0_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referencePath = "test/resources/reference/"; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -170,12 +172,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const double playbackRate = 1.7437; const std::string testName = "Wav_fwd_1_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referencePath = "test/resources/reference/"; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -207,10 +208,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_mono_loop_forward_playback) const double playbackRate = 8.7437; const std::string testName = "Wav_fwd_8_7437_quadratic_mono_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath + testName + ".wav"; + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp b/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp index 4902a0f..fa1738d 100644 --- a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp +++ b/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp @@ -10,9 +10,12 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) + const uint16_t numberOfChannels = 2; + const std::string referencePath = "test/resources/reference/"; + const std::string inputPath = "test/resources/input/"; + const std::string outputPath = "output/"; - const uint16_t numberOfChannels = 2; BOOST_FIXTURE_TEST_CASE(Wav_fwd_1_0000_quadratic_stereo_noloop, AudioWavFixture) { // GUItool: begin automatically generated code @@ -25,11 +28,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 1.0; const std::string testName = "Wav_fwd_1_0000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -61,11 +64,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 0.5; const std::string testName = "Wav_fwd_0_5000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); @@ -99,10 +102,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 2.0; const std::string testName = "Wav_fwd_2_0000_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; - const std::string referenceFileName = "test/resources/reference/"+testName+".wav"; + const std::string outputFileName = outputPath + outputFile; + const std::string referenceFileName = referencePath + testName + ".wav"; + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -135,11 +139,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 0.7437; const std::string testName = "Wav_fwd_0_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -173,11 +177,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 1.7437; const std::string testName = "Wav_fwd_1_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); @@ -210,11 +214,11 @@ BOOST_AUTO_TEST_SUITE(test_audio_wav_stereo_loop_forward_playback) const double playbackRate = 8.7437; const std::string testName = "Wav_fwd_8_7437_quadratic_stereo_noloop"; const std::string outputFile = testName+".wav"; - const std::string outputFileName = "output/" + outputFile; + const std::string outputFileName = outputPath + outputFile; const std::string referenceFileName = referencePath + testName + ".wav"; - SD.setSDCardFolderPath(referencePath); + SD.setSDCardFolderPath(inputPath); - testout.saveOutputFile(outputFile.c_str()); + testout.saveOutputFile(outputPath.c_str(), outputFile.c_str()); wave.begin(); wave.enableInterpolation(true); wave.setPlaybackRate(playbackRate); 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]; diff --git a/third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp b/third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp index 6487cdf..59096e2 100644 --- a/third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp +++ b/third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp @@ -23,7 +23,8 @@ BOOST_AUTO_TEST_SUITE(WaveHeaderParsingTests) SD.setSDCardFileData((char*) test_sndhdrdata_sndhdr_wav, test_sndhdrdata_sndhdr_wav_len); wav_header header; - bool success = waveHeaderParser->readWaveHeader("blah.wav", header); + wav_data_header data_header; + bool success = waveHeaderParser->readWaveHeader("blah.wav", header, data_header); BOOST_CHECK_EQUAL(success, true); const char expectedRIFF[5] = "RIFF"; BOOST_CHECK_EQUAL_COLLECTIONS(&header.riff_header[0], &header.riff_header[3],&expectedRIFF[0], &expectedRIFF[3]); @@ -33,7 +34,7 @@ BOOST_AUTO_TEST_SUITE(WaveHeaderParsingTests) const char expectedfmt[5] = "fmt "; BOOST_CHECK_EQUAL_COLLECTIONS(&header.fmt_header[0], &header.fmt_header[3],&expectedfmt[0], &expectedfmt[3]); const char expecteddata[5] = "data"; - BOOST_CHECK_EQUAL_COLLECTIONS(&header.data_header[0], &header.data_header[3],&expecteddata[0], &expecteddata[3]); + BOOST_CHECK_EQUAL_COLLECTIONS(&data_header.data_header[0], &data_header.data_header[3],&expecteddata[0], &expecteddata[3]); //BOOST_CHECK_EQUAL(File::numOpenFiles,0); //BOOST_CHECK_EQUAL(File::numInstances,0); }