Teensy Audio Library Scrubber Object

dangelo

Member
Heyo everyone,

So I've been working on implementing live scrubbing through audio on the teensy for a large project I'm creating. Think scratching vinyl or scrubbing through a tape machine. I was trying to implement it with the queue object, but I was running into a ton of issues with clicking and weird sound artifacts, so I decided to create an object to make it easier: AudioPlayScrub.

Basically, the object stores a pointer to an audio array (an array of 16 bit int values), which you can store in PROGMEM, and then, after being activated, you can set a target position (from 0.0 to 1.0), and the internal playhead of the object will approach that target by a rate that you can change with the setRate(float 0.0-1.0) function. It's super easy to fit into projects, and it sounds quite nice. I'm still working on smoothing out the sound a bit to get rid of some weird digital artifacts, but I'd love to let everyone take a look, get some feedback, and give it to anyone that needs a live scrubber for their next project.

Here's a GitHub link:
https://github.com/dangelo729/teensy_audio_library_scrubber

Let me know if anybody has any questions about it.
 
Well done mr. dangelo! You are wonderful and your scrubber object beautiful. I will definitely use this at some point, excited to scrub!
 
This is very cool!
Do you have a demo of it on Youtube or some other platform?
Have you tried to scrub through using a jog wheel of some sort?
 
Great!! Looking forward to try this out! Thanks for the effort and sharing.

Is it a matter of limited memory or would it somehow be possible to load files from SD card to an array to use it?

Is it possible to play reverse somehow? Would be a nice feature. Maybe with the possibility to set rate to a negative value?

Another really nice feature would be to set a length to be looped. That would make a great granular synthesis effect. (as far as I remember, with the existing granular object you have to "feed" the input first...)
 
I have a T4 dev board with SDRAM, wondering if the entire audio track can be copied to a buffer in SDRAM, then scrubbed from there.

Is there is a sample sketch I can test?
 
Great!! Looking forward to try this out! Thanks for the effort and sharing.

Is it a matter of limited memory or would it somehow be possible to load files from SD card to an array to use it?

Is it possible to play reverse somehow? Would be a nice feature. Maybe with the possibility to set rate to a negative value?

Another really nice feature would be to set a length to be looped. That would make a great granular synthesis effect. (as far as I remember, with the existing granular object you have to "feed" the input first...)
Thank you for the kind words! I actually have revamped the project entirely so it works with raw audio files from an SD card. It does it in a kind of weird way where you declare a DMAMEM array of some size and pass that as a pointer to the scrubber object, allowing you to play super long SD length audio files. It allows scrubbing and variable speed playback of RAW audio files from an SD card (the speed can be negative). This way you can use it with the queue recording object to scrub through your own recordings and such (I'm working on it for this tape scrubber sampler thing I'm creating).

The loop length is a really cool idea, I'll definitely look into incorporating that.

I should be able to update the GitHub with the new code this week, though my code, specifically the update function, is somewhat spaghetti so prepare yourself.
 
I have a T4 dev board with SDRAM, wondering if the entire audio track can be copied to a buffer in SDRAM, then scrubbed from there.

Is there is a sample sketch I can test?
I'll work on getting a simple example together sometime this week, but the new version uses a raw audio files on an SD card and a smaller DMAMEM array of uint_16 values, which sounds close to what your looking for.
 
This is very cool!
Do you have a demo of it on Youtube or some other platform?
Have you tried to scrub through using a jog wheel of some sort?
Thank you!
I don't have a demo yet, but I'm getting to a point with the project I'm working on (a multi-engine art-synth thing) where I'm excited to share it soon. I hope to put out a simple example sketch asap though.
I'm currently using a rotary encoder for the scrubbing, which works quite well, a jog wheel would work perfectly and sound great.
 
I've little experience with the Audio library, just getting into it now.
So this will scrub a raw audio file which is mono. So to support stereo, you'd need two classes and two pointers?

As mentioned, an example of implementing this would be great
 
Hey anyone who's following this, sorry about being MIA! I had a super busy summer, but I finally updated the GitHub for the scrubber. It now includes the most up to date code, allowing for variable speed playback as well as smooth scrubbing of raw audio files stored on an SD card. I have also included an example in the repo. Here's a link:
https://github.com/dangelo729/teensy_audio_library_scrubber/tree/main
Let me know what you all think, and if you use it in your project please let me know! I'd love to see it being used out there.
@Rezo @TomChiron
Tagging previous commenters so this reply gets to them
 
Thanks for the update and tagging me!

