Sumita GhoshAshish JoshiJeff Mueller
Published © GPL3+

Keychain Synth

A hand-held synthesizer with cap touch buttons, various modes on a four-key drumpad, and a looper for on-the-go composing.

IntermediateFull instructions provided5,403

Things used in this project

Hardware components

Sound Card
This allows you go get microphone input and audio output.
×1
Adafruit Standalone Momentary Capacitive Touch Sensor
Used the circuit for this, though this could easily be bought and put on a breadboard if you don't make the PCB.
×5
Adafruit Standalone Toggle Capacitive Touch Sensor Breakout
Repeat from Momentary Sensor: Used the circuit for this, though this could easily be bought and put on a breadboard if you don't make the PCB.
×3
1 kOhm resistors
Only get these if you aren't using the Adafruit buttons.
×8
Buttons/Switches
Only get these if you aren't using the Adafruit buttons.
×8
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×24
BeagleBone Black
BeagleBoard.org BeagleBone Black
×1

Story

Read more

Schematics

Schematic

Eagle file for schematic of the board.

Board

Eagle file for the board. This is where the gerbers come from, which are used to order PCBs.

Code

Python Code

Python
This connects the inputs from the BeagleBone Black's GPIO pins to the Pure Data program. Only run this once the Pure Data file is running on the BeagleBone Black. SAVE THIS AS A .py FILE.
#Poll exported Beagle pins and connect to PureData for transmission on change
#Tyler Worman tsworman@novaslp.net
#7/1/14

#Edited by Team Sunshine
#	Ashish Joshi
#	Sumita Ghosh
#GPIO and ADC handled by Adafruit BB Library

import subprocess
import time
import socket
import Adafruit_BBIO.GPIO as GPIO

lPin44 = 0 #Store value for gpio44
lPin45 = 0 #Store value for gpio45
lPin46 = 0 #Store value for gpio46
lPin47 = 0 #Store value for gpio47
lPin48 = 0 #Store value for gpio48
lPin49 = 0 #Store value for gpio49
lpin50 = 0 #Store value for gpio50
lpin51 = 0 #Store value for gpio51
lpin52 = 0 #Store value for gpio52

#Setup GPIO
GPIO.setup("GPIO1_12", GPIO.IN) #play
GPIO.setup("GPIO1_13", GPIO.IN) #Record
GPIO.setup("GPIO1_14", GPIO.IN) #Clear Loop
GPIO.setup("GPIO1_15", GPIO.IN) #Set Tempo
GPIO.setup("GPIO1_16", GPIO.IN) #Bank
GPIO.setup("GPIO1_17", GPIO.IN) #Key A
GPIO.setup("GPIO1_18", GPIO.IN) #Key B
GPIO.setup("GPIO1_19", GPIO.IN) #Key C
GPIO.setup("GPIO1_20", GPIO.IN) #Key D

#Setup Events
GPIO.add_event_detect("GPIO1_12", GPIO.BOTH) #Play
GPIO.add_event_detect("GPIO1_13", GPIO.BOTH) #Record
GPIO.add_event_detect("GPIO1_14", GPIO.BOTH) #Clear
GPIO.add_event_detect("GPIO1_15", GPIO.BOTH) #Set Tempo
GPIO.add_event_detect("GPIO1_16", GPIO.BOTH) #Bank
GPIO.add_event_detect("GPIO1_17", GPIO.BOTH) #Key A
GPIO.add_event_detect("GPIO1_18", GPIO.BOTH) #Key B
GPIO.add_event_detect("GPIO1_19", GPIO.BOTH) #Key C
GPIO.add_event_detect("GPIO1_20", GPIO.BOTH) #Key D

#Connection to PD
TCP_IP = '127.0.0.1'
TCP_PORT = 13131
BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

def sendChange(name, oldVal, newVal):
	#print "action %s %s %s" %(name,oldVal,newVal)
	if oldVal <> newVal:
		if newVal:
			s.send('%s 1;' %(name))
		else:
			s.send('%s 0;' %(name))

