Class: TimeStretching
Interactive example
Below is an example of the TimeStretching class being used with a realtime audio feed. Notice that we removed the rate
property, because it's logically impossible to speed up a realtime audio source and slowing down requires ever growing memory. For a demo on how Superpowered time-stretching sound, please check the Advanced Audio Player interactive example.
Alternatively, for non-realtime processing examples, please see the Offline Time-stretching section.
Overview
TimeStretching is the process of altering the duration of a sound, tempo of a piece of audio without altering the pitch. This effect morphs the perceived passage of time in the audio signal without modifying its unique characteristics. However this class is also able to change the pitch of the audio independently from its tempo, so it's capable of high quality pitch shifting.
Tempo (time-stretching) can be controlled using the rate
property. It's sonic behavior can be changed with sound
: 0
represents slightly lower quality while saving CPU, 1
is best for DJ apps and “complete” music, and 2
is tuned for instrumental loops and single instruments. Please consider the types of device your app will be running on here.
Pitch-shifting can be controlled with pitchShiftCents
, where cents are used to denote a unit of pitch based upon the equal tempered octave. The minimum and maximum values of this property are set between `-2400` (two octaves down) and `2400` (two octaves up) respectively. Pitch-shifting can sound unnatural for some vocals, and to mitigate that you can use the formantCorrection
property with adjustable strength from 0.0
to 1.0
.
One instance allocates around 220 kb memory.
Implementation example
Pitch-shifting real-time audio
The time doesn't change when using the pitch-shifting capability only, therefore the audio processing code is fairly simple as buffers are simply passing through.
class SuperpoweredTimeStretchingStageProcessor extends SuperpoweredWebAudio.AudioWorkletProcessor {
onReady() {
this.effect = new this.Superpowered.TimeStretching(
this.samplerate,
1,
2
);
this.effect.pitchShiftCents = 0;
this.effect.formantCorrection = 1;
}
onDestruct() {
this.effect.destruct();
}
onMessageFromMainScope(message) {
if (typeof message.pitchShiftCents !== 'undefined') this.effect.pitchShiftCents = message.pitchShiftCents;
if (typeof message.formatCorrection !== 'undefined') this.effect.formatCorrection = message.formatCorrection;
}
processAudio(inputBuffer, outputBuffer, buffersize, parameters) {
this.effect.samplerate = this.samplerate;
this.effect.addInput(
inputBuffer.pointer,
buffersize
);
if (!this.effect.getOutput(outputBuffer.pointer, buffersize )) {
for (let n = 0; n < buffersize * 2 ; n++) outputBuffer.array[n] = inputBuffer.array[n];
}
}
}
#include "SuperpoweredTimeStretching.h"
void initTimestretchingEffect() {
timeStretching = new Superpowered::TimeStretching(
48000,
1,
2
);
timeStretching->pitchShiftCents = 0;
timeStretching->formantCorrection = 1;
}
void changePitch(int pitchShiftCents) {
timeStretching->pitchShiftCents = pitchShiftCents;
}
void processAudio(float *interleavedInput, float *interleavedOutput, unsigned int numberOfFrames, unsigned int samplerate) {
timeStretching->samplerate = samplerate;
timeStretching->addInput(interleavedInput, numberOfFrame);
bool success = timeStretching->getOutput(interleavedOutput, numberOfFrames);
...
}
Offline time-stretching
Using the time stretcher is more complex than using other effects, because it changes time and therefore buffering as well. It's fully integrated into the AdvancedAudioPlayer class, which is recommended for simple use. But if the time-stretcher has to be used alone, the logic could look like this:
while (ts.getOutputLengthFrames() < numberOfFramesNeeded) {
let timestretcherNeedsThisAmountOfFramesToProduceAudio = ts.getNumberOfInputFramesNeeded();
if (timestretcherNeedsThisAmountOfFramesToProduceAudio can not be acquired) break;
acquire timestretcherNeedsThisAmountOfFramesToProduceAudio into some Superpowered.Float32Buffer;
ts.addInput(some Superpowered.Float32Buffer pointer, number_of_frames_acquired);
}
if (ts.getOutputLengthFrames() >= numberOfFramesNeeded) {
ts.getOutput(output buffer pointer, numberOfFramesNeeded);
} else {
}
int main(int argc, char *argv[]) {
Superpowered::Initialize("ExampleLicenseKey-WillExpire-OnNextUpdate");
Superpowered::Decoder *decoder = new Superpowered::Decoder();
int openReturn = decoder->open("test.m4a");
if (openReturn != Superpowered::Decoder::OpenSuccess) {
printf("\rOpen error %i: %s\n", openReturn, Superpowered::Decoder::statusCodeToString(openReturn));
delete decoder;
return 0;
};
mkdir("./results", 0777);
FILE *destinationFile = Superpowered::createWAV("./results/offline2.wav", decoder->getSamplerate(), 2);
if (!destinationFile) {
printf("\rFile creation error.\n");
delete decoder;
return 0;
};
Superpowered::TimeStretching *timeStretch = new Superpowered::TimeStretching(decoder->getSamplerate());
timeStretch->rate = 1.04f;
short int *intBuffer = (short int *)malloc(decoder->getSamplerate() * 2 * sizeof(short int) + 16384);
float *floatBuffer = (float *)malloc(decoder->getSamplerate() * 2 * sizeof(float));
while (true) {
int framesDecoded = decoder->decodeAudio(intBuffer, decoder->getFramesPerChunk());
if (framesDecoded < 1) break;
Superpowered::ShortIntToFloat(intBuffer, floatBuffer, framesDecoded);
timeStretch->addInput(floatBuffer, framesDecoded);
unsigned int outputFramesAvailable = timeStretch->getOutputLengthFrames();
if ((outputFramesAvailable > 0) && timeStretch->getOutput(floatBuffer, outputFramesAvailable)) {
Superpowered::FloatToShortInt(floatBuffer, intBuffer, outputFramesAvailable);
Superpowered::writeWAV(destinationFile, intBuffer, outputFramesAvailable * 4);
}
};
Superpowered::closeWAV(destinationFile);
delete decoder;
delete timeStretch;
free(intBuffer);
free(floatBuffer);
printf("\rReady.\n");
return 0;
}
Properties
PROPERTYAmount of formant correction, between 0 (none) and 1 (full).
Type | Min | Max | Default |
---|
Number | 0 | 1 | 0 |
PROPERTYAmount of formant correction, between 0 (none) and 1 (full).
Type | Min | Max | Default |
---|
float | 0 | 1 | 0 |
outputList
PROPERTYThe AudiopointerList storing the audio output. To be used with the advancedProcess() method. Read only.
Type | Min | Max | Default |
---|
Superpowered::AudiopointerList | | | |
pitchShiftCents
PROPERTYPitch shift cents, limited from -2400 (two octaves down) to 2400 (two octaves up). Examples: 0 (no pitch shift), -100 (one note down), 300 (3 notes up). When the value if a multiply of 100 and is >= -1200 and <= 1200, changing the pitch shift needs only a few CPU clock cycles. Any change in pitchShiftCents involves significant momentary CPU load otherwise.
Type | Min | Max | Default |
---|
Number | -2400 | 2400 | 0 |
pitchShiftCents
PROPERTYPitch shift cents, limited from -2400 (two octaves down) to 2400 (two octaves up). Examples: 0 (no pitch shift), -100 (one note down), 300 (3 notes up). When the value if a multiply of 100 and is >= -1200 and <= 1200, changing the pitch shift needs only a few CPU clock cycles. Any change in pitchShiftCents involves significant momentary CPU load otherwise.
Type | Min | Max | Default |
---|
int | -2400 | 2400 | 0 |
preciseTurningOn
PROPERTYMaintain precise timing when the time-stretcher turns on. Useful for all use-cases except when the audio is heavily manipulated with some resampler (scratching).
Type | Min | Max | Default |
---|
Boolean | | | true |
preciseTurningOn
PROPERTYMaintain precise timing when the time-stretcher turns on. Useful for all use-cases except when the audio is heavily manipulated with some resampler (scratching).
Type | Min | Max | Default |
---|
bool | | | true |
rate
PROPERTYTime stretching rate (tempo). 1 means no time stretching. Values above 2 or below 0.5 are not recommended on mobile devices with low latency audio due high CPU load and risk of audio dropouts.
Type | Min | Max | Default |
---|
Number | minimumRate in Constructor | 4 | 1 |
rate
PROPERTYTime stretching rate (tempo). 1 means no time stretching. Values above 2 or below 0.5 are not recommended on mobile devices with low latency audio due high CPU load and risk of audio dropouts.
Type | Min | Max | Default |
---|
float | minimumRate in Constructor | 4 | 1 |
samplerate
PROPERTYSample rate in Hz. High quality pitch shifting requires 44100 Hz or more, the sound is "echoing" on lower sample rates.
samplerate
PROPERTYSample rate in Hz. High quality pitch shifting requires 44100 Hz or more, the sound is "echoing" on lower sample rates.
sound
PROPERTYDefault: 1.
Type | Min | Max | Default |
---|
0: save CPU with slightly lower audio quality
1: DJ apps, modern and "complete" music
2: instrumental loops and single instruments | 0 | 2 | 1 |
sound
PROPERTYDefault: 1.
Type | Min | Max | Default |
---|
0: save CPU with slightly lower audio quality
1: DJ apps, modern and "complete" music
2: instrumental loops and single instruments | 0 | 2 | 1 |
Methods
constructor
METHODCreates a TimeStretching instance.
ParametersName | Type | Description |
---|
samplerate | Number | The initial sample rate in Hz. |
minimumRate | Number | The minimum value of rate. For example: if the rate will never go below 0.5, minimumRate = 0.5 will save significant computing power and memory. Min: 0.01, max 0.75. |
ReturnsType | Description |
---|
Superpowered.TimeStretching | The constructed instance. |
constructor
METHODCreates a TimeStretching instance.
ParametersName | Type | Default | Description |
---|
samplerate | unsigned int | | The initial sample rate in Hz. |
minimumRate | float | 0.01 | The minimum value of rate. For example: if the rate will never go below 0.5, minimumRate = 0.5 will save significant computing power and memory. Min: 0.01, max 0.75. |
ReturnsType | Description |
---|
Superpowered::TimeStretching | The constructed instance. |
METHODAdd audio input.
ParametersName | Type | Description |
---|
input | Number (pointer in Linear Memory) | 32-bit interleaved stereo input. |
numberOfFrames | Number | Number of frames in input. |
ReturnsNone
METHODAdd audio input.
ParametersName | Type | Default | Description |
---|
input | float * | | 32-bit interleaved stereo input. |
numberOfFrames | unsigned int | | Number of frames in input. |
ReturnsNone
advancedProcess
METHODProcesses audio. For advanced uses to:
- Save memory bandwidth (uses no memory copy) for maximum performance.
- Support up to 8 audio channels.
- Provide sub-sample precise positioning.
It's never blocking for real-time usage. You can change all properties on any thread, concurrently with process(). Use it in the same thread with the other real-time methods of this class. Do not use this method with addInput() or getOutput().
ParametersName | Type | Default | Description |
---|
input | AudiopointerlistElement * | | The input buffer. It will take ownership on the input. |
ReturnsNone
METHODReturns with how many frames of input should be provided to the time stretcher to produce some output. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersNone
ReturnsType | Description |
---|
Number | The result can be 0 if rate is 1 and pitch shift is 0, because in that case the time stretcher is fully "transparent" and any number of input frames will produce some output. |
METHODReturns with how many frames of input should be provided to the time stretcher to produce some output. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersNone
ReturnsType | Description |
---|
unsigned int | The result can be 0 if rate is 1 and pitch shift is 0, because in that case the time stretcher is fully "transparent" and any number of input frames will produce some output. |
getOutput
METHODGets the audio output into a buffer. It's never blocking for real-time usage. You can change all properties on any thread, concurrently with process(). Use it in the same thread with the other real-time methods of this class.
ParametersName | Type | Description |
---|
output | Number (pointer in Linear Memory) | 32-bit interleaved stereo output. |
numberOfFrames | Number | Number of frames to return with. |
ReturnsType | Description |
---|
Boolean | True if it has enough output frames stored and output is successfully written, false otherwise. |
getOutput
METHODGets the audio output into a buffer. It's never blocking for real-time usage. You can change all properties on any thread, concurrently with process(). Use it in the same thread with the other real-time methods of this class.
ParametersName | Type | Default | Description |
---|
output | float * | | 32-bit interleaved stereo output. |
numberOfFrames | unsigned int | | Number of frames to return with. |
ReturnsType | Description |
---|
bool | True if it has enough output frames stored and output is successfully written, false otherwise. |
getOutputLengthFrames
METHODReturns with how many frames of output is available. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersNone
ReturnsType | Description |
---|
Number | Frames of output available. |
getOutputLengthFrames
METHODReturns with how many frames of output is available. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersNone
ReturnsType | Description |
---|
unsigned int | Frames of output available. |
METHODRemoves audio from the end of the input buffer. Can be useful for some looping use-cases for example. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersName | Type | Description |
---|
numberOfFrames | Number | The number of frames to remove. |
ReturnsNone
METHODRemoves audio from the end of the input buffer. Can be useful for some looping use-cases for example. It's never blocking for real-time usage. Use it in the same thread with the other real-time methods of this class.
ParametersName | Type | Default | Description |
---|
numberOfFrames | unsigned int | | The number of frames to remove. |
ReturnsNone
reset
METHODReset all internals, sets the instance as good as new. Don't call this concurrently with process() and in a real-time thread.
ParametersNone
ReturnsNone
reset
METHODReset all internals, sets the instance as good as new. Don't call this concurrently with process() and in a real-time thread.
ParametersNone
ReturnsNone
setStereoPairs
METHODThis class can handle one stereo audio channel pair by default (left + right). Maybe you need more if you load some music with 4 channels, then less if you load a regular stereo track. Don't call this concurrently with process() and in a real-time thread.
ParametersName | Type | Description |
---|
numStereoPairs | Number | The number of stereo audio channel pairs. Valid values: one (stereo) to four (8 channels). |
dontFree | Boolean | If true, this function will not free up any memory if numStereoPairs was bigger before, so no reallocation happens if numStereoPairs needs to be increased later. |
ReturnsNone
setStereoPairs
METHODThis class can handle one stereo audio channel pair by default (left + right). Maybe you need more if you load some music with 4 channels, then less if you load a regular stereo track. Don't call this concurrently with process() and in a real-time thread.
ParametersName | Type | Default | Description |
---|
numStereoPairs | unsigned int | | The number of stereo audio channel pairs. Valid values: one (stereo) to four (8 channels). |
dontFree | bool | | If true, this function will not free up any memory if numStereoPairs was bigger before, so no reallocation happens if numStereoPairs needs to be increased later. |
ReturnsNone