I actually took an entirely different route on my project, although this object is what sparked my initial interest and got me going alltogether!

I am using interrupt based audio stream (no DMA), using a large 7MB ring buffer in PSRAM
I am able to control playback speed using a pitch variable and some interpolation algorithm taken from an STM32 based project I found online.
I am also able to set play direction using a boolean variable.

So to scrub/scratch the audio I use a jog wheel with an optical quad encoder + touch sensor. As soon as I touch the jog wheel I can then alter the pitch directly from the jog wheel speed, as well as determine the direction of the wheel movement, and reproduce vinyl scratching.

My next goal is to implement some form of time stretching for the pitch control, so that I can lock they key but change the tempo.
So far it seems like granular synthesis might be the only available option to use with the audio stream method I have implemented on the 1062 as there is no hardware DSP available to use other methods which usually require FFT etc..
 
Last edited:
Hey @dangelo ,
I feel a bit stupid to ask this, but:
Where do I drop the .cpp and .h files to get the example to work? It sounds like that this is exactly what I was looking for.
Would be awesome to get it to work. Thanks for the effort!
Cheers!
 
Hey @dangelo ,
I feel a bit stupid to ask this, but:
Where do I drop the .cpp and .h files to get the example to work? It sounds like that this is exactly what I was looking for.
Would be awesome to get it to work. Thanks for the effort!
Cheers!
Not stupid at all! I had my environment setup in platformio, so I had a modified version of the audio library installed which included the scrub object.

If you just want to get the example to work in Teensyduino, you should just be able to include play_scrub.h for it to work, like this:


Code:
/ Example code for using the custom scrubber audio object
//This example is based on the recorder example from the teensy audio library
// Scrubber object and addition of scrubber to this example by: Peter D'Angelo
// Enjoy the scrubber let me know if you have issues or ideas
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// Three pushbuttons need to be connected:
//   Record Button: pin 0 to GND
//   Stop Button:   pin 1 to GND
//   Play Button:   pin 2 to GND
//
// This example code is in the public domain.
// This example uses 3 buttons and a potentiometer to control the scrubber object

#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "play_scrub.h" //include play scrub here

// GUItool: begin automatically generated code
AudioInputI2S i2s2;      // xy=105,63
AudioAnalyzePeak peak1;  // xy=278,108
AudioRecordQueue queue1; // xy=281,63
AudioPlayScrub scrubber; // xy=302,157
AudioOutputI2S i2s1;     // xy=470,120
AudioConnection patchCord1(i2s2, 0, queue1, 0);
AudioConnection patchCord2(i2s2, 0, peak1, 0);
AudioConnection patchCord3(scrubber, 0, i2s1, 0);
AudioConnection patchCord4(scrubber, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; // xy=265,212
// GUItool: end automatically generated code

// rest of the example after this

Let me know if it ends up working for you or if you have any issues with it!
 
Not stupid at all! I had my environment setup in platformio, so I had a modified version of the audio library installed which included the scrub object.

If you just want to get the example to work in Teensyduino, you should just be able to include play_scrub.h for it to work, like this:


Code:
/ Example code for using the custom scrubber audio object
//This example is based on the recorder example from the teensy audio library
// Scrubber object and addition of scrubber to this example by: Peter D'Angelo
// Enjoy the scrubber let me know if you have issues or ideas
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// Three pushbuttons need to be connected:
//   Record Button: pin 0 to GND
//   Stop Button:   pin 1 to GND
//   Play Button:   pin 2 to GND
//
// This example code is in the public domain.
// This example uses 3 buttons and a potentiometer to control the scrubber object

#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "play_scrub.h" //include play scrub here

// GUItool: begin automatically generated code
AudioInputI2S i2s2;      // xy=105,63
AudioAnalyzePeak peak1;  // xy=278,108
AudioRecordQueue queue1; // xy=281,63
AudioPlayScrub scrubber; // xy=302,157
AudioOutputI2S i2s1;     // xy=470,120
AudioConnection patchCord1(i2s2, 0, queue1, 0);
AudioConnection patchCord2(i2s2, 0, peak1, 0);
AudioConnection patchCord3(scrubber, 0, i2s1, 0);
AudioConnection patchCord4(scrubber, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; // xy=265,212
// GUItool: end automatically generated code

// rest of the example after this

Let me know if it ends up working for you or if you have any issues with it!
Thanks for the answer! Just figured it out, literally minutes before your reply (but did not work on it too much during that time)

Great, will start experimenting around with this :)
 
Last edited:
Back
Top