#include <Arduino.h>
#include <Audio.h>
#include <SD.h>
#include <TeensyVariablePlayback.h>
// GUItool: begin automatically generated code
AudioPlaySdResmp playSdWav1; //xy=324,457
AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906
AudioConnection patchCord1(playSdWav1, 0, i2s2, 0);
AudioConnection patchCord2(playSdWav1, 0, i2s2, 1);
AudioControlSGTL5000 audioShield;
// GUItool: end automatically generated code
double getPlaybackRate(int16_t analog);
char* _filename = "SPACE.WAV";
void setup() {
Serial.begin(9600);
if (!(SD.begin(10))) {
// stop here if no SD card, but print a message
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
AudioMemory(24);
audioShield.enable();
audioShield.volume(0.5);
playSdWav1.enableInterpolation(true);
playSdWav1.setPlaybackRate(1.0);
playSdWav1.playWav(_filename);
playSdWav1.setLoopType(looptype_repeat);
// Sound sample isn't seamless, so pick some loop points
playSdWav1.setLoopStart(19374);
playSdWav1.setLoopFinish(643346);
// Extra stuff to mask glitches at loop point
playSdWav1.setUseDualPlaybackHead(true);
playSdWav1.setCrossfadeDurationInSamples(1000);
}
float rate=1.0f, factor = 1.001f;
uint32_t next;
bool increasing;
void loop() {
int newsensorValue = analogRead(A0);
// I don't have a sensor, so just ramp rate up and down
if (millis() >= next)
{
next = millis() + 3; // every 3ms, i.e. roughly every audio update
if (increasing)
{
rate *= factor;
increasing = rate < 2.0f;
}
else
{
rate /= factor;
increasing = rate < 0.5f;
}
playSdWav1.setPlaybackRate(rate);
}
if(!playSdWav1.isPlaying()) {
Serial.println("Restarted playback"); // This Never Happens
playSdWav1.playWav(_filename);
}
}
// map analog input 0-1023 to 0.5-3.0 pitch
double getPlaybackRate(int16_t analog) { //analog: 0..1023
return ((analog / 1023) * 2.5) + 0.5;
}
Just wanted to report back that the code works. Thanks!Slightly surprised that you're surprised by the "audible interruptions", given that your audio file fades in at the start and out at the end, so there's about 0.5s of near-silence if it's looped without setting start and finish points.
Here's an edit of your code which works, as far as I can tell:
C++:#include <Arduino.h> #include <Audio.h> #include <SD.h> #include <TeensyVariablePlayback.h> // GUItool: begin automatically generated code AudioPlaySdResmp playSdWav1; //xy=324,457 AudioOutputI2S i2s2; //xy=840.8571472167969,445.5714416503906 AudioConnection patchCord1(playSdWav1, 0, i2s2, 0); AudioConnection patchCord2(playSdWav1, 0, i2s2, 1); AudioControlSGTL5000 audioShield; // GUItool: end automatically generated code double getPlaybackRate(int16_t analog); char* _filename = "SPACE.WAV"; void setup() { Serial.begin(9600); if (!(SD.begin(10))) { // stop here if no SD card, but print a message while (1) { Serial.println("Unable to access the SD card"); delay(500); } } AudioMemory(24); audioShield.enable(); audioShield.volume(0.5); playSdWav1.enableInterpolation(true); playSdWav1.setPlaybackRate(1.0); playSdWav1.playWav(_filename); playSdWav1.setLoopType(looptype_repeat); // Sound sample isn't seamless, so pick some loop points playSdWav1.setLoopStart(19374); playSdWav1.setLoopFinish(643346); // Extra stuff to mask glitches at loop point playSdWav1.setUseDualPlaybackHead(true); playSdWav1.setCrossfadeDurationInSamples(1000); } float rate=1.0f, factor = 1.001f; uint32_t next; bool increasing; void loop() { int newsensorValue = analogRead(A0); // I don't have a sensor, so just ramp rate up and down if (millis() >= next) { next = millis() + 3; // every 3ms, i.e. roughly every audio update if (increasing) { rate *= factor; increasing = rate < 2.0f; } else { rate /= factor; increasing = rate < 0.5f; } playSdWav1.setPlaybackRate(rate); } if(!playSdWav1.isPlaying()) { Serial.println("Restarted playback"); // This Never Happens playSdWav1.playWav(_filename); } } // map analog input 0-1023 to 0.5-3.0 pitch double getPlaybackRate(int16_t analog) { //analog: 0..1023 return ((analog / 1023) * 2.5) + 0.5; }
setLoopStart() and setLoopFinish() aswell.AudioPlayArrayResmp wave;
uint32_t audio_samples = 0;
wave.enableInterpolation(true);
wave.setUseDualPlaybackHead(true);
wave.setPlayStart(play_start_loop);
wave.setLoopType(looptype_pingpong);
void noteOn(uint8_t MIDInote, uint8_t MIDIvel, bool slide)
{
wave.playRaw(audio_buffer, audio_samples, 1); // 1 = mono
}
void setParam(uint8_t param, uint8_t val)
{
switch(param)
{
case setPitch:
wave.setPlaybackRate(pitch[val]);
break;
case setLoopStart:
wave.setLoopStart((audio_samples * val) / 127);
break;
case setLoopEnd:
wave.setLoopFinish((audio_samples * val) / 127);
break;
case setCrossfade:
wave.setCrossfadeDurationInSamples((audio_samples * val) / 127);
break;
}
}
play() methods have a tendency to reset the loop start and end values, so you need to (re)set those after you start playback. (It's not an ideal API, but I wasn't about to change it since I didn't know how many existing applications would break as a result.) Your code fragment above suggests you're only setting them from MIDI values, without making a record of those settings that you can re-use in subsequent noteOn() calls.// stop all samples
for (size_t i = 0; i < 8; i++)
{
sampleOsc[i].stop();
}
// load new sample from list
String Folder = "Samples/";
String name_ = Folder + fileNames[new_index - 1]; // sample name from string list
char *_filename = const_cast<char*>(name_.c_str());
newdigate::flashloader loader;
Sample = loader.loadSample(_filename);
flashloader isn't very good - it has no way of freeing up unused samples. And your posted code fragment doesn't even attempt to address my point 2 in post #236...No, it just creates an instance of the loader. You only need, and ideally should only have, one such instance in your entire program. It attempts (rather poorly...) to keep track of the amount of PSRAM used, but each instance has its own count.The line "newdigate::flashloader loader;" resets the sample address to the beginning
I think it should be something likeHow make free the PSRAM they’re in ?
extmem_free(Sample->sampledata);
delete Sample;
Sample = nullptr;
// new flashloader.cpp
#include "flashloader.h"
#include <Audio.h>
namespace newdigate {
audiosample * flashloader::loadSample(const char *filename ) {
uint8_t *data = nullptr;
Serial.printf("Reading %s\n", filename);
File f = SD.open(filename, O_READ);
if (f) {
if (f.size() < _bytesavailable) {
//noInterrupts();
AudioNoInterrupts();
uint32_t total_read = 0;
//auto *data = (int8_t*)extmem_malloc(f.size() );
data = (uint8_t*)extmem_malloc(f.size());
uint8_t *index = data;
while (f.available()) {
size_t bytesRead = f.read(index, flashloader_default_sd_buffersize);
if (bytesRead == 0)
break;
total_read += bytesRead;
index += bytesRead;
}
//interrupts();
AudioInterrupts();
_bytesavailable -= total_read;
audiosample *sample = new audiosample();
sample->sampledata = (int16_t*)data;
sample->samplesize = f.size();
Serial.printf("\tsample start %x\n", (uint32_t)data);
Serial.printf("\tsample size %d\n", sample->samplesize);
Serial.printf("\tavailable: %d\n", _bytesavailable);
return sample;
}
}
Serial.printf("not found %s\n", filename);
return nullptr;
}
}
// press patch list on page1 -----------------------------------
void press_patch_list_page1()
{
uint8_t new_index = 0;
static uint8_t old_index = 0;
static uint8_t old_key_index = 1;
new_index = touch_patch_list();
if (new_index != old_index)
{
old_index = new_index;
if (new_index >= 1 && new_index <= 7)
{
if (new_index != old_key_index)
{
old_key_index = new_index;
sample_busy_flag = false;
extmem_free(Sample->sampledata);
//delete Sample;
Sample = nullptr;
print_file_List(new_index - 1);
tft.updateScreenAsync(false); // draw screen
// stop all samples
for (size_t i = 0; i < 8; i++)
{
sampleOsc[i].stop();
}
String Folder = "Samples/";
String name_ = Folder + fileNames[new_index - 1]; // sample name from string list
char *_filename = const_cast<char*>(name_.c_str());
Sample = loader.loadSample(_filename);
sample_busy_flag = true;
}
}
else if (new_index == 0)
{
for (size_t i = 0; i < 7; i++)
{
TK_state_List_P1[i] = false; // clear touch state
}
}
}
if (new_index > 0)
{
Reset_screensaver();
}
}
audiosample *sample = new audiosample();
//delete Sample;
#ifndef PSRAMLOADER_H
#define PSRAMLOADER_H
#include <Arduino.h>
#include <SD.h>
extern "C" uint8_t external_psram_size;
namespace newdigate {
const uint32_t flashloader_default_sd_buffersize = 4 * 1024;
struct audiosample {
int16_t *sampledata;
uint32_t samplesize;
};
class Psramloader {
public:
Psramloader() {
_bytesavailable = external_psram_size * 1048576;
}
uint32_t _bytesavailable = 0;
audiosample * loadSample(const char *filename);
};
};
#endif
#include "psramloader.h"
#include <Audio.h>
namespace newdigate {
audiosample * Psramloader::loadSample(const char *filename ) {
uint8_t *data = nullptr;
Serial.printf("Reading %s\n", filename);
File f = SD.open(filename, O_READ);
if (f) {
if (f.size() < _bytesavailable) {
AudioNoInterrupts();
uint32_t total_read = 0;
data = (uint8_t*)extmem_malloc(f.size());
uint8_t *index = data;
while (f.available()) {
size_t bytesRead = f.read(index, flashloader_default_sd_buffersize);
if (bytesRead == 0)
break;
total_read += bytesRead;
index += bytesRead;
}
AudioInterrupts();
_bytesavailable -= total_read;
audiosample *sample = new audiosample();
sample->sampledata = (int16_t*)data;
sample->samplesize = f.size();
Serial.printf("\tsample start %x\n", (uint32_t)data);
Serial.printf("\tsample size %d\n", sample->samplesize);
Serial.printf("\tavailable: %d\n", _bytesavailable);
return sample;
}
}
Serial.printf("not found %s\n", filename);
return nullptr;
}
}
Really? It doesn’t look much like it…I used the psramloader.cpp from MicroDexed-touch.
…it looks like you still have a slightly modified copy of the original newdigate code. Which as I’ve repeatedly said, is not fit for serious use!There's something wrong with the Psramloader. The available memory keeps getting smaller, even though I delete the old sample before reloading it.
unloadSample method that keeps track of PSRAM memory that it has consumed. Though it may or may not keep track of memory allocated by other modules - I haven’t looked very closely at the source code. You could probably adopt it, with changes, if you don’t use PSRAM for anything other than samples. But it looks a bit specific to the Microdexed-touch … it would probably be easier to write your own code from scratch.