123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
-
- 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 json
- import ast
- import RPi.GPIO as GPIO
- from time import sleep
- import threading
- import os
- GPIO.setmode(GPIO.BCM)
-
- # set up GPIO output channel - internal led
- GPIO.setup(29, 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.start_mixer()
- #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/zpc_ct/fonts/Pixellari.ttf", 30)
- self.h2 = ImageFont.truetype("/home/pi/zpc_ct/fonts/Pixellari.ttf", 20)
- self.h3 = ImageFont.truetype("/home/pi/zpc_ct/fonts/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.half_bpm = 0.0
- 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.patternFollow = False
- self.patternClipboard = []
- self.soundClipboard = []
- self.last_load_sound_id = None
- self.odub = False
- self.undo_buf = []
- 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/user/themes/" + self.mconf['theme'] + ".thm")
- self.load_song_dir = '/home/pi/zpc_ct/user/sounds/'
-
- 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.create_sound()
- #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 create_sound(self):
- self.mixerSound = pygame.mixer.Sound(self.file)
- self.soundArray = pygame.sndarray.array(self.mixerSound)
- self.og_sound = self.mixerSound
-
- 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.bpm) / 4
- self.half_bpm = bpm / 2
- if self.playing:
- 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.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.grey = self.theme["grey"]
- self.light_grey = self.theme["light_grey"]
- self.dark_grey = self.theme["dark_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/"
- title = self.sconf['title']
- fname = base_dir + self.sconf['title'] + '.sng'
- if os.path.exists(fname):
- os.utime(fname, None)
- print(fname, ' exists')
- else:
- print('make a damn file ', fname)
- open(fname, 'a').close()
-
- self.sconf = ConfigObj(fname)
- self.sconf['volume'] = self.volume
- self.sconf['song'] = self.song
- self.sconf['bpm'] = self.bpm
- self.sconf['title'] = title
- notes = []
- for n in self.soundSlots:
- notes.append([n.id, n.file, n.volume, n.pitch, n.notes])
- self.sconf['sounds'] = notes
- self.sconf.write()
-
- def save_config(self):
- base_dir = "/home/pi/zpc_ct/"
- 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/user/themes/" + self.mconf['theme'] + ".thm")
-
- def load_song(self):
- #self.stop_playback()
- self.quit_mixer()
- self.start_mixer()
- 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')
- self.last_load_sound_id = None
- print('song bpm is ', self.sconf['bpm'])
- self.bpm = self.sconf.as_int('bpm')
- song = []
- for i in self.sconf['song']:
- song.append(int(i))
-
- obj_id = 0
- self.soundSlots = []
-
-
- _iter = 0
- while _iter < 64:
- #for s in self.sconf.as_list('sounds'):
- snd = self.sconf.as_list('sounds')[_iter]
- #print('-----')
- snd_lst = ast.literal_eval(snd)
- obj_id = snd_lst[0]
- filename = snd_lst[1]
- volume = int(snd_lst[2])
- pitch = int(snd_lst[3])
- notes = snd_lst[4]
- _iter += 1
-
- p1 = self.SoundSlot(filename, obj_id, self)
- p1.volume = volume
- p1.pitch = pitch
- if pitch != 0:
- p1.set_pitch()
- self.soundSlots.append(p1)
- p1.notes = notes
-
- _iter = 0
-
- self.song = song
-
- self.songCycle = cycle(self.song)
- self.curPattern = next(self.songCycle)
- self.songStart = self.curPattern
- self.update_bpm()
- # if self.playing:
- # self.stop_playback()
- # self.start_playback()
-
- 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)
- try:
- self.disp.image(self.image, self.rotation)
- except:
- print('display failed')
- 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
-
- def start_mixer(self):
- print('starting mixer')
- mixer.init()
- mixer.set_num_channels(32)
-
- def quit_mixer(self):
- if mixer.get_init() != None:
- print('quitting mixer')
- mixer.quit()
-
- 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(29, GPIO.OUT)
- GPIO.output(29, GPIO.LOW)
- else:
- GPIO.setup(29, GPIO.OUT)
- GPIO.output(29, GPIO.HIGH)
-
- p.pub.dispatch('beat', [p.curPattern, p.playhead])
-
- clock.tick_busy_loop()
- #print(clock.get_fps())
-
- pygame.quit()
|