FruitBox Sequencer Musically Delicious Step Pattern Generator
2018-01-05 | By Adafruit Industries
License: See Original Project
Courtesy of Adafruit
Guide by John Park
Overview
Make a classic 16-step sequencer to create dope beats using fruity inputs.
Who doesn't love a fat drum beat? Nobody, that's who! You can make your own awesome rhythms once you've built the FruitBox Sequencer! Use the Circuit Playground Express board's built-in capacitive touch sensors plugged into pieces of fruit to program and trigger your beats, in a 16-step sequence. Play back sampled drum sounds in perfect time with the on-board speaker, or optionally, you can plug it into an amplified speaker to rock the crowd!
Parts
- 1 x Circuit Playground Express
- 1 x Small Alligator Clip to Male Jumper Wire Bundle
- 1 x 3 x AAA Battery Holder
- 1 x Alkaline AAA batteries
The following parts are optional if you'd like a louder sequencer, you can output to an external, powered speaker.
- 1 x 100uF 16V Electrolytic Capacitors
- 1 x USB Powered Speakers
- 1 x 5V USB port power supply
- 1 x Large Alligator Clip Test Lead
Materials
Additionally, you'll need to supply seven pieces of fruit. By conventions, these are typically:
- lemon
- apple
- grapefruit
- mango
- papaya
- lime
- satsuma
But there's no need to be stodgy about it. Use any fruit you like!
Now, let's get to coding the Circuit Playground Express with CircuitPython.
Code with CircuitPython
CircuitPython makes it fun and straightforward to program this project, but before we get going, there are a few things to do to make sure your Circuit Playground Express is all set up and ready to go.
Get Ready!
- First, make sure you're familiar with the basics of using CircuitPython on the Circuit Playground Express. Follow this guide to familiarize yourself.
- Then, install CircuitPython on your board by following these instructions.
- The last thing to do to prepare is to install the library bundle onto your board as shown here. The libraries give us what we need to code easily with high level commands!
Download the latest bundle from this link.
Once you've uncompressed the contents of the zip file, drag its contents to your Circuit Playground Express lib directory.
Code
On to the code! There are a few fundamental things we need to do to make the FruitBox Sequencer work:
- Play .wav file sound samples
- Light up NeoPixels
- Read the capacitive touch pads, with proper sensitivity
- Read the buttons and the switch
Drum Samples
Download the zip file linked here to get a set of drum samples to use. You can later provide your own, but these will get you off to a great start!
Unzip the file and then drag all seven .wav samples to your CIRCUITPY drive (that's the name that shows up when your Circuit Playground Express is plugged in and ready to be programmed.)
Below is our final, finished code. You can copy and paste this into your text or code editor, and then save it to you Circuit Playground Express board as main.py.
Download the FruitBox Python Code
Copy the code below, and paste it into a new text document in your text editor, or in the Mu code editor for CircuitPython.
Then save it to you Circuit Playground Express board as main.py.
# FruitBox Sequencer
# for Adafruit Circuit Playground express
# with CircuitPython
from adafruit_circuitplayground.express import cpx
import time
# Change this number to adjust touch sensitivity threshold, 0 is default
cpx.adjust_touch_threshold(600)
bpm = 60 # quarter note beats per minute, change this to suit your tempo
beat = 15 / bpm # 16th note expressed as seconds
WHITE = (30, 30, 30)
RED = (90, 0, 0)
YELLOW = (45, 45, 0)
GREEN = (0, 90, 0)
AQUA = (0, 45, 45)
BLUE = (0, 0, 90)
PURPLE = (45, 0, 45)
BLACK = (0, 0, 0)
cpx.pixels.brightness = 0.1 # set brightness value
# The seven files assigned to the touchpads
audio_files = ["fB_bd_tek.wav", "fB_elec_hi_snare.wav", "fB_elec_cymbal.wav",
"fB_elec_blip2.wav", "fB_bd_zome.wav", "fB_bass_hit_c.wav",
"fB_drum_cowbell.wav"]
step_advance = 0 # to count steps
step = 0
# sixteen steps in a sequence
step_note = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]
# step pixels
step_pixel = [9, 8, 7, 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
# step colors
step_col = [WHITE, RED, YELLOW, GREEN, AQUA, BLUE, PURPLE, BLACK]
def prog_mode(i):
cpx.play_file(audio_files[i])
step_note[step] = i
cpx.pixels[step_pixel[step]] = step_col[step_note[step]]
print("playing file " + audio_files[i])
while True:
# playback mode
if cpx.switch: # switch is slid to the left, play mode
cpx.red_led = False
if cpx.button_a:
cpx.pixels.fill(GREEN)
time.sleep(.2)
cpx.pixels.fill(BLACK)
if cpx.button_b:
for i in range(16):
step = i
# light a pixel
cpx.pixels[step_pixel[i]] = step_col[step_note[i]]
cpx.pixels[step_pixel[i - 1]] = BLACK
# play a file
cpx.play_file(audio_files[step_note[i]])
# sleep a beat
time.sleep(beat)
cpx.pixels.fill(BLACK)
# beat programming mode
else: # switch is slid to the right, record mode
cpx.red_led = True
if cpx.button_a: # clear pixels, reset step to first step
cpx.pixels.fill(RED)
time.sleep(.2)
cpx.pixels.fill(BLACK)
cpx.pixels[9] = WHITE
step = 0
step_advance = 0
# press B button to advance neo pixel steps
if cpx.button_b: # button has been pressed
step_advance += 1
step = step_advance % 16
cpx.play_file(audio_files[step_note[step]])
cpx.pixels[step_pixel[step]] = step_col[step_note[step]]
cpx.pixels[step_pixel[step - 1]] = BLACK
if cpx.touch_A1:
prog_mode(0)
if cpx.touch_A2:
prog_mode(1)
if cpx.touch_A3:
prog_mode(2)
if cpx.touch_A4:
prog_mode(3)
if cpx.touch_A5:
prog_mode(4)
if cpx.touch_A6:
prog_mode(5)
if cpx.touch_A7:
prog_mode(6)
Try it out! In playback mode, hit B to hear it play.
Then, switch to beat programming mode, and pick a sound per step. Advance to the next step with the B button, until you're finished with all sixteen steps. Then return to playback mode, hit B again and enjoy your composition! You can hold B to make it loop indefinitely.
Check out the code!
Let's have a look at the code in pieces.
Library Import
First, we'll import a couple of libraries that allow us to use simple, high-level commands to do more complicated things.
# FruitBox Step Sequencer
# Circuit Playground Express
from adafruit_circuitplayground.express import cpx
import time
The first line imports the adafruit_circuitplayground.express library, and allows us to refer to it as cpx throughout our code.
This library takes all sorts of CircuitPython functions that run on the Circuit Playground Express and gives us a consistent, simple way to control them.
For example, there are usually a few distinct steps associated with using a button in CircuitPython:
- Declaring a digital pin as an input
- Giving it a name
- Reading it to see if it's pressed or not
By using the adafruit_circuitplayground.express library with one of the buttons built onto the Circuit Playground Express, all of those steps are reduced to a single line -- cpx.button_a -- when we want to see if the button is pressed. That's much easier!
Then we import the time library which allows us to keep track of time for precise intervals -- an important feature when building a drum sequencer!
Touchpad Sensitivity
The next thing we'll do is tune the sensitivity of the on-board capacitive touchpads. They are automatically calibrated at startup, but within a range that's appropriate for the built-in pads, not external ones.
Since we'll be adding wires and fruit, the values need to be adjusted to prevent them from being overly sensitive and playing phantom beats!
The cpx.adjust_touch_threshold line includes a number that you can adjust (0 for no offset, higher numbers will reduce the sensitivity. 600 worked well for the fruit I used). We then list all of the pads by name that should be offset.
# Change this number to adjust touch sensitivity threshold, 0 is default
cpx.adjust_touch_threshold(600)
BPM Setup
Next, we'll create a couple of variables to help us with time signature. Sequencer music is usually expressed in beats per minute (BPM) so we'll make a variable with named bpm and assign a default value of 90 BPM. This is the number to change in your code if you want to slow down or speed up your sequence. A range of 40-120 works well for the samples we're playing.
Then, we'll create a variable call beat that is used to convert the chosen BPM into seconds. This is helpful because the time library uses seconds as its unit of measure.
bpm = 90 # quarter note beats per minute, change this to suit your tempo
beat = 15 / bpm # 16th note expressed as seconds
NeoPixel Setup
We'll use the on-board NeoPixels to indicate which sample is being played at a given time step by color coding them. The color of a NeoPixel is typically defined as an amount from 0-255 of red, green, and blue. Just to make our code simpler to read and edit later, we'll define a few colors by name, such as PURPLE = (45, 0, 45).
Additionally, we'll set an overall brightness of about 1/10th of the possible brightness (did we mention NeoPixels are BRIGHT!?)
WHITE = (30, 30, 30)
RED = (90, 0, 0)
YELLOW = (45, 45, 0)
GREEN = (0, 90, 0)
AQUA = (0, 45, 45)
BLUE = (0, 0, 90)
PURPLE = (45, 0, 45)
BLACK = (0, 0, 0)
cpx.pixels.brightness = 0.1 # set brightness value
Sound Sample Filename Array
When we play back sound files, the play_file command we'll be using takes an argument of the actual filenames we've saved to the board. Since these tend to be long, wild, and wooly names, we don't want to have to use them over and over again in our code. This is a great opportunity to create a list, which gives us a nice, concise shorthand for referring to the files later.
# The seven files assigned to the touchpads
audio_files = ["fB_bd_tek.wav", "fB_elec_hi_snare.wav", "fB_elec_cymbal.wav",
"fB_elec_blip2.wav", "fB_bd_zome.wav", "fB_bass_hit_c.wav",
"fB_drum_cowbell.wav"]
This way, we'll be able to ask for audio_files[2] in our code instead of using the name fb_elec_cymbal.wav, for example. (Note, Python lists are "zero indexed" which means the first item in the list is item 0, and so on.)
Also, when we want to loop through a series of items with different names, it's great to have them in a list, so that another simple integer variable, such as i, can be used as an easily controlled pointer that refers to items in the list. For example:
for i in range(7):
cpx.playFile(audio_files[i])
time.sleep(0.1)
That would play all of the files with a short delay in between without needing us to call them each by name.
Counting Variables
The next bit of setup we'll do involves creating variables to keep track of steps in the sequence, which sounds should play at each step, and which colors to show per step.
These have some defaults built in, so that it will play back before you've entered your own sequence. Those values will be changed interactively when you're using your FruitBox Sequencer.
step_advance = 0 # to count steps
step = 0
# sixteen steps in a sequence
step_note = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]
# step pixels
step_pixel = [9, 8, 7, 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
# step colors
step_col = [WHITE, RED, YELLOW, GREEN, AQUA, BLUE, PURPLE, BLACK]
Playback Function
The next thing we'll do is create a function called prog_mode() to handle the playback of notes and color coding of pixels when you touch a fruit trigger while you're in beat programming mode. This function will be fed a number corresponding to which fruit you've pressed, numbers 0-6.
That number is then passed to each remaining line to:
- tell the sound file associated with that fruit trigger to play
- store that note at the current step in the list which holds the full step sequence
- light up the current step pixel to the color associated with that fruit trigger
- print out the name of the file for debugging purposes to the REPL console
def prog_mode(i):
cpx.play_file(audio_files[i])
step_note[step] = i
cpx.pixels[step_pixel[step]] = step_col[stepnote[step]]
print("playing file " + audio_files[i])
Loop & Switch
Now we're finished with the setup and function definition, and on to the meat of the code, the while True: loop. This is what runs over and over and over again.
while True:
# playback mode
if cpx.switch: # switch is slid to the left, play mode
# lots of things happen here...
# beat programming mode
else:
# other stuff happens here...
The first thing we do is establish if the Circuit Playground Express board's toggle switch has been slid to the left (Playback mode) or to the right (Beat programming mode). We'll then build up code within those two sections.
In CircuitPython, the line if cpx.switch: is using the adafruit_circuitplayground.express library to query the slide switch, and the answer to that query is True if the switch is on the left, and all of the code below and indented from that line will run. else: it returns False, meaning the switch is on the right, and all of the code under that section will run.
Playback Mode
Here's what happens if the switch is on the left. First, the small, on-board red LED (to the right of the USB jack) is turned off. We'll reserve that indicator for when we're in beat programming mode, kind of like the red light on a camera when you're recording a video.
We'll then query button A, the one on the left side of the board. If it is pressed, we'll flash all of the NeoPixels green and then turn them off. This is to clear any colors left over from playback. You could program this button to do other things in the future if you like!
cpx.red_led = False
if cpx.button_a:
cpx.pixels.fill(GREEN)
time.sleep(.2)
cpx.pixels.fill(BLACK)
Next, we'll look at what happens when button B is pressed while in Playback mode.
We will run a loop sixteen times where the variable i is increased in value each time, starting at 0 and ending at 15. Then, all of the lines of code within the loop will be executed sixteen times, each time with the 'i' value increasing.
cpx.pixels[step_pixel[i]] = step_col[step_note[i]] is the first of these. This may seem a bit complicated, but what it does is set the color of the current pixel (the step_pixel[] list we created earlier allows us to run through the upper right pixel, clockwise through to the eighth one, and then wrap back again up to the upper right) to equal the color associated with the current note that is stored in the step_note[] list.
So, if we consider the CircuitPython point-of-view the first iteration of running through this, based on the default step_note, it would see this line like this:
cpx.pixels[step_pixel[0]] = step_col[step_note[0]]
So, when we look back in the Counting Variables section above at what values are in the 0 position of the arrays being called we see:
- step_pixel[0] is NeoPixel 9
- step_note[0] is 1, therefore:
- step_col[1] is RED
So, we can now imagine the line of code resolves to this:
cpx.pixels[9] = RED
So, not too tricky after all, but it takes this type of logic to loop through elements that can contain a few volatile variables efficiently!
Given the default values when the FruitBox Sequencer starts up, here's how that line of code resolves internally for the full sixteen step loop:
cpx.pixels[9] = RED
cpx.pixels[8] = RED
cpx.pixels[7] = RED
cpx.pixels[6] = RED
cpx.pixels[5] = RED
cpx.pixels[4] = RED
cpx.pixels[3] = RED
cpx.pixels[2] = RED
cpx.pixels[9] = WHITE
cpx.pixels[8] = WHITE
cpx.pixels[7] = WHITE
cpx.pixels[6] = WHITE
cpx.pixels[5] = RED
cpx.pixels[4] = RED
cpx.pixels[3] = RED
cpx.pixels[2] = RED
Whew! We wouldn't want to do that by hand, especially because these colors will be changing as you program in different beats!
We follow each step with a command to turn the previous pixel off, and then to play the proper sound file. For the case of the first step in our loop that would resolve to this:
cpx.play_file("fB_bd_tek.wav")
We pause for a beat as defined by the bpm variable after each note, and then, when the loop has completed, turn all of the NeoPixels off.
if cpx.button_b:
for i in range(16):
step = i
# light a pixel
cpx.pixels[step_pixel[i]] = step_col[step_note[i]]
cpx.pixels[step_pixel[i - 1]] = BLACK
# play a file
cpx.play_file(audio_files[step_note[i]])
# sleep a beat
time.sleep(beat)
cpx.pixels.fill(BLACK)
Beat Programming Mode
Flip the switch to the right, and it's time to input different sounds per step!
First, the red LED is turned on, to indicate the mode change.
Then, we again check the A button. If it's pressed, we'll flash all of the pixels red, and then turn them off, and set pixel 9 to white.
Also, note how the step and step_advance counter variables are reset to 0. This way, you can press A at any time to go back to the start of the pattern.
# beat programming mode
else: # switch is slid to the right, record mode
cpx.red_led = True
if cpx.button_a: # clear pixels, reset step to first step
cpx.pixels.fill(RED)
time.sleep(.2)
cpx.pixels.fill(BLACK)
cpx.pixels[9] = WHITE
step = 0
step_advance = 0
We'll check the B button next. When it is pressed, the step is advanced, this is how you move step-by-step through the sequence. Whatever sound is currently stored in that slot of the step_note list is played, and the corresponding color is set on the proper NeoPixel.
# press B button to advance neopixel steps
if cpx.button_b: # button has been pressed
step_advance += 1
step = step_advance % 16
cpx.play_file(audio_files[step_note[step]])
cpx.pixels[step_pixel[step]] = step_col[step_note[step]]
cpx.pixels[step_pixel[step - 1]] = BLACK
Note, you can change the sample triggered at the current step at any time by pressing one of the fruit triggers. How, you ask? Like this!:
Fruit Triggers
In this final bit of code, we check each capacitive pad to see if it is touched. When it is, it calls the prog_mode function we defined at the top, with an input value that corresponds to the touchpad.
So, for example, if A2 is touched while we're on step 0, the value 1 is fed to the prog_mode function, which plays the fB_elec_hi_snare.wav file, and updates the step_note[] list with this information. Now, when the FruitBox Sequencer needs to play a note while on step 0, it will play the snare!
if cpx.touch_A1:
prog_mode(0)
if cpx.touch_A2:
prog_mode(1)
if cpx.touch_A3:
prog_mode(2)
if cpx.touch_A4:
prog_mode(3)
if cpx.touch_A5:
prog_mode(4)
if cpx.touch_A6:
prog_mode(5)
if cpx.touch_A7:
prog_mode(6)
Make the Fruit Triggers
First, attach the alligator clip leads as shown to the capacitive pads on the Circuit Playground Express.
Next, arrange your fruit triggers in a circle around the board. Then, plug the lead pins into each piece of fruit.
To avoid capacitive antenna interference, make sure none of the wires overlap or touch each other.
Plug in the battery pack and then turn it on -- you want to avoid touching any fruit triggers or wires while the Circuit Playground Express is starting up -- as this is when the capacitive sense pads are calibrated.
You're ready to make music!
Create Rad Beats
Just as when we coded it, you can now use the two modes -- Playback and Beat Program -- on your FruitBox Sequencer. Try mixing it up and making some funky beats!
Sound Boost Option
Want to make it loud by hooking up to headphones or an external powered speaker? OK! Here's how. Take the long alligator clips and attach them to the sleeve and tip of the 1/8" phono plug as shown:
- White to sleeve
- Red to tip
Next, you'll attach them to the sound ground and audio output pin (A0) on the Circuit Playground Express -- however, to protect the external speaker, we'll add a capacitor.
The capacitor has a negative side -- the leg near the white stripe. This will go to ground. The positive side will go to A0.
You can bend the legs so they sit in the pin holes a bit, and then attach the alligator clips to them, making solid contact with the capacitor legs and the holes as shown. This will take care of the sound popping!
Connect white to ground, and red to A0.
Make sure the capacitor has enough clearance that it doesn't touch the A1 pin or clip.
Now go and rock the party!
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum