Hex Beats

Hexadecimal (base 16) has been used in various forms of computer music for a very long time, generally as a condensed way to notate values within a power-of-two range.  For example, rather than write out “15” as a decimal value (base 10), one can use “F”, and rather than write out “255”, one can use “FF”. The notation of hexadecimal numbers, in general, take up less horizontal space on the screen than its base 10 counterpart.

The differences in screen real estate is even more pronounced when comparing the binary value (base 2) to the decimal and hex values.  Let’s compare some values here:

Binary:  1101
Decimal: 14
Hex:     E

Binary:  11001111
Decimal: 207
Hex:     CF

A chart showing the binary, decimal, and hex values for number 0-255 are available here.

Now, one of the interesting challenges in live coding pattern-oriented music for me has been trying to have a very condensed notation for expressing beats (onsets).  One way I’ve seen used is to notate values in a binary form within a string, such as “1000100010101000” which would mean “play notes where there are 1’s, but don’t play notes where there are 0’s”. In this case, on beat 1, 5, 9, 11, and 13.

Binary values in a string, on the one hand, quite clearly notates when an instrument should play. On the other hand, I’ve found it visually takes up quite some space and can be a bit slow to parse mentally.

One thing I’ve found rather useful is to notate onset patterns using hexadecimal strings.  I first explored this in my Clojure systems Pink and Score, but recently translated the function I was using to Csound code.  The Csound code turned out to be quite simple:

opcode hexbeat, i, Si
  Spat, ibeat xin

  ;; 4 bits/beats per hex value
  ipatlen = strlen(Spat) * 4
  ;; get beat within pattern length
  ibeat = ibeat % ipatlen
  ;; figure which hex value to use from string
  ipatidx = int(ibeat / 4)
  ;; figure out which bit from hex to use
  ibitidx = ibeat % 4 
  
  ;; convert individual hex from string to decimal/binary
  ibeatPat = strtol(strcat("0x", strsub(Spat, ipatidx, ipatidx + 1))) 

  ;; bit shift/mask to check onset from hex's bits
  xout (ibeatPat >> (3 - ibitidx)) & 1 

endop

And an example of its use is shown here:

  if(hexbeat("f0d0d0f0", ibeat % 32) == 1) then
    schedule("Synth1", 0, p3, inScale(48, 0) )
  endif

The above is saying: “within the hexadecimal beat string of f0d0d0f0, and given the current beat value between 0 and 32, check if the onset is a 1 and, if so, perform Synth 1”.

The code above may be a little tricky to grok at first glance. I’ve started a Github repository for this code and made an online web app for live coding with Csound and this library. The live web site is available at:

https://kunstmusik.github.io/csound-live-code/

and the source code is available at:

https://github.com/kunstmusik/csound-live-code

In working with the hex beat patterns, I found it took a little practice but the meaning of various hex values started to become intuitive over time.  Hexadecimal works really well, in my opinion, for notating pattern onsets as each hex value maps to 4 bits, which works perfectly for 4 16th-notes.  With this, 4 hex values can be used to notate a single measure of 16 16th-notes, 8 hex for 2 measures, and so on.

Published
Categorized as csound