Fixes for performance data (only midi channel adaptions). Added midi learning lowest/highest note for Dexed and EP.dev
parent
acc4740ff8
commit
7d0a2a2922
@ -1 +0,0 @@ |
|||||||
#include "IndexableFile.h" |
|
@ -1,340 +0,0 @@ |
|||||||
#include "ResamplingArrayReader.h" |
|
||||||
#include "interpolation.h" |
|
||||||
#include "waveheaderparser.h" |
|
||||||
|
|
||||||
// read n samples into each buffer (1 buffer per channel)
|
|
||||||
unsigned int ResamplingArrayReader::read(void **buf, uint16_t nsamples) { |
|
||||||
if (!_playing) return 0; |
|
||||||
|
|
||||||
int16_t *index[_numChannels]; |
|
||||||
unsigned int count = 0; |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
index[channel] = (int16_t*)buf[channel]; |
|
||||||
} |
|
||||||
|
|
||||||
while (count < nsamples) { |
|
||||||
|
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
if (readNextValue(index[channel], channel)) { |
|
||||||
if (channel == _numChannels - 1) |
|
||||||
count++; |
|
||||||
index[channel]++; |
|
||||||
} |
|
||||||
else { |
|
||||||
// we have reached the end of the file
|
|
||||||
|
|
||||||
switch (_loopType) { |
|
||||||
case looptype_repeat: |
|
||||||
{ |
|
||||||
if (_playbackRate >= 0.0)
|
|
||||||
_bufferPosition = _loop_start; |
|
||||||
else |
|
||||||
_bufferPosition = _loop_finish - _numChannels; |
|
||||||
|
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
case looptype_pingpong: |
|
||||||
{ |
|
||||||
if (_playbackRate >= 0.0) { |
|
||||||
_bufferPosition = _loop_finish - _numChannels; |
|
||||||
//printf("switching to reverse playback...\n");
|
|
||||||
} |
|
||||||
else { |
|
||||||
_bufferPosition = _header_offset; |
|
||||||
//printf("switching to forward playback...\n");
|
|
||||||
} |
|
||||||
_playbackRate = -_playbackRate; |
|
||||||
break; |
|
||||||
}
|
|
||||||
|
|
||||||
case looptype_none:
|
|
||||||
default: |
|
||||||
{ |
|
||||||
//Serial.printf("end of loop...\n");
|
|
||||||
/* no looping - return the number of (resampled) bytes returned... */ |
|
||||||
_playing = false; |
|
||||||
return count; |
|
||||||
} |
|
||||||
}
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
// read the sample value for given channel and store it at the location pointed to by the pointer 'value'
|
|
||||||
bool ResamplingArrayReader::readNextValue(int16_t *value, uint16_t channel) { |
|
||||||
|
|
||||||
if (_playbackRate >= 0 ) { |
|
||||||
//forward playback
|
|
||||||
if (_bufferPosition >= _loop_finish ) |
|
||||||
return false; |
|
||||||
|
|
||||||
} else if (_playbackRate < 0) { |
|
||||||
// reverse playback
|
|
||||||
if (_bufferPosition < _header_offset) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
int16_t result = _sourceBuffer[_bufferPosition + channel]; |
|
||||||
if (_interpolationType == ResampleInterpolationType::resampleinterpolation_linear) { |
|
||||||
|
|
||||||
double abs_remainder = abs(_remainder); |
|
||||||
if (abs_remainder > 0.0) { |
|
||||||
|
|
||||||
if (_playbackRate > 0) { |
|
||||||
if (_remainder - _playbackRate < 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
|
|
||||||
if (_playbackRate > 1.0) { |
|
||||||
// need to update last sample
|
|
||||||
_interpolationPoints[channel][1].y = _sourceBuffer[_bufferPosition-_numChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_playbackRate < 0) { |
|
||||||
if (_remainder - _playbackRate > 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
|
|
||||||
if (_playbackRate < -1.0) { |
|
||||||
// need to update last sample
|
|
||||||
_interpolationPoints[channel][1].y = _sourceBuffer[_bufferPosition+_numChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (_numInterpolationPoints > 1) { |
|
||||||
result = abs_remainder * _interpolationPoints[channel][1].y + (1.0 - abs_remainder) * _interpolationPoints[channel][0].y; |
|
||||||
//Serial.printf("[%f]\n", interpolation);
|
|
||||||
} |
|
||||||
} else { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
|
|
||||||
result =_interpolationPoints[channel][0].y; |
|
||||||
//Serial.printf("%f\n", result);
|
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_interpolationType == ResampleInterpolationType::resampleinterpolation_quadratic) { |
|
||||||
double abs_remainder = abs(_remainder); |
|
||||||
if (abs_remainder > 0.0) { |
|
||||||
if (_playbackRate > 0) {
|
|
||||||
if (_remainder - _playbackRate < 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
int numberOfSamplesToUpdate = - floor(_remainder - _playbackRate); |
|
||||||
if (numberOfSamplesToUpdate > 4)
|
|
||||||
numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples
|
|
||||||
for (int i=numberOfSamplesToUpdate; i > 0; i--) { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = _sourceBuffer[_bufferPosition-(i*_numChannels)+1+channel]; |
|
||||||
if (_numInterpolationPoints < 4) _numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_playbackRate < 0) {
|
|
||||||
if (_remainder - _playbackRate > 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
int numberOfSamplesToUpdate = ceil(_remainder - _playbackRate); |
|
||||||
if (numberOfSamplesToUpdate > 4)
|
|
||||||
numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples
|
|
||||||
for (int i=numberOfSamplesToUpdate; i > 0; i--) { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = _sourceBuffer[_bufferPosition+(i*_numChannels)-1+channel]; |
|
||||||
if (_numInterpolationPoints < 4) _numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (_numInterpolationPoints >= 4) { |
|
||||||
//int16_t interpolation = interpolate(_interpolationPoints, 1.0 + abs_remainder, 4);
|
|
||||||
int16_t interpolation
|
|
||||||
= fastinterpolate( |
|
||||||
_interpolationPoints[channel][0].y,
|
|
||||||
_interpolationPoints[channel][1].y,
|
|
||||||
_interpolationPoints[channel][2].y,
|
|
||||||
_interpolationPoints[channel][3].y,
|
|
||||||
1.0 + abs_remainder);
|
|
||||||
result = interpolation; |
|
||||||
//Serial.printf("[%f]\n", interpolation);
|
|
||||||
} else
|
|
||||||
result = 0; |
|
||||||
} else { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = result; |
|
||||||
if (_numInterpolationPoints < 4) { |
|
||||||
_numInterpolationPoints++; |
|
||||||
result = 0; |
|
||||||
} else
|
|
||||||
result = _interpolationPoints[channel][1].y; |
|
||||||
//Serial.printf("%f\n", result);
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (channel == _numChannels - 1) { |
|
||||||
_remainder += _playbackRate; |
|
||||||
|
|
||||||
auto delta = static_cast<signed int>(_remainder); |
|
||||||
_remainder -= static_cast<double>(delta); |
|
||||||
_bufferPosition += (delta * _numChannels); |
|
||||||
} |
|
||||||
|
|
||||||
*value = result; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::initializeInterpolationPoints(void) { |
|
||||||
if (_numChannels < 0) |
|
||||||
return; |
|
||||||
|
|
||||||
deleteInterpolationPoints(); |
|
||||||
_interpolationPoints = new InterpolationData*[_numChannels]; |
|
||||||
for (int channel=0; channel < _numChannels; channel++) {
|
|
||||||
InterpolationData *interpolation = new InterpolationData[4]; |
|
||||||
interpolation[0].y = 0.0; |
|
||||||
interpolation[1].y = 0.0;
|
|
||||||
interpolation[2].y = 0.0;
|
|
||||||
interpolation[3].y = 0.0; |
|
||||||
_interpolationPoints[channel] = interpolation ; |
|
||||||
} |
|
||||||
_numInterpolationPointsChannels = _numChannels; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::deleteInterpolationPoints(void) { |
|
||||||
if (!_interpolationPoints) return; |
|
||||||
for (int i=0; i<_numInterpolationPointsChannels; i++) { |
|
||||||
delete [] _interpolationPoints[i]; |
|
||||||
} |
|
||||||
delete [] _interpolationPoints; |
|
||||||
_interpolationPoints = nullptr; |
|
||||||
_numInterpolationPointsChannels = 0; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::begin(void) |
|
||||||
{ |
|
||||||
if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { |
|
||||||
initializeInterpolationPoints(); |
|
||||||
} |
|
||||||
_playing = false; |
|
||||||
_bufferPosition = _header_offset; |
|
||||||
_file_size = 0; |
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingArrayReader::playRaw(int16_t *array, uint32_t length, uint16_t numChannels) |
|
||||||
{ |
|
||||||
_sourceBuffer = array; |
|
||||||
stop(); |
|
||||||
|
|
||||||
_header_offset = 0; |
|
||||||
_file_size = length * 2; |
|
||||||
_loop_start = 0; |
|
||||||
_loop_finish = length; |
|
||||||
setNumChannels(numChannels); |
|
||||||
|
|
||||||
reset(); |
|
||||||
//updateBuffers();
|
|
||||||
_playing = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingArrayReader::playWav(int16_t *array, uint32_t length) // length == total number of 16-bit samples for all channels, including header
|
|
||||||
{ |
|
||||||
_sourceBuffer = array; |
|
||||||
stop(); |
|
||||||
|
|
||||||
wav_header wav_header; |
|
||||||
wav_data_header data_header; |
|
||||||
|
|
||||||
WaveHeaderParser wavHeaderParser; |
|
||||||
if (!wavHeaderParser.readWaveHeaderFromBuffer((const char *) array, wav_header)) { |
|
||||||
Serial.println("Not able to read header! Aborting.... "); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (wav_header.bit_depth != 16) { |
|
||||||
Serial.printf("Needs 16 bit audio! Aborting.... (got %d)\n", wav_header.bit_depth); |
|
||||||
return false; |
|
||||||
} |
|
||||||
setNumChannels(wav_header.num_channels); |
|
||||||
unsigned infoTagsSize; |
|
||||||
if (!wavHeaderParser.readInfoTags((unsigned char *)array, 36, infoTagsSize)) |
|
||||||
{ |
|
||||||
Serial.println("Not able to read header! Aborting..."); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (!wavHeaderParser.readDataHeader((unsigned char *)array, 36 + infoTagsSize, data_header)) { |
|
||||||
Serial.println("Not able to read header! Aborting..."); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
_header_offset = (44 + infoTagsSize) / 2; |
|
||||||
_file_size = data_header.data_bytes + 44 + infoTagsSize; //2 bytes per sample
|
|
||||||
if (_file_size > length * 2){ |
|
||||||
Serial.printf("TeensyVariablePlayback: warning: length of array in bytes (%d) is smaller than the file data size in bytes (%d) according to the header - defaulting length to filesize...", length * 2, _file_size); |
|
||||||
_loop_finish = length; |
|
||||||
} else |
|
||||||
_loop_finish = _file_size / 2; |
|
||||||
_loop_start = _header_offset; |
|
||||||
|
|
||||||
reset(); |
|
||||||
_playing = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingArrayReader::play() |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
reset(); |
|
||||||
_playing = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::reset(){ |
|
||||||
_numInterpolationPoints = 0; |
|
||||||
if (_playbackRate > 0.0) { |
|
||||||
// forward playabck - set _file_offset to first audio block in file
|
|
||||||
_bufferPosition = _header_offset; |
|
||||||
} else { |
|
||||||
// reverse playback - forward _file_offset to last audio block in file
|
|
||||||
_bufferPosition = _loop_finish - _numChannels; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::stop() |
|
||||||
{ |
|
||||||
if (_playing) {
|
|
||||||
_playing = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int ResamplingArrayReader::available(void) { |
|
||||||
return _playing; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingArrayReader::close(void) { |
|
||||||
if (_playing) { |
|
||||||
stop(); |
|
||||||
deleteInterpolationPoints(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -1,392 +0,0 @@ |
|||||||
#include "ResamplingSdReader.h" |
|
||||||
#include "interpolation.h" |
|
||||||
#include "waveheaderparser.h" |
|
||||||
bool ResamplingSdReader::isUsingSPI = false; |
|
||||||
|
|
||||||
// read n samples into each buffer (1 buffer per channel)
|
|
||||||
unsigned int ResamplingSdReader::read(void **buf, uint16_t nsamples) { |
|
||||||
if (!_playing) return 0; |
|
||||||
|
|
||||||
int16_t *index[_numChannels]; |
|
||||||
unsigned int count = 0; |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
index[channel] = (int16_t*)buf[channel]; |
|
||||||
} |
|
||||||
|
|
||||||
while (count < nsamples) { |
|
||||||
|
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
if (readNextValue(index[channel], channel)) { |
|
||||||
if (channel == _numChannels - 1) |
|
||||||
count++; |
|
||||||
index[channel]++; |
|
||||||
} |
|
||||||
else { |
|
||||||
// we have reached the end of the file
|
|
||||||
|
|
||||||
switch (_loopType) { |
|
||||||
case looptype_repeat: |
|
||||||
{ |
|
||||||
if (_playbackRate >= 0.0)
|
|
||||||
_bufferPosition = _loop_start; |
|
||||||
else |
|
||||||
_bufferPosition = _loop_finish / _numChannels - _numChannels; |
|
||||||
|
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
case looptype_pingpong: |
|
||||||
{ |
|
||||||
if (_playbackRate >= 0.0) { |
|
||||||
_bufferPosition = _loop_finish / _numChannels - _numChannels; |
|
||||||
//printf("switching to reverse playback...\n");
|
|
||||||
} |
|
||||||
else { |
|
||||||
_bufferPosition = _header_offset; |
|
||||||
//printf("switching to forward playback...\n");
|
|
||||||
} |
|
||||||
_playbackRate = -_playbackRate; |
|
||||||
break; |
|
||||||
}
|
|
||||||
|
|
||||||
case looptype_none:
|
|
||||||
default: |
|
||||||
{ |
|
||||||
//Serial.printf("end of loop...\n");
|
|
||||||
/* no looping - return the number of (resampled) bytes returned... */ |
|
||||||
_playing = false; |
|
||||||
if (_sourceBuffer) _sourceBuffer->close(); |
|
||||||
_sourceBuffer = nullptr; |
|
||||||
StopUsingSPI(); |
|
||||||
return count; |
|
||||||
} |
|
||||||
}
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
// read the sample value for given channel and store it at the location pointed to by the pointer 'value'
|
|
||||||
bool ResamplingSdReader::readNextValue(int16_t *value, uint16_t channel) { |
|
||||||
|
|
||||||
if (_playbackRate >= 0 ) { |
|
||||||
//forward playback
|
|
||||||
if (_bufferPosition >= _loop_finish ) |
|
||||||
return false; |
|
||||||
|
|
||||||
} else if (_playbackRate < 0) { |
|
||||||
// reverse playback
|
|
||||||
if (_bufferPosition < _header_offset) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
newdigate::IndexableFile<128, 2> &sourceBuffer = (*_sourceBuffer); |
|
||||||
|
|
||||||
int16_t result = sourceBuffer[_bufferPosition + channel]; |
|
||||||
if (_interpolationType == ResampleInterpolationType::resampleinterpolation_linear) { |
|
||||||
|
|
||||||
double abs_remainder = abs(_remainder); |
|
||||||
if (abs_remainder > 0.0) { |
|
||||||
|
|
||||||
if (_playbackRate > 0) { |
|
||||||
if (_remainder - _playbackRate < 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
|
|
||||||
if (_playbackRate > 1.0) { |
|
||||||
// need to update last sample
|
|
||||||
_interpolationPoints[channel][1].y = sourceBuffer[_bufferPosition-_numChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_playbackRate < 0) { |
|
||||||
if (_remainder - _playbackRate > 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
|
|
||||||
if (_playbackRate < -1.0) { |
|
||||||
// need to update last sample
|
|
||||||
_interpolationPoints[channel][1].y = sourceBuffer[_bufferPosition+_numChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (_numInterpolationPoints > 1) { |
|
||||||
result = abs_remainder * _interpolationPoints[channel][1].y + (1.0 - abs_remainder) * _interpolationPoints[channel][0].y; |
|
||||||
//Serial.printf("[%f]\n", interpolation);
|
|
||||||
} |
|
||||||
} else { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = result; |
|
||||||
if (_numInterpolationPoints < 2) |
|
||||||
_numInterpolationPoints++; |
|
||||||
|
|
||||||
result =_interpolationPoints[channel][0].y; |
|
||||||
//Serial.printf("%f\n", result);
|
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_interpolationType == ResampleInterpolationType::resampleinterpolation_quadratic) { |
|
||||||
double abs_remainder = abs(_remainder); |
|
||||||
if (abs_remainder > 0.0) { |
|
||||||
if (_playbackRate > 0) {
|
|
||||||
if (_remainder - _playbackRate < 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
int numberOfSamplesToUpdate = - floor(_remainder - _playbackRate); |
|
||||||
if (numberOfSamplesToUpdate > 4)
|
|
||||||
numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples
|
|
||||||
for (int i=numberOfSamplesToUpdate; i > 0; i--) { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = sourceBuffer[_bufferPosition-(i*_numChannels)+1+channel]; |
|
||||||
if (_numInterpolationPoints < 4) _numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
}
|
|
||||||
else if (_playbackRate < 0) {
|
|
||||||
if (_remainder - _playbackRate > 0.0){ |
|
||||||
// we crossed over a whole number, make sure we update the samples for interpolation
|
|
||||||
int numberOfSamplesToUpdate = ceil(_remainder - _playbackRate); |
|
||||||
if (numberOfSamplesToUpdate > 4)
|
|
||||||
numberOfSamplesToUpdate = 4; // if playbackrate > 4, only need to pop last 4 samples
|
|
||||||
for (int i=numberOfSamplesToUpdate; i > 0; i--) { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = sourceBuffer[_bufferPosition+(i*_numChannels)-1+channel]; |
|
||||||
if (_numInterpolationPoints < 4) _numInterpolationPoints++; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (_numInterpolationPoints >= 4) { |
|
||||||
//int16_t interpolation = interpolate(_interpolationPoints, 1.0 + abs_remainder, 4);
|
|
||||||
int16_t interpolation
|
|
||||||
= fastinterpolate( |
|
||||||
_interpolationPoints[channel][0].y,
|
|
||||||
_interpolationPoints[channel][1].y,
|
|
||||||
_interpolationPoints[channel][2].y,
|
|
||||||
_interpolationPoints[channel][3].y,
|
|
||||||
1.0 + abs_remainder);
|
|
||||||
result = interpolation; |
|
||||||
//Serial.printf("[%f]\n", interpolation);
|
|
||||||
} else
|
|
||||||
result = 0; |
|
||||||
} else { |
|
||||||
_interpolationPoints[channel][0].y = _interpolationPoints[channel][1].y; |
|
||||||
_interpolationPoints[channel][1].y = _interpolationPoints[channel][2].y; |
|
||||||
_interpolationPoints[channel][2].y = _interpolationPoints[channel][3].y; |
|
||||||
_interpolationPoints[channel][3].y = result; |
|
||||||
if (_numInterpolationPoints < 4) { |
|
||||||
_numInterpolationPoints++; |
|
||||||
result = 0; |
|
||||||
} else
|
|
||||||
result = _interpolationPoints[channel][1].y; |
|
||||||
//Serial.printf("%f\n", result);
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (channel == _numChannels - 1) { |
|
||||||
_remainder += _playbackRate; |
|
||||||
|
|
||||||
auto delta = static_cast<signed int>(_remainder); |
|
||||||
_remainder -= static_cast<double>(delta); |
|
||||||
_bufferPosition += (delta * _numChannels); |
|
||||||
} |
|
||||||
|
|
||||||
*value = result; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::initializeInterpolationPoints(void) { |
|
||||||
if (_numChannels < 0) |
|
||||||
return; |
|
||||||
|
|
||||||
deleteInterpolationPoints(); |
|
||||||
_interpolationPoints = new InterpolationData*[_numChannels]; |
|
||||||
for (int channel=0; channel < _numChannels; channel++) {
|
|
||||||
InterpolationData *interpolation = new InterpolationData[4]; |
|
||||||
interpolation[0].y = 0.0; |
|
||||||
interpolation[1].y = 0.0;
|
|
||||||
interpolation[2].y = 0.0;
|
|
||||||
interpolation[3].y = 0.0; |
|
||||||
_interpolationPoints[channel] = interpolation ; |
|
||||||
} |
|
||||||
_numInterpolationPointsChannels = _numChannels; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::deleteInterpolationPoints(void) { |
|
||||||
if (!_interpolationPoints) return; |
|
||||||
for (int i=0; i<_numInterpolationPointsChannels; i++) { |
|
||||||
delete [] _interpolationPoints[i]; |
|
||||||
} |
|
||||||
delete [] _interpolationPoints; |
|
||||||
_interpolationPoints = nullptr; |
|
||||||
_numInterpolationPointsChannels = 0; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::begin(void) |
|
||||||
{ |
|
||||||
if (_interpolationType != ResampleInterpolationType::resampleinterpolation_none) { |
|
||||||
initializeInterpolationPoints(); |
|
||||||
} |
|
||||||
_playing = false; |
|
||||||
_bufferPosition = _header_offset; |
|
||||||
_file_size = 0; |
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingSdReader::playRaw(const char *filename, uint16_t numChannels) { |
|
||||||
return play(filename, false, numChannels);
|
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingSdReader::playWav(const char *filename) { |
|
||||||
return play(filename, true);
|
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingSdReader::play(const char *filename, bool isWave, uint16_t numChannelsIfRaw) |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
//system("echo ${PWD}");
|
|
||||||
if (!isWave) // if raw file, then hardcode the numChannels as per the parameter
|
|
||||||
setNumChannels(numChannelsIfRaw); |
|
||||||
|
|
||||||
if (_sourceBuffer) { |
|
||||||
//Serial.printf("closing %s\n", _filename);
|
|
||||||
__disable_irq(); |
|
||||||
_sourceBuffer->close(); |
|
||||||
__enable_irq(); |
|
||||||
} |
|
||||||
if (_sourceBuffer) delete _sourceBuffer; |
|
||||||
if (_filename) delete [] _filename; |
|
||||||
_filename = new char[strlen(filename)+1] {0}; |
|
||||||
memcpy(_filename, filename, strlen(filename) + 1); |
|
||||||
StartUsingSPI(); |
|
||||||
|
|
||||||
__disable_irq(); |
|
||||||
File file = SD.open(_filename); |
|
||||||
__enable_irq(); |
|
||||||
|
|
||||||
if (!file) { |
|
||||||
StopUsingSPI(); |
|
||||||
Serial.printf("Not able to open file: %s\n", _filename); |
|
||||||
if (_filename) delete [] _filename; |
|
||||||
_filename = nullptr; |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
__disable_irq(); |
|
||||||
_file_size = file.size(); |
|
||||||
__enable_irq(); |
|
||||||
|
|
||||||
if (isWave) { |
|
||||||
wav_header wav_header; |
|
||||||
wav_data_header data_header; |
|
||||||
|
|
||||||
WaveHeaderParser wavHeaderParser; |
|
||||||
char buffer[36]; |
|
||||||
__disable_irq(); |
|
||||||
size_t bytesRead = file.read(buffer, 36); |
|
||||||
|
|
||||||
__enable_irq(); |
|
||||||
wavHeaderParser.readWaveHeaderFromBuffer((const char *) buffer, wav_header); |
|
||||||
if (wav_header.bit_depth != 16) { |
|
||||||
Serial.printf("Needs 16 bit audio! Aborting.... (got %d)", wav_header.bit_depth); |
|
||||||
return false; |
|
||||||
} |
|
||||||
setNumChannels(wav_header.num_channels); |
|
||||||
|
|
||||||
bytesRead = file.read(buffer, 8); |
|
||||||
unsigned infoTagsSize; |
|
||||||
if (!wavHeaderParser.readInfoTags((unsigned char *)buffer, 0, infoTagsSize)) |
|
||||||
{ |
|
||||||
Serial.println("Not able to read header! Aborting..."); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
file.seek(36 + infoTagsSize); |
|
||||||
bytesRead = file.read(buffer, 8); |
|
||||||
|
|
||||||
if (!wavHeaderParser.readDataHeader((unsigned char *)buffer, 0, data_header)) { |
|
||||||
Serial.println("Not able to read header! Aborting..."); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
_header_offset = (44 + infoTagsSize) / 2; |
|
||||||
_loop_finish = ((data_header.data_bytes) / 2) + _header_offset;
|
|
||||||
} else
|
|
||||||
_loop_finish = _file_size / 2; |
|
||||||
|
|
||||||
__disable_irq(); |
|
||||||
file.close(); |
|
||||||
__enable_irq(); |
|
||||||
|
|
||||||
_sourceBuffer = new newdigate::IndexableFile<128, 2>(_filename); |
|
||||||
_loop_start = _header_offset; |
|
||||||
if (_file_size <= _header_offset * newdigate::IndexableFile<128, 2>::element_size) { |
|
||||||
_playing = false; |
|
||||||
if (_filename) delete [] _filename; |
|
||||||
_filename = nullptr; |
|
||||||
Serial.printf("Wave file contains no samples: %s\n", filename); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
reset(); |
|
||||||
_playing = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool ResamplingSdReader::play() |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
reset(); |
|
||||||
_playing = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::reset(){ |
|
||||||
initializeInterpolationPoints(); |
|
||||||
if (_playbackRate > 0.0) { |
|
||||||
// forward playabck - set _file_offset to first audio block in file
|
|
||||||
_bufferPosition = _header_offset; |
|
||||||
} else { |
|
||||||
// reverse playback - forward _file_offset to last audio block in file
|
|
||||||
_bufferPosition = _loop_finish / _numChannels - _numChannels; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::stop() |
|
||||||
{ |
|
||||||
if (_playing) { |
|
||||||
__disable_irq(); |
|
||||||
_playing = false; |
|
||||||
//_file.close();
|
|
||||||
__enable_irq(); |
|
||||||
//StopUsingSPI();
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int ResamplingSdReader::available(void) { |
|
||||||
return _playing; |
|
||||||
} |
|
||||||
|
|
||||||
void ResamplingSdReader::close(void) { |
|
||||||
if (_playing) |
|
||||||
stop(); |
|
||||||
if (_sourceBuffer) { |
|
||||||
_sourceBuffer->close(); |
|
||||||
delete _sourceBuffer; |
|
||||||
} |
|
||||||
StopUsingSPI(); |
|
||||||
_sourceBuffer = nullptr; |
|
||||||
//TODO: dispose _sourceBuffer properly
|
|
||||||
deleteInterpolationPoints(); |
|
||||||
} |
|
@ -1,92 +0,0 @@ |
|||||||
//
|
|
||||||
// Created by Nicholas Newdigate on 18/07/2020.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "playarrayresmp.h" |
|
||||||
|
|
||||||
void AudioPlayArrayResmp::begin() |
|
||||||
{ |
|
||||||
file_size = 0; |
|
||||||
arrayReader.begin(); |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlayArrayResmp::playRaw(int16_t *data, uint32_t numSamples, uint16_t numChannels) |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
bool playing = arrayReader.playRaw(data, numSamples, numChannels); |
|
||||||
return playing; |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlayArrayResmp::playRaw(const unsigned int *data, uint32_t numSamples, uint16_t numChannels)
|
|
||||||
{ |
|
||||||
return playRaw((int16_t *) data, numSamples, numChannels); |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlayArrayResmp::playWav(int16_t *data, uint32_t fileSize) |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
bool playing = arrayReader.playWav(data, fileSize); |
|
||||||
|
|
||||||
return playing; |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlayArrayResmp::playWav(const unsigned int *data, uint32_t fileSize) { |
|
||||||
return playWav((int16_t *) data, fileSize); |
|
||||||
} |
|
||||||
|
|
||||||
void AudioPlayArrayResmp::stop() |
|
||||||
{ |
|
||||||
arrayReader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
void AudioPlayArrayResmp::update() |
|
||||||
{ |
|
||||||
int _numChannels = arrayReader.getNumChannels(); |
|
||||||
if (_numChannels == -1) |
|
||||||
return; |
|
||||||
|
|
||||||
unsigned int i, n; |
|
||||||
audio_block_t *blocks[_numChannels]; |
|
||||||
int16_t *data[_numChannels]; |
|
||||||
// only update if we're playing
|
|
||||||
if (!arrayReader.isPlaying()) return; |
|
||||||
|
|
||||||
// allocate the audio blocks to transmit
|
|
||||||
for (int i=0; i < _numChannels; i++) { |
|
||||||
blocks[i] = allocate(); |
|
||||||
if (blocks[i] == nullptr) return; |
|
||||||
data[i] = blocks[i]->data; |
|
||||||
} |
|
||||||
|
|
||||||
if (arrayReader.available()) { |
|
||||||
// we can read more data from the file...
|
|
||||||
n = arrayReader.read((void**)data, AUDIO_BLOCK_SAMPLES); |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
for (i=n; i < AUDIO_BLOCK_SAMPLES; i++) { |
|
||||||
blocks[channel]->data[i] = 0; |
|
||||||
} |
|
||||||
transmit(blocks[channel], channel); |
|
||||||
} |
|
||||||
|
|
||||||
if(_numChannels == 1) { |
|
||||||
transmit(blocks[0], 1); |
|
||||||
} |
|
||||||
} else { |
|
||||||
arrayReader.close(); |
|
||||||
} |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
release(blocks[channel]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
|
|
||||||
|
|
||||||
uint32_t AudioPlayArrayResmp::positionMillis() |
|
||||||
{ |
|
||||||
return ((uint64_t)file_size * B2M) >> 32; |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t AudioPlayArrayResmp::lengthMillis() |
|
||||||
{ |
|
||||||
return ((uint64_t)file_size * B2M) >> 32; |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
//
|
|
||||||
// Created by Nicholas Newdigate on 18/07/2020.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "playsdresmp.h" |
|
||||||
#include "spi_interrupt.h" |
|
||||||
#include "waveheaderparser.h" |
|
||||||
|
|
||||||
void AudioPlaySdResmp::begin() |
|
||||||
{ |
|
||||||
file_size = 0; |
|
||||||
sdReader.begin(); |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlaySdResmp::playRaw(const char *filename, uint16_t numChannels) |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
bool playing = sdReader.playRaw(filename, numChannels); |
|
||||||
return playing; |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioPlaySdResmp::playWav(const char *filename) |
|
||||||
{ |
|
||||||
stop(); |
|
||||||
bool playing = sdReader.playWav(filename); |
|
||||||
return playing; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
void AudioPlaySdResmp::stop() |
|
||||||
{ |
|
||||||
sdReader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
void AudioPlaySdResmp::update() |
|
||||||
{ |
|
||||||
int _numChannels = sdReader.getNumChannels(); |
|
||||||
if (_numChannels == -1) |
|
||||||
return; |
|
||||||
|
|
||||||
unsigned int i, n; |
|
||||||
audio_block_t *blocks[_numChannels]; |
|
||||||
int16_t *data[_numChannels]; |
|
||||||
// only update if we're playing
|
|
||||||
if (!sdReader.isPlaying()) return; |
|
||||||
|
|
||||||
// allocate the audio blocks to transmit
|
|
||||||
for (int i=0; i < _numChannels; i++) { |
|
||||||
blocks[i] = allocate(); |
|
||||||
if (blocks[i] == nullptr) return; |
|
||||||
data[i] = blocks[i]->data; |
|
||||||
} |
|
||||||
|
|
||||||
if (sdReader.available()) { |
|
||||||
// we can read more data from the file...
|
|
||||||
n = sdReader.read((void**)data, AUDIO_BLOCK_SAMPLES); |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
for (i=n; i < AUDIO_BLOCK_SAMPLES; i++) { |
|
||||||
blocks[channel]->data[i] = 0; |
|
||||||
} |
|
||||||
transmit(blocks[channel], channel); |
|
||||||
} |
|
||||||
|
|
||||||
if(_numChannels == 1) { |
|
||||||
transmit(blocks[0], 1); |
|
||||||
} |
|
||||||
} else { |
|
||||||
sdReader.close(); |
|
||||||
} |
|
||||||
for (int channel=0; channel < _numChannels; channel++) { |
|
||||||
release(blocks[channel]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t AudioPlaySdResmp::positionMillis() |
|
||||||
{ |
|
||||||
return sdReader.positionMillis(); |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t AudioPlaySdResmp::lengthMillis() |
|
||||||
{ |
|
||||||
return sdReader.lengthMillis(); |
|
||||||
} |
|
Loading…
Reference in new issue