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.quantizeFunction
quantize(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.classifyFunction
classify(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!Function
humanize!(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. Keywords d = 0.25, φ = SVector(-0.5, -1.5) are propagated to function arfima.
  • :white plain ol' white noise.

Use humanize for a non-modifying version.