Mutable Instruments Grids Drum Fill visualisation

This is a modified copy of devdsp's writeup/demo (original link). I just wanted to play with the code and figured at the same time I could put this on Github (source).

Visualizer


Coordinates: (0:0,0:0)
0
0
0
0
0
120
Map data
Source Binary Decimal
X 01010101 85
X balance
X page
Y
Y balance
Y page
Map visualizer


Original article

While researching the modules I have on order for my first Eurorack system I was looking for examples of people who had played with the Grids firmware, specifically I was looking for anyone who'd changed the drum loop tables. There were a few people asking about it on the muffwiggler forums but no examples of people having done it. The other thing that popped up a few times were people asking about some way of visualising the stock firmware from pichenettes and again, no examples of people having done it. Since, at the time of writing this, I don't have my rails or powersupply (or even my Grids), and I have some experience reading microcontroller firmware (though any C is likely to be enough for this project) and some experience writing visualisations in Javascript - I figured I'd give writing a visualisation a try.

This tool aims to be a true representation of how the X/Y and 3 Fill CV on a Muteable Instruments Grids effect the Trig and Acc outputs over the 32 steps. It uses D3js, HTML5's range input control and is derived from the Grids firmware source code. I believe it to be a true translation of everything except the chaos control and euclidean mode, but I don't yet have my Grids to actually test it against. Feel free to point out bugs. :)

The X and Y controls interpolate the threshold levels between the 5x5 matrix of 32 by 3 track drum loops using the same 4 way mixing and look up tables from the original firmware. pichenettes explains the theory of how this works in the thread Understanding code Grids: U8Mix and bitshift in pattern_generator.cc¹ over on the MI forums. The interpolated thresholds are displayed as coloured squares for each step & track; the higher the threshold the more blue the square is. Higher valued steps get introduced first as you raise the fill control for that track. If the threshold is high enough (the cutoff is a value of 192), any time it fires on the trigger output it will also fire on the accent output. Watch the borders of the squares as you raise the fill controls for the different tracks, when a trig would be generated on that step and track the stroke turns black and when an acc would be generated that step and track's stroke is 2px thicker than a non acc generating step.

Update!

It took less than a day for someone (drinkcorpsevomit) on the forum to request I add sound and I'll be honest, it had definitely crossed my mind. Props to them for the prompt and for pointing me to howler.js which made it super easy. The interface is a bit rough and I'm not sure I have the BPM to timeout duration right(this version taps out correctly now), but it definitely plays very 808 drums when you press the [run?] button.


Matthew's notes

¹ U8Mix and bitshift in pattern_generator

The original MI forum is dead, but I was able to find this thread on the Internet Archive's Wayback Machine. I'll copy it here just to mirror it.

gphg

I'm trying to understand the code of Grids (especially the drum-pattern-generator).
line 94 in pattern_generator.cc:
return U8Mix(U8Mix(a, b, x << 2), U8Mix(c, d, x << 2), y << 2);

First up: U8Mix
As I understand it U8Mix returns a weighted sum:
the input numbers are all 8bit unsigned
the computation inside U8Mix is b*x + a*(255-x)
this sum is (to my understanding) 16bit
but the return of U8Mix is only 8bit unsigned
could someone explain? or point out where my reasoning is false?
preferrably with an example of meaningfull / relevant numbers

Second: the bitshift in ReadDrumMap: U8Mix(a, b, x << 2)
Why the bitshift?

Thank you very much.
pichenettes

U8Mix returns x * b + (255 - x) * a >> 8 (it 'crossfades' between a and b where x sets the position of the crossfader, with a slight loss of 255/256.0). It internally calculates the 16-bit result as you've noticed but drops the lowest byte.

The drum map is 5 x 5 - which means that that each axis is divided into 4 zones. If a coordinate (say x) has a range of 8-bit, x >> 6 will give you the zone index ; and x << 2 will give you the relative position within the zone.
gphg

Thank you very much,
I'm beginning to understand I need to learn working with bits

Now I feel confident in upgrading my MIdrum from Anushri-mode to Grids-mode

Much appreciated!
toneburst

Apologies for dredging up this long-dead thread.

I've need trying to prototype a variation of Grids using JavaScript (I know; not ideal, but it's the language I know best).

Your explanation above has been really useful, pichenettes. I've not really used bit-shift operators before (no formal background in computer science here, but been writing code for years), so I've learnt a lot.
toneburst

@pichenettes Am I right in thinking that your U8Mix function never quite reaches 'b' value? I did a simple JS test, using the function to crossfade between values of 0 and 255, and the output only reached 254, with x at 255. Is this expected behaviour?

I was hoping to use the same mix function to fade between values in the 0-11 range, but again, the output of the mix function only gets to 10.

Is there some variation of the mix function that will return exactly the target value at x=255?

I'd normally do this using floating-point numbers, but I'm hoping eventually to have this code running on an ATmega328, so I thought it might be best to use an integer-only version.

Happy New Year, incidentally! May 2016 bring you health, happiness, prosperity (and many brilliant ideas which we all look forward to seeing transformed into Euro goodness).
pichenettes

> Am I right in thinking that your U8Mix function never quite reaches 'b' value?

Yes. That's the expected behaviour because reaching b would require the crossfade parameter to be at 256 (that is to say 1.0 using an 8-bit fractional part).

If you want 255 to mean “100% b, 0% a”, you need to divide by 255 instead of shifting by 8, and what took 1 CPU cycle will suddenly take 40+
toneburst

Thanks for getting back to me. Thinking about it, it's not necessarily a problem, since when we're inside the next cell, and x=0, b will become a, and we'll get the original value.

I may make a lookup table, to create little plateaus in x around each node.
toneburst

Or this:

function U8Mix(a, b, x) { return (x == 255) ? b : x * b + (255 - x) * a >> 8; }

also works.