Editing Basics
Editing here means changing the sound of one part: its pitch, its length, or the character of a voice.
It is different from analysis, which only measures a track and leaves it untouched. It is also different from mixing, which balances several parts together.
This page defines the words you need before reading the Editing DSP guide. A short example near the end shows the two main edits as calls.
Where editing sits
You usually edit individual parts (fix timing, tune a note, change a voice), then mix them together, then master the finished stereo file for delivery. Editing is the per-part repair-and-shape step. Conservative edits keep the part natural; aggressive edits are a creative effect.
Pitch and time are independent
Speed up a tape and the music gets both faster and higher — the two are coupled. The whole point of modern editing DSP is to separate them, so you can change one without dragging the other along:
- Time stretch changes the duration without changing the pitch. Here
rateis a playback-speed multiplier: arateabove1.0plays faster, so the clip gets shorter; below1.0plays slower, so it gets longer. - Pitch shift changes the pitch without changing the duration.
Doing this cleanly is hard: the algorithm has to invent or remove time while keeping each note's frequency, so very large moves always leave some artifacts (smearing, a "phasey" or robotic tone). Small moves stay transparent.
Semitones and cents
Pitch in music is measured in semitones, the smallest step on a piano keyboard. Twelve semitones make one octave, and an octave doubles the frequency: A4 = 440 Hz, A5 = 880 Hz.
So semitones = 12 shifts up one octave, -12 shifts down one octave, and 7 is a perfect fifth.
A cent is 1/100 of a semitone. It is used for fine tuning; most listeners start to notice pitch error past roughly ±5-10 cents.
MIDI note numbers
When a function asks for a MIDI note number (such as pitchCorrectToMidi's currentMidi and targetMidi), it wants an integer (or fractional) index where each whole number is one semitone. Two anchors are worth memorizing:
- A4 = 69 = 440 Hz — the tuning reference.
- C4 = 60 — "middle C".
Every octave adds 12, so C5 = 72, C3 = 48. A few common targets:
| Note | MIDI | Frequency |
|---|---|---|
| C3 | 48 | 130.81 Hz |
| C4 (middle C) | 60 | 261.63 Hz |
| E4 | 64 | 329.63 Hz |
| G4 | 67 | 392.00 Hz |
| A4 (tuning reference) | 69 | 440.00 Hz |
| C5 | 72 | 523.25 Hz |
To convert by hand: midi = 69 + 12 · log2(freq / 440) and freq = 440 · 2^((midi − 69) / 12). A fractional MIDI value like 68.7 is simply a pitch a little below A4 — useful because a singer is rarely exactly on a note, and you can pass the measured pitch as the current value and a whole number as the target.
Formant: pitch's independent partner
A formant is a resonance peak in the spectrum's envelope, set by the shape of the vocal tract. It moves when the vowel changes, not when the pitch does. In instruments, similar resonances come from the body of the instrument.
Formants shape vowels and the perceived size or character of a voice. They are different from pitch: a singer can sing different notes while the vowel character still comes from the same vocal-tract resonances.
This is why voice tools keep pitch and formant on separate controls.
| Control | What changes | What you hear |
|---|---|---|
| Pitch | The musical note | The same voice singing higher or lower |
| Formant | The vocal character | A smaller/brighter or larger/darker voice at the same note |
Moving both together too aggressively creates the familiar artificial "chipmunk" effect. By default in voiceChange (formantMode: 'relative') a pitch shift moves the formants too, so formantFactor: 1 with +12 semitones sounds like a chipmunk. Set formantMode: 'absolute' with formantFactor: 1 to keep the voice's size while changing the note: the factor is then measured against the input, so 1 leaves the formants where they were whatever the pitch shift.
Samples vs seconds
Editing functions that act on a region (like note stretch) take sample offsets, because sample positions are exact and never drift. Most also accept the same position in seconds under a *Sec name (onsetSec, offsetSec, startSec, endSec, paddingSec), converted at the call's sample rate; give one spelling or the other, never both. The conversion is samples = round(seconds × sampleRate) — at 48 kHz, 0.25 s is sample 12000. See Audio Basics for what a sample and sample rate are.
Try it in libsonare
Time stretch and pitch shift are one call each. A rate of 0.5 plays at half speed, so the result is twice as long at the same pitch; semitones: 12 raises the pitch an octave and keeps the length.
import { init, timeStretch, pitchShift } from '@libraz/libsonare';
await init();
const slower = timeStretch(samples, sampleRate, 0.5); // twice as long, same pitch
const higher = pitchShift(samples, sampleRate, 12); // one octave up, same lengthimport libsonare as sonare
slower = sonare.time_stretch(samples, sample_rate, 0.5) # twice as long, same pitch
higher = sonare.pitch_shift(samples, sample_rate, 12) # one octave up, same lengthsonare time-stretch take.wav --rate 0.5 -o slower.wav
sonare pitch-shift take.wav --semitones 12 -o higher.wavHow libsonare implements editing DSP
The implementation uses the same C++ DSP core as analysis and mastering, but these functions rewrite the signal instead of measuring it.
| Function family | Implementation idea | Beginner takeaway |
|---|---|---|
| Time stretch / pitch shift | Phase vocoder (rebuilds the signal from short overlapping spectral frames) plus resampling | Duration and pitch can be changed separately, but large moves create artifacts |
voiceChange | Shift the spectral envelope separately from harmonic pitch | Voice character can move without changing the target note |
pitchCorrectToMidi | Move from a caller-supplied current MIDI note to a target MIDI note | Estimate the current pitch first with pitchYin, pitchPyin, or your own detector |
autoTune | Snap a monophonic recording to the notes of a detected (key: 'detect') or named key, with a strength control | Returns the corrected samples and the key it used |
noteStretch | Process an exact sample-offset region | Pass onsetSample / offsetSample, or onsetSec / offsetSec |
Related: Editing DSP, Audio Basics, MIR Overview, JavaScript API