Clapping Music for one flip-disc display: Byte and variations
In response to previous post Clapping Music for two flip-disc displays, a reader commented
I’d love to see a version played on a single board, with the two performers represented by the left and right sides of the board. It would more closely match the layout of a typical performance (two people standing side by side), and I think it would make it easier to see the phasing points.
Great idea—let’s do it!
Quite pleasing. Thanks for the suggestion!
***
The code changes to support this variation were fairly minimal, but in the process of implementing them I accidentally stumbled into a nice illustration of how commands are sent to the flip-disc board. Let’s take a look!
Clapping Anything Music
First, the minimal code changes to support the new playback mode. If you’ll recall from the last post, our code that looked like this:
function clapping_music(sink_dots, sink_digits; pause=0.15,
=Bool[1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
clap_pattern=12, num_shifts=length(clap_pattern) + 1,
num_repeats=28, num_digits_to_set=2)
num_dots_to_set= 0
i_pattern_shift for _ in 1:num_shifts
for _ in 1:num_repeats, i_pattern in eachindex(clap_pattern)
&&
clap_pattern[i_pattern] write_to_sink(sink_dots, rand(0x00:0x7F, num_dots_to_set))
mod1(i_pattern + i_pattern_shift, length(clap_pattern))] &&
clap_pattern[write_to_sink(sink_digits, rand(0x00:0x7F, num_digits_to_set))
sleep(pause)
end
+= 1
i_pattern_shift end
end
Pulling the “make a clap” phrases out into input arguments clap_a
and clap_b
fully decouples the performance from the flip-disc boards:
function clapping_music(; clap_a=() -> print("A"), clap_b=() -> print("B"),
=0.15, clap_pattern=Bool[1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
pause=12, num_shifts=length(clap_pattern))
num_repeats= 0
i_pattern_shift for _ in 0:num_shifts
for _ in 1:num_repeats, i_pattern in eachindex(clap_pattern)
&& clap_a()
clap_pattern[i_pattern] mod1(i_pattern + i_pattern_shift, length(clap_pattern))] && clap_b()
clap_pattern[sleep(pause)
end
+= 1
i_pattern_shift end
end
Now it can be used to perform Clapping Music with any two clap functions that you want: lights, sounds, motors, whatever you want.1 2
The default behavior is to print a comment to the command line, which gives a lovely realtime-captioned mashup of Clapping Music with John Cage’s 4’33”:
A transcription of the full performance is even less interesting:
> clapping_music()
julia
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAA
BABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAA
BBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBAABABABABABABABBAABABABABABABABB
AABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABA
BABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABA
BABABABBABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABAB
ABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABAB
ABABABAABBABABABABABABAABBABABABABABABAABBABABABABAABABABABBABABABAABABABABB
ABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABA
BABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAA
BABABABBABABAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABB
AABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABB
AABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBABAABBAABBABABABABAABBAA
BBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAA
BBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABAB
ABAABBAABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBAB
ABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABA
BBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABABABABABABABBAABABAB
ABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAAB
ABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABAB
BAABABABABABABABBAABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBAB
ABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAA
BBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABAABABBAABBABAABB
AABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBAB
AABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAA
BBABAABBAABABBAABBABAABBABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
Anyway.
To recreate the dual-board performance of the original post, we do
= () -> write_to_sink(sink_dots, rand(0x00:0x7F, 28))
clap_a = () -> write_to_sink(sink_digits, rand(0x00:0x7F, 2))
clap_b
# Play it:
clapping_music(; clap_a, clap_b)
and the new single-board performance is generated with
function randomize_cols!(board_state, col_range)
.= rand(0x00:0x7F, length(col_range))
board_state[col_range] return board_state
end
= zeros(UInt8, 28)
board_state clap_left!() = write_to_sink(sink_dots, randomize_cols!(board_state, 1:10))
clap_right!() = write_to_sink(sink_dots, randomize_cols!(board_state, 18:28))
# Play it:
clapping_music(; clap_a=clap_left!, clap_b=clap_right!)
You’ll note that this single-board implementation involves keeping track of—and updating—the full display state of the board throughout the duration of the piece (via board_state
). This is necessary because a single
update of the display board updates all discs on the board, so we can’t update just a few columns with each clap.3 Instead, we send the
complete desired state to the board each time we want the board to update.
Binary diversion
I didn’t remember exactly how the “send update to boards” function worked—it had been awhile since I wrote it!—and my first guess, before going back to read the code, ended up not being what I intended BUT looking delightful anyway:4
As those of you familiar with counting in binary have likely recognized, in this variation, for each the right and left clappers, we are counting the number of claps that have been clapped thus far.
In these displays, one byte of data corresponds to one column of flip-disc board. Let’s send bytes values 0 through 127 (i.e., 27) one at a time, to illustrate:
for x in 0x00:0x7F
write_to_sink(sink_dots, [x])
sleep(0.1)
end
For the fun of it, let’s do all columns at the same time:
for x in 0x00:0x7F
write_to_sink(sink_dots, fill(x, 28))
sleep(0.2)
end
Given that knowledge, here’s the most basic “flip only one disc at a time” Clapping Music—aka, “The Littlest Clap”, as requested by AF:
= zeros(UInt8, 28)
board_state
function clap_a!()
14] = board_state[14] == 0 ? 1 : 0
board_state[return write_to_sink(sink_dots, board_state)
end
function clap_b!()
15] = board_state[15] == 0 ? 1 : 0
board_state[return write_to_sink(sink_dots, board_state)
end
clapping_music(; clap_a=clap_a!, clap_b=clap_b!, num_repeats=2, num_shifts=1)
Playtime
Here are another few examples of flip-disc behavior, because I think they’re fun.
Single column refresh rate
Flipping a whole column on and off, with a decreasing amount of time between “off” and “on”:
for s in 0.1:-0.001:0.0
clear(sink_dots)
sleep(s)
write_to_sink(sink_dots, fill(0x7F, 1))
sleep(.1)
end
Shimmer
Flipping a whole array off and on, with no pause in between—i.e., the board updates are sent faster than they can be (mechanically) fulfilled:
for _ in 1:30
clear(sink_dots)
write_to_sink(sink_dots, fill(0x7F, 10))
end
Multicolumn refresh rate
Flipping a set of counter columns on and off, again without enough time between requests for the board to fully update:
for x in 0x00:0x7F
clear(sink_dots)
sleep(0.2)
write_to_sink(sink_dots, fill(x, 10))
end
***
That’s all for now!
Code to generate all these examples lives here. Have an idea for a cool demo but don’t have access to flip-disc boards? Send me a snippet of code and I’ll run it for you!
Footnotes
-
Clapper beware: if your “clap” function is blocking (i.e., if it waits until the clap sound has been played to return to the main function call), you won’t get the desired dual-clap simultaneity. The simultaneity relies on calls to
clap_a()
andclap_b()
triggering an external clap production and then returning immediately, before the sound has had a be produced. In the case of these flip dots displays, this works out fine: the clap functions send a serial command to the boards (which is fast!) and then returns without waiting for the display board to have been updated. The delay happens quickly relative to both the duration of sound production and the timescale of the full piece, so the two claps are perceived as happening simultaneously. While there is a slight delay, it is not one that matters on the timescale of the piece’s default tempo.↩︎ -
Due to the previous caveat, if you do make your “smart” home play Clapping Music, you’ll probably have to slow the piece down A LOT to allow for the production of each clap before moving on to the next. This sounds pretty entertaining—if perhaps a great way to accidentally break your door lock—and if you have a home you’d be willing to let me experiment on, let me know. MB, looking at you….↩︎
-
At least, I don’t think that’s possible given the current serial communication protocol specification; it’s possible that some board configuration allows it, and I’m just not familiar with it.↩︎
-
I neglected to keep track of the exact thing I tried (whoops!) but I was aiming for a full-column result akin to the Littlest Clapping variant, above. Here’s an equivalent implementation:
= zeros(UInt8, 28) board_state function clap_a!() 12:14] = mod1.(board_state[12:14] .+ 1, 256) board_state[return write_to_sink(sink_dots, board_state) end function clap_b!() 15:17] = mod1.(board_state[15:17] .+ 1, 256) board_state[return write_to_sink(sink_dots, board_state) end clapping_music(; clap_a=clap_a!, clap_b=clap_b!)
- Created: 2024-10-18
- Last updated: 2024-10-18
- Type: Project write-up
- Tags: electromechanical-display, raspberry-pi, music, programming, hardware, software, julia