Code
Download Code as zipWe named the files such that the tones order from left to right, from bottom row (i.e.Z-M, A-L, Q-P on a Western QWERTY keyboard) would correspond to F3-F♯5 and A = 1.wav, B = 2.wav, etc. For instance, Z plays F3, which is saved in 26.wav, since Z is the 26th letter in the alphabet. In order to use the one-octave piano keyboard with this same naming of files, the following key-to-sound file mapping was used:
controller.py
#
# Yiming Song(ys765), Stephanie Chan(spc87)
# Final Project
# 5/19/17
# Set up menus and user interfaces and save tracks
#
import pygame
import os
import time
import pickle
from pygame.locals import *
import subprocess
import signal
os.putenv('SDL_VIDEODRIVER', 'fbcon') # Display on piTFT
os.putenv('SDL_FBDEV', '/dev/fb1')
os.putenv('SDL_MOUSEDRV', 'TSLIB') # Track mouse clicks on piTFT
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')
def main():
# Initialize pygame music mixer
pygame.mixer.pre_init(44100,-16,2,2048)
pygame.mixer.init()
pygame.init()
pygame.mouse.set_visible(False)
# Colors for text and buttons
BLACK = (0,0,0)
WHITE = (255,255,255)
GLOW = (255,255,255,215)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# Coordinates of drum pad touch indicator
x_cube = -100
y_cube = -100
state = 0 # 0 = mode selection, 1 = track mode, 2 = piano mode, 3 = drum mode
recorded = [0,0,0,0] # Keep track of whether track has been recorded
selected = [0,0,0,0] # Keep track of whether track is selected to play
filename = "keysound/"
keysound = ['1.wav','2.wav','3.wav','4.wav','5.wav','6.wav','7.wav','8.wav','9.wav',
'10.wav','11.wav','12.wav','13.wav','14.wav','15.wav','16.wav','17.wav','18.wav',
'19.wav','20.wav','21.wav','22.wav','23.wav','24.wav','25.wav','26.wav']
screen = pygame.display.set_mode((320, 240))
piano_keyboard_or = pygame.image.load("piano_keyboard.png") # Image of keyboard
piano_keyboard = pygame.transform.scale(piano_keyboard_or,(320,120))
pianorect = piano_keyboard.get_rect()
pianorect = pianorect.move(0,120)
drum_keyboard_or = pygame.image.load("drum_keyboard.png") # Image of drumpad
drum_keyboard = pygame.transform.scale(drum_keyboard_or,(320,120))
drumrect = drum_keyboard.get_rect()
drumrect = drumrect.move(0,120)
mode_buttons = {'Piano Mode':(160,40),'Drum Mode':(160,120),'Track Mode':(160,200)}
# Buttons on main menu
name = ""
font = pygame.font.Font(None, 20)
subtrack = [] # Track currently being recorded
start_time = 0
while True:
for evt in pygame.event.get():
if evt.type == KEYDOWN: # Computer keyboard interface
if evt.unicode.isalpha():
key = filename + keysound[ord(evt.unicode)-97]
pygame.mixer.music.load(key)
pygame.mixer.music.play(0)
name += evt.unicode
key_time = time.time() - start_time
start_time = time.time()
subtrack += [(key, key_time)]
elif(evt.type is MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
if state == 3: # Drum pad indicator animation
if y > 120 and y < 160 and x > 0 and x < 64:
x_cube, y_cube = 0, 120
elif y > 160 and y < 194 and x > 0 and x < 64:
x_cube, y_cube = 0, 160
elif y > 194 and y < 240 and x > 0 and x < 64:
x_cube, y_cube = 0, 194
elif y > 120 and y < 160 and x > 64 and x < 128:
x_cube, y_cube = 64, 120
elif y > 160 and y < 194 and x > 64 and x < 128:
x_cube, y_cube = 64, 160
elif y > 194 and y < 240 and x > 64 and x < 128:
x_cube, y_cube = 64, 194
elif y > 120 and y < 160 and x > 128 and x < 196:
x_cube, y_cube = 128, 120
elif y > 160 and y < 194 and x > 128 and x < 196:
x_cube, y_cube = 128, 160
elif y > 194 and y < 240 and x > 128 and x < 196:
x_cube, y_cube = 128, 194
elif y > 120 and y < 160 and x > 196 and x < 256:
x_cube, y_cube = 192, 120
elif y > 160 and y < 194 and x > 196 and x < 256:
x_cube, y_cube = 192, 160
elif y > 194 and y < 240 and x > 196 and x < 256:
x_cube, y_cube = 192, 194
elif y > 120 and y < 160 and x > 256 and x < 320:
x_cube, y_cube = 256, 120
elif y > 160 and y < 194 and x > 256 and x < 320:
x_cube, y_cube = 256, 160
elif y > 194 and y < 240 and x > 256 and x < 320:
x_cube, y_cube = 256, 194
elif(evt.type is MOUSEBUTTONUP):
pos = pygame.mouse.get_pos()
x,y = pos
if state == 0: # Mode select menu
if y > 20 and y < 60 and x > 120 and x < 200:
state = 2
elif y > 100 and y < 140 and x > 120 and x < 200:
state = 3
elif y > 180 and y < 220 and x > 120 and x < 200:
state = 1
elif state == 1: # Track mode
if y > 20 and y < 60 and x > 240 and x < 280: # Quit
for line in os.popen("ps ax | grep track | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
filelist = [ f for f in os.listdir("tracks")]
for f in filelist:
os.remove("tracks/"+f)
quit()
if y > 20 and y < 60 and x > 110 and x < 150: # Save
i = 1
if len(subtrack) != 0:
while i < 5 :
if ("track%i.txt" % i) in os.listdir("tracks"):
i += 1
else:
if i != 1:
k, t = subtrack[0]
subtrack[0] = (k, t-1)
with open(("tracks/track%i.txt" % i), "wb") as file_track:
pickle.dump(subtrack, file_track)
print 'track saved'
recorded[i-1] = 1
selected[i-1] = 1
subtrack = []
break
if i >= 5:
print 'tracks are full'
else:
print "track empty"
if y > 20 and y < 60 and x > 40 and x < 80: # Start
print 'starting'
for line in os.popen("ps ax | grep track | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
start_time = time.time()
subtrack = []
for i in range (4):
if selected[i] == 1:
subprocess.Popen(["python", "trackplay.py", str(i+1)])
if y > 20 and y < 60 and x > 160 and x < 200: # Mode
state = 0
name = ""
if y > 170 and y < 200 and x > 20 and x < 60: # Track 1
if not "track1.txt" in os.listdir("tracks"):
print 'track1 empty'
else:
selected[0] = 1 if selected[0] == 0 else 0
if y > 210 and y < 240 and x > 20 and x < 60:
if not "track1.txt" in os.listdir("tracks"):
print 'track1 empty, nothing to delete'
else:
recorded[0] = 0
os.remove("tracks/track1.txt")
if y > 170 and y < 200 and x > 80 and x < 120: # Track 2
if not "track2.txt" in os.listdir("tracks"):
print 'track2 empty'
else:
selected[1] = 1 if selected[1] == 0 else 0
if y > 210 and y < 240 and x > 80 and x < 120:
if not "track2.txt" in os.listdir("tracks"):
print 'track2 empty, nothing to delete'
else:
recorded[1] = 0
os.remove("tracks/track2.txt")
if y > 170 and y < 200 and x > 140 and x < 180: # Track 3
if not "track3.txt" in os.listdir("tracks"):
print 'track3 empty'
else:
selected[2] = 1 if selected[2] == 0 else 0
if y > 210 and y < 240 and x > 140 and x < 180:
if not "track3.txt" in os.listdir("tracks"):
print 'track3 empty, nothing to delete'
else:
recorded[2] = 0
os.remove("tracks/track3.txt")
if y > 170 and y < 200 and x > 200 and x < 240: # Track 4
if not "track4.txt" in os.listdir("tracks"):
print 'track4 empty'
else:
selected[3] = 1 if selected[3] == 0 else 0
if y > 210 and y < 240 and x > 200 and x < 240:
if not "track4.txt" in os.listdir("tracks"):
print 'track4 empty, nothing to delete'
else:
recorded[3] = 0
os.remove("tracks/track4.txt")
if y > 120 and y < 160 and x > 260 and x < 300: # Play
for i in range (4):
if selected[i] == 1:
subprocess.Popen(["python", "trackplay.py", str(i+1)])
if y > 180 and y < 220 and x > 260 and x < 300: # Stop
for line in os.popen("ps ax 2>/dev/null | grep track | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
elif state == 2: # Piano mode
if y > 20 and y < 60 and x > 240 and x < 280: # Quit
for line in os.popen("ps ax | grep track | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
filelist = [ f for f in os.listdir("tracks")]
for f in filelist:
os.remove("tracks/"+f)
quit()
if y > 20 and y < 60 and x > 110 and x < 150: # Save
i = 1
if len(subtrack) != 0:
while i < 5 :
if ("track%i.txt" % i) in os.listdir("tracks"):
i += 1
else:
if i != 1:
k, t = subtrack[0]
subtrack[0] = (k, t-1)
with open(("tracks/track%i.txt" % i), "wb") as file_track:
pickle.dump(subtrack, file_track)
print 'track saved'
recorded[i-1] = 1
selected[i-1] = 1
subtrack = []
break
if i >= 5:
print 'tracks are full'
if y > 20 and y < 60 and x > 40 and x < 80: # Start
print 'starting'
start_time = time.time()
subtrack = []
for i in range (4):
if selected[i] == 1:
subprocess.Popen(["python", "trackplay.py", str(i+1)])
if y > 20 and y < 60 and x > 160 and x < 200: # Mode
state = 0
name = ""
key_no = 0 # Key encodings
if y > 193 and y < 240 and x > 0 and x < 46:
key_no = 1
elif y > 120 and y < 193 and x > 33 and x < 59:
key_no = 19
elif y > 193 and y < 240 and x > 46 and x < 92:
key_no = 4
elif y > 120 and y < 193 and x > 80 and x < 103:
key_no = 6
elif y > 193 and y < 240 and x > 92 and x < 138:
key_no = 7
elif y > 193 and y < 240 and x > 138 and x < 182:
key_no = 8
elif y > 120 and y < 193 and x > 170 and x < 195:
key_no = 10
elif y > 193 and y < 240 and x > 182 and x < 228:
key_no = 11
elif y > 120 and y < 193 and x > 215 and x < 241:
key_no = 12
elif y > 193 and y < 240 and x > 228 and x < 273:
key_no = 17
elif y > 120 and y < 193 and x > 260 and x < 288:
key_no = 23
elif y > 193 and y < 240 and x > 273 and x < 320:
key_no = 5
if key_no != 0:
key = "keysound/" + str(key_no) + ".wav"
pygame.mixer.music.load(key)
pygame.mixer.music.play(0)
key_time = time.time() - start_time
start_time = time.time()
subtrack += [(key, key_time)]
elif state == 3: # Drum mode
if y > 20 and y < 60 and x > 240 and x < 280: # Quit
for line in os.popen("ps ax | grep track | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
filelist = [ f for f in os.listdir("tracks")]
for f in filelist:
os.remove("tracks/"+f)
quit()
if y > 20 and y < 60 and x > 110 and x < 150: # Save
i = 1
if len(subtrack) != 0:
while i < 5 :
if ("track%i.txt" % i) in os.listdir("tracks"):
i += 1
else:
if i != 1:
k, t = subtrack[0]
subtrack[0] = (k, t-1)
with open(("tracks/track%i.txt" % i), "wb") as file_track:
pickle.dump(subtrack, file_track)
print 'track saved'
recorded[i-1] = 1
selected[i-1] = 1
subtrack = []
break
if i >= 5:
print 'tracks are full'
if y > 20 and y < 60 and x > 40 and x < 80: # Start
print 'starting'
start_time = time.time()
subtrack = []
for i in range (4):
if selected[i] == 1:
subprocess.Popen(["python", "trackplay.py", str(i+1)])
if y > 20 and y < 60 and x > 160 and x < 200: # Mode
state = 0
name = ""
key_no = 0 # Drum pad encodings
if y > 120 and y < 160 and x > 0 and x < 64:
key_no = 24
elif y > 160 and y < 194 and x > 0 and x < 64:
key_no = 22
elif y > 194 and y < 240 and x > 0 and x < 64:
key_no = 14
elif y > 120 and y < 160 and x > 64 and x < 128:
key_no = 11
elif y > 160 and y < 194 and x > 64 and x < 128:
key_no = 21
elif y > 194 and y < 240 and x > 64 and x < 128:
key_no = 17
elif y > 120 and y < 160 and x > 128 and x < 196:
key_no = 23
elif y > 160 and y < 194 and x > 128 and x < 196:
key_no = 20
elif y > 194 and y < 240 and x > 128 and x < 196:
key_no = 25
elif y > 120 and y < 160 and x > 196 and x < 256:
key_no = 16
elif y > 160 and y < 194 and x > 196 and x < 256:
key_no = 9
elif y > 194 and y < 240 and x > 196 and x < 256:
key_no = 1
elif y > 120 and y < 160 and x > 256 and x < 320:
key_no = 12
elif y > 160 and y < 194 and x > 256 and x < 320:
key_no = 18
elif y > 194 and y < 240 and x > 256 and x < 320:
key_no = 2
x_cube, y_cube = -100, -100
if key_no != 0:
key = "drumsound/" + str(key_no) + ".wav"
pygame.mixer.music.load(key)
pygame.mixer.music.play(0)
key_time = time.time() - start_time
start_time = time.time()
subtrack += [(key, key_time)]
screen.fill ((0, 0, 0)) # Clear screen every time
block = font.render(name, True, (255, 255, 255))
rect = block.get_rect()
rect.center = (160, 80)
screen.blit(block, rect)
if state == 0: # Mode select menu
for my_text, text_pos in mode_buttons.items():
text_surface = font.render(my_text, True, WHITE)
rect = text_surface.get_rect(center=text_pos)
screen.blit(text_surface, rect)
if state == 1: # Track mode
for i in range(4):
if recorded[i] == 1
text_color = GREEN # Track text green if recorded
else:
text_color = WHITE
text_surface = font.render('Track'+str(i+1), True, text_color)
rect = text_surface.get_rect(center=(40+60*i, 160))
screen.blit(text_surface, rect)
if recorded[i] == 1:
if selected[i] == 1: #Select
my_text = 'Deselect'+str(i+1)
else:
my_text = 'Select'+str(i+1)
text_surface = font.render(my_text, True, WHITE)
rect = text_surface.get_rect(center=(40+60*i, 190))
screen.blit(text_surface, rect)
text_surface = font.render('Delete'+str(i+1), True, WHITE) #Delete
rect = text_surface.get_rect(center=(40+60*i, 220))
screen.blit(text_surface, rect)
text_surface = font.render('Play', True, WHITE)
circle = pygame.draw.circle(screen, GREEN, (280, 140), 20, 0)
rect = text_surface.get_rect(center=(280, 140))
screen.blit(text_surface, rect)
text_surface = font.render('Stop', True, WHITE)
circle = pygame.draw.circle(screen, RED, (280, 200), 20, 0)
rect = text_surface.get_rect(center=(280, 200))
screen.blit(text_surface, rect)
if state == 1 or state == 2 or state == 3: # Save, Start, Mode, Quit buttons
text_surface = font.render('Save', True, WHITE)
circle = pygame.draw.circle(screen, GREEN, (130, 40), 20, 0)
rect = text_surface.get_rect(center=(130, 40))
screen.blit(text_surface, rect)
text_surface = font.render('Quit', True, WHITE)
circle = pygame.draw.circle(screen, RED, (260, 40), 20, 0)
rect = text_surface.get_rect(center=(260, 40))
screen.blit(text_surface, rect)
text_surface = font.render('Start', True, WHITE)
circle = pygame.draw.circle(screen, GREEN, (60, 40), 20, 0)
rect = text_surface.get_rect(center=(60, 40))
screen.blit(text_surface, rect)
text_surface = font.render('Mode', True, WHITE)
circle = pygame.draw.circle(screen, GREEN, (190, 40), 20, 0)
rect = text_surface.get_rect(center=(190, 40))
screen.blit(text_surface, rect)
if state == 2: # Piano mode
screen.blit(piano_keyboard,pianorect)
if state == 3: # Drum mode
screen.blit(drum_keyboard,drumrect)
cube = pygame.Surface((65, 40))
cube.set_alpha(120)
cube.fill((255, 255, 255))
screen.blit(cube, (x_cube,y_cube-3))
pygame.display.flip()
if __name__ == "__main__":
main()
trackplay.py
#
# Yiming Song(ys765), Stephanie Chan(spc87)
# Final Project
# 5/19/17
# Play recorded track from trackn.txt file where n = integer command line argument
#
import pygame
import os
import time
import pickle
from pygame.locals import *
import sys
# Initialize pygame music mixer
pygame.mixer.pre_init(44100,-16,2,2048)
pygame.mixer.init()
pygame.init()
# Array of keyboard sound files
keysound = ['1.wav','2.wav','3.wav','4.wav','5.wav','6.wav','7.wav','8.wav','9.wav',
'10.wav','11.wav','12.wav','13.wav','14.wav','15.wav','16.wav','17.wav','18.wav',
'19.wav','20.wav','21.wav','22.wav','23.wav','24.wav','25.wav','26.wav']
# Pass in track number as argument
track_no = int(sys.argv[1])
if ("track%i.txt" % track_no) in os.listdir("tracks"):
track = []
with open(("tracks/track%i.txt" % track_no),"rb") as file_track:
track = pickle.load(file_track)
for i in range(len(track)):
key, key_time = track[i]
time.sleep(key_time)
pygame.mixer.music.load(key) # Load sound file
pygame.mixer.music.play(0) # Play sound file
time.sleep(1)