My year teaching Sonic Pi - Week 16

I had it in my mind for a while that once I showed the students how to use samples, I would use that as the springboard into introducing concurrency. My thought was that I could have students make simple drum beats with a kick, snare and high hat each as a separate thread playing concurrently. Making drum beats are always a big hit in music class and it is also an example that nearly every student can connect to. Early on in the school year when I was thinking ahead to lessons and concepts that I would introduce, I had thought about showing what a drumbeat would look like without using concurrency and even made out a template that breaks down all the beats subdivided into eighth notes.

live_loop :drumbeat do
#1
sample :bd_ada
sample  :drum_cymbal_closed
sleep 0.5
#and
sample  :drum_cymbal_closed
sleep 0.5
#2
sample :sn_dolf
sample  :drum_cymbal_closed
sleep 0.5
#and
sample  :drum_cymbal_closed
sleep 0.5
#3
sample :bd_ada
sample  :drum_cymbal_closed
sleep 0.5
#and
sample  :drum_cymbal_closed
sleep 0.5
#4
sample :sn_dolf
sample  :drum_cymbal_closed
sleep 0.5
#and
sample  :drum_cymbal_closed
sleep 0.5
end

But the more I have been teaching and gotten a better feel of how to introduce new concepts and how I want a class period to flow, I felt like this would be too much of an explanation for the end result I was trying to achieve. I have really been subscribing to the “less is more” philosophy teaching Sonic Pi, so I decided I would forego this plan. Not just presenting what a drumbeat would look like without concurrency, but also starting out using a drumbeat as the introduction to concurrency. I want to present concepts in digestible pieces and when breaking down what was necessary to do the drum beats, the first part was simply introducing the syntax needed to achieve concurrency which is the in_thread block. Being able to play two or more blocks of code simultaneously seemed like such a big step in what we have been learning and opened up a whole new world of possibilities, I felt that I wanted to give students ample time to explore this concept and just this seemingly basic command. So I decided all I was going to do was show them the in_thread syntax with a few examples and turn them loose.

I introduced the concept of concurrency by first pointing out that up till this point we have been making compositions that mostly play one note at a time. We have found ways to play multiple notes at the same time by having two play commands one after another or putting notes in an array and playing them without .tick or .choose. But we haven’t really had two separate pieces of code running together at the same time. Today we are going to learn how to do that. I explained that the concept of having multiple things happening at once in computer programming is called concurrency. Now in music, having multiple parts happening together at the same time seems like a no brainer. But in the world of computer programming, having this happen is not something that is necessarily easy to execute, especially with precise timing. But since Sonic Pi is a programming language meant specifically for making music, it has a command that allows us to do this.

This command is called in_thread. I typed it out on the board. I explained that this was a block of code which means it will include a ‘do’ after the in_thread command and an ‘end at the bottom. I told the class that when typing blocks of code a good practice is to write the do, add a few spaces and then immediately type the end. I know a lot of students at some point this year have gotten an error message concerning an ‘end’ that they were missing or had too many of. So by writing the end at the same time as the do, this can help to avoid those types of errors.

Inside of the in_thread block, I can now put some code. I started by putting an 8.times repetition block (again typing the do and end together) and inside this I put sample :bd_haus and sleep 1.
I then ran the code. It played the bass drum. I pointed out that although I had the in_thread, nothing was happening yet because I only have one block of code running at the moment. I then typed another 8.times block and put a ring of notes: play (ring, 60, 64, 67, 72).tick and sleep for 1. I explained that since the block before it was inside an in_thread, the code below is going to be “threaded” together with that block causing them to play together at the same time. I ran the code and we heard the bass drum and notes play at the same time. That is basically all there is to it. With this, there are so many possibilities to make our compositions more dynamic and interesting.

in_thread do
  8.times do
    sample :bd_haus
    sleep 1
  end
end

  8.times do
    play (ring, 60, 64, 67, 72).tick
    sleep 1
  end