##Main program
#Set initial state
lPin44 = GPIO.input("GPIO1_12")
sendChange('Play', 0, lPin44)
lPin45 = GPIO.input("GPIO1_13")
sendChange('Record', 0, lPin45)
lPin46 = GPIO.input("GPIO1_14")
sendChange('Clear', 0, lPin46)
lPin47 = GPIO.input("GPIO1_15")
sendChange('Timing', 0, lPin47)

lPin48 = GPIO.input("GPIO1_16")
sendChange('Bank', 0, lPin48)
lPin49 = GPIO.input("GPIO1_17")
sendChange('A', 0, lPin49)
lPin50 = GPIO.input("GPIO1_18")
sendChange('B', 0, lPin50)
lPin51 = GPIO.input("GPIO1_19")
sendChange('C', 0, lPin51)
lPin52 = GPIO.input("GPIO1_20")
sendChange('D', 0, lPin52)


#Wait on events
while True:
	#Wait for event to trigger
	if GPIO.event_detected("GPIO1_12"):
		lPinT = GPIO.input("GPIO1_12")
		sendChange('Play', lPin44, lPinT)
		lPin44 = lPinT
	if GPIO.event_detected("GPIO1_13"):
		lPinT = GPIO.input("GPIO1_13")
		sendChange('Record', lPin45, lPinT)
		lPin45 = lPinT
	if GPIO.event_detected("GPIO1_14"):
		lPinT = GPIO.input("GPIO1_14")
		sendChange('Clear', lPin46, lPinT)
		lPin46 = lPinT
    if GPIO.event_detected("GPIO1_15"):                   
		lPinT = GPIO.input("GPIO1_15")
                sendChange('Timing', lPin47, lPinT)
		lPin47 = lPinT

    if GPIO.event_detected("GPIO1_16"):                   
		lPinT = GPIO.input("GPIO1_16")
                sendChange('Bank', lPin48, lPinT)
		lPin48 = lPinT
    if GPIO.event_detected("GPIO1_17"):                   
		lPinT = GPIO.input("GPIO1_17")
                sendChange('A', lPin49, lPinT)
		lPin49 = lPinT
	if GPIO.event_detected("GPIO1_18"):
		lPinT = GPIO.input("GPIO1_18")
				sendChange('B', lPin50, lPinT)
		lPin50 = lPinT
    if GPIO.event_detected("GPIO1_19"):                   
		lPinT = GPIO.input("GPIO1_19")
                sendChange('C', lPin51, lPinT)
		lPin49 = lPinT
	if GPIO.event_detected("GPIO1_20"):
		lPinT = GPIO.input("GPIO1_20")
				sendChange('D', lPin52, lPinT)
		lPin50 = lPinT

##Won't ever get here except if error
s.close()

Pure Data File

