Quantizer
As mentioned in the discussions I've been playing around with adding a quantizer node. Snap a frequency to the nearest note in a scale.

I've made quantizer node branch, but thought that I should check here as per the contribution guidelines before raising the PR. https://github.com/AlasdairMott/noisecraft/tree/feature/quantizer
Hi Alasdair. Can you explain what the Quantize node does and what kind of inputs it accepts, how the implementation works a little bit, and what the node allows you to do? A video demo might help.
The quantizer node snaps an incoming value or audio signal to the nearest value in a chosen scale. https://user-images.githubusercontent.com/25563790/166134230-330e6cd8-177b-42fc-902c-159b59798e90.mp4
Background
It works by first converting the incoming frequency to a "1 volt per octave control voltage" (1v/o) value. This scale is used in eurorack modular systems to convert control voltage to pitch. It makes it easy to extract the note and octave from a pitch value. To convert from frequency to 1v/o and back again:
frequencyToCV = (freq) => Math.log2(freq / 55.0);
CVToFrequency = (cv) => Math.pow(2, cv) * 55;
Scales are stored as arrays with numbers from 0 to 1, which represent a single octave in that scale.
How it works
The Quantize AudioNode works as follows:
- Convert the incoming frequency value to a cv value:
const cv = this.frequencyToCV(input);
- The octave and note (the decimal part of the cv value) are extracted:
const octave = Math.floor(cv);
const decimal = cv - octave;
- Map the note (the decimal value) to the nearest note in the selected scale.
const closestNote = this.scale.reduce((prev, curr) => {
return (Math.abs(curr - decimal) < Math.abs(prev - decimal)) ? curr : prev;
});
- Convert the cv value back to a frequency value. Offset according to the current root note.
return this.CVToFrequency(octave + closestNote + this.scaleRoot / 12.0);
What is it useful for?
- Mapping notes to a given scale
- Converting signals to something more musical. Works well with S&H noise.
- Transpose a note a few semitones
I can see the use case, that's pretty neat. I wish this were done without the CV conversion though. Seems to me it ought to be possible to go from frequencies to the closest MIDI note number. The logic for that could be implemented in the Note class: https://github.com/maximecb/noisecraft/blob/main/public/music.js#L93
I can use the scale generation from music.js
this.midiScale = music.genScale(state.scaleRoot, state.scaleName, 10);
Then map to the nearest midi note from there
const note = (( 12 * Math.log(input / 220.0) / Math.log(2.0) ) + 57.01);
//find the nearest note number from the midi scale
const nearest = this.midiScale.reduce((prev, curr) => {
return Math.abs(curr.noteNo - note) < Math.abs(prev.noteNo - note) ? curr : prev;
});
return nearest.getFreq();
This is a fair bit simpler than what I had before and there's no need to define the scales twice. However the midi notes are limited to 10 octaves it seems?
There are a few advantages to v/o cv values I can think of -
- since they're logarithmic they can be added/subtracted and the results make sense musically
- unlimited number of octaves
- non-western scales
Since this isn't already a part of noise craft I understand it's a fairly different way of working.