May i contact to "Bleep Labs."???

@charnjit
Good that you have Granular effect running now. I have been following your various threads in the last week.
Your goal, to pitch shift/time stretch in professional audio quality, is one of the most complicated tasks in digital signal processing! Even professional dsp programmers seem to have a hard time implementing that.
Some time ago, I tested the quality of the existing pitch shift algorithms that -to my knowledge- had been implemented up to now on the Teensy. See here: https://github.com/DD4WH/BirdSongPitchShifter
Granular was not satisfactory in terms of audio quality. However, as you can see from the code, the other approaches are MUCH more complex.
In order to perform your task, I would recommend that you:
* search for scientific papers on time stretching and try to understand how it works
* begin with easy examples that you program by yourself (you could use help from an LLM like Claude or perplexity
* do not expect that there is time stretching code for the Teensy. If it was like that, you would have had responses here.
* you could start with the approach by
Stefan Bernsee phase Vocoder and Duff2013 implementation, search for them in the web and ask an LLM to explain the approach to you
* set your expectation: you will need a long time, because you are tackling a very complex task!
* please do not open another thread
Hope that helps, good luck with your task!
 
Thank you very much .. DD4WH...
You can judge my ability.. from your answer that I learned that the change in speed is called "time stretching" event here. I am not that good at coding to create complex objects like developer. yes that much,.. I can look at the example code and use it in my own sketch.
We have complete faith in the things we see.This is the video of "Nic N" Teensy variable playback speed
that inspired me to buy "TEENSY:4.1 + AUDIO BOARD".In this I can clearly see "time-stretching" of Drum loop without affect the pitch.
I thought that by looking at its code I would be able to achieve my goal. But I am unable to see its code. I had also asked by commenting on this video. Either my request didn't reach them, or they don't want to show the code.

Can you help me get the code that's running in that video?
If yes, thank you very much..............
If this doesn't happen, I will have to continue my efforts for many years, and perhaps keep getting entangled.
in both condition,
Please give some answer............Thank You
 
Thank you very much .. DD4WH...
You can judge my ability.. from your answer that I learned that the change in speed is called "time stretching" event here. I am not that good at coding to create complex objects like developer. yes that much,.. I can look at the example code and use it in my own sketch.
We have complete faith in the things we see.This is the video of "Nic N" Teensy variable playback speed
that inspired me to buy "TEENSY:4.1 + AUDIO BOARD".In this I can clearly see "time-stretching" of Drum loop without affect the pitch.
I thought that by looking at its code I would be able to achieve my goal. But I am unable to see its code. I had also asked by commenting on this video. Either my request didn't reach them, or they don't want to show the code.

Can you help me get the code that's running in that video?
If yes, thank you very much..............
If this doesn't happen, I will have to continue my efforts for many years, and perhaps keep getting entangled.
in both condition,
Please give some answer............Thank You
Hmm, not sure what you mean.
The code is here:
It was easy to find by typing the name of the video author "newdigate" together with "github" as prompt into an internet search machine.
But maybe you already tried that code?
 
@DD4WH really interesting work on the BirdSongPitchShifter and a thanks for the pointer to the phase vocoder implementation

@charnjit I had a play about with a new object which I've attached with and example sketch. It is using a basic WSOLA algorithm (great explanation of WSOLA approach here). I hacked it together with more than a little help from Claude, if it is useful it will need more work.

I believe you're aiming to dynamically change the length of a sample (say a beat like your example) without changing pitch. I've set this to limit changes in speed to 0.5x - 1.5x although what I think is decent quality is probably more like 0.8x - 1.2x. I think this range would be fine for beat matching but I don't really know what you are trying to do with it. When I run it I can hear some pitch distortion when I change speed but I'm not at home at the moment so don't have any test equipment and I don't know what you will think is acceptable or not.

I also only have a Teensy 3.6 with me - it runs fine on this so should also run on a 4.1. I've hard coded loading a file "01.wav" from SD card with a 2 second limit - in timestretch_pot.ino you can change that to PSRAM if you want longer samples.

As I only have a bare teensy and audiboard with me I have the controls set up to read from the serial monitor. The controls are :
'p' → increase speed
'q' → decrease speed
'r' → restart sample from the beginning
's' → stop playback
'b' → toggle reverse playback
You can change the code in timestretch_pot.ino to read the speed from a pot.

Let me know if it is doing what you expect. I may have a go at a phase vocoder approach when I get back to base so getting a better idea of what is needed musically in terms of range and quality etc. would be useful.

Cheers, Paul
 

Attachments

  • timestretch_pot.ino
    3.9 KB · Views: 13
  • effect_timestretcher.cpp
    11.8 KB · Views: 12
  • effect_timestretcher.h
    4.2 KB · Views: 11
@DD4WH really interesting work on the BirdSongPitchShifter and a thanks for the pointer to the phase vocoder implementation

@charnjit I had a play about with a new object which I've attached with and example sketch. It is using a basic WSOLA algorithm (great explanation of WSOLA approach here). I hacked it together with more than a little help from Claude, if it is useful it will need more work.

I believe you're aiming to dynamically change the length of a sample (say a beat like your example) without changing pitch. I've set this to limit changes in speed to 0.5x - 1.5x although what I think is decent quality is probably more like 0.8x - 1.2x. I think this range would be fine for beat matching but I don't really know what you are trying to do with it. When I run it I can hear some pitch distortion when I change speed but I'm not at home at the moment so don't have any test equipment and I don't know what you will think is acceptable or not.

I also only have a Teensy 3.6 with me - it runs fine on this so should also run on a 4.1. I've hard coded loading a file "01.wav" from SD card with a 2 second limit - in timestretch_pot.ino you can change that to PSRAM if you want longer samples.

As I only have a bare teensy and audiboard with me I have the controls set up to read from the serial monitor. The controls are :
'p' → increase speed
'q' → decrease speed
'r' → restart sample from the beginning
's' → stop playback
'b' → toggle reverse playback
You can change the code in timestretch_pot.ino to read the speed from a pot.

Let me know if it is doing what you expect. I may have a go at a phase vocoder approach when I get back to base so getting a better idea of what is needed musically in terms of range and quality etc. would be useful.

Cheers, Paul
Hey Paul

I tested this
It's playing really slow, and the pitch is really low too. this is at 1.0f playback speed

I think the phase vocoder approach will be the only way do do this properly
 
Hey Paul

I tested this
It's playing really slow, and the pitch is really low too. this is at 1.0f playback speed

I think the phase vocoder approach will be the only way do do this properly
Thats's weird - plays at normal speed for me at 100%. I'm using this wav from the OP
 

Attachments

  • 01.wav.zip
    169.9 KB · Views: 14
Thats's weird - plays at normal speed for me at 100%. I'm using this wav from the OP
Also should have asked, are you using a teensy 4 ? I've only tested on a 3.6 so far. Also, at the moment it will only work with 128 sample per block.
 
It needs a mono, 44.1khz with no additional (meta) data in the header WAV file.

If you use FFMPEG, # ffmpeg -i original.wav -ac 1 -ar 44100 -c:a pcm_s16le -map_metadata -1 clean.wav
should clean up an original.
 
Yes, thank you all for your kind words.
Thank God I was able to convey my true message to you.
@houston...
This is exactly what I was asking about [Time-Stretching].
Thanks for these functions added:

C++:
              stretcher.stop();
                          stretcher.play();
                    applySpeed(speed + CONTROL_STEP);
                   applySpeed(speed - CONTROL_STEP);
                  stretcher.loop(true);
I need all of these in my Sketch.
Now Report: What I saw/listen after Uploading your code.

I used "A01.WAV" in the example code you gave in Reply#29 to see result of Pitch after Stretching.In "A01.wav", guitar chords
are also played along with drums. Also attached below.
---The entire drum loop is not playing, the last part is being skipped.About 3/4 of total duration is repeated. full duration of wav is 2.264
---The pitch is also deteriorating as the speed increases.
I want you to listen to it separately in Audacity or some other software in proper Seamless looping.
Then compare it with the output of "Teensy Audio".
I wish that the above defects be rectified to the extent they can be rectified,Stay pitch constant and Maintain
quality of Audio/Pitch as much as possible while stretching.

I am very encouraged by the code you have created.Now I see my problem getting solved to a great extent.
The functions you added to this object are all very useful.Just improve on the above points.
Makes a great rhythm musical instrument
code what i upload to test: [01.wav is replaced with A01.wav]
C++:
// timestretch_pot.ino
//
// Plays a mono WAV file from PSRAM with time-stretching controlled by serial
// keys or an optional potentiometer wired to an analogue pin.
//
// Hardware:
//   Teensy 3.x or 4.x + Audio Shield (SGTL5000)
//
// Contols:
//   Serial 'p' → increase speed by 0.02×
//   Serial 'q' → decrease speed by 0.02×
//   Serial 'r' → restart sample from the beginning
//   Serial 's' → stop playback
//   Serial 'b' → toggle reverse playback
//   Alternative control:
//   Pot wiper → A0 Pot range maps to playback speed 0.5× … 1.5×.
//
// WAV requirements:
//   Mono, 16-bit PCM, 44100 Hz
//   Load before calling play()

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include "effect_timestretcher.h"


AudioEffectTimeStretch stretcher;
AudioOutputI2S audioOut;
AudioControlSGTL5000 codec;

AudioConnection patchLeft(stretcher, 0, audioOut, 0);
AudioConnection patchRight(stretcher, 0, audioOut, 1);

// Pin & timing config
static const int POT_PIN = A0;
static const float SPEED_MIN = 0.5f; // full CCW  → half speed
static const float SPEED_MAX = 1.5f; // full C  qsW   → 1.5× speed
static const int POT_READ_MS = 50;   // read pot every 50 ms
static const float CONTROL_STEP = 0.01f;
static const bool USE_POT = false;
float speed   = 1.0f;
bool  reverse = false;

static float clampf(float value, float lo, float hi)
{
    if (value < lo)
        return lo;
    if (value > hi)
        return hi;
    return value;
}

static void applyReverse(bool newReverse)
{
    reverse = newReverse;
    stretcher.setReverse(reverse);
    stretcher.stop();
    stretcher.play();
    Serial.println(reverse ? "Reverse: on" : "Reverse: off");
}

static void applySpeed(float newSpeed)
{
    speed = clampf(newSpeed, SPEED_MIN, SPEED_MAX);
    stretcher.setSpeed(speed);

    Serial.print("Speed: ");
    Serial.print(speed, 3);
    Serial.println("x");
}

// sample buffer — fill this before calling stretcher.play()
// Example: EXTMEM places the array in PSRAM on Teensy 4.1.
// Replace with your own load routine (SD card, USB, etc.).
int16_t sampleBuffer[44100 * 2]; // space for 2.5s at 44100 Hz
static uint32_t sampleCount = 0; // actual samples loaded

// load raw 16-bit PCM from SD into PSRAM.
// Replace with your real loader.
bool loadSampleFromSD(const char *filename)
{
    if (!SD.begin(BUILTIN_SDCARD))
        return false;
    File f = SD.open(filename);
    if (!f)
        return false;
    f.seek(44);
    sampleCount = 0;
    while (f.available() && sampleCount < sizeof(sampleBuffer) / sizeof(sampleBuffer[0]))
    {
        int lo = f.read();
        int hi = f.read();
        if (lo < 0 || hi < 0)
            break;
        sampleBuffer[sampleCount++] = (int16_t)((hi << 8) | lo);
    }
    f.close();
    return sampleCount > 0;
}

void setup()
{
    AudioMemory(40);
    codec.enable();
    codec.volume(0.5f);

    // Load your sample — swap in your own loader as needed.
    if (loadSampleFromSD("A01.wav"))
    {
        stretcher.setSample(sampleBuffer, sampleCount);
        stretcher.loop(true);
        applySpeed(speed);
        stretcher.play();
    }
}

void loop()
{
    static uint32_t lastRead = 0;
    if (Serial.available() > 0)
    {
        char key = Serial.read();

        if (key == 'p')
        {
            applySpeed(speed + CONTROL_STEP);
        }
        else if (key == 'q')
        {
            applySpeed(speed - CONTROL_STEP);
        }
        else if (key == 'r')
        {
            stretcher.stop();
            stretcher.play();
        }
        else if (key == 's')
        {
            stretcher.stop();
            Serial.println("Stopped");
        }
        else if (key == 'b')
        {
            applyReverse(!reverse);
        }
    }

    if (USE_POT && millis() - lastRead >= POT_READ_MS)
    {
        lastRead = millis();
        int raw = analogRead(POT_PIN);
        float mappedSpeed = SPEED_MIN + (raw / 1023.0f) * (SPEED_MAX - SPEED_MIN);
        applySpeed(mappedSpeed);
    }
}

I hope you will update its quality thoroughly with free minded.
Thank You..............
 

Attachments

  • A01.zip
    170.7 KB · Views: 13
Last edited:
I've hard coded loading a file "01.wav" from SD card with a 2 second limit - in timestretch_pot.ino you can change that to PSRAM if you want longer samples.


@charnjit - if you want the full sample to play and loop you need to make the buffer larger - I'm limited to 2 second as I'm using a teensy3.6 - something like :
Code:
EXTMEM int16_t sampleBuffer[44100 * 5]; // space for 5s at 44100 Hz
will create 5 seconds of buffer in your PSRAM

I think the quality you are hearing is probably as good as it will get using this WSOLA algorithm

cheers, Paul
 
it works playing full duration of 2.264 sec seamlessly. using
C++:
EXTMEM int16_t sampleBuffer[44100 * 5];

Now I can only hear some pitch errors. If this can also be improved while maintaining the audio quality, then it will serve me as a good sampler.
Note: I have to also beat match its speed with another running Tempo.
There is no hurry, you can set it up whenever you have free time for it . Just give it some time in reply, after which I will remember you again, if you forget......
Thank you...
 
I forked and did a rehash of the Stefan Bernsee/Duff2013 phase vocoder implementation.

This can now do time stretching and pitch shifting independently - both can run simultaneously - using a phase vocoder approach. I also added configurable transient detection to try to improve drum sounds.

This one is teensy 4.x only. When not just passing through it takes ~17% of available interrupt time with 128 sample blocks, at 8× overlap with all features active (thanks to @DD4WH for the performance profiling idea).

I've included an example sketch with keyboard controls for the various functions - the example runs off wav file samples from the SD card and is routing the audio back through usb, if you want it to use the audio board then you will need to update it. I also include 3 test wav file in the test directory - 2 of these were provided by @charnjit (hope its ok to include them, let me know if it is not).

Any feedback welcome, https://github.com/houtson/AudioEffectPhaseVocoder

Cheers Paul
 
@houtson this is quite amazing, am pleasantly surprised you got this to work!!

I haven’t tested it yet, but I have a few questions
1. I understand this supports only mono at the moment, would stereo be possible?
2. Are you able to estimate how performance heavy this is on the Teensy?

This would be highly valuable for my CDJ project, will reach out privately to consult with you on possible implementation if that’s okay with you.
 
@houtson this is quite amazing, am pleasantly surprised you got this to work!!

I haven’t tested it yet, but I have a few questions
1. I understand this supports only mono at the moment, would stereo be possible?
2. Are you able to estimate how performance heavy this is on the Teensy?

This would be highly valuable for my CDJ project, will reach out privately to consult with you on possible implementation if that’s okay with you.
Hi @Rezo - have a listen to it, it works better with some material than others but it’s great fun change both the pitch and speed.
It uses about 18% of teensy 4.1 available interrupt time, the WSOLA version was much less <5% but the phase vocoder is much better. Stereo would be possible but 2x the load.
Any improvement ideas welcome, I’m far from an expert on this but using Claude has helped accelerate learning.
Cheers Paul
 
Than you .... Houston
You made this, it looks really useful.But unfortunately I was not able to test it.
i tried download Zip library from https://github.com/houtson/AudioEffectPhaseVocoder
when tried include zip library ,,, i got error:
Code:
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\AppData\Local\Temp\arduino_build_296148\sketch\timestretch_pot.ino.cpp.o: in function `AudioStream::AudioStream(unsigned char, audio_block_struct**)':
C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `win1024_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `coefA_512_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `coefB_512_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\AppData\Local\Temp\arduino_build_296148\sketch\effect_phaseVocoder.cpp.o: in function `AudioEffectPhaseVocoder::update()':
C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:126: undefined reference to `split_rfft_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:140: undefined reference to `atan2_fast'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:214: undefined reference to `atan2_fast'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:229: undefined reference to `split_rifft_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:202: undefined reference to `atan2_fast'
collect2.exe: error: ld returned 1 exit status
Using library Audio at version 1.3 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\Audio
Using library SPI at version 1.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SPI
Using library SD at version 2.0.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SD
Using library SdFat at version 2.1.2 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SdFat
Using library SerialFlash at version 0.5 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SerialFlash
Using library Wire at version 1.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\Wire
Error compiling for board Teensy 4.1.
Specified folder/zip file does not contain a valid library
when i copied and paste effect_phaseVocoder.cpp and effect_phaseVocoder.h into main sketch Directory.
on compile i get error
C++:
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\AppData\Local\Temp\arduino_build_296148\sketch\timestretch_pot.ino.cpp.o: in function `AudioStream::AudioStream(unsigned char, audio_block_struct**)':
C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `win1024_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `coefA_512_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Program Files\Arduino\hardware\teensy\avr\cores\teensy4/AudioStream.h:137: undefined reference to `coefB_512_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\AppData\Local\Temp\arduino_build_296148\sketch\effect_phaseVocoder.cpp.o: in function `AudioEffectPhaseVocoder::update()':
C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:126: undefined reference to `split_rfft_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:140: undefined reference to `atan2_fast'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:214: undefined reference to `atan2_fast'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:229: undefined reference to `split_rifft_f32'
c:/program files/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\acer\Documents\Arduino\timestretch_pot/effect_phaseVocoder.cpp:202: undefined reference to `atan2_fast'
collect2.exe: error: ld returned 1 exit status
Using library Audio at version 1.3 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\Audio
Using library SPI at version 1.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SPI
Using library SD at version 2.0.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SD
Using library SdFat at version 2.1.2 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SdFat
Using library SerialFlash at version 0.5 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\SerialFlash
Using library Wire at version 1.0 in folder: C:\Program Files\Arduino\hardware\teensy\avr\libraries\Wire
Error compiling for board Teensy 4.1.

Has anyone been able to use it successfully or not? ... or .... My method is wrong????
I can't see this on library manager
 
Last edited:
Hi @charnjit - I take it you are using the Arduino IDE? I'm using PlatformIO so it will not work directly with Arduino IDE or the library manager.

For it to work in the Arduino IDE try this:
- create a new directory called timestretch_example in your Arduino sketch directory
- copy all the files in the /src directory from the zip file into the timestretch_example directory
- copy the 01.wav, 02.wav and 03.wav files in the test directory to the SD card

Cheers Paul
 
What if we want to create a new sketch using stretcher functions?
It would be great if you could make it easier work directly in Arduino IDE windows 7 like normal zip library.By the way, I'll go home and try your method....Thank you...
 
compiled and upload example successfully ,,,, but no Audio from board .. i can also see status Playing in serial monitor, but no Audio...........
 
Did you mean..is routing the audio back through usb .......No audio from PC ..No audio from Board Out Line.
If you mean something else, please tell me. I've never heard any audio of Teensy in the connected PC.........Yes, it is like this that as long as Teensy is connected to the computer, the sound of the computer is not heard. Then I disable Teensy in the audio setting of the computer, then I can hear the audio of the computer in the computer Speakers.
i also tried in code
C++:
//  AudioOutputUSB          audioOut;
 AudioOutputI2S       audioOut;
 AudioControlSGTL5000 codec;
It would be better if the audio of Teensy is heard only on the outline of the audio board.
 
Last edited:
@charnjit if using the audio board you need to update the example ino.---- do what you did above and also remove the comments from the codec in setup()
Code:
    // codec.enable();
    // codec.volume(0.5f);
 
Thank you ..houston
i know without enable codec.enable(); for AudioControlSGTL5000 and set volume codec.volume(0.5f); ,no sound will be produced.But here the matter is something else .I hadn't looked at the serial monitor.The sound was coming after 30-35 seconds from teensy power on, even though I had not added any delay(); in the void setup().I have made a slight change in the code.I don't think I did anything to slow down the void setup()code. In my code the audio samples are loaded into the PSRAM, and is loaded and played in the vocoder.Please take a look at my code & see running speed of void setup () in this video.
code is:

C++:
// timestretch_example.ino
//
// Phase vocoder time-stretching — Teensy 4.x + Audio Shield (SGTL5000)
//
// Controls (Serial):
//   SPACE  start / restart playback
//   s      stop playback
//   p      faster  (decrease stretch)
//   q      slower  (increase stretch)
//   1-3    load and play 01.WAV / 02.WAV / 03.WAV
//   t      transient threshold: 4  (sensitive — more phase resets)
//   y      transient threshold: 8  (default)
//   u      transient threshold: 16 (subtle — fewer phase resets)
//   d      toggle profiling report
//   h      print help
//
// Optional: pot wiper → A0 (outer legs to 3.3 V and GND)
//   Pot maps to stretch STRETCH_MIN … STRETCH_MAX

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include "effect_phaseVocoder.h"

// ---------------------------------------------------------------------------
// Audio graph
// ---------------------------------------------------------------------------
AudioEffectPhaseVocoder vocoder;
//  AudioOutputUSB          audioOut;
 AudioOutputI2S       audioOut;
 AudioControlSGTL5000 codec;

AudioConnection patchLeft (vocoder, 0, audioOut, 0);
AudioConnection patchRight(vocoder, 0, audioOut, 1);

// ---------------------------------------------------------------------------
// Config
// ---------------------------------------------------------------------------
static const int   POT_PIN      = A0;
static const float STRETCH_MIN  = 0.5f;   // 0.5 = half duration (2× faster)
static const float STRETCH_MAX  = 1.5f;   // 1.5 = 1.5× duration (slower)
static const int   POT_READ_MS  = 50;
static const float CONTROL_STEP = 0.01f;
static const bool  USE_POT      = false;

float stretch   = 1.0f;
float pitchSt   = 0.0f;   // pitch shift in semitones
bool  profiling = false;

// RAM CODE
#define NUM_WAVS 16 // avoid silly mistakes
 const char *SMP_WAV[NUM_WAVS] = { "A01.WAV", "A02.WAV", "A03.WAV", "A04.WAV", "05.WAV", "06.WAV", "07.WAV", "08.WAV", "09.WAV", "10.WAV", "11.WAV", "12.WAV", "13.WAV", "14.WAV", "15.WAV", "16.WAV" };
File x_File;
int16_t *SMP_addr[NUM_WAVS];
uint32_t sizes[NUM_WAVS];

// ---------------------------------------------------------------------------
// Sample buffer
// DMAMEM places in OCRAM (512 KB) instead of DTCM.
// On Teensy 4.1 with PSRAM: replace DMAMEM with EXTMEM for much larger buffers.
// ---------------------------------------------------------------------------
EXTMEM int16_t sampleBuffer[44100 * 5];  //  DMAMEM
static uint32_t sampleCount = 0;

static const char *sampleFiles[] = { "A01.WAV", "A02.WAV", "A03.WAV" };
static int currentSample = 0;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
static float clampf(float v, float lo, float hi) {
    return v < lo ? lo : v > hi ? hi : v;
}

static void applyStretch(float newStretch) {
    stretch = clampf(newStretch, STRETCH_MIN, STRETCH_MAX);
    vocoder.setStretch(stretch);
    float durationS = stretch * (sampleCount / (float)AUDIO_SAMPLE_RATE_EXACT);
    Serial.print("x  Speed: "); Serial.print(1.0f / stretch, 2);
    Serial.print("x  Duration: "); Serial.print(durationS, 1); Serial.println("s");
}

static void printHelp() {
    Serial.println("--- Controls ---");
    Serial.println("  SPACE  start / restart");
    Serial.println("  s      stop");
    Serial.println("  p      faster");
    Serial.println("  q      slower");
    Serial.println("  1-3    load 01.WAV / 02.WAV / 03.WAV");
    Serial.println("  w      pitch up 1 semitone");
    Serial.println("  x      pitch down 1 semitone");
    Serial.println("  z      reset pitch to 0");
    Serial.println("  t      transient threshold: 4  (sensitive)");
    Serial.println("  y      transient threshold: 8  (default)");
    Serial.println("  u      transient threshold: 16 (subtle)");
    Serial.println("  d      toggle profiling");
    Serial.println("  e      print per-section costs");
    Serial.println("  h      this help");
}

// ---------------------------------------------------------------------------
// WAV loader — handles mono and stereo (stereo: left channel only).
// Reads channel count from WAV header offset 22.
// ---------------------------------------------------------------------------
static bool loadSampleFromSD(const char *filename) {
    File f = SD.open(filename);
    if (!f) {
        Serial.print("Could not open: "); Serial.println(filename);
        return false;
    }

    f.seek(22);
    int chanLo = f.read();
    int chanHi = f.read();
    if (chanLo < 0 || chanHi < 0) { f.close(); return false; }
    const uint16_t numChannels = (uint16_t)((chanHi << 8) | chanLo);

    f.seek(44);  // skip 44-byte WAV header to raw PCM data
    sampleCount = 0;
    const uint32_t maxSamples = sizeof(sampleBuffer) / sizeof(sampleBuffer[0]);

    while (f.available() && sampleCount < maxSamples) {
        int lo = f.read();
        int hi = f.read();
        if (lo < 0 || hi < 0) break;
        sampleBuffer[sampleCount++] = (int16_t)((hi << 8) | lo);
        if (numChannels == 2) {
            // skip right channel sample
            if (f.read() < 0 || f.read() < 0) break;
        }
    }
    f.close();

    Serial.print("Loaded: "); Serial.print(filename);
    Serial.print("  samples: "); Serial.print(sampleCount);
    Serial.print("  ch: "); Serial.print(numChannels);
    Serial.print("  dur: "); Serial.print(sampleCount / (float)AUDIO_SAMPLE_RATE_EXACT, 1);
    Serial.println("s");
    return sampleCount > 0;
}

static void loadAndPlay(int index) {
    currentSample = index;
    vocoder.stop();
    if (loadSampleFromSD(sampleFiles[index])) {
       // vocoder.setSample(sampleBuffer, sampleCount);
        vocoder.setSample(SMP_addr[3], sizes[3]/2);   //
        vocoder.setLoop(true);
        applyStretch(stretch);
        vocoder.play();
    }
}

// ---------------------------------------------------------------------------
// setup
// ---------------------------------------------------------------------------
void setup() {
    AudioMemory(40);
    Serial.begin(57600);
    while (!Serial && millis() < 2000) {}

     codec.enable();
     codec.volume(0.5f);

    if (!SD.begin(BUILTIN_SDCARD)) {
        Serial.println("SD init failed");
        return;
    }

    vocoder.setTransientThreshold(16.0f);
     RAM_LOAD ();     
    loadAndPlay(0);
    printHelp();
}

// ---------------------------------------------------------------------------
// loop
// ---------------------------------------------------------------------------
void loop() {
    static uint32_t lastPotRead = 0;
    static uint32_t lastProf    = 0;

    if (Serial.available() > 0) {
        char key = Serial.read();
        if      (key == ' ')                { vocoder.stop(); vocoder.play(); Serial.println("Playing"); }
        else if (key == 's')                { vocoder.stop(); Serial.println("Stopped"); }
        else if (key == 'p')                applyStretch(stretch - CONTROL_STEP);  // faster
        else if (key == 'q')                applyStretch(stretch + CONTROL_STEP);  // slower
        else if (key >= '1' && key <= '3')  loadAndPlay(key - '1');
        else if (key == 'w')                { pitchSt += 1.0f; vocoder.setPitchShift(pitchSt); Serial.print("Pitch: "); Serial.print(pitchSt, 0); Serial.println(" st"); }
        else if (key == 'x')                { pitchSt -= 1.0f; vocoder.setPitchShift(pitchSt); Serial.print("Pitch: "); Serial.print(pitchSt, 0); Serial.println(" st"); }
        else if (key == 'z')                { pitchSt = 0.0f;  vocoder.setPitchShift(pitchSt); Serial.println("Pitch: 0 st"); }
        else if (key == 't')                { vocoder.setTransientThreshold(4.0f);  Serial.println("Transient threshold: 4"); }
        else if (key == 'y')                { vocoder.setTransientThreshold(8.0f);  Serial.println("Transient threshold: 8"); }
        else if (key == 'u')                { vocoder.setTransientThreshold(16.0f); Serial.println("Transient threshold: 16"); }
        else if (key == 'd')                { profiling = !profiling; Serial.println(profiling ? "Profiling: on" : "Profiling: off"); }
        else if (key == 'e')                {
            float tW, tF, tA, tS, tI, tO;
            if (vocoder.getProfilingDetailed(tW, tF, tA, tS, tI, tO)) {
                Serial.println("--- Section costs (mean µs) ---");
                Serial.print("  window fill:     "); Serial.println(tW, 1);
                Serial.print("  forward FFT:     "); Serial.println(tF, 1);
                Serial.print("  phase analysis:  "); Serial.println(tA, 1);
                Serial.print("  phase synthesis: "); Serial.println(tS, 1);
                Serial.print("  inverse FFT:     "); Serial.println(tI, 1);
                Serial.print("  OLA + output:    "); Serial.println(tO, 1);
            }
        }
        else if (key == 'h')                printHelp();
    }

    // Profiling report every 2 seconds
    if (profiling && millis() - lastProf >= 2000) {
        lastProf = millis();
        float    meanUs;
        uint32_t peakUs;
        if (vocoder.getProfiling(meanUs, peakUs)) {
            const float budgetUs = 1e6f / (AUDIO_SAMPLE_RATE_EXACT / (float)AUDIO_BLOCK_SAMPLES);
            Serial.print("update()  mean: "); Serial.print(meanUs, 1);
            Serial.print(" us  peak: ");      Serial.print(peakUs);
            Serial.print(" us  load: ");      Serial.print(meanUs / budgetUs * 100.0f, 1);
            Serial.println("%");
        }
    }

    if (USE_POT && millis() - lastPotRead >= POT_READ_MS) {
        lastPotRead = millis();
        int raw = analogRead(POT_PIN);
        applyStretch(STRETCH_MIN + (raw / 1023.0f) * (STRETCH_MAX - STRETCH_MIN));
    }
}

///=========================================
            void RAM_LOAD ()  {         
              
  int SMP_FILE_NUM=0;
  for (int i = 0; i < NUM_WAVS; i++) {
                              x_File = SD.open(SMP_WAV[i], FILE_READ);
                             if (x_File)
                                         {
      sizes[i] = x_File.size();
      SMP_addr[i] = (int16_t*) extmem_malloc(sizes[i]);
      if (nullptr == SMP_addr[i])
        Serial.printf("Failed to allocate %d in EXTMEM for %s\n", sizes[i], SMP_WAV[i]);
      else     {
        if (sizes[i] != x_File.read(SMP_addr[i], sizes[i]))     
        {
          Serial.printf("Failed to read in %s - wrong length\n", SMP_WAV[i]);
          extmem_free(SMP_addr[i]); // free memory
          SMP_addr[i] = nullptr;    // mark as "not loaded"
        }
        else
          Serial.printf("Read %s into memory at %08X; %d bytes\n", SMP_WAV[i], SMP_addr[i], sizes[i]);
      }
      x_File.close();
    }
    else
      Serial.printf("Failed to open %s\n", SMP_WAV[i]);
  }
}

The samples are not separate; they are mostly multiple duplicates of the same sample.. Audio samples are not large enough to take so long to load into PSRAM.If you see anything strange in my code, please let us know.
yes....after setup the code runs fine.

The audio quality in Time Stretching/PitchShift is very good to a large extent.We thank you very much for this.

What role does vocoder.setTransientThreshold(16.0f); play in this? ..What is its minimum and maximum range?...How much should it be kept for best quality?
 
Last edited:
@charnjit I'm glad you got it running.

It looks like you have modified the code to preload 16 samples into PSRAM? I have no idea how large the files are or the speed of your SD card but I can only imagine that is what is taking the time in setup. Are you using a good quality SD card ? Either way your video does look slow but I don't have much experience of SD card reading. It might be to do with reading direct from SD card to PSRAM/EXTMEM, could you try reading to RAM first then to PSRAM - someone else might have a better idea.

You have also modified loadAndPlay() - I am assuming this is only half complete as it is loading one sample from the SD card again (
loadSampleFromSD(sampleFiles[index]) then playing a different sample (vocoder.setSample(SMP_addr[3], sizes[3] / 2); ) - why are you /2 size? also are you removing the WAV file header somewhere or are these WAV files pre-processed?

vocoder.setTransientThreshold is used to set how sensitive the vocoder is to transients (like drum hits). It is an attempt to give some control over this - the default is 16 making it only 'fire' when there are really strong transients, lower values make it more sensitive. Depending on the music / source you can try different values there is not fixed value fro best quality. My experience was that it was quite subtle and I just left it as 16 mainly.

Cheers, Paul
 
Back
Top