Storing large integers as parameters

I’ve run out of global parameters, and so I’m trying to figure out clever ways to pack multiple data into a single paramater in order to free up some global parameter slots.

I have eight patches, and for each patch I need to store two boolean values, four note numbers, two small integers representing octave transpositions ranging from -7 to +7, two real numbers representing gain values, and a real number representing a compression ratio. Already that’s 88 global parameters right there, and in addition to all of these patch parameters there are other parameters I need to store as well.

If I treat all of these values as bit fields, then for each of my eight patches I need one bit for each boolean, seven bits for each note number, four bits for each transposition value, 7 bits for each gain value (giving me a resolution of about 0.4dB), and let’s say 7 bits for the compression ratio. I would need a total of 59 bits per patch. If I can store all 59 bits in a single global parameter, then I would only need 8 widgets instead of 88, and then I’d have plenty of global parameter slots to serve all of my needs.

If I encode all of these data as a 59-bit number (using shifts and bitwise-OR operations), and then convert this 59-bit number into a parameter by dividing by 2^60 (1152921504606846976), will I then be able to follow the inverse process to recover all of the original data precisely, or will I be killed by rounding errors?

A double has only 53 bits for the ‘precision’ part and the other 11 bits are for the exponent, so I guess this is not possible.

In c++ you could have used a union with a double and a long, (or maybe just simply cast the double to a long). That would give you the possibility to use a double as a bunch of bits. GP doesn’t support that directly. You could write an extension to do that for you.

Okay, I’ve spent some time experimenting with this and it seems that I can’t reliably store more than 8 bits in a global parameter. See attached gig file to see what I was playing with: dial up a number of bits, press the green pad, and then the script will choose a random integer in that many bits, encode it as a parameter, decode it back again, and compare the results. The green LED lights up if it’s a match.

bitsTest.gig (110.9 KB)

I find that with 8 or fewer bits, it works every time, and with 9 bits it occasionally gets off by 1. With 10 or more bits it’s usually off by more than that.

So now I’m looking for other creative ways of dealing with the global parameter barrier.

Why do you need so many global parameters? I know you explained what you were doing but that didn’t explain why you were doing it? I’m curious as to the musical purpose of what you’re doing.

Well, the system I’m working on has a main UI panel in the global rackspace, and I want to be able to store its state in the local rackspace.

Why do you have everything in the global rackspace?

I’m just putting stuff in the global rackspace which I expect to need in every rackspace: handling of incoming MIDI events, plugins for the more common instruments that i’ll be needing, an eight-channel mixer for my patches, handling of the expression pedal and sustain pedal, “proportional catch” for my physical faders and encoders so that my rackspaces won’t need to deal with “jump” or “catch” behaviour, and so forth.

I still don’t understand. Is there a reason you can’t use the Follow Hardware option for your widgets so that when you switch rackspaces, the widgets tied to hardware will automatically be set to the position of your hardware faders and pedals?

I don’t see how that would be useful. For example, let’s say I switch to a rackspace that uses my hardware faders as organ drawbars, and it’s a rackspace that has the initial values of the drawbars set to get a particular sound. When I switch to that rackspace, I wouldn’t want the drawbars to jump to the positions of the hardware faders, as that would defeat the purpose of switching to the rackspace in the first place.

What would you want to happen?

This is something I’ve already implemented to my satisfaction: when the physical control moves, I want the abstract control to move in the same direction, by a proportional amount, regardless of the initial conditions. I’m calling this behaviour “proportional catch”.

For example, if the initial state of the abstract control (e.g. the organ drawbar) is at 0.8 and the initial state of the physical control (e.g. a physical slider) is at 0.4, then if I slide the physical control halfway down (from 0.4 to 0.2), then the abstract control also goes halfway down (from 0.8 to 0.4). If I subsequently move the physical slider halfway from its current position toward its maximum value (i.e. from 0.2 to 0.6), then the abstract control likewise moves halfway up (from 0.4 to 0.7). Within a few fader moves the value of the abstract control is very nearly equal to the value of the physical control, without any jumping or catching needed.

OK - the next update will support 256 global parameters, hopefully that will help.

1 Like

Hey this is very interesting, could you post a simple gig file which demonstrates this ? I really would like to play with it. :+1: