Success getting TMRpcm to change the pitch and speed of sounds

Thanks to an old post here I inserted some code into the TMRpcm library, and getting it to play clips with a variable speed and pitch by changing the sample rate for each time the sound file is played.

In file TRMpcm.cpp I changed these two lines:

    SAMPLE_RATE = sFile.read();
    SAMPLE_RATE = sFile.read() << 8 | SAMPLE_RATE;

to these four lines of code:

    SAMPLE_RATE = sFile.read();  // 	you need these two reads just to make sure that
    SAMPLE_RATE = sFile.read();  //  	the file pointer is in the right place for later code
    int pot = analogRead(A0); 	     // 	or what ever the pot is on
    SAMPLE_RATE = map(pot, 0, 1023, 8000, 44000);

It works PERFECTLY!!! Problem: the sample rate and therefore pitch/speed are only changed at the beginning of each playback event, which is no biggie for my project. But if I wanted to change the sample rate mid-playback, how..... how would I do that???

But if I wanted to change the sample rate mid-playback,

Can you explain what you mean by that, that has me confused.

Grumpy_Mike:
Can you explain what you mean by that, that has me confused.

Sure, I want pitch/speed (basically, the sample playback rate) to change in the middle of playing a sound. At the moment, it only detects the sample rate when the clip starts to be played.

Not sure you can do that. As it would mean receiving MIDI messages while you are outputting the samples. When you do that what you are producing will glitch as you will interfere with the sample rate.

I think this is how (assuming you're using a 16-bit timer):

unsigned long newSR = map(analogRead(A0), 0, 1023, 8000, 44100);

if (bitRead(optionByte,6)) *ICRn[tt] = 10 * (800000 /  newSR);
else *ICRn[tt] = 10 * (1600000 / newSR);

optionByte and ICRn are actually global variables, so there should be no problem in using them anywhere (at compiling time though); furthermore no library modification required.
Although not sure if there would be a side effect since this also changes PWM's (carrier) frequency, and if the duty cycle (outputted sample's value) will be scaled accordingly.

Lucario448:
I think this is how (assuming you're using a 16-bit timer):

unsigned long newSR = map(analogRead(A0), 0, 1023, 8000, 44100);

if (bitRead(optionByte,6)) *ICRn[tt] = 10 * (800000 /  newSR);
else *ICRn[tt] = 10 * (1600000 / newSR);



**optionByte** and **ICRn** are actually global variables, so there should be no problem in using them anywhere (at compiling time though); furthermore no library modification required.
Although not sure if there would be a side effect since this also changes PWM's (carrier) frequency, and if the duty cycle (outputted sample's value) will be scaled accordingly.

Hello there.

How to exactly implement this code? I am unable to do it... Please help!

Thanx.

Put it anywhere in your code (as long as TMRpcm.h is included), you can even wrap this snippet into a function if you wish...

Lucario448:
Put it anywhere in your code (as long as TMRpcm.h is included), you can even wrap this snippet into a function if you wish...

Thanx! I tried it earlier also but i get the "optionByte was not declared in this scope" error.

optionByte is a Global variable and could be accessed from anywhere as you said but...

TMRpcm.h is included in the sketch and the code is in the loop section.

That means you are doing something wrong that you don't know. Please post the code ( using code tags ) of your sketch.

TMRpcm.h is included in the sketch

Where have you put this file, not in the sketch itself.

Maybe I am doing something wrong. Here is the code

#include <SD.h>                      // need to include the SD library
//#define SD_ChipSelectPin 53  //example uses hardware SS pin 53 on Mega2560
#define SD_ChipSelectPin 4  //using digital pin 4 on arduino nano 328, can use other pins
#include <TMRpcm.h>           //  also need to include this library...
#include <SPI.h>

TMRpcm tmrpcm;   // create an object for use in this sketch

unsigned long time = 0;

unsigned long newSR ;

  tmrpcm.speakerPin = 9; //5,6,11 or 46 on Mega, 9 on Uno, Nano, etc
  //Complimentary Output or Dual Speakers:
  //pinMode(10,OUTPUT); Pin pairs: 9,10 Mega: 5-2,6-7,11-12,46-45 
  
  Serial.begin(115200);
  pinMode(13,OUTPUT); //LED Connected to analog pin 0
  if (!SD.begin(SD_ChipSelectPin)) {  // see if the card is present and can be initialized:
    Serial.println("SD fail");  
    return;   // don't do anything more if not

  }
  else{   
    Serial.println("SD ok");   
  }
  //tmrpcm.play("idleNew.wav"); //the sound file "music" will play each time the arduino powers up, or is reset
}



void loop(){  

newSR = map(analogRead(A0), 0, 1023, 8000, 44100);

if (bitRead(optionByte,6)) *ICRn[tt] = 10 * (800000 /  newSR);
else *ICRn[tt] = 10 * (1600000 / newSR);

if (!tmrpcm.isPlaying()) {
    tmrpcm.play("idleNew.wav");
    
  }

 
}




[code]

[/code]

Any leads??

Well it was the idea of Lucario448 who seems to have gone away. I would try a PM to see if he is still around.

Did anyone ever solve the "optionByte was not declared in this scope" error?

Just solved that as I needed it to work for my project.

The problem is that this code is trying to modify variables that are global within the tmrpcm library code, but not your program. It works if you put it inside the tmrpcm library.

(For me, the tmrpcm library is stored in my sketch folder in a folder called libraries, the files needing modifying are Arduino/libraries/TMRpcm/TMRpcm.h and Arduino/libraries/TMRpcm/TMRpcm.cpp)
(These can't be modified from Arduino IDE but must be modified with a text editor eg. Notepad or KWrite)

Create a function in the TMRpcm.cpp file, which contains the code which Lucario448 wrote. I called this 'changespeed', so my function declaration was this:

void TMRpcm::changespeed(unsigned int newspeed){
    unsigned long newSR = map(newspeed, 0, 1023, 8000, 44100);

    if (bitRead(optionByte,6)) *ICRn[tt] = 10 * (800000 /  newSR);
	else *ICRn[tt] = 10 * (1600000 / newSR);
}

and I put this at line 448, just before the area marked

//***************************************************************************************
//********************** Functions for single track playback ****************************

(though I could probably have put this anywhere in the tmrpcm.cpp file).

Then, I modified the 'TMRpcm.h' file accordingly, to add the new function to the list of available functions or the compiler gets confused if you don't. Again, I added this before the play function (line 31)

 	void changespeed(unsigned int newspeed);

Now, I can call this function from anywhere in my code whenever I want to change the playback speed, if I want to control if from analog0, I just call tmrpcm.changespeed(analogRead(A0));

Please note, when I updated libraries this function got forgotten. I don't know how to block Arduino IDE from updating this library and forgetting this change, but also keeping a copy of those 2 modified files elsewhere could be a good idea just i case