raspberry pi zero based drum machine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. import sys
  2. sys.path.insert(0, './lib')
  3. from pygame import mixer
  4. import time
  5. import digitalio
  6. import board
  7. import adafruit_matrixkeypad
  8. import pygame
  9. from PIL import Image, ImageDraw, ImageFont
  10. import adafruit_rgb_display.st7789 as st7789
  11. import digitalio
  12. import board
  13. from adafruit_rgb_display.rgb import color565
  14. import adafruit_rgb_display.st7789 as st7789
  15. import FSM
  16. from itertools import cycle
  17. import observer
  18. from scikits.samplerate import resample
  19. from configobj import ConfigObj
  20. import ast
  21. #from configobj import ConfigObj
  22. import RPi.GPIO as GPIO
  23. from time import sleep
  24. GPIO.setmode(GPIO.BCM)
  25. # set up GPIO output channel
  26. GPIO.setup(47, GPIO.OUT)
  27. cols = [digitalio.DigitalInOut(x) for x in (board.D21, board.D20, board.D16, board.D12)]
  28. rows = [digitalio.DigitalInOut(x) for x in (board.D26, board.D13, board.D6, board.D5)]
  29. # keys = ((3, 2, 1, 0),
  30. # (7, 6, 5, 4),
  31. # (11, 10, 9, 8),
  32. # (15, 14, 13, 12))
  33. keys = ((15, 14, 13, 12),
  34. (11, 10, 9, 8),
  35. (7, 6, 5, 4),
  36. (3, 2, 1, 0))
  37. keypad = adafruit_matrixkeypad.Matrix_Keypad(rows, cols, keys)
  38. # while True:
  39. # keys = keypad.pressed_keys
  40. # if keys:
  41. # print("Pressed: ", keys)
  42. # time.sleep(0.1)
  43. pygame.init()
  44. mixer.init()
  45. done = False
  46. clock = pygame.time.Clock()
  47. TIMER = pygame.USEREVENT + 1
  48. #pygame.time.set_timer(pygame.USEREVENT + 1, 444)
  49. #pygame.time.set_timer(TIMER, 161)
  50. playhead = 0
  51. timer = pygame.time.get_ticks
  52. start = now = timer()
  53. cs_pin = digitalio.DigitalInOut(board.CE0)
  54. dc_pin = digitalio.DigitalInOut(board.D25)
  55. reset_pin = None
  56. BAUDRATE = 64000000 # The pi can be very fast!
  57. # display = st7789.ST7789(
  58. # board.SPI(),
  59. # cs=cs_pin,
  60. # dc=dc_pin,
  61. # rst=reset_pin,
  62. # baudrate=BAUDRATE,
  63. # width=135,
  64. # height=240,
  65. # x_offset=53,
  66. # y_offset=40,
  67. # )
  68. backlight = digitalio.DigitalInOut(board.D22)
  69. backlight.switch_to_output()
  70. backlight.value = True
  71. buttonA = digitalio.DigitalInOut(board.D23)
  72. buttonB = digitalio.DigitalInOut(board.D24)
  73. buttonA.switch_to_input()
  74. buttonB.switch_to_input()
  75. spi = board.SPI()
  76. # Create blank image for drawing.
  77. # Make sure to create image with mode 'RGB' for full color.
  78. # Get drawing object to draw on image.
  79. # Draw a black filled box to clear the image.
  80. #draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
  81. #disp.image(image, rotation)
  82. # Draw some shapes.
  83. # First define some constants to allow easy resizing of shapes.
  84. # Move left to right keeping track of the current x position for drawing shapes.
  85. x = 0
  86. # Turn on the backlight
  87. backlight = digitalio.DigitalInOut(board.D22)
  88. backlight.switch_to_output()
  89. backlight.value = True
  90. class Prog:
  91. def __init__(self):
  92. self.FSM = FSM.ProgFSM(self)
  93. self.font = ImageFont.truetype("/home/pi/examples/HLM.ttf", 64)
  94. #self.h1 = ImageFont.truetype("/home/pi/examples/HLM.ttf", 26)
  95. self.h1 = ImageFont.truetype("/home/pi/Pixellari.ttf", 30)
  96. self.h2 = ImageFont.truetype("/home/pi/Pixellari.ttf", 20)
  97. self.h3 = ImageFont.truetype("/home/pi/Pixellari.ttf", 54)
  98. self.disp = st7789.ST7789(
  99. spi,
  100. cs=cs_pin,
  101. dc=dc_pin,
  102. rst=reset_pin,
  103. baudrate=BAUDRATE,
  104. width=135,
  105. height=240,
  106. x_offset=53,
  107. y_offset=40,
  108. )
  109. self.height = self.disp.width # we swap height/width to rotate it to landscape!
  110. self.width = self.disp.height
  111. self.rotation = 270
  112. self.padding = -2
  113. self.top = self.padding
  114. self.bottom = self.height - self.padding
  115. self.image = Image.new("RGB", (self.width, self.height))
  116. self.draw = ImageDraw.Draw(self.image)
  117. self.playhead = 0
  118. self.playing = False
  119. #self.bpm = 90
  120. self.bpm_inc = 1
  121. self.note_bank = 0
  122. self.volume = 10
  123. self.note_vol = 16
  124. self.note_pitch = 0.0
  125. self.soundSlots = []
  126. self.keys = []
  127. self.keyState = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
  128. #self.song = [2, 3, 0, 1, 0, 1, 0, 1, 0, 1]
  129. self.song = [0]
  130. self.songCycle = cycle(self.song)
  131. self.curPattern = next(self.songCycle)
  132. self.songStart = self.curPattern
  133. self.eSound = 0
  134. self.ePattern = 0
  135. self.black = "#000000"
  136. self.bg_color = "#336699"
  137. self.color_a = "#FFFFFF"
  138. self.color_b = "#929230"
  139. self.color_c = "#FFFF00"
  140. self.dark_grey = "#404040"
  141. self.grey = "#808080"
  142. self.light_grey = "#D3D3D3"
  143. self.blue = "#336699"
  144. self.dark_blue = "#2A2AD5"
  145. self.pink = "#D52A80"
  146. self.red = "#D52A2A"
  147. self.olive = "#80D52A"
  148. self.green = "#2AD52A"
  149. self.pub = observer.Publisher(['beat', 'the joint'])
  150. self.song_file_name = "default.sng"
  151. self.mconf = ConfigObj("config.txt")
  152. self.sconf = ConfigObj("/home/pi/zpc_ct/user/songs/" + self.mconf['default_song'])
  153. self.theme = ConfigObj("/home/pi/zpc_ct/themes/" + self.mconf['theme'])
  154. self.light_grey = self.theme['light_grey']
  155. self.apply_theme()
  156. #self.config['title'] = "default"
  157. #self.config['bpm'] = self.bpm
  158. #self.config['volume'] = self.volume
  159. #self.config.write()
  160. #self.save_song()
  161. class SoundSlot:
  162. def __init__(self, file, obj_id, o):
  163. self.file = file
  164. self.id = obj_id
  165. self.o = o
  166. self.mixerSound = pygame.mixer.Sound(self.file)
  167. self.og_sound = self.mixerSound
  168. self.pattern = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]
  169. self.notes = self.init_notes()
  170. self.pitch = 0.0
  171. self.volume = 16
  172. self.start = 0
  173. self.end = 0
  174. def play(self, vol):
  175. # snd_array = pygame.sndarray.array(self.mixerSound)
  176. # snd_resample = resample(snd_array, 2.5, "sinc_fastest").astype(snd_array.dtype)
  177. # snd_out = pygame.sndarray.make_sound(snd_resample)
  178. # snd_out.play()
  179. if vol != 0:
  180. vol = (vol / 16) * (self.volume / 16) * (self.o.volume / 16)
  181. self.mixerSound.set_volume(vol)
  182. pygame.mixer.Sound.play(self.mixerSound)
  183. def init_notes(self):
  184. #print('initing notes')
  185. outp = []
  186. _id = 0
  187. while _id < 64:
  188. outp2 = []
  189. _id2 = 0
  190. while _id2 < 16:
  191. outp2.append([0,16])
  192. _id2 += 1
  193. outp.append(outp2)
  194. _id += 1
  195. # for n in outp:
  196. # print('sound ', self.id, ' ', n)
  197. #print(outp)
  198. return outp
  199. def Execute(self):
  200. #print('doing the thing')
  201. #self.check_buttons()
  202. self.keys = keypad.pressed_keys
  203. #self.key_flip()
  204. self.update_keys()
  205. self.FSM.Execute()
  206. #self.disp.image(self.image, self.rotation)
  207. def update_keys(self):
  208. _id = 0
  209. for k in self.keyState:
  210. if k == 0:
  211. if _id in self.keys:
  212. self.keyState[_id] = 1
  213. elif k == 1:
  214. if _id in self.keys:
  215. self.keyState[_id] = 2
  216. else:
  217. self.keyState[_id] = 3
  218. elif k == 2:
  219. if _id in self.keys:
  220. self.keyState[_id] = 2
  221. else:
  222. self.keyState[_id] = 3
  223. else:
  224. self.keyState[_id] = 0
  225. _id += 1
  226. if buttonA.value == 0:
  227. #print('a on `', self.keyState[16])
  228. if self.keyState[16] == 0:
  229. self.keyState[16] = 1
  230. elif self.keyState[16] == 1:
  231. self.keyState[16] = 2
  232. else:
  233. self.keyState[16] = 2
  234. else:
  235. if self.keyState[16] == 3:
  236. self.keyState[16] = 4
  237. elif self.keyState[16] == 2:
  238. self.keyState[16] = 3
  239. elif self.keyState[16] == 4:
  240. self.keyState[16] = 0
  241. if buttonB.value == 0:
  242. #print('a on `', self.keyState[16])
  243. if self.keyState[17] == 0:
  244. self.keyState[17] = 1
  245. elif self.keyState[17] == 1:
  246. self.keyState[17] = 2
  247. else:
  248. self.keyState[17] = 2
  249. else:
  250. if self.keyState[17] == 3:
  251. self.keyState[17] = 4
  252. elif self.keyState[17] == 2:
  253. self.keyState[17] = 3
  254. elif self.keyState[17] == 4:
  255. self.keyState[17] = 0
  256. # if buttonB.value == 0:
  257. # #print('b on')
  258. # if self.keyState[17] == 0 or self.keyState[17] == 1:
  259. # self.keyState[17] += 1
  260. # else:
  261. # if self.keyState[17] == 2:
  262. # self.keyState[17] += 1
  263. # else:
  264. # self.keyState[17] = 0
  265. def update_bpm(self):
  266. bpm = (60000 / self.sconf['bpm']) / 4
  267. pygame.time.set_timer(TIMER, int(bpm))
  268. def start_playback(self):
  269. self.playing = True
  270. self.playhead = -1
  271. self.playhead = 0
  272. #self.songCycle = cycle(self.song)
  273. #self.curPattern = next(self.songCycle)
  274. #self.songStart = self.curPattern
  275. #self.curPattern = self.song[0]
  276. #self.curPattern = self.songStart
  277. self.songCycle = cycle(self.song)
  278. self.curPattern = next(self.songCycle)
  279. self.songStart = self.curPattern
  280. bpm = (60000 / self.sconf.as_int('bpm')) / 4
  281. pygame.time.set_timer(TIMER, int(bpm))
  282. def stop_playback(self):
  283. self.playing = False
  284. self.playhead = -1
  285. self.playhead = 0
  286. self.curPattern = self.song[0]
  287. #self.curPattern = self.songStart
  288. pygame.time.set_timer(TIMER, 0)
  289. def center_text(self, text, font, width, y, color):
  290. w,h = font.getsize(text)
  291. self.draw.text(((width-w)/2,(y-h)/2), text, font=font, fill=color)
  292. def center_block(self, message, font, bounding_box, color):
  293. x1, y1, x2, y2 = bounding_box # For easy reading
  294. w, h = self.draw.textsize(message, font=font)
  295. x = (x2 - x1 - w)/2 + x1
  296. y = (y2 - y1 - h)/2 + y1
  297. self.draw.text((x, y), message, align='center', font=font, fill=color)
  298. def apply_theme(self):
  299. self.light_grey = self.theme["light_grey"]
  300. self.blue = self.theme["blue"]
  301. self.pink = self.theme["pink"]
  302. self.olive = self.theme["olive"]
  303. self.black = self.theme["black"]
  304. self.dark_blue = self.theme["dark_blue"]
  305. self.green = self.theme["green"]
  306. self.red = self.theme["red"]
  307. def save_song(self):
  308. base_dir = "/home/pi/zpc_ct/user/songs/"
  309. #self.song_file_name
  310. #file1 = open(base_dir + self.song_file_name,"w")#write mode
  311. #file1.write("Tomorrow \n")
  312. #file1.close()
  313. self.sconf = ConfigObj(base_dir + self.song_file_name)
  314. self.sconf['title'] = "default"
  315. #self.config['bpm'] = self.bpm
  316. self.sconf['volume'] = self.volume
  317. sounds = []
  318. notes = []
  319. vols = []
  320. pitches = []
  321. for x in self.soundSlots:
  322. sounds.append(x.file)
  323. notes.append(x.notes)
  324. vols.append(x.volume)
  325. pitches.append(x.pitch)
  326. self.sconf['sounds'] = sounds
  327. self.sconf['notes'] = notes
  328. self.sconf['song'] = self.song
  329. #self.config['volumes'] = ['this', 'that', 'the other']
  330. self.sconf.write()
  331. def load_song(self):
  332. base_dir = "/home/pi/zpc_ct/user/songs/"
  333. self.sconf = ConfigObj(base_dir + 'default.sng')
  334. one = self.sconf['song']
  335. #two = ast.literal_eval(self.sconf['song'])
  336. three = self.sconf.as_list('song')
  337. #four = ast.literal_eval(self.sconf.as_list('song'))
  338. song = []
  339. for i in self.sconf['song']:
  340. song.append(int(i))
  341. #lst = ast.literal_eval(self.sconf['song'][0])
  342. self.song = song
  343. print('this is the song')
  344. #print(lst)
  345. print(song)
  346. #print(two)
  347. #print(three)
  348. #print(four)
  349. #print(five)
  350. self.songCycle = cycle(self.song)
  351. self.curPattern = next(self.songCycle)
  352. self.songStart = self.curPattern
  353. #self.config = ConfigObj(base_dir + self.song_file_name)
  354. #print('these sounds should get loaded ', self.config['sounds'])
  355. p = Prog()
  356. #while True:
  357. while not done:
  358. p.Execute()
  359. if pygame.event.get(pygame.USEREVENT + 1):
  360. if p.playhead == 0:
  361. print('pattern ', p.curPattern)
  362. p.playhead += 1
  363. if p.playhead == 16:
  364. p.playhead = 0
  365. #self.draw.text((0, 0), str(self.playhead), font=font, fill="#FFFFFF")
  366. #self.disp.image(self.image, self.rotation)
  367. p.curPattern = next(p.songCycle)
  368. #print('pattern ', p.curPattern)
  369. if p.playhead % 8 == 0:
  370. GPIO.setup(47, GPIO.OUT)
  371. GPIO.output(47, GPIO.LOW)
  372. #print('led on')
  373. else:
  374. GPIO.setup(47, GPIO.OUT)
  375. GPIO.output(47, GPIO.HIGH)
  376. #print('led off')
  377. p.pub.dispatch('beat', [p.curPattern, p.playhead])
  378. #clock.tick(240)
  379. clock.tick_busy_loop()
  380. #print(clock.get_fps())
  381. pygame.quit()
  382. # 1 main
  383. # 2 select pattern
  384. # 3 note edit
  385. # 6 song edit
  386. # 7 program load
  387. # 8 song load
  388. # 10 utility
  389. # 11 bpm+
  390. # 12 bpm-
  391. # 13 play
  392. # 14 stop
  393. # 15 vol+
  394. # 16 vol-