how to make a musically effective quantizer

Hello, i have a semi-working prototype of a quantizer that will map to the chords you play in it. the scale will change when you play a chord, so it only needs to get updated then. however, the quantizer has multiple notes repeating, like say you play an Ab and a Bb in the key of C major, it wouldn’t be ideal to map both notes to A. how do i get as few repeating notes as possible when quantizing? currently i’m using a naive approach of whenever a note is played, linearly search through the scale to find the closest note. i know this is not the most efficient quantizing method but i didn’t understand the approach used in the vcv quantizer. here is the source for anyone interested: Plurm/chordscalequantizer.cpp at master · terrytheplatypus/Plurm · GitHub

2 Likes

Quantizers are not trivial. Although the VCV Quantizer algorithm is a bit tedious to understand, it is a good approach and I suggest taking the time to understand how it works.

I would love to use your chord quantizer when you get it finished. I have created a number of similar approaches using multiple modules to quantize to a chord “scale” with good results. It would be nice to have a single module that could handle this, I think… I usually feed my chords into some type of sequential switch and then do all sorts of arpeggiations and ostinatos, etc. by sequencing the switch. Quantizing to a chord would behave quite differently but would still be very interesting and useful to me.

1 Like

As I remember things, a key part of the VCV Quantizer is that it does a two direction search, from below and from above in pitch.

I still think this would be a very useful module. Yesterday and today i worked on a patch to duplicate a chord scale quantizer functionality. It took me 63 modules to accomplish that! Most of them were comparator modules. The end result though was very pleasing when fed chords from my Meander module.

By the way, my patch jumps through a lot of hoops to build a dynamic poly scale that can be sent to the Grande Quant module which understands poly scales.

good to hear you’re interested, that motivates me more to work on it. for my module, the idea is to map a chord to a diatonic scale, so for example if the chord input is c e g it could quantize to either the c, f, or g major scales, so it will randomly pick one of those. you can see in the source that i have a scoring idea to select the scales that best fit the chord.

1 Like

Ah, I was afraid that I might be misunderstanding what you are trying to do. I will have to rethink what I have said.

So, how would your poly quantizer differ from other poly quantizers for Rack? I.E., Grande Quant poly quantizer takes scale and root inputs to configure the quantizer. I usually just supply the poly scale to Grande Quant and do not send a root input. Grande also has their “Scale” module which generates a poly scale based on the preset scale type you select. But you cannot select the root or “key” for the scale, but you can select the root in Quant.

Thanks for setting me straight on what you want to accomplish.

my idea differs from the existing quantizers because the idea here is to guess the right diatonic scale based on a chord that is passed in, but a given chord can fit in multiple diatonic scales. to my understanding, grande quant, will only quantize to the notes you pass in, while my thing would “fill in the blanks”. so if i pass in D1 F1 A1 C1, that will then quantize to either C major or F major. the octaves are stripped from the chord notes when determining the scale, so C1 E1 G1 is treated the same as E1 G1 C2.

1 Like

Oh, okay, these are key changes or modulations. I really do not know much about that, but I am interested. Will you stochastically change the key for the melody upon each chord change? I.E., you will quantize the melody to the new scale? So, you are not limiting your changes to whole step and half step modulations? Or are you modulating between relative major and minor keys and their scales only?

Obviously, I am out of my depth in this topic, but I am very interested. I am always looking for modules that can musically mangle my Meander module chords and melody in interesting ways.

yes, for each new chord, if there are two or more scales that fit equally well for a given chord, it would pick randomly between them. i guess it would be better to maintain the previous scale, if possible. there’s no limit to the modulation as it can go from any diatonic scale to any other diatonic scale.

1 Like

Thanks for explaining. Interesting. I can imagine having a parameter that controls the maximum number of whole or half step modulations. Actually, I can imagine a 1st order Markov chain that would define the probability of transitions from one key to any other. Of course what you are planning sounds like it will be interesting. It might have a very jazzy feel to it where the key is changing by large, unexpected number of steps.

Looking forward to seeing it. :grinning:

I’m wondering if you should have a “capture” trigger input to diferentiate between setting the scale to use and then using the quantizer to quantize the incoming notes to the selected scale?

not sure what you mean but for my use case i would just want any chord notes changed to trigger changing the scale.

I have spent the last few days trying to build a quantizer. Mostly this is an exercise for learning, and I am trying to do some additional stuff to help me learn (like evenly tempered non-12 note scales all using common code). And at some level I want to believe I can do it more efficiently than the code I am seeing in some of the other implementations.

That said, after I got my module “working”, I took a bunch of other quantizers side by side and fed the same voltages into them to see if the outputs aligned. They do not. There are I think two different things going on here.

In some cases, two quantizers will split the difference at the same value, but one will round up and the other down. I can live with that infinitesimally small value on the boundary.

The second case is the one I don’t understand. When I want to find the closest value, I expect to split the difference in the voltage range between two notes. For instance, in a C Major scale, I want to quantize values between E and F to E or F. If the voltages for E and F are 0.3333 and 0.4166 respectively, then I expect the split point at 0.3750, and that is true for all the quantizers I tried (aside the round up/down thing). However, if I want to split the difference between C and D I get unexpected results. Using 0.0000V and 0.1666V respectively, I expect the split point at 0.08333. But many of the quantizers are splitting 0.125 which is actually halfway between C# and D not halfway between C and D. I found only one quantizer that actually splits that difference at 0.08333.

So why is that? I have a guess, but I don’t want to be presumptuous and I am super anxious to be educated on this. I don’t want to drill into the math here (unless you have to (and yes, I have looked at the code for all of them)), or any specific implementation using known voltages and closest distance searches, or quarter step intervals and multiplication/division, etc. I just want to understand the reason the split point is not halfway through the voltage range.

Any help is greatly appreciated.

I believe a lot of them work differently than each other. some quantize first to 12 tone even. Some put the dividing point on the input, some on the output. Some have hysteresis, some don’t. There’s a lot of variability.

btw, unless you are doing something idiotic, a quantizer should not take much CPU. Do you have an example of one that uses a lot?

everyone does different things, there isn’t any science about this. Is it musically effective? I guess if ppl like it it is.

Also, very , very few VCV users test anything. If it says it’s quantizing to c-min and all the notes that come out are in c-min, then it “works”. and they are all different for who knows what reason - probably because whoever wrote them did the first thing that occurred to them that worked. Like all programming.

Thanks. I don’t have one that uses too much, but my day job is professional global scale systems (millions of transactions per second), and graphics device drivers before that. If assembly were practical, then I would use it to save a clock cycle on the inner loop. Yes, compared to the IO, most might consider this negligible, but for my level of OCD, it adds up. Less glitching, static, etc. And that kind of optimization is fun for me.

And at the same time, I recognize many people in this forum are here for their own brand of fun - not to be criticized. So, as a newbie to this forum, I want to avoid calling out any one implementation by name (except for VCV itself which seems more like fair game).

At least one implementation does a linear search through an array of values to find the closest note. In that loop it does about 20 operations (assignments, comparisons, addition/subtraction, mutliplication/division, indexing, etc (at the assembly level)). Most of the scales have 8 notes, but even assuming 7 is enough, it is an average of 70 operations per channel. On the other hand, VCV does it in constant time - which I consider goodness/better. That said it looks to be about 25 total operations, and I think I can get that constant smaller (to about 15 operations).

On my PC (nothing special), using the VCV performance meters, this comes out to 0.1% (with an occasional dip to 0.0%) for my implementation. Some of the other implementations oscillate between 0.1% and 0.2%, one is consistently 0.2% and still another is jumping between 0.3% and 0.4%. VCV is one of the lowest (almost always 0.1% on my PC), but it is also missing an octave shift function (so slightly less function). VCV is also one of those where the math doesn’t quite meet expectations on splitting the distance between notes.

And for me, correctness is more important than efficiency/optimization - and why I am asking. There is no point in being fast if it is producing incorrect results. And yes, it might be “good enough” and each might be musically useful, but it might produce different results when swapping quantizers (which I suppose some argue is a good thing (to have variety)).

I am trying to think of where the correctness matters. One possible place (and I am making this up (will need to test / listen) would be a linear/triangle slew oscillating between two notes (say C and D). Using some implementations (splitting at 0.0833) the time spent playing each note will be approximately half. If the voltage went up and down logarithmically, it would be a different amount of time on up than down - but still 50/50 overall. If instead the split occurs at 0.125 (as it is in VCV when selecting all the white keys) then 3/4ths of the time is spent playing C for the linear signal. A logarithmic input would skew the time up or down from 3/4ths depending on the log or inverse log. Is that wrong? I don’t know, but I feel like I would rather control that kind of thing with a clock.

QuantSplitTest.vcv (1.6 KB)

Here is a sample patch with a triangle input signal quantized to either C or D. click the select on the switch to listen to VCV (which splits 3/4ths of the way) and JW which splits half of the way. Same input signal - different timing.

Try and have a look at the context/right-click menu of ML Quantum. There are choices of different methods of quantizing and they are all valid and can produce interesting musical results when used together.

1 Like

I agree with @Squinky that there is no standard for quantizers and there are many ways to implement a quantizer. I do not know what a most correct implementation would look like. When implementing my Meander quantizer (for quantizing external inputs to the current Meander scale), I looked at the code for as many quantizers as I could. They are quite different. In the end I chose to follow the VCV QNT strategy that involves a two direction search of the solution space for the “best” choice. This involves search from lower to higher note pitch as well as from higher to lower.

But, in the end, I did not spend a lot of time as my philosophy is that Eurorack has few standards other than for some voltages and physical dimensions and VCV has only a few standards, there will always be multiple ways to accomplish the same goal and multiple “correct” endpoints.

I didn’t even consider speed performance. But, at least in Meander, most incoming CV or V/Oct signals are not handled at sample rate. With other quantizers, there are probably some that work at DSP sample rate and some that do not. You may want to consider this in your investigations.

Edit: I was wrong. Meander does handle quantization at sample rate. Sorry.

1 Like

As I refresh my memory, let me add just a bit bit here.

Many quantizers offer a number of search options such as Up, Down, Closest Up and Closest Down. In contrast, VCV QNT has no search options but tries to find the closest up and down search note. I would have to look back at QNT source code to see how a tie is resolved or if such a tie can occur.