#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H #define TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H #include #include #include "loop_type.h" #include "interpolation.h" #include "waveheaderparser.h" namespace newdigate { template class ResamplingReader { public: ResamplingReader() { } virtual ~ResamplingReader() { } virtual TFile open(char *filename) = 0; virtual TArray* createSourceBuffer() = 0; virtual int16_t getSourceBufferValue(long index) = 0; virtual void close(void) = 0; void begin(void) { if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { initializeInterpolationPoints(); } _playing = false; _bufferPosition = _header_offset; _file_size = 0; } bool playRaw(TArray *array, uint32_t length, uint16_t numChannels) { _sourceBuffer = array; stop(); _header_offset = 0; _file_size = length * 2; _loop_start = 0; _loop_finish = length; setNumChannels(numChannels); reset(); _playing = true; return true; } bool playRaw(TArray *array, uint16_t numChannels) { return playRaw(array, false, numChannels); } bool playWav(TArray *array, uint32_t length) { return playRaw(array, true); } bool play(const char *filename, bool isWave, uint16_t numChannelsIfRaw = 0) { close(); if (!isWave) // if raw file, then hardcode the numChannels as per the parameter setNumChannels(numChannelsIfRaw); _filename = new char[strlen(filename)+1] {0}; memcpy(_filename, filename, strlen(filename) + 1); TFile file = open(_filename); if (!file) { Serial.printf("Not able to open file: %s\n", _filename); if (_filename) delete [] _filename; _filename = nullptr; return false; } _file_size = file.size(); if (isWave) { wav_header wav_header; wav_data_header data_header; WaveHeaderParser wavHeaderParser; char buffer[36]; size_t bytesRead = file.read(buffer, 36); wavHeaderParser.readWaveHeaderFromBuffer((const char *) buffer, wav_header); if (wav_header.bit_depth != 16) { Serial.printf("Needs 16 bit audio! Aborting.... (got %d)", wav_header.bit_depth); return false; } setNumChannels(wav_header.num_channels); bytesRead = file.read(buffer, 8); unsigned infoTagsSize; if (!wavHeaderParser.readInfoTags((unsigned char *)buffer, 0, infoTagsSize)) { Serial.println("Not able to read header! Aborting..."); return false; } file.seek(36 + infoTagsSize); bytesRead = file.read(buffer, 8); if (!wavHeaderParser.readDataHeader((unsigned char *)buffer, 0, data_header)) { Serial.println("Not able to read header! Aborting..."); return false; } _header_offset = (44 + infoTagsSize) / 2; _loop_finish = ((data_header.data_bytes) / 2) + _header_offset; } else _loop_finish = _file_size / 2; file.close(); if (_file_size <= _header_offset * sizeof(int16_t)) { _playing = false; if (_filename) delete [] _filename; _filename = nullptr; Serial.printf("Wave file contains no samples: %s\n", filename); return false; } _sourceBuffer = createSourceBuffer(); _loop_start = _header_offset; reset(); _playing = true; return true; } bool playRaw(const char *filename, uint16_t numChannelsIfRaw){ return play(filename, false, numChannelsIfRaw); } bool playWav(const char *filename){ return play(filename, true); } bool play() { stop(); reset(); _playing = true; return true; } void stop(void) { if (_playing) { _playing = false; } } bool isPlaying(void) { return _playing; } unsigned int read(void **buf, uint16_t nsamples) { if (!_playing) return 0; int16_t *index[_numChannels]; unsigned int count = 0; for (int channel=0; channel < _numChannels; channel++) { index[channel] = (int16_t*)buf[channel]; } while (count < nsamples) { for (int channel=0; channel < _numChannels; channel++) { if (readNextValue(index[channel], channel)) { if (channel == _numChannels - 1) count++; index[channel]++; } else { // we have reached the end of the file switch (_loopType) { case looptype_repeat: { if (_playbackRate >= 0.0) _bufferPosition = _loop_start; else _bufferPosition = _loop_finish - _numChannels; break; } case looptype_pingpong: { if (_playbackRate >= 0.0) { _bufferPosition = _loop_finish - _numChannels; //printf("switching to reverse playback...\n"); } else { _bufferPosition = _header_offset; //printf("switching to forward playback...\n"); } _playbackRate = -_playbackRate; break; } case looptype_none: default: { //Serial.printf("end of loop...\n"); /* no looping - return the number of (resampled) bytes returned... */ close(); return count; } } } } } return count; } // read the sample value for given channel and store it at the location pointed to by the pointer 'value' bool readNextValue(int16_t *value, uint16_t channel) { if (_playbackRate >= 0 ) { //forward playback if (_bufferPosition >= _loop_finish ) return false; } else if (_playbackRate < 0) { // reverse playback if (_bufferPosition < _header_offset) return false; } int16_t result = getSourceBufferValue(_bufferPosition + channel); if (_interpolationType == ResampleInterpolationType::resampleinterpolation_linear) { double abs_remainder = abs(_remainder); if (abs_remainder > 0.0) { if (_playbackRate > 0) { if (_remainder - _playbackRate < 0.0){ // we crossed over a whole number, make sure we update the samples for interpolation if (_playbackRate > 1.0) { // need to update last sample _interpolationPoints[channel][1].y = getSourceBufferValue(_bufferPosition-_numChannels); } _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = result; if (_numInterpolationPoints < 2) _numInterpolationPoints++; } } else if (_playbackRate < 0) { if (_remainder - _playbackRate > 0.0){ // we crossed over a whole number, make sure we update the samples for interpolation if (_playbackRate < -1.0) { // need to update last sample _interpolationPoints[channel][1].y = getSourceBufferValue(_bufferPosition+_numChannels); } _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = result; if (_numInterpolationPoints < 2) _numInterpolationPoints++; } } if (_numInterpolationPoints > 1) { result = abs_remainder * _interpolationPoints[channel][1].y + (1.0 - abs_remainder) * _interpolationPoints[channel][0].y; //Serial.printf("[%f]\n", interpolation); } } else { _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = result; if (_numInterpolationPoints < 2) _numInterpolationPoints++; result =_interpolationPoints[channel][0].y; //Serial.printf("%f\n", result); } } else if (_interpolationType == ResampleInterpolationType::resampleinterpolation_quadratic) { double abs_remainder = abs(_remainder); if (abs_remainder > 0.0) { if (_playbackRate > 0) { if (_remainder - _playbackRate < 0.0){ // we crossed over a whole number, make sure we update the samples for interpolation int numberOfSamplesToUpdate = - floor(_remainder - _playbackRate); if (numberOfSamplesToUpdate > 4) numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples for (int i=numberOfSamplesToUpdate; i > 0; i--) { _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; _interpolationPoints[channel][3].y = getSourceBufferValue(_bufferPosition-(i*_numChannels)+1+channel); if (_numInterpolationPoints < 4) _numInterpolationPoints++; } } } else if (_playbackRate < 0) { if (_remainder - _playbackRate > 0.0){ // we crossed over a whole number, make sure we update the samples for interpolation int numberOfSamplesToUpdate = ceil(_remainder - _playbackRate); if (numberOfSamplesToUpdate > 4) numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples for (int i=numberOfSamplesToUpdate; i > 0; i--) { _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; _interpolationPoints[channel][3].y = getSourceBufferValue(_bufferPosition+(i*_numChannels)-1+channel); if (_numInterpolationPoints < 4) _numInterpolationPoints++; } } } if (_numInterpolationPoints >= 4) { //int16_t interpolation = interpolate(_interpolationPoints, 1.0 + abs_remainder, 4); int16_t interpolation = fastinterpolate( _interpolationPoints[channel][0].y, _interpolationPoints[channel][1].y, _interpolationPoints[channel][2].y, _interpolationPoints[channel][3].y, 1.0 + abs_remainder); result = interpolation; //Serial.printf("[%f]\n", interpolation); } else result = 0; } else { _interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; _interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; _interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; _interpolationPoints[channel][3].y = result; if (_numInterpolationPoints < 4) { _numInterpolationPoints++; result = 0; } else result = _interpolationPoints[channel][1].y; //Serial.printf("%f\n", result); } } if (channel == _numChannels - 1) { _remainder += _playbackRate; auto delta = static_cast(_remainder); _remainder -= static_cast(delta); _bufferPosition += (delta * _numChannels); } *value = result; return true; } void setPlaybackRate(double f) { _playbackRate = f; if (f < 0.0 && _bufferPosition == 0) { //_file.seek(_file_size); _bufferPosition = _file_size/2 - _numChannels; } } float playbackRate() { return _playbackRate; } void loop(uint32_t numSamples) { _loop_start = _bufferPosition; _loop_finish = _bufferPosition + numSamples * _numChannels; _loopType = loop_type::looptype_repeat; } void setLoopType(loop_type loopType) { _loopType = loopType; } loop_type getLoopType() { return _loopType; } int available(void) { return _playing; } void reset(void) { if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { initializeInterpolationPoints(); } _numInterpolationPoints = 0; if (_playbackRate > 0.0) { // forward playabck - set _file_offset to first audio block in file _bufferPosition = _header_offset; } else { // reverse playback - forward _file_offset to last audio block in file _bufferPosition = _loop_finish - _numChannels; } } void setLoopStart(uint32_t loop_start) { _loop_start = _header_offset + (loop_start * _numChannels); } void setLoopFinish(uint32_t loop_finish) { // sample number, (NOT byte number) _loop_finish = _header_offset + (loop_finish * _numChannels); } void setInterpolationType(ResampleInterpolationType interpolationType) { if (interpolationType != _interpolationType) { _interpolationType = interpolationType; initializeInterpolationPoints(); } } int16_t getNumChannels() { return _numChannels; } void setNumChannels(uint16_t numChannels) { if (numChannels != _numChannels) { _numChannels = numChannels; initializeInterpolationPoints(); } } void setHeaderSizeInBytes(uint32_t headerSizeInBytes) { _header_offset = headerSizeInBytes / 2; if (_bufferPosition < _header_offset) { if (_playbackRate >= 0) { _bufferPosition = _header_offset; } else _bufferPosition = _loop_finish - _numChannels; } } #define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592 uint32_t positionMillis() { return ((uint64_t)_file_size * B2M) >> 32; } uint32_t lengthMillis() { return ((uint64_t)_file_size * B2M) >> 32; } protected: volatile bool _playing = false; int32_t _file_size; int32_t _header_offset = 0; // == (header size in bytes ) / 2 double _playbackRate = 1.0; double _remainder = 0.0; loop_type _loopType = looptype_none; int _bufferPosition = 0; int32_t _loop_start = 0; int32_t _loop_finish = 0; int16_t _numChannels = -1; uint16_t _numInterpolationPointsChannels = 0; char *_filename = nullptr; TArray *_sourceBuffer = nullptr; ResampleInterpolationType _interpolationType = ResampleInterpolationType::resampleinterpolation_none; unsigned int _numInterpolationPoints = 0; InterpolationData **_interpolationPoints = nullptr; void initializeInterpolationPoints(void) { if (_numChannels < 0) return; deleteInterpolationPoints(); _interpolationPoints = new InterpolationData*[_numChannels]; for (int channel=0; channel < _numChannels; channel++) { InterpolationData *interpolation = new InterpolationData[4]; interpolation[0].y = 0.0; interpolation[1].y = 0.0; interpolation[2].y = 0.0; interpolation[3].y = 0.0; _interpolationPoints[channel] = interpolation ; } _numInterpolationPointsChannels = _numChannels; } void deleteInterpolationPoints(void) { if (!_interpolationPoints) return; for (int i=0; i<_numInterpolationPointsChannels; i++) { delete [] _interpolationPoints[i]; } delete [] _interpolationPoints; _interpolationPoints = nullptr; _numInterpolationPointsChannels = 0; } }; } #endif //TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H