parent
dcc4304c70
commit
c1f3ae4d53
@ -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) |
||||
|
@ -1 +0,0 @@ |
||||
#include "IndexableFile.h" |
@ -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<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; |
||||
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(); |
||||
} |
||||
} |
||||
|
@ -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<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) |
||||
{ |
||||
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(); |
||||
} |
@ -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; |
||||
} |
@ -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(); |
||||
} |
Loading…
Reference in new issue