Plain text
This is the Pure Data file that contains the composition program and can get inputs from the BeagleBone Black. After this file is opened, run the python file. SAVE THIS AS A .pd FILE.
#N canvas 18 69 1351 677 10;
#X obj 116 121 tgl 25 0 empty empty empty 17 7 0 25 -262144 -1 -1 0
1;
#X obj 464 331 *;
#X obj 512 327 samplerate~;
#X obj 464 307 t f b;
#N canvas 163 142 813 514 set 0;
#X obj 314 83 inlet;
#X obj 314 205 realtime;
#X obj 314 109 t b b;
#X obj 153 201 +;
#X obj 126 200 f;
#X msg 153 159 1;
#X obj 359 178 spigot;
#X obj 126 285 > 1;
#X obj 618 330 sel 1;
#X msg 126 159 0;
#X obj 642 195 metro 1;
#X obj 642 251 +;
#X obj 618 251 f;
#X msg 642 215 1;
#X msg 614 215 0;
#X obj 172 255 sel 1;
#X obj 642 170 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
1;
#X obj 314 350 +;
#X obj 288 350 f;
#X obj 618 282 spigot;
#X obj 288 402 /;
#X obj 199 361 - 1;
#X obj 315 485 outlet;
#X obj 275 86 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
-1;
#X floatatom 341 463 12 0 0 0 - - -;
#X obj 126 306 s TAP_not_first;
#X obj 392 156 r TAP_not_first;
#X obj 671 253 r TAP_not_first;
#X obj 172 277 s TAP_start;
#X obj 642 148 r TAP_start;
#X obj 288 439 sel 0;
#X obj 618 311 > 3000;
#X obj 618 351 s TAP_finish;
#X obj 614 125 r TAP_finish;
#X obj 126 136 r TAP_finish;
#X msg 288 329 0;
#X obj 288 306 r TAP_finish;
#X text 500 24 A timer gets triggered and reset every time;
#X text 499 38 the inlet receives a bang. If nothing happens;
#X text 498 53 afterwards for 3000 ms \, the "tempo" is considered
;
#X text 498 68 definitive \, and everything gets reset.;
#X text 498 90 comment;
#X text 12 189 Count the number;
#X text 12 201 of bangs received.;
#X text 385 199 Measure the time between;
#X text 385 212 two bangs (making sure that;
#X text 386 223 we only start measuring at;
#X text 386 235 the second bang).;
#X text 358 342 Sum the measured durations...;
#X text 336 403 ... and divide it by the number of bangs-1;
#X text 336 417 to get the average time between 2 bangs.;
#X text 558 490 Pierre Massat \, Guitar Extended \, 2013;
#X text 6 2 "Tap tempo" : we don't measure the tempo;
#X text 5 16 here \, but the duration between 2 beats (bangs).;
#X text 5 30 This is used later to set the length of the loop.;
#X connect 0 0 2 0;
#X connect 1 0 17 0;
#X connect 2 0 1 0;
#X connect 2 1 5 0;
#X connect 2 1 6 0;
#X connect 2 1 14 0;
#X connect 3 0 4 0;
#X connect 4 0 3 1;
#X connect 4 0 7 0;
#X connect 4 0 21 0;
#X connect 4 0 15 0;
#X connect 5 0 3 0;
#X connect 6 0 1 1;
#X connect 7 0 25 0;
#X connect 8 0 32 0;
#X connect 9 0 4 0;
#X connect 10 0 13 0;
#X connect 11 0 12 0;
#X connect 12 0 11 1;
#X connect 12 0 19 0;
#X connect 13 0 11 0;
#X connect 14 0 12 0;
#X connect 15 0 28 0;
#X connect 16 0 10 0;
#X connect 17 0 18 0;
#X connect 18 0 17 1;
#X connect 18 0 20 0;
#X connect 19 0 31 0;
#X connect 20 0 30 0;
#X connect 21 0 20 1;
#X connect 23 0 2 0;
#X connect 26 0 6 1;
#X connect 27 0 19 1;
#X connect 29 0 16 0;
#X connect 30 1 22 0;
#X connect 30 1 24 0;
#X connect 31 0 8 0;
#X connect 33 0 16 0;
#X connect 34 0 9 0;
#X connect 35 0 18 0;
#X connect 36 0 35 0;
#X restore 464 179 pd set tempo;
#X obj 464 147 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X obj 64 344 r~ main_out;
#X obj 64 375 +~;
#X obj 80 244 *~;
#X obj 116 226 line~;
#X obj 116 207 pack 0 5;
#X obj 486 441 r loop_on_off;
#X text 109 93 Record;
#N canvas 330 257 450 300 into_blocks 0;
#X obj 173 101 % 64;
#X obj 146 81 t f f;
#X obj 146 121 -;
#X obj 146 59 int;
#X obj 146 17 inlet;
#X obj 146 167 outlet;
#X text 238 67 to be a multiple of the block size.;
#X text 238 49 Force the length of the tables;
#X text 134 261 Pierre Massat \, Guitar extended \, 2013;
#X connect 0 0 2 1;
#X connect 1 0 2 0;
#X connect 1 1 0 0;
#X connect 2 0 5 0;
#X connect 3 0 1 0;
#X connect 4 0 3 0;
#X restore 464 353 pd into_blocks;
#X obj 464 378 s new_length;
#X obj 761 267 r new_length;
#X obj 565 597 spigot;
#X obj 486 501 sel 1 0;
#X msg 486 534 bang;
#X msg 520 533 stop;
#X obj 484 613 s~ main_out;
#X obj 761 337 r empty_table;
#X obj 781 170 t f f;
#X obj 781 214 s loop_on_off;
#X obj 486 481 t f f;
#X obj 585 576 s stop_looping;
#X obj 132 394 r stop_looping;
#X obj 565 626 s new_cycle;
#X obj 151 415 r new_cycle;
#X obj 64 436 tabwrite~ bande;
#X obj 484 575 tabplay~ bande;
#X obj 761 409 table bande;
#N canvas 185 178 252 185 limiter 0;
#X obj 24 22 inlet~;
#X msg 121 85 limit 100;
#X obj 121 21 loadbang;
#X obj 24 60 z~ 64;
#X obj 24 169 *~;
#X obj 24 200 outlet~;
#X obj 121 113 limiter~ 10;
#X connect 0 0 3 0;
#X connect 0 0 6 0;
#X connect 1 0 6 0;
#X connect 2 0 1 0;
#X connect 3 0 4 0;
#X connect 4 0 5 0;
#X connect 6 0 4 1;
#X restore 81 294 pd limiter;
#X obj 611 148 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X obj 611 224 s empty_table;
#X text 452 104 Tempo;
#X obj 486 461 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
1;
#X text 110 671 Pierre Massat \, Guitar Extended \, 2013;
#X text 568 521 Here we play the table (loop) continuously.;
#X text 315 169 Get the duration between;
#X text 315 181 two beats...;
#X text 316 213 ... divide it by 4 (we;
#X text 316 226 assume that there are 4;
#X text 316 238 beats in a bar)...;
#X text 294 298 ...and compute the length;
#X text 294 312 of the table in samples \, as;
#X text 295 339 block size.;
#X text 295 327 a multiple of the;
#X obj 781 150 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
1;
#X obj 80 174 adc~;
#X obj 372 640 dac~;
#X msg 455 441 1;
#X obj 611 168 t b b;
#X obj 611 191 del 2;
#X text 752 101 Audio On/Off;
#X text 601 104 Clear;
#X obj 455 417 r new_length;
#N canvas 185 178 252 185 limiter 0;
#X obj 24 22 inlet~;
#X msg 121 85 limit 100;
#X obj 121 21 loadbang;
#X obj 24 60 z~ 64;
#X obj 24 169 *~;
#X obj 24 200 outlet~;
#X obj 121 113 limiter~ 10;
#X connect 0 0 3 0;
#X connect 0 0 6 0;
#X connect 1 0 6 0;
#X connect 2 0 1 0;
#X connect 3 0 4 0;
#X connect 4 0 5 0;
#X connect 6 0 4 1;
#X restore 371 616 pd limiter;
#X obj 808 195 switch~;
#X msg 761 359 \; bande const 0 \;;
#X msg 761 287 \; bande resize \$1 \;;
#X obj 372 596 *~;
#X obj 387 575 line~;
#X obj 387 555 pack 0 5;
#X obj 371 506 r~ main_out;
#X obj 387 534 r vol-output;
#X obj 757 236 s vol-output;
#X text 110 685 Modified by Tyler Worman tsworman@novaslp.net for BeagleBone
;
#X obj 231 9 netreceive 13131;
#X obj 146 40 print TCP;
#X floatatom 315 41 5 0 0 0 - - -;
#X text 350 41 Number of Active Connections;
#X floatatom 244 112 5 0 0 0 - - -;
#X floatatom 286 112 5 0 0 0 - - -;
#X floatatom 323 112 5 0 0 0 - - -;
#X floatatom 360 112 5 0 0 0 - - -;
#X obj 139 477 loadbang;
#X msg 127 589 \; pd dsp 1;
#X obj 154 525 delay 200;
#X obj 245 66 route Record Play Timing Clear Bank Key;
#X floatatom 291 146 5 0 0 0 - - -;
#X floatatom 239 146 5 0 0 0 - - -;
#X obj 660 299 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X msg 701 305 0;
#X obj 653 341 f;
#X obj 653 369 + 1;
#X floatatom 651 418 5 0 0 0 - - -;
#X obj 647 493 readsf~;
#X obj 726 557 dac~;
#X obj 865 553 readsf~;
#X obj 665 649 readsf~;
#X obj 798 648 readsf~;
#X obj 934 649 readsf~;
#X obj 1068 648 readsf~;
#X obj 1203 647 readsf~;
#X obj 464 285 / 1000;
#X msg 647 473 open 1.wav \, 1;
#X obj 1095 360 readsf~;
#X obj 1214 358 readsf~;
#X obj 1085 202 readsf~;
#X obj 992 538 readsf~;
#X obj 985 169 readsf~;
#X obj 1222 551 readsf~;
#X msg 665 621 open 1a.wav \, 1;
#X msg 798 626 open 2.wav \, 1;
#X msg 934 626 open 2a.wav \, 1;
#X msg 1068 626 open 3.wav \, 1;
#X msg 1203 627 open 3a.wav \, 1;
#X msg 866 523 open 4.wav \, 1;
#X msg 1223 521 open 4a.wav \, 1;
#X msg 1215 328 open 5.wav \, 1;
#X msg 993 508 open 5a.wav \, 1;
#X msg 1086 172 open 6.wav \, 1;
#X msg 986 139 open 6a.wav \, 1;
#X msg 1096 330 open 7.wav \, 1;
#X obj 1045 292 readsf~;
#X obj 1148 294 readsf~;
#X obj 1243 293 readsf~;
#X obj 931 503 readsf~;
#X obj 996 602 readsf~;
#X obj 1121 550 readsf~;
#X msg 1046 262 open 7a.wav \, 1;
#X msg 1149 264 open 8.wav \, 1;
#X msg 1244 263 open 8a.wav \, 1;
#X msg 932 473 open 9.wav \, 1;
#X obj 652 393 mod 21;
#X obj 464 241 * 2;
#X msg 997 572 open 9a.wav \, 1;
#X msg 1122 520 open 10.wav \, 1;
#X obj 1102 480 readsf~;
#X msg 1103 450 open 10a.wav \, 1;
#X obj 654 444 route 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
20;
#X obj 178 129 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
1;
#X connect 0 0 10 0;
#X connect 1 0 13 0;
#X connect 2 0 1 1;
#X connect 3 0 1 0;
#X connect 3 1 2 0;
#X connect 4 0 126 0;
#X connect 5 0 4 0;
#X connect 6 0 7 0;
#X connect 7 0 29 0;
#X connect 8 0 32 0;
#X connect 9 0 8 1;
#X connect 10 0 9 0;
#X connect 11 0 36 0;
#X connect 13 0 14 0;
#X connect 15 0 60 0;
#X connect 16 0 27 0;
#X connect 16 0 30 0;
#X connect 17 0 18 0;
#X connect 17 1 19 0;
#X connect 18 0 27 0;
#X connect 18 0 30 0;
#X connect 19 0 25 0;
#X connect 19 0 30 0;
#X connect 21 0 59 0;
#X connect 22 0 23 0;
#X connect 22 0 66 0;
#X connect 22 1 58 0;
#X connect 24 0 17 0;
#X connect 24 1 16 1;
#X connect 26 0 29 0;
#X connect 28 0 29 0;
#X connect 30 0 20 0;
#X connect 30 1 16 0;
#X connect 32 0 7 1;
#X connect 33 0 52 0;
#X connect 36 0 24 0;
#X connect 48 0 22 0;
#X connect 49 0 8 0;
#X connect 51 0 36 0;
#X connect 52 0 53 0;
#X connect 52 1 34 0;
#X connect 53 0 34 0;
#X connect 56 0 51 0;
#X connect 57 0 50 0;
#X connect 57 0 50 1;
#X connect 61 0 57 0;
#X connect 62 0 61 1;
#X connect 63 0 62 0;
#X connect 64 0 61 0;
#X connect 65 0 63 0;
#X connect 68 0 69 0;
#X connect 68 0 79 0;
#X connect 68 1 70 0;
#X connect 76 0 78 0;
#X connect 78 0 77 0;
#X connect 79 0 72 0;
#X connect 79 0 0 0;
#X connect 79 1 73 0;
#X connect 79 1 132 0;
#X connect 79 2 74 0;
#X connect 79 2 4 0;
#X connect 79 3 75 0;
#X connect 79 3 52 0;
#X connect 79 4 81 0;
#X connect 79 4 82 0;
#X connect 79 5 80 0;
#X connect 82 0 84 0;
#X connect 83 0 84 1;
#X connect 84 0 85 0;
#X connect 85 0 84 1;
#X connect 85 0 125 0;
#X connect 86 0 131 0;
#X connect 87 0 32 0;
#X connect 89 0 32 0;
#X connect 90 0 32 0;
#X connect 91 0 32 0;
#X connect 92 0 32 0;
#X connect 93 0 32 0;
#X connect 94 0 32 0;
#X connect 95 0 3 0;
#X connect 96 0 87 0;
#X connect 97 0 32 0;
#X connect 98 0 32 0;
#X connect 99 0 32 0;
#X connect 100 0 32 0;
#X connect 101 0 32 0;
#X connect 102 0 32 0;
#X connect 103 0 90 0;
#X connect 104 0 91 0;
#X connect 105 0 92 0;
#X connect 106 0 93 0;
#X connect 107 0 94 0;
#X connect 108 0 89 0;
#X connect 109 0 102 0;
#X connect 110 0 98 0;
#X connect 111 0 100 0;
#X connect 112 0 99 0;
#X connect 113 0 101 0;
#X connect 114 0 97 0;
#X connect 115 0 32 0;
#X connect 116 0 32 0;
#X connect 117 0 32 0;
#X connect 118 0 32 0;
#X connect 119 0 32 0;
#X connect 120 0 32 0;
#X connect 121 0 115 0;
#X connect 122 0 116 0;
#X connect 123 0 117 0;
#X connect 124 0 118 0;
#X connect 125 0 86 0;
#X connect 126 0 95 0;
#X connect 127 0 119 0;
#X connect 128 0 120 0;
#X connect 129 0 32 0;
#X connect 130 0 129 0;
#X connect 131 0 96 0;
#X connect 131 1 103 0;
#X connect 131 2 104 0;
#X connect 131 3 105 0;
#X connect 131 4 106 0;
#X connect 131 5 107 0;
#X connect 131 6 108 0;
#X connect 131 7 109 0;
#X connect 131 8 110 0;
#X connect 131 9 111 0;
#X connect 131 10 112 0;
#X connect 131 11 113 0;
#X connect 131 12 114 0;
#X connect 131 13 121 0;
#X connect 131 14 122 0;
#X connect 131 15 123 0;
#X connect 131 16 124 0;
#X connect 131 17 127 0;
#X connect 131 18 128 0;
#X connect 131 19 130 0;

Credits

Sumita Ghosh

Sumita Ghosh

1 project • 8 followers
Berkeley student interning at TI
Ashish Joshi

Ashish Joshi

1 project • 4 followers
Masters Student in Electrical Engineering at Texas Tech University
Jeff Mueller

Jeff Mueller

1 project • 4 followers

Comments