Fixing some accidentally replaced strlcpy() in some library files.dev
parent
2c1c3bb5bb
commit
bd89c6f07b
@ -1,6 +1,8 @@ |
|||||||
cmake_minimum_required(VERSION 3.5) |
cmake_minimum_required(VERSION 3.5) |
||||||
add_subdirectory(array) |
add_subdirectory(array) |
||||||
|
add_subdirectory(LittleFS) |
||||||
add_subdirectory(sampleloader) |
add_subdirectory(sampleloader) |
||||||
add_subdirectory(sd_play_all) |
add_subdirectory(sd_play_all) |
||||||
add_subdirectory(sd_raw) |
add_subdirectory(sd_raw) |
||||||
add_subdirectory(sd_wav) |
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