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).
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; }