Quantizing & Humanizing
Quantization
Quantization is the process of moving all note starting positions to a specified and periodic grid. This "grid" does not have the be equi-spaced but it has to be periodic per quarter note.
MusicManipulations.quantize
— Functionquantize(notes::Notes, grid, duration = true)
quantize(note::AbstractNote, grid, tpq::Integer)
Return a quantized copy of the given notes on the given grid
, which can be any sorted iterable that starts on 0
and ends on 1
.
Each note is quantized (relocated) to its closest point of the grid
, by first identifying that point using classify
. It is assumed that the grid is the same for all quarter notes of the track.
If duration
is true
, the function also quantizes the duration of the notes on the same grid, while ensuring a duration spanning at least one grid point.
This function respects the notes' absolute position and quantizes in absolute position, not relative.
Here are some examples
using MusicManipulations
midi = readMIDIFile(testmidi())
notes = getnotes(midi, 4)
sixteens = 0:1//4:1
notes16 = quantize(notes, sixteens)
swung_8s = [0, 2//3, 1]
swung_notes = quantize(notes, swung_8s)
533 Notes with tpq=960
Note F4 | vel = 69 | pos = 7360, dur = 320
Note A♯4 | vel = 85 | pos = 7680, dur = 640
Note D5 | vel = 91 | pos = 8320, dur = 320
Note D4 | vel = 88 | pos = 8320, dur = 320
Note G♯3 | vel = 88 | pos = 8320, dur = 320
Note A♯4 | vel = 76 | pos = 8640, dur = 640
Note G4 | vel = 66 | pos = 9280, dur = 320
⋮
Note D5 | vel = 91 | pos = 189760, dur = 320
Note D5 | vel = 106 | pos = 190720, dur = 320
Note A4 | vel = 99 | pos = 190720, dur = 320
Note D♯4 | vel = 101 | pos = 190720, dur = 960
Note A3 | vel = 106 | pos = 190720, dur = 960
Note D4 | vel = 106 | pos = 190720, dur = 960
Note G4 | vel = 109 | pos = 190720, dur = 320
The first quantization quantized the notes on the 16-th subdivision. The second one did something a bit more specialized, as it separated the notes to "quarter notes" and swung 8th notes, the typical way a Jazz standard (like Serenade To A Cuckoo) is played.
Classification
quantize
works in two steps. In the first step, each note is "classified", according to which is the closest grid point to this note (modulo the quarter note).
MusicManipulations.classify
— Functionclassify(notes::Notes, grid)
classify(note::AbstractNote, grid, tpq::Integer)
Classify given notes according to the given grid.
Returns an integer (or vector of integers) that corresponds to the index of the closest grid point to the note position modulo the quarter note. 1
means start of the grid and length(grid)
means end of the grid (i.e. next quarter note).
After the notes have been classified, this classification vector is used to simply relocate all notes to the closest grid point (modulo the quarter note).
Humanizing
MusicManipulations.humanize!
— Functionhumanize!(notes, property, σ, noise = :ARFIMA; kwargs...)
Humanize given notes
by adding noise of standard deviation σ
to their property
, typically either :position
or :velocity
. Research[Datseris2019] suggests that σ
should be around 40 (for :position
) but that depends on the BPM, and around 10 for :velocity
.
The noise
argument decides the type of noise:
:ARFIMA
uses ARFIMA.jl and attempts to generate a power-law correlated (pink) noise. Keywordsd = 0.25, φ = SVector(-0.5, -1.5)
are propagated to functionarfima
.:white
plain ol' white noise.
Use humanize
for a non-modifying version.
- Datseris2019Datseris, G., et al. Microtiming Deviations and Swing Feel in Jazz. Sci Rep 9, 19824 (2019)