123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
-
- import sys
- sys.path.insert(0, './lib')
- 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 FSM
- from itertools import cycle
- import observer
- from scikits.samplerate import resample
- from configobj import ConfigObj
- import ast
- import RPi.GPIO as GPIO
- from time import sleep
- import threading
- GPIO.setmode(GPIO.BCM)
-
- # set up GPIO output channel - internal led
- GPIO.setup(47, GPIO.OUT)
-
-
- 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 = ((15, 14, 13, 12),
- (11, 10, 9, 8),
- (7, 6, 5, 4),
- (3, 2, 1, 0))
-
-
- keypad = adafruit_matrixkeypad.Matrix_Keypad(rows, cols, keys)
-
- pygame.init()
- mixer.init()
- mixer.set_num_channels(32)
- done = False
- clock = pygame.time.Clock()
- TIMER = pygame.USEREVENT + 1
- 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!
- #BAUDRATE = 24000000
- # 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.pat_bank = 0
- self.volume = 10
- self.note_vol = 16
- self.note_pitch = 0.0
- self.soundSlots = []
- self.slots = []
- 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.patternClipboard = []
- self.soundClipboard = []
- 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 = "#336699"
- self.dark_blue = "#2A2AD5"
- self.pink = "#D52A80"
- self.red = "#D52A2A"
- self.olive = "#80D52A"
- self.green = "#2AD52A"
- self.notes_on = []
- self.clear_notes_on()
-
-
- self.pub = observer.Publisher(['beat', 'the joint'])
-
- #self.song_file_name = "default.sng"
- self.mconf = ConfigObj("config.txt")
- self.sconf = ConfigObj("/home/pi/zpc_ct/user/songs/" + self.mconf['default_song'])
- self.theme = ConfigObj("/home/pi/zpc_ct/themes/" + self.mconf['theme'] + ".thm")
-
- self.light_grey = self.theme['light_grey']
- self.apply_theme()
- self.display_thread = threading.Thread(target=self.update_display, args=([0]))
-
- class SoundSlot:
- def __init__(self, file, obj_id, o):
- self.file = file
- self.id = obj_id
- self.o = o
- self.mixerSound = None
- self.soundArray = None
- self.og_sound = None
- if file != None:
- self.mixerSound = pygame.mixer.Sound(self.file)
- self.soundArray = pygame.sndarray.array(self.mixerSound)
- self.og_sound = self.mixerSound
- #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
- self.volume = 16
- self.start = 0
- self.end = 0
-
-
- def play(self, vol):
- # 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()
-
- #lst = ast.literal_eval(self.o.sconf.as_list('volumes')[self.id])
- #print('---------- ', 'this is a volume')
- #print(lst)
- #print('note vol ', self.volume)
- #o.sconf
- if self.file != None:
- 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 set_pitch(self):
- if self.pitch == 0:
- self.mixerSound = self.og_sound
- else:
- snd_resample = resample(self.soundArray, ((self.pitch * - 1) / 10) + 1, "sinc_fastest").astype(self.soundArray.dtype)
- self.mixerSound = pygame.sndarray.make_sound(snd_resample)
- # snd_array = pygame.sndarray.array(self.mixerSound)
- print('setting pitch to ', self.pitch)
-
-
- def init_notes(self):
- #print('initing notes')
- outp = []
- _id = 0
- while _id < 64:
- outp2 = []
- _id2 = 0
- while _id2 < 16:
- outp2.append([0,16])
- _id2 += 1
- outp.append(outp2)
- _id += 1
- return outp
-
- def init_slots(self):
- # slot_id, file, volume, pitch, notes
- _iter = 0
- self.slots = []
- while _iter < 64:
-
- #def __init__(self, file, obj_id, o):
- p1 = self.SoundSlot(None, _iter, self)
- self.slots.append(p1)
- _iter += 1
- print('adding slot ', _iter)
-
- def Execute(self):
- self.keys = keypad.pressed_keys
- self.update_keys()
- self.FSM.Execute()
-
- 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:
- 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:
- 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
-
- def update_bpm(self):
- bpm = (60000 / self.sconf['bpm']) / 4
- pygame.time.set_timer(TIMER, int(bpm))
-
- def start_playback(self):
- self.playing = True
- self.playhead = -1
- self.playhead = 0
- self.songCycle = cycle(self.song)
- self.curPattern = next(self.songCycle)
- self.songStart = self.curPattern
- bpm = (60000 / self.sconf.as_int('bpm')) / 4
- pygame.time.set_timer(TIMER, int(bpm))
-
-
- def stop_playback(self):
- self.playing = False
- self.playhead = -1
- self.playhead = 0
- self.curPattern = self.song[0]
- 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 apply_theme(self):
- self.light_grey = self.theme["light_grey"]
- self.blue = self.theme["blue"]
- self.pink = self.theme["pink"]
- self.olive = self.theme["olive"]
- self.black = self.theme["black"]
- self.dark_blue = self.theme["dark_blue"]
- self.green = self.theme["green"]
- self.red = self.theme["red"]
-
- def save_song(self):
- base_dir = "/home/pi/zpc_ct/user/songs/"
- self.sconf = ConfigObj(base_dir + self.sconf['title'] + '.sng')
- #self.sconf['title'] = "default"
- self.sconf['volume'] = self.volume
- sounds = []
- notes = []
- vols = []
- pitches = []
-
- outp = []
- for x in self.soundSlots:
- sounds.append(x.file)
- notes.append(x.notes)
- vols.append(x.volume)
- pitches.append(x.pitch)
- outp.append([x.id, x.file, x.volume, x.pitch, x.notes])
- self.sconf['sounds'] = sounds
- self.sconf['notes'] = notes
- self.sconf['song'] = self.song
- self.sconf['volumes'] = vols
- self.sconf['pitches'] = pitches
- self.sconf['Sound Slot'] = outp
- self.sconf.write()
-
-
- c2 = ConfigObj(base_dir + self.sconf['title'] + '.sn2')
- c2['volume'] = self.volume
- c2['song'] = self.song
- c2['bpm'] = self.sconf['bpm']
- notes = []
- for n in self.slots:
- notes.append([n.id, n.file, n.volume, n.pitch, n.notes])
- c2['sounds'] = notes
-
- #tmp
- for s in self.soundSlots:
- c2['sounds'][s.id] = [s.id, s.file, s.volume, s.pitch, s.notes]
-
- c2.write()
-
- def save_config(self):
- base_dir = "/home/pi/zpc_ct/"
- #self.mconf = ConfigObj(base_dir + 'config.txt')
- print('before writing')
- print(self.mconf['theme'])
- self.mconf.write()
-
- def load_config(self):
- base_dir = "/home/pi/zpc_ct/"
- self.sconf = ConfigObj(base_dir + 'config.txt')
- self.theme = ConfigObj("/home/pi/zpc_ct/themes/" + self.mconf['theme'] + ".thm")
-
- def load_song(self):
- base_dir = "/home/pi/zpc_ct/user/songs/"
- self.sconf = ConfigObj(base_dir + self.mconf['default_song'] + '.sng')
- print(base_dir + self.mconf['default_song'] + '.sng')
- #one = self.sconf['song']
- one = self.sconf['title']
- print('song ', one)
-
- song = []
- for i in self.sconf['song']:
- song.append(int(i))
-
- obj_id = 0
- self.soundSlots = []
- for s in self.sconf['sounds']:
- p1 = self.SoundSlot(s, obj_id, self)
- self.soundSlots.append(p1)
- if (len(self.sconf.as_list('volumes')) - 1) > obj_id:
- p1.volume = int(self.sconf.as_list('volumes')[obj_id])
- else:
- p1.volume = 12
- if (len(self.sconf.as_list('pitches')) - 1) > obj_id:
- p1.pitch = int(self.sconf.as_list('pitches')[obj_id])
- p1.set_pitch()
- else:
- p1.pitch = 0
- print('adding ', s)
- obj_id += 1
- print(self.soundSlots)
-
- _iter = 0
-
- lst = ast.literal_eval(self.sconf['notes'][0])
- print('length ', len(lst))
-
- for n in self.soundSlots:
- m = []
- if (len(self.sconf.as_list('notes')) - 1) > _iter:
- lst = ast.literal_eval(self.sconf.as_list('notes')[_iter])
- n.notes = lst
- _iter += 1
-
- self.song = song
-
- self.songCycle = cycle(self.song)
- self.curPattern = next(self.songCycle)
- self.songStart = self.curPattern
-
- def update_display(self, x):
- # if self.display_thread.is_alive():
- # pass
- # #self.disp.image(self.image, self.rotation)
- # else:
- # self.display_thread = threading.Thread(target=self.ud, args=([0]))
-
- # self.display_thread.start()
- # time.sleep(.001)
- self.disp.image(self.image, self.rotation)
- def ud(self, x):
- self.disp.image(self.image, self.rotation)
-
- def clear_notes_on(self):
- _iter = 0
- self.notes_on = []
- while _iter < 64:
- self.notes_on.append(0)
- _iter += 1
-
- p = Prog()
-
- #while True:
-
- while not done:
- p.Execute()
- if pygame.event.get(pygame.USEREVENT + 1):
-
- #if p.playhead == 0:
- #print('pattern ', p.curPattern)
- p.playhead += 1
-
- if p.playhead == 16:
- p.playhead = 0
- p.curPattern = next(p.songCycle)
-
- #blink internal led
- if p.playhead % 8 == 0:
- GPIO.setup(47, GPIO.OUT)
- GPIO.output(47, GPIO.LOW)
- else:
- GPIO.setup(47, GPIO.OUT)
- GPIO.output(47, GPIO.HIGH)
-
- p.pub.dispatch('beat', [p.curPattern, p.playhead])
-
- clock.tick_busy_loop()
- #print(clock.get_fps())
-
- pygame.quit()
|