Take a look here:
https://forum.pjrc.com/threads/46793-Anyone-Doing-Pitch-Shifting
I also wrote some code using this technique. I'll see if I can dig it up.
Take a look here:
https://forum.pjrc.com/threads/46793-Anyone-Doing-Pitch-Shifting
I also wrote some code using this technique. I'll see if I can dig it up.
Tiran wrote " I am curious if I can do a pitch change of a single sample file and adjust the pitch as desired."
Yes, Arbitrary Waveform, play sample at what ever frequency, duration and amplitude you want, see the teensy audio examples and the GUI design tool
Teenfor3:
I love the simplicity of your approach. Unfortunately I am doing this using the Audio library and the Audio card. I need to output through the card and have 2-3 other audio track playing. For instance the engine might be revved up high because we are in pursuit and a torpedo sound fires while the captain announces "we are at full speed!". I need the pitch modulation to occur within the audio library for the PJRC audio card therefor...
if (rawfile.available()) {
// we can read more data from the file...
n = rawfile.read(block->data, AUDIO_BLOCK_SAMPLES*2);
file_offset += n;
for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++) {
block->data[i] = 0;
}
transmit(block);
}
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <Arduino.h>
#include "play_serialflash_raw.h"
#include "spi_interrupt.h"
int16_t ride; // ****** number of time update is called
int16_t box[128]; // ****** sound samples, local vector
int16_t first_sample; // ****** each ride, the last sample read is stored here
unsigned long t, t_0; // ***** computational time, must be within 2500 micros
void AudioPlaySerialflashRaw::begin(void)
{
playing = false;
file_offset = 0;
file_size = 0;
}
bool AudioPlaySerialflashRaw::play(const char *filename)
{
stop();
ride=0; // ***** my code
AudioStartUsingSPI();
rawfile = SerialFlash.open(filename);
if (!rawfile) {
//Serial.println("unable to open file");
AudioStopUsingSPI();
return false;
}
file_size = rawfile.size();
file_offset = 0;
//Serial.println("able to open file");
playing = true;
return true;
}
void AudioPlaySerialflashRaw::stop(void)
{
__disable_irq();
if (playing) {
playing = false;
__enable_irq();
rawfile.close();
AudioStopUsingSPI();
} else {
__enable_irq();
}
}
void AudioPlaySerialflashRaw::update(void)
{
int16_t b[128]; // ******* temporay vector used to store caluculated values
unsigned int h, k; // ***** my code
unsigned int i, n;
audio_block_t *block;
// only update if we're playing
if (!playing) return;
// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;
if (rawfile.available()) {
// ***** my code starts...
t_0=micros(); // ***** checking the computational time, must be within 2500 micros
if (ride==0) {
// we can read more data from the file...
n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
file_offset += n;
for (i=n/2; i < 65; i++) {
block->data[i] = 0;
}
for (i=0;i<128;i++) {
h=(floor((float)i/2.0)); // ***** lower sample needed for calculation
k=(ceil((float)i/2.0)); // ***** upper sample needed for calculation
b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
}
first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
}
else {
// we can read more data from the file...
n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
file_offset += n;
for (i=n/2; i < 64; i++) {
block->data[i] = 0;
}
b[0]=first_sample;
for (i=1;i<128;i++) {
h=(floor((float)i/2.0)); // ***** lower sample needed for calculation
k=(ceil((float)i/2.0)); // ***** upper sample needed for calculation
b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
}
first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
}
for (i=0;i<128;i++) { // ***** preparing samples to be sent out
block->data[i]=b[i];
}
t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
ride ++;
// ***** my code ends
transmit(block);
}
else {
rawfile.close();
AudioStopUsingSPI();
playing = false;
//Serial.println("Finished playing sample"); //TODO
}
release(block);
}
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
uint32_t AudioPlaySerialflashRaw::positionMillis(void)
{
return ((uint64_t)file_offset * B2M) >> 32;
}
uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
{
return ((uint64_t)file_size * B2M) >> 32;
}
int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time, must be within 2500 micros
{
return t;
}
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <Arduino.h>
#include "play_serialflash_raw.h"
#include "spi_interrupt.h"
int16_t ride; // ****** number of time update is called
int16_t box[128]; // ****** sound samples, local vector
int16_t first_sample; // ****** each ride, the last sample read is stored here
unsigned long t, t_0; // ***** t is the computational time, must be within 2500 microseconds
void AudioPlaySerialflashRaw::begin(void)
{
playing = false;
file_offset = 0;
file_size = 0;
}
bool AudioPlaySerialflashRaw::play(const char *filename)
{
stop();
ride=0; // ***** my code
AudioStartUsingSPI();
rawfile = SerialFlash.open(filename);
if (!rawfile) {
//Serial.println("unable to open file");
AudioStopUsingSPI();
return false;
}
file_size = rawfile.size();
file_offset = 0;
//Serial.println("able to open file");
playing = true;
return true;
}
void AudioPlaySerialflashRaw::stop(void)
{
__disable_irq();
if (playing) {
playing = false;
__enable_irq();
rawfile.close();
AudioStopUsingSPI();
} else {
__enable_irq();
}
}
void AudioPlaySerialflashRaw::update(void)
{
int16_t b[128]; // ******* temporay vector used to store caluculated values
unsigned int h, k; // ******* two variables used to stor indexes
unsigned int i, n;
audio_block_t *block;
// only update if we're playing
if (!playing) return;
// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;
if (rawfile.available()) {
// ***** main code starts here
t_0=micros(); // ***** checking the computational time, must be within 2500 micros
if (ride==0) {
// we can read more data from the file...
n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
file_offset += n;
for (i=n/2; i < 65; i++) {
block->data[i] = 0;
}
for (i=0;i<128;i++) {
h=(floor((float)i/2.0)); // ***** lower sample needed for calculation
k=(ceil((float)i/2.0)); // ***** upper sample needed for calculation
b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
}
first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
}
else {
// we can read more data from the file...
n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
file_offset += n;
for (i=n/2; i < 64; i++) {
block->data[i] = 0;
}
for (i=0;i<128;i++) {
h=(floor((float)i/2.0))-1; // ***** lower sample needed for calculation
k=(ceil((float)i/2.0))-1; // ***** upper sample needed for calculation
b[i]=(((h<0) ? first_sample: block->data[h])+((k<0)? first_sample: block->data[k]))/2; // **** interpolated value
}
first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
}
for (i=0;i<128;i++) { // ***** preparing samples to be sent out
block->data[i]=b[i];
}
t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
ride ++;
// ***** main code ends here
transmit(block);
}
else {
rawfile.close();
AudioStopUsingSPI();
playing = false;
//Serial.println("Finished playing sample"); //TODO
}
release(block);
}
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
uint32_t AudioPlaySerialflashRaw::positionMillis(void)
{
return ((uint64_t)file_offset * B2M) >> 32;
}
uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
{
return ((uint64_t)file_size * B2M) >> 32;
}
int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time t, must be within 2500 microseconds
{
return t;
}
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <Arduino.h>
#include "play_serialflash_raw.h"
#include "spi_interrupt.h"
int16_t ride; // ****** number of time update() is called
int16_t first_sample; // ****** each ride, the last sample read is stored here
unsigned long t, t_0; // ***** t is the computational time, must be within 2500 microseconds
int16_t bytes_read; // ****** number of valid bytes read each update()
void AudioPlaySerialflashRaw::begin(void)
{
playing = false;
file_offset = 0;
file_size = 0;
}
bool AudioPlaySerialflashRaw::play(const char *filename)
{
stop();
ride=0; // ***** my code
AudioStartUsingSPI();
rawfile = SerialFlash.open(filename);
if (!rawfile) {
//Serial.println("unable to open file");
AudioStopUsingSPI();
return false;
}
file_size = rawfile.size();
file_offset = 0;
//Serial.println("able to open file");
playing = true;
return true;
}
void AudioPlaySerialflashRaw::stop(void)
{
__disable_irq();
if (playing) {
playing = false;
__enable_irq();
rawfile.close();
AudioStopUsingSPI();
} else {
__enable_irq();
}
}
void AudioPlaySerialflashRaw::update(void)
{
int16_t b[128]; // ******* temporay vector used to store caluculated values
int16_t h, k; // ******* two variables used to store indexes
unsigned int i, n;
audio_block_t *block;
// only update if we're playing
if (!playing) return;
// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;
if (rawfile.available()) {
// ***** main code starts here
t_0=micros(); // ***** checking the computational time, must be within 2500 micros
if (ride==0) {
// we can read more data from the file...
n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
bytes_read=n;
file_offset += n;
for (i=n/2; i < 65; i++) {
block->data[i] = 0;
}
for (i=0;i<128;i++) {
h=(floor((float)i/2.0)); // ***** lower sample needed for calculation
k=(ceil((float)i/2.0)); // ***** upper sample needed for calculation
b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
}
first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
}
else {
// we can read more data from the file...
n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
bytes_read=n;
file_offset += n;
for (i=n/2; i < 64; i++) {
block->data[i] = 0;
}
for (i=0;i<128;i++) {
h=(floor((float)i/2.0))-1; // ***** lower sample needed for calculation
k=(ceil((float)i/2.0))-1; // ***** upper sample needed for calculation
b[i]=(((h<0) ? first_sample: block->data[h])+((k<0)? first_sample: block->data[k]))/2; // **** interpolated value
}
first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
}
for (i=0;i<128;i++) { // ***** preparing samples to be sent out
block->data[i]=b[i];
}
t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
ride ++;
// ***** main code ends here
transmit(block);
}
else {
rawfile.close();
AudioStopUsingSPI();
playing = false;
//Serial.println("Finished playing sample"); //TODO
}
release(block);
}
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
uint32_t AudioPlaySerialflashRaw::positionMillis(void)
{
return ((uint64_t)file_offset * B2M) >> 32;
}
uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
{
return ((uint64_t)file_size * B2M) >> 32;
}
int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time t, must be within 2500 microseconds
{
return t;
}
int AudioPlaySerialflashRaw::bytes(void) // // ***** reporting the numeber of bytes read, just for some verification
{
return bytes_read;
}
bool AudioPlaySdRawPitch::setPitch(float pitch_value)
{
pitch = pitch_value; // ***** set pitch value
return true;
}
float tapeSpeed(float _speed, float _depth, float _lp) {
if (_depth > 0.0) {
static float ws = 0.0;
static uint8_t w8 = 128;
w8 += random(3) - 1;
ws += ( ( ( ( (float) w8 / 256.0 ) - 0.5 ) * _depth ) - ws ) * _lp;
return ws + _speed;
} else {
return _speed;
}
}
void loop() {
myCustomPlayer.setPitch( tapeSpeed( 1.0, 0.1, 0.001 ) );
}
Probably not much simpler than this. This is a sample from Audacity export sample data as text from wav file and play in teensy using interval timer. the rate in the sketch is varied by counting down a timer but could be controlled by buttons or pot also the loudness or amplitude could also be controlled.
Long time reader, first time poster.
I'm trying to accomplish the same thing and for my use case, slowing down all of the samples would work great! I don't see any files attached to your post though... any chance that you could repost it?