Fixing some accidentally replaced strlcpy() in some library files.dev
parent
2c1c3bb5bb
commit
bd89c6f07b
@ -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) |
||||
|
@ -0,0 +1,7 @@ |
||||
cmake_minimum_required(VERSION 3.10) |
||||
project(sd_raw) |
||||
set(CMAKE_CXX_STANDARD 14) |
||||
add_definitions(-DPROG_FLASH_SIZE=10000) |
||||
teensy_include_directories(../../src) |
||||
teensy_add_executable(littlefs_raw littlefs_raw.ino) |
||||
teensy_target_link_libraries(littlefs_raw teensy_variable_playback SD SdFat Audio LittleFS SPI SerialFlash cores Wire arm_math) |
@ -0,0 +1,95 @@ |
||||
// Plays a RAW (16-bit signed) PCM audio file at slower or faster rate
|
||||
// this example requires an uSD-card inserted to teensy 3.6 with a file called DEMO.RAW
|
||||
#include <Arduino.h> |
||||
#include <Audio.h> |
||||
#include <LittleFS.h> |
||||
#include <TeensyVariablePlayback.h> |
||||
|
||||
LittleFS_Program myfs; |
||||
#define PROG_FLASH_SIZE 1024 * 1024 * 1 // Specify size to use of onboard Teensy Program Flash chip
|
||||
|
||||
// GUItool: begin automatically generated code
|
||||
AudioPlayLfsResmp playLfsRaw1(myfs); //xy=324,457
|
||||
AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906
|
||||
AudioConnection patchCord1(playLfsRaw1, 0, i2s2, 0); |
||||
AudioConnection patchCord2(playLfsRaw1, 0, i2s2, 1); |
||||
AudioControlSGTL5000 audioShield; |
||||
// GUItool: end automatically generated code
|
||||
|
||||
#define A14 10 |
||||
|
||||
const char* _filename = "DEMO.RAW"; |
||||
const int analogInPin = A14; |
||||
unsigned long lastSamplePlayed = 0; |
||||
uint32_t diskSize; |
||||
|
||||
double getPlaybackRate(int16_t analog) { //analog: 0..1023
|
||||
return (analog - 512.0) / 512.0; |
||||
} |
||||
|
||||
void setup() { |
||||
analogReference(0); |
||||
pinMode(analogInPin, INPUT_DISABLE); // i.e. Analog
|
||||
|
||||
// see if the Flash is present and can be initialized:
|
||||
// lets check to see if the T4 is setup for security first
|
||||
#if ARDUINO_TEENSY40 |
||||
if ((IOMUXC_GPR_GPR11 & 0x100) == 0x100) { |
||||
//if security is active max disk size is 960x1024
|
||||
if (PROG_FLASH_SIZE > 960 * 1024) { |
||||
diskSize = 960 * 1024; |
||||
Serial.printf("Security Enables defaulted to %u bytes\n", diskSize); |
||||
} else { |
||||
diskSize = PROG_FLASH_SIZE; |
||||
Serial.printf("Security Not Enabled using %u bytes\n", diskSize); |
||||
} |
||||
} |
||||
#else |
||||
diskSize = PROG_FLASH_SIZE; |
||||
#endif |
||||
|
||||
// checks that the LittFS program has started with the disk size specified
|
||||
if (!myfs.begin(diskSize)) { |
||||
Serial.printf("Error starting %s\n", "PROGRAM FLASH DISK"); |
||||
while (1) { |
||||
// Error, so don't do anything more - stay stuck here
|
||||
} |
||||
} |
||||
Serial.println("LittleFS initialized."); |
||||
|
||||
audioShield.enable(); |
||||
audioShield.volume(0.5); |
||||
|
||||
playLfsRaw1.enableInterpolation(true); |
||||
int newsensorValue = analogRead(analogInPin); |
||||
playLfsRaw1.setPlaybackRate(getPlaybackRate(newsensorValue)); |
||||
|
||||
AudioMemory(24); |
||||
} |
||||
|
||||
void loop() { |
||||
|
||||
int newsensorValue = analogRead(analogInPin); |
||||
playLfsRaw1.setPlaybackRate(getPlaybackRate(newsensorValue)); |
||||
|
||||
unsigned currentMillis = millis(); |
||||
if (currentMillis > lastSamplePlayed + 500) { |
||||
if (!playLfsRaw1.isPlaying()) { |
||||
playLfsRaw1.playRaw(_filename, 1); |
||||
lastSamplePlayed = currentMillis; |
||||
|
||||
Serial.print("Memory: "); |
||||
Serial.print(AudioMemoryUsage()); |
||||
Serial.print(","); |
||||
Serial.print(AudioMemoryUsageMax()); |
||||
Serial.println(); |
||||
} |
||||
} |
||||
delay(10); |
||||
} |
||||
|
||||
|
||||
namespace std { |
||||
void __throw_bad_function_call() {} |
||||
void __throw_length_error(char const*) {} |
||||
} |
@ -0,0 +1,7 @@ |
||||
cmake_minimum_required(VERSION 3.10) |
||||
project(serialflash_raw) |
||||
set(CMAKE_CXX_STANDARD 14) |
||||
add_definitions(-DPROG_FLASH_SIZE=10000) |
||||
teensy_include_directories(../../src) |
||||
teensy_add_executable(serialflash serialflash.ino) |
||||
teensy_target_link_libraries(serialflash teensy_variable_playback Audio SerialFlash SPI cores Wire arm_math) |
@ -0,0 +1,79 @@ |
||||
// Plays a RAW (16-bit signed) PCM audio file at slower or faster rate
|
||||
// this example requires an uSD-card inserted to teensy 3.6 with a file called DEMO.RAW
|
||||
#include <Arduino.h> |
||||
#include <Audio.h> |
||||
#include <SerialFlash.h> |
||||
#include <TeensyVariablePlayback.h> |
||||
|
||||
#define CSPIN 6 |
||||
|
||||
SerialFlashChip myfs; |
||||
|
||||
// GUItool: begin automatically generated code
|
||||
AudioPlaySerialFlashResmp playSerialFlash1(myfs); //xy=324,457
|
||||
AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906
|
||||
AudioConnection patchCord1(playSerialFlash1, 0, i2s2, 0); |
||||
AudioConnection patchCord2(playSerialFlash1, 0, i2s2, 1); |
||||
AudioControlSGTL5000 audioShield; |
||||
// GUItool: end automatically generated code
|
||||
|
||||
#define A14 10 |
||||
|
||||
const char* _filename = "DEMO.RAW"; |
||||
const int analogInPin = A14; |
||||
unsigned long lastSamplePlayed = 0; |
||||
uint32_t diskSize; |
||||
|
||||
double getPlaybackRate(int16_t analog) { //analog: 0..1023
|
||||
return (analog - 512.0) / 512.0; |
||||
} |
||||
|
||||
void setup() { |
||||
analogReference(0); |
||||
pinMode(analogInPin, INPUT_DISABLE); // i.e. Analog
|
||||
|
||||
|
||||
if (!SerialFlash.begin(CSPIN)) { |
||||
while (1) { |
||||
Serial.println(F("Unable to access SPI Flash chip")); |
||||
delay(1000); |
||||
} |
||||
} |
||||
Serial.println("SerialFlash initialized."); |
||||
|
||||
audioShield.enable(); |
||||
audioShield.volume(0.5); |
||||
|
||||
playSerialFlash1.enableInterpolation(true); |
||||
int newsensorValue = analogRead(analogInPin); |
||||
playSerialFlash1.setPlaybackRate(getPlaybackRate(newsensorValue)); |
||||
|
||||
AudioMemory(24); |
||||
} |
||||
|
||||
void loop() { |
||||
|
||||
int newsensorValue = analogRead(analogInPin); |
||||
playSerialFlash1.setPlaybackRate(getPlaybackRate(newsensorValue)); |
||||
|
||||
unsigned currentMillis = millis(); |
||||
if (currentMillis > lastSamplePlayed + 500) { |
||||
if (!playSerialFlash1.isPlaying()) { |
||||
playSerialFlash1.playRaw(_filename, 1); |
||||
lastSamplePlayed = currentMillis; |
||||
|
||||
Serial.print("Memory: "); |
||||
Serial.print(AudioMemoryUsage()); |
||||
Serial.print(","); |
||||
Serial.print(AudioMemoryUsageMax()); |
||||
Serial.println(); |
||||
} |
||||
} |
||||
delay(10); |
||||
} |
||||
|
||||
|
||||
namespace std { |
||||
void __throw_bad_function_call() {} |
||||
void __throw_length_error(char const*) {} |
||||
} |
@ -0,0 +1,43 @@ |
||||
#ifndef TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H |
||||
#define TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H |
||||
|
||||
#include <Arduino.h> |
||||
#include "IndexableFile.h" |
||||
#include <LittleFS.h> |
||||
#include <vector> |
||||
|
||||
namespace newdigate { |
||||
|
||||
template<size_t BUFFER_SIZE, size_t MAX_NUM_BUFFERS> // BUFFER_SIZE needs to be a power of two
|
||||
class IndexableLittleFSFile : public IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS, File> { |
||||
public: |
||||
static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); |
||||
|
||||
IndexableLittleFSFile(LittleFS &fs, const char *filename) :
|
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS, File>(filename), |
||||
_myFS(fs)
|
||||
{ |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::_file = _myFS.open(filename); |
||||
} |
||||
|
||||
File open(const char *filename) override { |
||||
return _myFS.open(filename); |
||||
} |
||||
|
||||
virtual ~IndexableLittleFSFile() { |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::close(); |
||||
} |
||||
|
||||
int16_t &operator[](int i) { |
||||
return IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::operator[](i); |
||||
} |
||||
|
||||
private: |
||||
LittleFS &_myFS; |
||||
}; |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
#endif //TEENSY_RESAMPLING_INDEXABLELITTLEFS_FILE_H
|
@ -0,0 +1,38 @@ |
||||
#ifndef TEENSY_RESAMPLING_INDEXABLESD_FILE_H |
||||
#define TEENSY_RESAMPLING_INDEXABLESD_FILE_H |
||||
|
||||
#include <Arduino.h> |
||||
#include "IndexableFile.h" |
||||
#include <SD.h> |
||||
#include <vector> |
||||
|
||||
namespace newdigate { |
||||
|
||||
template<size_t BUFFER_SIZE, size_t MAX_NUM_BUFFERS> // BUFFER_SIZE needs to be a power of two
|
||||
class IndexableSDFile : public IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File> { |
||||
public: |
||||
static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); |
||||
|
||||
IndexableSDFile(const char *filename) :
|
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>(filename) { |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::_file = open(filename); |
||||
} |
||||
|
||||
File open(const char *filename) override { |
||||
return SD.open(filename); |
||||
} |
||||
|
||||
virtual ~IndexableSDFile() { |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::close(); |
||||
} |
||||
|
||||
int16_t &operator[](int i) { |
||||
return IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,File>::operator[](i); |
||||
} |
||||
}; |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
#endif |
@ -0,0 +1,43 @@ |
||||
#ifndef TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H |
||||
#define TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H |
||||
|
||||
#include <Arduino.h> |
||||
#include "IndexableFile.h" |
||||
#include <SerialFlash.h> |
||||
#include <vector> |
||||
|
||||
namespace newdigate { |
||||
|
||||
template<size_t BUFFER_SIZE, size_t MAX_NUM_BUFFERS> // BUFFER_SIZE needs to be a power of two
|
||||
class IndexableSerialFlashFile : public IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,SerialFlashFile> { |
||||
public: |
||||
static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); |
||||
|
||||
IndexableSerialFlashFile(SerialFlashChip &fs, const char *filename) :
|
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,SerialFlashFile>(filename), |
||||
_myFS(fs)
|
||||
{ |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,SerialFlashFile>::_file = _myFS.open(filename); |
||||
} |
||||
|
||||
SerialFlashFile open(const char *filename) override { |
||||
return _myFS.open(filename); |
||||
} |
||||
|
||||
virtual ~IndexableSerialFlashFile() { |
||||
IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,SerialFlashFile>::close(); |
||||
} |
||||
|
||||
int16_t &operator[](int i) { |
||||
return IndexableFile<BUFFER_SIZE, MAX_NUM_BUFFERS,SerialFlashFile>::operator[](i); |
||||
} |
||||
|
||||
private: |
||||
SerialFlashChip &_myFS; |
||||
}; |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
#endif //TEENSY_RESAMPLING_INDEXABLESERIALFLASH_FILE_H
|
@ -0,0 +1,78 @@ |
||||
//
|
||||
// Created by Nicholas Newdigate on 10/02/2019.
|
||||
//
|
||||
|
||||
#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H |
||||
#define TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H |
||||
|
||||
#include <cstdint> |
||||
#include "IndexableLittleFSFile.h" |
||||
#include "ResamplingReader.h" |
||||
#include "LittleFS.h" |
||||
|
||||
#define RESAMPLE_BUFFER_SAMPLE_SIZE 128 |
||||
|
||||
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
|
||||
|
||||
namespace newdigate { |
||||
|
||||
class ResamplingLfsReader : public ResamplingReader< IndexableLittleFSFile<128, 2>, File > { |
||||
public: |
||||
ResamplingLfsReader(LittleFS &fs) :
|
||||
ResamplingReader(), |
||||
_myFS(fs)
|
||||
{ |
||||
} |
||||
virtual ~ResamplingLfsReader() { |
||||
} |
||||
|
||||
int16_t getSourceBufferValue(long index) override { |
||||
return (*_sourceBuffer)[index]; |
||||
} |
||||
|
||||
int available(void) |
||||
{ |
||||
return _playing; |
||||
} |
||||
|
||||
File open(char *filename) override { |
||||
return _myFS.open(filename); |
||||
} |
||||
|
||||
void close(void) override |
||||
{ |
||||
if (_playing) |
||||
stop(); |
||||
if (_sourceBuffer != nullptr) { |
||||
_sourceBuffer->close(); |
||||
delete _sourceBuffer; |
||||
_sourceBuffer = nullptr; |
||||
} |
||||
if (_filename != nullptr) { |
||||
delete [] _filename; |
||||
_filename = nullptr; |
||||
} |
||||
deleteInterpolationPoints(); |
||||
} |
||||
|
||||
IndexableLittleFSFile<128, 2>* createSourceBuffer() override { |
||||
return new IndexableLittleFSFile<128, 2>(_myFS, _filename); |
||||
} |
||||
|
||||
uint32_t positionMillis(void) { |
||||
if (_file_size == 0) return 0; |
||||
|
||||
return (uint32_t) (( (double)_bufferPosition * lengthMillis() ) / (double)(_file_size/2)); |
||||
} |
||||
|
||||
uint32_t lengthMillis(void) { |
||||
return ((uint64_t)_file_size * B2M) >> 32; |
||||
} |
||||
|
||||
protected:
|
||||
LittleFS &_myFS; |
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif //TEENSYAUDIOLIBRARY_RESAMPLINGLFSREADER_H
|
@ -0,0 +1,500 @@ |
||||
#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H |
||||
#define TEENSYAUDIOLIBRARY_RESAMPLINGREADER_H |
||||
|
||||
#include <Arduino.h> |
||||
#include <cstdint> |
||||
#include "loop_type.h" |
||||
#include "interpolation.h" |
||||
#include "waveheaderparser.h" |
||||
|
||||
namespace newdigate { |
||||
|
||||
template<class TArray, class TFile> |
||||
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<signed int>(_remainder); |
||||
_remainder -= static_cast<double>(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
|
@ -0,0 +1,82 @@ |
||||
//
|
||||
// Created by Nicholas Newdigate on 10/02/2019.
|
||||
//
|
||||
|
||||
#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H |
||||
#define TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H |
||||
|
||||
#include <cstdint> |
||||
#include "spi_interrupt.h" |
||||
#include "loop_type.h" |
||||
#include "interpolation.h" |
||||
#include "IndexableSerialFlashFile.h" |
||||
#include "ResamplingReader.h" |
||||
#include "SerialFlash.h" |
||||
|
||||
#define RESAMPLE_BUFFER_SAMPLE_SIZE 128 |
||||
|
||||
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
|
||||
|
||||
namespace newdigate { |
||||
|
||||
class ResamplingSerialFlashReader : public ResamplingReader< IndexableSerialFlashFile<128, 2>, SerialFlashFile > { |
||||
public: |
||||
ResamplingSerialFlashReader(SerialFlashChip &fs) :
|
||||
ResamplingReader(), |
||||
_myFS(fs)
|
||||
{ |
||||
} |
||||
|
||||
virtual ~ResamplingSerialFlashReader() { |
||||
} |
||||
|
||||
int16_t getSourceBufferValue(long index) override { |
||||
return (*_sourceBuffer)[index]; |
||||
} |
||||
|
||||
int available(void) |
||||
{ |
||||
return _playing; |
||||
} |
||||
|
||||
SerialFlashFile open(char *filename) override { |
||||
return _myFS.open(filename); |
||||
} |
||||
|
||||
void close(void) override |
||||
{ |
||||
if (_playing) |
||||
stop(); |
||||
if (_sourceBuffer != nullptr) { |
||||
_sourceBuffer->close(); |
||||
delete _sourceBuffer; |
||||
_sourceBuffer = nullptr; |
||||
} |
||||
if (_filename != nullptr) { |
||||
delete [] _filename; |
||||
_filename = nullptr; |
||||
} |
||||
deleteInterpolationPoints(); |
||||
} |
||||
|
||||
IndexableSerialFlashFile<128, 2>* createSourceBuffer() override { |
||||
return new IndexableSerialFlashFile<128, 2>(_myFS, _filename); |
||||
} |
||||
|
||||
uint32_t positionMillis(void) { |
||||
if (_file_size == 0) return 0; |
||||
|
||||
return (uint32_t) (( (double)_bufferPosition * lengthMillis() ) / (double)(_file_size/2)); |
||||
} |
||||
|
||||
uint32_t lengthMillis(void) { |
||||
return ((uint64_t)_file_size * B2M) >> 32; |
||||
} |
||||
|
||||
protected:
|
||||
SerialFlashChip &_myFS; |
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif //TEENSYAUDIOLIBRARY_RESAMPLINGSERIALFLASHREADER_H
|
@ -0,0 +1,26 @@ |
||||
//
|
||||
// Created by Nicholas Newdigate on 18/07/2020.
|
||||
//
|
||||
|
||||
#ifndef TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H |
||||
#define TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H |
||||
|
||||
#include "ResamplingLfsReader.h" |
||||
|
||||
class AudioPlayLfsResmp : public AudioPlayResmp<newdigate::ResamplingLfsReader> |
||||
{ |
||||
public: |
||||
AudioPlayLfsResmp(LittleFS &fs) : |
||||
AudioPlayResmp<newdigate::ResamplingLfsReader>() |
||||
{ |
||||
reader = new newdigate::ResamplingLfsReader(fs); |
||||
begin(); |
||||
} |
||||
|
||||
virtual ~AudioPlayLfsResmp() { |
||||
delete reader; |
||||
} |
||||
}; |
||||
|
||||
|
||||
#endif //TEENSY_RESAMPLING_SDREADER_PLAYLFSRAWRESMP_H
|
@ -0,0 +1,31 @@ |
||||
//
|
||||
// Created by Nicholas Newdigate on 18/07/2020.
|
||||
//
|
||||
|
||||
#ifndef TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H |
||||
#define TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H |
||||
|
||||
#include "Arduino.h" |
||||
#include "AudioStream.h" |
||||
#include "SerialFlash.h" |
||||
#include "stdint.h" |
||||
#include "ResamplingSerialFlashReader.h" |
||||
#include "playresmp.h" |
||||
|
||||
class AudioPlaySerialFlashResmp : public AudioPlayResmp<newdigate::ResamplingSerialFlashReader> |
||||
{ |
||||
public: |
||||
AudioPlaySerialFlashResmp(SerialFlashChip &fs) : |
||||
AudioPlayResmp<newdigate::ResamplingSerialFlashReader>() |
||||
{ |
||||
reader = new newdigate::ResamplingSerialFlashReader(fs); |
||||
begin(); |
||||
} |
||||
|
||||
virtual ~AudioPlaySerialFlashResmp() { |
||||
delete reader; |
||||
} |
||||
}; |
||||
|
||||
|
||||
#endif //TEENSY_RESAMPLING_SDREADER_PLAYSERIALFLASHRAWRESMP_H
|
@ -0,0 +1,118 @@ |
||||
//
|
||||
// Created by Nicholas Newdigate on 18/07/2020.
|
||||
//
|
||||
|
||||
#ifndef TEENSY_AUDIO_SDWAV_STEREO_READERTESTS_CPP |
||||
#define TEENSY_AUDIO_SDWAV_STEREO_READERTESTS_CPP |
||||
|
||||
#include <boost/test/unit_test.hpp> |
||||
#include "AudioWavFixture.h" |
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_audio_wav_tags_in_header) |
||||
|
||||
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(Wav_with_tags_in_header_1, AudioWavFixture) { |
||||
|
||||
// GUItool: begin automatically generated code
|
||||
AudioPlaySdResmp wave; //xy=306,225
|
||||
TestAudioOutput testout; //xy=612,224
|
||||
AudioConnection patchCord1(wave, 0, testout, 0); |
||||
AudioConnection patchCord2(wave, 1, testout, 1); |
||||
// GUItool: end automatically generated code
|
||||
|
||||
const double playbackRate = 1.0; |
||||
const std::string testName = "SDTEST1"; |
||||
const std::string outputFile = testName+".wav"; |
||||
const std::string outputFileName = outputPath + outputFile; |
||||
const std::string referenceFileName = referencePath + testName + ".wav"; |
||||
SD.setSDCardFolderPath(inputPath); |
||||
|
||||
if (!testout.saveOutputFile(outputPath.c_str(), outputFile.c_str())) { |
||||
std::cout << "not able to save output file..." << std::endl; |
||||
BOOST_ERROR("not able to save output file (1)..."); |
||||
} |
||||
wave.begin(); |
||||
wave.enableInterpolation(true); |
||||
wave.setPlaybackRate(playbackRate); |
||||
bool isPlaying = wave.playWav(outputFile.c_str()); |
||||
if (!isPlaying) { |
||||
std::cout << "input audio file is not able to play..." << std::endl; |
||||
BOOST_ERROR("input audio file is not able to play (1)..."); |
||||
} |
||||
if (!wave.isPlaying()) { |
||||
std::cout << "input audio file is not able to play..." << std::endl; |
||||
BOOST_ERROR("input audio file is not able to play (2)..."); |
||||
} |
||||
|
||||
while (wave.isPlaying()) { |
||||
testout.isr(); |
||||
} |
||||
testout.closeOutputfile(numberOfChannels); |
||||
patchCord1.disconnect(); |
||||
AudioConnection::reset(); |
||||
arduino_should_exit = true; |
||||
|
||||
std::cout << "comparing " << outputFileName << " with " << referenceFileName << ";" << std::endl; |
||||
|
||||
std::ifstream ifs1(outputFileName); |
||||
std::ifstream ifs2(referenceFileName); |
||||
std::istream_iterator<char> b1(ifs1), e1; |
||||
std::istream_iterator<char> b2(ifs2), e2; |
||||
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(b1, e1, b2, e2); |
||||
} |
||||
BOOST_FIXTURE_TEST_CASE(Wav_with_tags_in_header_2, AudioWavFixture) { |
||||
|
||||
// GUItool: begin automatically generated code
|
||||
AudioPlaySdResmp wave; //xy=306,225
|
||||
TestAudioOutput testout; //xy=612,224
|
||||
AudioConnection patchCord1(wave, 0, testout, 0); |
||||
AudioConnection patchCord2(wave, 1, testout, 1); |
||||
// GUItool: end automatically generated code
|
||||
|
||||
const double playbackRate = 1.0; |
||||
const std::string testName = "SDTEST2"; |
||||
const std::string outputFile = testName+".wav"; |
||||
const std::string outputFileName = outputPath + outputFile; |
||||
const std::string referenceFileName = referencePath + testName + ".wav"; |
||||
SD.setSDCardFolderPath(inputPath); |
||||
|
||||
if (!testout.saveOutputFile(outputPath.c_str(), outputFile.c_str())) { |
||||
std::cout << "not able to save output file..." << std::endl; |
||||
BOOST_CHECK(false); |
||||
return; |
||||
} |
||||
wave.begin(); |
||||
wave.enableInterpolation(true); |
||||
wave.setPlaybackRate(playbackRate); |
||||
wave.playWav(outputFile.c_str()); |
||||
|
||||
if (!wave.isPlaying()) { |
||||
std::cout << "input audio file is not able to play..." << std::endl; |
||||
BOOST_CHECK(false); |
||||
return; |
||||
} |
||||
|
||||
while (wave.isPlaying()) { |
||||
testout.isr(); |
||||
} |
||||
testout.closeOutputfile(numberOfChannels); |
||||
patchCord1.disconnect(); |
||||
AudioConnection::reset(); |
||||
arduino_should_exit = true; |
||||
|
||||
std::ifstream ifs1(outputFileName); |
||||
std::ifstream ifs2(referenceFileName); |
||||
std::istream_iterator<char> b1(ifs1), e1; |
||||
std::istream_iterator<char> b2(ifs2), e2; |
||||
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(b1, e1, b2, e2); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() |
||||
|
||||
#endif //TEENSY_AUDIO_SDWAV_STEREO_READERTESTS_CPP
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue