Code

Download Code as zip

We 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:

Bild

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)