Skip to content
Snippets Groups Projects
Jake's avatar
Jake Read authored
fc49d512
History

Machine for Playing Music for Pieces of Wood by Steve Reich by Jake Read

video

I've been really stoked by Steve Riech's music lately. This piece came out as a really fun project:

Music for Pieces of Wood

Reich is obsessed with polyrhythms... this is also fun, and this is wonderful.

To bring up network motion control (although a really awesome version of this uses virtual impedence control for bldc motors, I'm using stepper because they work already), I want to write a small sequencer program in mods that sends drum-ticks to a set of networked motors.

wb-sequencer

Here's my sketch for how I'll do this mods. To 'sequence' over periods, I need a delay module so that I can insert periods between looping events. Then I use a 'seqencer' which, on each event, reads a bit pattern to either fire-or-not-fire an output. I have one loop for firing sequences, and another for firing sub-sequences. They have nested periods. Then I use a 'step sequencer' which, on any event, pushes a sequence of steps to a motor. In this case the sequence of steps defines one mallet strike. The stepper motor module (the bit that will later be replaced with some elegant hardware:software representation) formats these moves into packets that will make their way over the network to a motor, which will execute those moves.

To do the business, I'll get 5 motors up and running. Each of those will have a mallet on a flexure (to 'hit' but not rest on the key) and will strike a key. I can 3DP most of the finniky mechanical bits, including the flexure, and I'll cut the mounting equipment on a laser, or the mill, out of plywood. I can make the keys maybe out of CNC'd plywood, or if I can find some solid hardwood stock, out of that. A nicely bounded project!

Mods

I wrote a few mods (a sequencer, a step sequencer, a stepper abstraction, an event gate, and I added some counting to the delay module).

I have one 'sequencer' stage set up:

mods-onesequencer

Now I'll just add an address field to that stepper module, and then I can plug that into the serial server. I'll be sending steps, in sequence, to my motor. Hurray!

As for the embedded code, I have most of that documented here. But I need to write the application side of APA - the bit that will actually take key:value bytes, bit shift them into words (sometimes) and execute commands - in my setup, that will mean just updating values in a state machine as soon as they arrive. So I can remotely change the stepper's target position and speed, and the active statemachine will work with new values once they arrive.

Keys

Vic Firth has a blog post about the piece so I've learned that the keys are an A, B, C-sharp, D-sharp and a D-sharp one octave up. Music! I'll need to learn how to tune (or design) the size of my keys, and then I'll need a way to lay them out.

The shortest period is also quite short... er, fast. I'm a bit worried my little steppers aren't going to be able to beat the beat quickly enough.

Turns out the best way to do this is to make one and measure it, then the rest of the spectrum follows with some simple maths. I found my top-end D# to be about 10 & 7/8 " - with a note of 2489 Hz.

d

d

From the length of that key, I can ascend the scale by multiplying that length by

2^(-1/24)
, or descend by
2^(1/24)
. That means I have these keys:

Note | Hz | Length (mm)

A | 880.0 | 379.0 B | 987.8 | 357.8 C# | 1108.7 | 337.7 D# | 1244.5 | 318.7 D# up one | 2489.0 | 225.4

Hardware

I figured out how to do this this morning. Hurray. I've wanted to mount it on a wall but have been at a miss as to how I might suspend the keys properly - of course, that's the trick: I'll just suspend them.

Tom Sachs is coming to town on Saturday, I have a major crush on him... see below 'Objects of Devotion'

sachs

So I'm going to do this pegboard style...

millpattern

I've got my keys, pegboard and 3DP bits ready to assembly now. Something else I learned about which seems elementary now that it occurs to me, is that I want to pin or suspend the bars at their nodes - for a free bar vibrating in the fundamental mode, the nodes are 0.224 * L from the ends. I'm changing some of my 3DP bits to reflect this.

free bar

Put this together:

assy

Mods: Plumbing

Here's a short video of the system running, this took a lot of footwork in C and some footwork in JavaScript, to write and then route and then act on network packets.

mods shoothrough

Inside my mammalian brain, this certainly feels very much lke real time. The Serial Server runs about 0.1us overhead (there and back, looks like 0.2us average ring - although this varies up to 0.5us). Then packets take some time to get through to the motors (they're about 15 bytes each, at 115200 baud, so that's 1ms transmit time, over 2 hops). A note: one of my short order tasks is to use a better Serial Bridge to go to 3MBaud, so this will be more like 40us. If we get those FPGAs up and running at 60MBaud, that's a 2us packet. Hot fire. In any case, this works now.

Mods: Composing

Next up is building a deeper understanding of Music for Pieces of Wood and trying to invent a fun / weird way to 'write' it with sequencing modules.

This would involve some kind of zen, empty-neuron state, a careful watch & whiteboard of the song, and then some attempt to reduce that to an algorithm / series of building blocks.

OK, Reich's Music for Pieces of Wood is deceptively simple / complex. Imagine Sol Lewitt, but this time he is a drummer.

Keys are descending 1: D# +1 oct, 2: D#, 3: C#, 4: B, 5: A

The piece is broken into three sections, each following the same algorithm.

Starting at 12 Bases,

K1 repeats P101010101010
4 bases pass

K4 plays 4 bases P111011010110

until pattern is full,
every 4 bases,
K5 adds one element of P010110111011
randomly

until pattern is full,
every 4 bases,
K3 adds one element of P010110111011
randomly

until pattern is full,
every 4 bases
K2 adds one element of P111011010110
randomly

K2-5 play 4 bases K4P

K2,3,5 stop, K4 plays 4 bases K4P

Then we truncate the 'input' K1 and K4 patterns, first by 4 (for a base 8 rhythm) and then by 6 for a base 6 rhythm. We repeat the pattern above, adding in K5, K3 and K2 sequentially. Those patterns are basically bit-shifted copies of K4. Forgive my musical ignorance as I trounce about without any of the right nomenclature.

To the Javascript

wb

wb

mods

Adding add-on Reich Gates

mods

This seems to run wonderfully. Now I'll add a final-sync step for the last 4 bars, and then a 'return and truncate' to the metronome. I think I'm done writing the mods, we'll have to see.

OK, here's the final sketch as it ran:

mods

This was, to say the least, a PITA. There's a temptation with MODS to write everything into one little program - I could have just written one 'Music for Pieces of Wood' mod - but then mods would just be a tool for experts to house their code in, not a tool for beginners to scratch together programs from basic elements. So to do this, I tried breaking the rhythm generation down into bite-size elements that I strung together.

That said, making the bitwise components straightforward and fungible enough to be really universal takes careful thought. There should be effort made to specify and convert for different types of events, and clarity in labelling inputs and outputs. There's a lot of work to do in mods until it can really be approachable, I think.

Adding State back into MODS

These are notes for me, mostly, to reconstruct the Reich program. All of the mods used have default states that need to have the right initial conditions for the thing to work.

Keys are descending 1: D# +1 oct, 2: D#, 3: C#, 4: B, 5: A

Addresses

K1, D#: 0,4 K4, B: 0,1 K5, A: 0,1,0 K3, C#: 0,2 K2, D#: 0,3

2,0,2,0,2,0,2,0,2,0,2,0 2,2,2,0,2,2,0,2,0,2,2,0

update shifts -> -6, 2, 0 update truncate and startcount sequences

-18,1000 | 100,1000 do 2, 75ms delay, no loop set delay step gates open

Debugging

So, this is nice because it means the experiment was worthwhile. First note is that javascript-in-browser is maybe not perfect for 'realtime' control, I suppose no one should be surprised by this. When we're not in the tab, the runtimes drop drastically - so if a user is running mods, they'd better be running mods for as long as their task is active.

What is the speed of console logs?

There's a way around this: write messages to a variable that we can inspect.

Perhaps the answer for realtime is to run a node server that dishes graph pages: graphs are mirrors of the node server... I like this because we can put the node server on an RPi and put it to sleep when we're not using it. Taking a look at what that architecture would be like could be fun. Alternately, mods and this node-server can play together: some computation is done in browser, some on the server. This is just like adding a new layer.

The next issue is that I'm overrunning the network.

My current network speed is 115200kbps - limited by the FTDI bridge I'm using.

I'm pushing packets on 50ms (at the shortest) intervals. In the worst case, I'm driving 5 packets of speed & position: those packets are 0,1,0,ptr,end,steps,b1,b2,b3,b4,speed,b1,b2,b3,b4 15 bytes long, so I'm pushing 600 bits down the line, which actually should be well within the limit... 5ms.

There's some variance in the program - sometimes the final beats sync up, other times some seem to miss. This is either (1) some mods events not being handled, or being handled out of order, or (2) some errors on my part in event sequencing, etc. Likely a healthy, confusing mix of both. In any case, I was able to get a semi-passable version of this to work, and that's in the video that I will likely put at the top of this page.

Notes for Next Time

I want to circle back on this when I've made some more progress on mods and on my stepper motor controls. I want to do this with the hardware / software representation I'm dreaming of. There's some chance that becomes a node server running locally on a raspberry pi (I noticed some behaviour from in-browser mods that I don't want to see on time critical machines).

I would also like to improve the motor controller for weird tasks like this. For example 'hit the thing' rather than 'go to this position at this speed' would be really cool. I think I can do this with encoder input and the raw current value registers on the stepper motor. This is something I may get to, or may not. There's also some chance I eventually do this with brushless motors, but I have some controls flexing to do before I get there.