signalsmith-stretch: a C++ pitch and time stretcher you can drop into a header
C++ polyphonic pitch/time library (GitHub mirror)
At a glance
- What is it?
- Signalsmith Stretch is a single header C++ library that stretches and transposes polyphonic audio in blocks, and its real subject is the awkward part of the problem, which is latency: how much you have to feed in, how much you get back, and how to make a fixed length file land exactly where it should.
- Who is it for?
- signalsmith-stretch is the right tool for an audio developer who needs pitch shifting or time stretching inside a real time pipeline, where the block size, the latency and the exact output length all matter, and it is the right tool for polyphonic material since that is what it was written for.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A single header library for polyphonic pitch and time
Signalsmith Stretch is a C++11 library for pitch and time stretching, and the implementation follows the final approach from the ADC22 presentation Four Ways To Write A Pitch-Shifter. The repository is a GitHub mirror of the library, and the project page carries audio examples and an interactive web demo.
The usage model is as small as it gets. You include one header where you need it.
#include "signalsmith-stretch.h"
signalsmith::stretch::SignalsmithStretch<float> stretch;The class is templated on the sample type, so the same code can work in float or double internally, and the repository is laid out accordingly with a signalsmith-stretch.h at the top level, an include directory, a cmd directory, a CMakeLists.txt and a web directory holding the browser demo. The primary language recorded for the repository is JavaScript, which is a consequence of that demo living in the tree.
The pitch side handles a wide range of pitch shifts, including multiple octaves. The time stretching side is more modest, and the readme says time stretching sounds best for changes between 0.75x and 1.5x. That asymmetry is worth internalising before you start, because it defines the envelope of the library: extreme transposition is fine, extreme stretching is not, and the two are used together in real work more often than people expect.
The licence is MIT, with the licence text in LICENSE.txt at the root of the repository.
Configuration: presets, or your own block and interval sizes
There are two ways to configure the stretcher, and the choice is a real trade-off.
The easy path is a preset method.
stretch.presetDefault(channels, sampleRate);
stretch.presetCheaper(channels, sampleRate);The preset takes the channel count and the sample rate, and there is a default preset and a cheaper one. The names tell you what the trade is: the cheaper preset presumably spends less per block, and the documentation does not quantify it.
If you want to try different block sizes yourself, you can configure it manually.
stretch.configure(channels, blockSamples, intervalSamples);
// query the current configuration
int block = stretch.blockSamples();
int interval = stretch.intervalSamples();So the two numbers that define the processing are the block size and the interval, and the library will tell you what it is using. Block size is the unit of spectral work, and interval is the hop between blocks.
Those two numbers also determine the latency you will live with, so the manual path is not a curiosity. A larger block means finer frequency resolution and more latency; a smaller block means the opposite. The readme's guidance later on, that a strict situation might want the split computation flag, is really guidance about this same trade.
The library also exposes the current configuration as queries, which is what you need if you want to change one setting and keep the rest, or if your host application wants to display the active configuration.
Processing: buffers in, buffers out, and the integer ratio problem
Processing a block is a single call.
float **inputBuffers, **outputBuffers;
int inputSamples, outputSamples;
stretch.process(inputBuffers, inputSamples, outputBuffers, outputSamples);The buffer handling is more flexible than it first looks. The input and output buffers cannot be the same, but they can be any type where buffer[channel][index] gives you a sample, which might be a float pointer to pointer, a double pointer to pointer, or some custom object providing access to an interleaved buffer, regardless of what sample type the stretcher is using internally. That is a real constraint on your host code, because in place processing is not allowed and because the buffer abstraction has to match.
The reset method clears the internal buffers, which you want between unrelated items of audio.
Time stretching works by handing differently sized input and output buffers to the process call, and there is no maximum block size for either. The catch is stated plainly: since the buffer lengths are integers, it is up to you to make sure that the block lengths average out to the ratio you want over time. The library is not going to schedule your buffers for you, and a caller that always passes the same input length gets a stretch rate that is a rounding artefact of that length rather than the rate you asked for.
That is the whole mechanism, and it is why the library is a building block rather than a command. You are the scheduler.
Pitch control: transpose, tonality limit and custom frequency maps
Pitch shifting has three entry points, and they exist for different situations.
The straightforward one sets a ratio or an interval.
stretch.setTransposeFactor(2); // up one octave
stretch.setTransposeSemitones(12); // also one octaveThe factor is a linear multiplier and the semitone count is the same thing in musical units, so the two are redundant except for the convenience of thinking in one or the other.
The more interesting one is the tonality limit, which uses a non-linear frequency map to preserve a bit more of the timbre.
stretch.setTransposeSemitones(4, 8000/sampleRate);The second argument is a frequency in the same normalised space the custom map uses, and the effect is that the shift is applied progressively across the spectrum rather than uniformly, which is what keeps a shifted voice from sounding like the same voice played back at a different speed.
The most general one takes a custom frequency map, mapping input frequencies to output frequencies, both normalised against the sample rate.
stretch.setFreqMap([](float inputFreq) {
return inputFreq*2; // up one octave
});Because the map is a plain function, you can do things a fixed factor cannot, such as leaving a specific range untouched or bending the mapping in a way that suits a particular instrument. The cost is that the mapping is a per frequency sample operation inside the processing, so a map with fine structure is more expensive than a constant one.
Latency, split computation, and getting the output length right
This is the part of the library that takes reading, and it is the part that decides whether your timing is right.
Latency is reported in two halves, and the readme explains why: the input latency and the output latency are separate numbers.
int inputLatency = stretch.inputLatency();
int outputLatency = stretch.outputLatency();The convention is that you supply input samples slightly ahead of the processing time, which is where changes to pitch shift or stretch rate are centred, and you receive output samples slightly behind that processing time.
The split computation flag is the second idea. All of the preset and configure methods take an optional splitComputation flag, and when it is enabled it introduces one extra interval of output latency, using that to spread the computation out more evenly. Without it, the library will occasionally do a bunch of computation all at once to compute the next spectral block of audio, which is often fine when audio is processed across multiple threads with a decent amount of buffering, as in mixing in a DAW, but in a stricter situation the flag helps.
Automation follows from the same model. To follow pitch and time automation accurately you give automation values from the current processing time, which is outputLatency samples ahead of the output, and feed input from inputLatency samples ahead of the current processing time.
Seeking and starting are where the two halves have to be reconciled. The seek method lets you move around the input audio by providing a bunch of input samples, and you should ideally provide at least one block length plus one interval of input data. At the very start of playback, or after a reset, the current processing time is inputLatency samples before the first input samples you give it, so you may want to call seek to provide the first inputSamples equal to inputLatency samples of input, so that the processing time matches the start of the input and the pre-roll output is only outputLatency samples long.
For a fixed length sound, the ending mirrors the beginning. You pass an additional inputLatency samples of silence to process so that the processing time reaches the end, and then you read the final part of the output with the flush method, which the readme recommends reading at least outputLatency samples of. Using seek and flush this way you can perform an exact time stretch on a fixed length sound, and the result has outputLatency of pre-roll.
Formant compensation, and where it stops being sharp
Pitch shifting moves the whole spectrum, including the formants, which is why a shifted voice can sound like a different instrument or a different age. The library offers a correction.
stretch.setFormantFactor(1.2); // up ~3 semitones
stretch.setTransposeSemitones(3);Both methods take an optional compensatePitch flag, which adjusts for the pitch shift, or the non-linear map, when correcting or shifting formants.
The documentation is refreshingly direct about the limits. The formant correction is not as sharp as monophonic algorithms such as PSOLA, and it needs you to give it a rough estimate of the fundamental frequency relative to Nyquist.
// if 200Hz is the middle-register of the instrument
stretch.setFormantBase(200/sampleRate);So there is a manual parameter in there, and it is an instrument specific one. The middle register of the instrument is what the correction is anchored to, and for a monophonic voice a library that specialises in monophonic voices will do a better job on the same material.
That is the boundary of the library, and it is worth being clear about it. The time and pitch machinery is general and polyphonic, which is the hard part and the part it does well. The formant correction is a usable approximation with a parameter you set by ear, which is the part you will tune per source.
Compiling, the FFT dependency, and the state of the project
The compiling section is where the practical constraints are, and there are several.
The library has mostly been tested with AppleClang on macOS and MSVC on Windows, and the aim is to support other compilers, with an invitation to get in touch if you have problems. The fast math flag is fine unless you are on Apple Clang 16.0.0, where it deliberately breaks because that version generates incorrect SIMD code. And the note about performance is blunt: the algorithm does a lot of number crunching, so Debug builds are much slower, up to ten times, and you might want to enable optimisation even in Debug builds where Stretch is used.
The dependency is the Signalsmith Linear library, used for FFTs and other speedups, and it is included as a submodule, which is why there is a .gitmodules file. There are flags to use faster FFT implementations, namely SIGNALSMITH_USE_ACCELERATE, SIGNALSMITH_USE_IPP, SIGNALSMITH_USE_PFFFT and SIGNALSMITH_USE_PFFFT_DOUBLE, with corresponding CMake options, and the CMakeLists.txt at the root is how you select between them. On a machine with an Apple or Intel FFT library available, that choice is where the throughput comes from.
The licence is MIT, and the readme adds that you can get in touch if you need something, which for a commercial project usually means a licence question rather than a feature one.
On maintenance, the repository is not archived and the last push was on 2026-09-25, but it publishes no GitHub releases, so there is no tagged version to pin to, which for a library you vendor into an audio application is the one packaging fact to sort out first.
Editorial conclusion
signalsmith-stretch is the right tool for an audio developer who needs pitch shifting or time stretching inside a real time pipeline, where the block size, the latency and the exact output length all matter, and it is the right tool for polyphonic material since that is what it was written for. It is the wrong choice for a one off file conversion, where a command line tool would do, and it is the wrong choice when formant accuracy is the point, because the documentation is direct that its formant correction is not as sharp as monophonic algorithms such as PSOLA. Before adopting, read the latency and seeking sections in full and measure inputLatency() and outputLatency() in your own buffer size, because those two numbers decide how your timing has to be written.
Frequently asked questions
What is signalsmith-stretch?
It is a C++11 library for pitch and time stretching, using the final approach from the ADC22 presentation Four Ways To Write A Pitch-Shifter. It is a header only library, licensed under MIT, and it handles polyphonic material, with pitch shifts over multiple octaves and time stretching that sounds best between 0.75x and 1.5x.
How do you configure the Signalsmith Stretch library?
Either call a preset method such as presetDefault or presetCheaper with the channel count and the sample rate, or call configure with the channels, the block size and the interval, and query the active values with blockSamples and intervalSamples. The preset and configure methods also take an optional splitComputation flag that adds one interval of output latency and spreads the computation more evenly.
How do I time the input and output buffers when time stretching?
The library reports latency in two halves, inputLatency and outputLatency. You supply input slightly ahead of the processing time and receive output slightly behind it, and because the buffer lengths are integers it is up to you to make sure the block lengths average out to the ratio you want over time.
How do I get an exact time stretch of a fixed length file?
Use seek and flush. Provide the first inputLatency samples through seek so the processing time matches the start of the input, pass an additional inputLatency samples of silence to process so the processing time reaches the end, and then read the final output with flush, taking at least outputLatency samples. The result still has outputLatency of pre-roll.
How accurate is the formant correction in signalsmith-stretch?
The documentation says it is not as sharp as monophonic algorithms such as PSOLA, and it needs a rough estimate of the fundamental frequency relative to Nyquist, given through setFormantBase. The correction and the pitch shift both take an optional compensatePitch flag.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/signalsmith-audio-signalsmith-stretch)Was this useful?