from pygame import mixer import time import digitalio import board import adafruit_matrixkeypad import pygame from PIL import Image, ImageDraw, ImageFont import adafruit_rgb_display.st7789 as st7789 import digitalio import board from adafruit_rgb_display.rgb import color565 import adafruit_rgb_display.st7789 as st7789 import lib.FSM from itertools import cycle import lib.observer from scikits.samplerate import resample from lib.configobj import ConfigObj #from configobj import ConfigObj cols = [digitalio.DigitalInOut(x) for x in (board.D21, board.D20, board.D16, board.D12)] rows = [digitalio.DigitalInOut(x) for x in (board.D26, board.D13, board.D6, board.D5)] # keys = ((3, 2, 1, 0), # (7, 6, 5, 4), # (11, 10, 9, 8), # (15, 14, 13, 12)) keys = ((15, 14, 13, 12), (11, 10, 9, 8), (7, 6, 5, 4), (3, 2, 1, 0)) keypad = adafruit_matrixkeypad.Matrix_Keypad(rows, cols, keys) # while True: # keys = keypad.pressed_keys # if keys: # print("Pressed: ", keys) # time.sleep(0.1) pygame.init() mixer.init() done = False clock = pygame.time.Clock() TIMER = pygame.USEREVENT + 1 #pygame.time.set_timer(pygame.USEREVENT + 1, 444) #pygame.time.set_timer(TIMER, 161) playhead = 0 timer = pygame.time.get_ticks start = now = timer() cs_pin = digitalio.DigitalInOut(board.CE0) dc_pin = digitalio.DigitalInOut(board.D25) reset_pin = None BAUDRATE = 64000000 # The pi can be very fast! # display = st7789.ST7789( # board.SPI(), # cs=cs_pin, # dc=dc_pin, # rst=reset_pin, # baudrate=BAUDRATE, # width=135, # height=240, # x_offset=53, # y_offset=40, # ) backlight = digitalio.DigitalInOut(board.D22) backlight.switch_to_output() backlight.value = True buttonA = digitalio.DigitalInOut(board.D23) buttonB = digitalio.DigitalInOut(board.D24) buttonA.switch_to_input() buttonB.switch_to_input() spi = board.SPI() # Create blank image for drawing. # Make sure to create image with mode 'RGB' for full color. # Get drawing object to draw on image. # Draw a black filled box to clear the image. #draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) #disp.image(image, rotation) # Draw some shapes. # First define some constants to allow easy resizing of shapes. # Move left to right keeping track of the current x position for drawing shapes. x = 0 # Turn on the backlight backlight = digitalio.DigitalInOut(board.D22) backlight.switch_to_output() backlight.value = True class Prog: def __init__(self): self.FSM = FSM.ProgFSM(self) self.font = ImageFont.truetype("/home/pi/examples/HLM.ttf", 64) #self.h1 = ImageFont.truetype("/home/pi/examples/HLM.ttf", 26) self.h1 = ImageFont.truetype("/home/pi/Pixellari.ttf", 30) self.h2 = ImageFont.truetype("/home/pi/Pixellari.ttf", 20) self.h3 = ImageFont.truetype("/home/pi/Pixellari.ttf", 54) self.disp = st7789.ST7789( spi, cs=cs_pin, dc=dc_pin, rst=reset_pin, baudrate=BAUDRATE, width=135, height=240, x_offset=53, y_offset=40, ) self.height = self.disp.width # we swap height/width to rotate it to landscape! self.width = self.disp.height self.rotation = 270 self.padding = -2 self.top = self.padding self.bottom = self.height - self.padding self.image = Image.new("RGB", (self.width, self.height)) self.draw = ImageDraw.Draw(self.image) self.playhead = 0 self.playing = False self.bpm = 90 self.bpm_inc = 1 self.note_bank = 0 self.volume = 10 self.note_vol = 16 self.note_pitch = 0.0 self.soundSlots = [] self.keys = [] self.keyState = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #self.song = [2, 3, 0, 1, 0, 1, 0, 1, 0, 1] self.song = [0] self.songCycle = cycle(self.song) self.curPattern = next(self.songCycle) self.songStart = self.curPattern self.eSound = 0 self.ePattern = 0 self.black = "#000000" self.bg_color = "#336699" self.color_a = "#FFFFFF" self.color_b = "#929230" self.color_c = "#FFFF00" self.dark_grey = "#404040" self.grey = "#808080" self.light_grey = "#D3D3D3" self.blue = "#2A80D5" self.dark_blue = "#2A2AD5" self.pink = "#D52A80" self.red = "#D52A2A" self.olive = "#80D52A" self.green = "#2AD52A" self.pub = observer.Publisher(['beat', 'the joint']) self.song_file_name = "default.sng" self.config = ConfigObj("config.txt") self.config['title'] = "default" self.config['bpm'] = self.bpm self.config['volume'] = self.volume self.config.write() #self.save_song() class SoundSlot: def __init__(self, file, obj_id, o): self.file = file self.id = obj_id self.o = o self.mixerSound = pygame.mixer.Sound(self.file) self.pattern = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0] self.notes = self.init_notes() self.pitch = 0.0 self.volume = 16 def play(self, vol, pitch): # snd_array = pygame.sndarray.array(self.mixerSound) # snd_resample = resample(snd_array, 2.5, "sinc_fastest").astype(snd_array.dtype) # snd_out = pygame.sndarray.make_sound(snd_resample) # snd_out.play() if vol != 0: vol = (vol / 16) * (self.volume / 16) * (self.o.volume / 16) self.mixerSound.set_volume(vol) pygame.mixer.Sound.play(self.mixerSound) def init_notes(self): outp = [] _id = 0 while _id < 64: outp2 = [] _id2 = 0 while _id2 < 64: outp2.append([0,16,0]) _id2 += 1 outp.append(outp2) _id += 1 # for n in outp: # print('sound ', self.id, ' ', n) #print(outp) return outp def Execute(self): #print('doing the thing') #self.check_buttons() self.keys = keypad.pressed_keys #self.key_flip() self.update_keys() self.FSM.Execute() #self.disp.image(self.image, self.rotation) def update_keys(self): _id = 0 for k in self.keyState: if k == 0: if _id in self.keys: self.keyState[_id] = 1 elif k == 1: if _id in self.keys: self.keyState[_id] = 2 else: self.keyState[_id] = 3 elif k == 2: if _id in self.keys: self.keyState[_id] = 2 else: self.keyState[_id] = 3 else: self.keyState[_id] = 0 _id += 1 if buttonA.value == 0: #print('a on `', self.keyState[16]) if self.keyState[16] == 0: self.keyState[16] = 1 elif self.keyState[16] == 1: self.keyState[16] = 2 else: self.keyState[16] = 2 else: if self.keyState[16] == 3: self.keyState[16] = 4 elif self.keyState[16] == 2: self.keyState[16] = 3 elif self.keyState[16] == 4: self.keyState[16] = 0 if buttonB.value == 0: #print('a on `', self.keyState[16]) if self.keyState[17] == 0: self.keyState[17] = 1 elif self.keyState[17] == 1: self.keyState[17] = 2 else: self.keyState[17] = 2 else: if self.keyState[17] == 3: self.keyState[17] = 4 elif self.keyState[17] == 2: self.keyState[17] = 3 elif self.keyState[17] == 4: self.keyState[17] = 0 # if buttonB.value == 0: # #print('b on') # if self.keyState[17] == 0 or self.keyState[17] == 1: # self.keyState[17] += 1 # else: # if self.keyState[17] == 2: # self.keyState[17] += 1 # else: # self.keyState[17] = 0 def update_bpm(self): bpm = (60000 / self.bpm) / 4 pygame.time.set_timer(TIMER, int(bpm)) def start_playback(self): self.playing = True self.playhead = -1 self.curPattern = self.songStart bpm = (60000 / self.bpm) / 4 pygame.time.set_timer(TIMER, int(bpm)) def stop_playback(self): self.playing = False self.playhead = -1 self.curPattern = self.songStart pygame.time.set_timer(TIMER, 0) def center_text(self, text, font, width, y, color): w,h = font.getsize(text) self.draw.text(((width-w)/2,(y-h)/2), text, font=font, fill=color) def center_block(self, message, font, bounding_box, color): x1, y1, x2, y2 = bounding_box # For easy reading w, h = self.draw.textsize(message, font=font) x = (x2 - x1 - w)/2 + x1 y = (y2 - y1 - h)/2 + y1 self.draw.text((x, y), message, align='center', font=font, fill=color) def save_song(self): base_dir = "/home/pi/zpc_ct/user/songs/" #self.song_file_name #file1 = open(base_dir + self.song_file_name,"w")#write mode #file1.write("Tomorrow \n") #file1.close() self.config = ConfigObj(base_dir + self.song_file_name) self.config['title'] = "default" self.config['bpm'] = self.bpm self.config['volume'] = self.volume sounds = [] notes = [] for x in self.soundSlots: sounds.append(x.file) notes.append(x.notes) self.config['sounds'] = sounds self.config['notes'] = notes self.config['volumes'] = ['this', 'that', 'the other'] self.config.write() def load_song(self): base_dir = "/home/pi/zpc_ct/user/songs/" self.config = ConfigObj(base_dir + 'r100.sng') #self.config = ConfigObj(base_dir + self.song_file_name) #print('these sounds should get loaded ', self.config['sounds']) p = Prog() #while True: while not done: p.Execute() if pygame.event.get(pygame.USEREVENT + 1): p.playhead += 1 if p.playhead == 16: p.playhead = 0 #self.draw.text((0, 0), str(self.playhead), font=font, fill="#FFFFFF") #self.disp.image(self.image, self.rotation) p.curPattern = next(p.songCycle) print('pattern ', p.curPattern) p.pub.dispatch('beat', [p.curPattern, p.playhead]) #clock.tick(240) clock.tick_busy_loop() #print(clock.get_fps()) pygame.quit() # 1 main # 2 select pattern # 3 note edit # 6 song edit # 7 program load # 8 song load # 10 utility # 11 bpm+ # 12 bpm- # 13 play # 14 stop # 15 vol+ # 16 vol-