To show we are not limited to just two blocks, I put the second block of code inside of an in_thread block and then added a cymbal sound. This time I had it repeat for 16.times and cut the sleep time in half to have an eighth note feel, although I didn’t really explain it like that. I just wanted to hear that they can have sounds running concurrently at different rhythms.

in_thread do
  8.times do
    sample :bd_haus
    sleep 1
  end
end

in_thread do
  8.times do
    play (ring, 60, 64, 67, 72).tick
    sleep 1
  end
end

16.times do
  sample :drum_cymbal_closed
  sleep 0.5
end

I emphasized that if they no longer want their blocks of code to “thread” together, the last block of code should not have in_thread. It will still “thread” with the block above it but anything else after it will not play concurrently. To demonstrate, after the last block I added sample :guit_em9 and ran the code again. They heard the original code and then when it was finished, the guitar sample played by itself. Then if we want to thread the code after this, we would add in_thread again. To demonstrate this, I added a sleep smaple_duration :guit_em9 and then copy and pasted the threaded blocks of code from the beginning and ran the code which played the 3 threaded blocks, then the guitar sample and then the 3 threaded blocks again.

Since this example offered an opportunity to try and reinforce past concepts, I asked the class how I could have had the first 3 blocks play the second time without having to copy and paste all the code. While most of the class was quiet, I did get a couple of students who remembered that we could make the code into a function and call it. So I defined the threaded blocks of code as :beet (as we learned that beat will not work since it is already a command used in Sonic PI). I then called beet, then the guitar sample and then beet again.

I was glad that I had a chance to give that example, however I left the code on the board while the students were working and I noticed several of them were starting their code with a define for a function but it didn’t seem like they knew why aside from the fact I did it. I am also unsure if some of them thought they need it to do the in_thread. In hindsight, I would have deleted that from the code before having them start working on their own. Sometimes I’ve noticed when I try and add a little bit extra at the end, it can leave a lasting effect on some students who then focus on that over the main concept I was teaching from the beginning. I need to be mindful of this. Sometimes it is something I hadn’t initially planned to do but based on a question someone asks or something that pops into my head when I am going over it out loud. I think this just reinforces the philosophy I was talking about earlier which is less is more.

I was able to get through the teaching part fairly quick, so the students had plenty of time to play around. This was also an assignment I asked for them to turn in via Google Classroom so I could see how they understood it. For the most part, everyone got the basic idea. One or two students typed some basic lines of code using sample and sleep and then had one in_thread block at the end with nothing after it. This just highlights my concern that student don’t always know what to listen for to know if their code is working correctly or not because listening to the code they should hear that the last line isn’t really playing along with anything else. But since it is the first time working with this concept, I figure they may just need some more time to get acquainted with it.

Here’s a link to the reference sheet for this concept: https://docs.google.com/document/d/1G1482EwSH4RVxIiyWo7Dee3vPAQ4mo-l9qdTmyeoEs8/edit?usp=sharing

Here’s an example of one of the more interesting student submissions:

sample :guit_em9
sleep 0.5
sample :guit_em9, pitch: -2

in_thread do
  10.times do
    sample :ambi_piano
    sleep 1
  end
  
  3.times do
    sample :ambi_glass_hum
    sleep 1
  end
end

3.times do
  sample :guit_em9, pitch: [2, 3, 2, 1].choose
  sample :guit_em9, rate: -2
  sleep 0.5
  sample :ambi_drone
  sleep 0.5
end

in_thread do
  2.times do
    sample :guit_e_fifths, pitch: -4, amp: 2
    sleep 0.5
    sample :guit_e_fifths, pitch: 1
    sleep 0.5
  end
end

in_thread do
  10.times do
    sample :ambi_piano
    sleep 1
  end
  
  3.times do
    sample :ambi_glass_hum
    sleep 1
  end
end

5 Likes

Thank you! I’m glad that these posts are helpful to people not just for teaching but learning the content as well, even well after the fact.

Full disclosure: Last year, I opted to not teach in_thread and instead just focused on using multiple live_loops to teach concurrency. I was hoping to get some opportunities to try some live coding in class, but unfortunately we ran out of time in the school year.