raspberry pi zero based drum machine
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

main.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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 json
  21. import ast
  22. import RPi.GPIO as GPIO
  23. #from time import sleep
  24. import threading
  25. import os
  26. GPIO.setmode(GPIO.BCM)
  27. GPIO.setup(29, GPIO.OUT) #internal led GPIO output channel
  28. cols = [digitalio.DigitalInOut(x) for x in (board.D21, board.D20, board.D16, board.D12)]
  29. rows = [digitalio.DigitalInOut(x) for x in (board.D26, board.D13, board.D6, board.D5)]
  30. keys = ((15, 14, 13, 12),
  31. (11, 10, 9, 8),
  32. (7, 6, 5, 4),
  33. (3, 2, 1, 0))
  34. keypad = adafruit_matrixkeypad.Matrix_Keypad(rows, cols, keys)
  35. pygame.init()
  36. done = False
  37. clock = pygame.time.Clock()
  38. TIMER = pygame.USEREVENT + 1
  39. playhead = 0
  40. timer = pygame.time.get_ticks
  41. start = now = timer()
  42. cs_pin = digitalio.DigitalInOut(board.CE0)
  43. dc_pin = digitalio.DigitalInOut(board.D25)
  44. reset_pin = None
  45. BAUDRATE = 64000000
  46. backlight = digitalio.DigitalInOut(board.D22)
  47. backlight.switch_to_output()
  48. backlight.value = True
  49. buttonA = digitalio.DigitalInOut(board.D23)
  50. buttonB = digitalio.DigitalInOut(board.D24)
  51. buttonA.switch_to_input()
  52. buttonB.switch_to_input()
  53. spi = board.SPI()
  54. x = 0
  55. # Turn on the backlight
  56. backlight = digitalio.DigitalInOut(board.D22)
  57. backlight.switch_to_output()
  58. backlight.value = True
  59. class Prog:
  60. def __init__(self):
  61. self.FSM = FSM.ProgFSM(self)
  62. #self.start_mixer()
  63. #self.font = ImageFont.truetype("/home/pi/examples/HLM.ttf", 64)
  64. #self.h1 = ImageFont.truetype("/home/pi/examples/HLM.ttf", 26)
  65. self.h1 = ImageFont.truetype("/home/pi/zpc_ct/fonts/Pixellari.ttf", 30)
  66. self.h2 = ImageFont.truetype("/home/pi/zpc_ct/fonts/Pixellari.ttf", 20)
  67. self.h3 = ImageFont.truetype("/home/pi/zpc_ct/fonts/Pixellari.ttf", 54)
  68. self.disp = st7789.ST7789(
  69. spi,
  70. cs=cs_pin,
  71. dc=dc_pin,
  72. rst=reset_pin,
  73. baudrate=BAUDRATE,
  74. width=135,
  75. height=240,
  76. x_offset=53,
  77. y_offset=40,
  78. )
  79. self.height = self.disp.width # we swap height/width to rotate it to landscape!
  80. self.width = self.disp.height
  81. self.rotation = 270
  82. self.padding = -2
  83. self.top = self.padding
  84. self.bottom = self.height - self.padding
  85. self.image = Image.new("RGB", (self.width, self.height))
  86. self.draw = ImageDraw.Draw(self.image)
  87. self.playhead = 0
  88. self.playing = False
  89. self.bpm = 90
  90. self.bpm_inc = 1
  91. self.half_bpm = 0.0
  92. self.note_bank = 0
  93. self.pat_bank = 0
  94. self.volume = 10
  95. self.note_vol = 16
  96. self.note_pitch = 0.0
  97. self.soundSlots = []
  98. self.keys = []
  99. self.keyState = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
  100. self.song = [0]
  101. self.songCycle = cycle(self.song)
  102. self.curPattern = next(self.songCycle)
  103. self.songStart = self.curPattern
  104. self.eSound = 0
  105. self.ePattern = 0
  106. self.patternFollow = False
  107. self.patternClipboard = []
  108. self.soundClipboard = []
  109. self.last_load_sound_id = None
  110. self.odub = False
  111. self.undo_buf = []
  112. self.erase_buf = []
  113. self.erase = False
  114. self.mute_buf = []
  115. self.mute = False
  116. self.black = "#000000"
  117. self.bg_color = "#336699"
  118. self.color_b = "#929230"
  119. self.dark_grey = "#404040"
  120. self.grey = "#808080"
  121. self.light_grey = "#D3D3D3"
  122. self.blue = "#336699"
  123. self.dark_blue = "#2A2AD5"
  124. self.pink = "#D52A80"
  125. self.olive = "#80D52A"
  126. self.notes_on = []
  127. self.clear_notes_on()
  128. self.press_ticks_up = None
  129. self.press_ticks_down = None
  130. self.pub = observer.Publisher(['beat', 'the joint'])
  131. self.repeater_states = []
  132. _iter = 0
  133. while _iter < 18:
  134. self.repeater_states.append(None)
  135. _iter += 1
  136. self.mconf = ConfigObj("config.txt")
  137. self.sconf = ConfigObj("/home/pi/zpc_ct/user/songs/" + self.mconf['default_song'])
  138. self.theme = ConfigObj("/home/pi/zpc_ct/user/themes/" + self.mconf['theme'] + ".thm")
  139. self.load_sound_dir = '/home/pi/zpc_ct/user/sounds/'
  140. self.song_dir = self.mconf['song_dir']
  141. self.base_song_dir = '/home/pi/zpc_ct/user/songs/'
  142. self.light_grey = self.theme['light_grey']
  143. self.apply_theme()
  144. self.display_thread = threading.Thread(target=self.update_display, args=([0]))
  145. self.defender_sounds = []
  146. self.load_defender()
  147. class SoundSlot:
  148. def __init__(self, file, obj_id, o):
  149. self.file = file
  150. self.id = obj_id
  151. self.o = o
  152. self.mixerSound = None
  153. self.soundArray = None
  154. self.og_sound = None
  155. if file != None:
  156. self.create_sound()
  157. self.pitch = 0
  158. self.volume = 16
  159. self.start = 0
  160. self.end = 0
  161. def create_sound(self):
  162. self.mixerSound = pygame.mixer.Sound(self.file)
  163. self.soundArray = pygame.sndarray.array(self.mixerSound)
  164. self.og_sound = self.mixerSound
  165. def play(self, vol):
  166. if self.file != None:
  167. if vol != 0:
  168. vol = (vol / 16) * (self.volume / 16) * (self.o.volume / 16)
  169. self.mixerSound.set_volume(vol)
  170. pygame.mixer.Sound.play(self.mixerSound)
  171. def set_pitch(self):
  172. if self.pitch == 0:
  173. self.mixerSound = self.og_sound
  174. else:
  175. snd_resample = resample(self.soundArray, ((self.pitch * - 1) / 10) + 1, "sinc_fastest").astype(self.soundArray.dtype)
  176. self.mixerSound = pygame.sndarray.make_sound(snd_resample)
  177. def init_notes(self):
  178. outp = []
  179. _id = 0
  180. while _id < 64:
  181. outp2 = []
  182. _id2 = 0
  183. while _id2 < 16:
  184. outp2.append([0,16])
  185. _id2 += 1
  186. outp.append(outp2)
  187. _id += 1
  188. return outp
  189. def init_slots(self):
  190. _iter = 0
  191. self.slots = []
  192. while _iter < 64:
  193. p1 = self.SoundSlot(None, _iter, self)
  194. self.slots.append(p1)
  195. _iter += 1
  196. def Execute(self):
  197. self.keys = keypad.pressed_keys
  198. self.update_keys()
  199. self.FSM.Execute()
  200. def update_keys(self):
  201. _id = 0
  202. for k in self.keyState:
  203. if k == 0:
  204. if _id in self.keys:
  205. self.keyState[_id] = 1
  206. elif k == 1:
  207. if _id in self.keys:
  208. self.keyState[_id] = 2
  209. else:
  210. self.keyState[_id] = 3
  211. elif k == 2:
  212. if _id in self.keys:
  213. self.keyState[_id] = 2
  214. else:
  215. self.keyState[_id] = 3
  216. else:
  217. self.keyState[_id] = 0
  218. _id += 1
  219. if buttonA.value == 0:
  220. if self.keyState[16] == 0:
  221. self.keyState[16] = 1
  222. elif self.keyState[16] == 1:
  223. self.keyState[16] = 2
  224. else:
  225. self.keyState[16] = 2
  226. else:
  227. if self.keyState[16] == 3:
  228. self.keyState[16] = 4
  229. elif self.keyState[16] == 2:
  230. self.keyState[16] = 3
  231. elif self.keyState[16] == 4:
  232. self.keyState[16] = 0
  233. if buttonB.value == 0:
  234. if self.keyState[17] == 0:
  235. self.keyState[17] = 1
  236. elif self.keyState[17] == 1:
  237. self.keyState[17] = 2
  238. else:
  239. self.keyState[17] = 2
  240. else:
  241. if self.keyState[17] == 3:
  242. self.keyState[17] = 4
  243. elif self.keyState[17] == 2:
  244. self.keyState[17] = 3
  245. elif self.keyState[17] == 4:
  246. self.keyState[17] = 0
  247. def button_repeater(self, state, enables):
  248. if state == 0:
  249. pass
  250. elif state == 1:
  251. for i in self.repeater_states:
  252. i = None
  253. def update_bpm(self):
  254. bpm = (60000 / self.bpm) / 4
  255. self.half_bpm = bpm / 2
  256. if self.playing:
  257. pygame.time.set_timer(TIMER, int(bpm))
  258. def start_playback(self):
  259. self.playing = True
  260. self.playhead = -1
  261. self.playhead = 0
  262. self.songCycle = cycle(self.song)
  263. self.curPattern = next(self.songCycle)
  264. self.songStart = self.curPattern
  265. bpm = (60000 / self.bpm) / 4
  266. pygame.time.set_timer(TIMER, int(bpm))
  267. GPIO.setup(29, GPIO.OUT)
  268. GPIO.output(29, GPIO.LOW)
  269. def stop_playback(self):
  270. self.playing = False
  271. self.playhead = -1
  272. self.playhead = 0
  273. self.curPattern = self.song[0]
  274. pygame.time.set_timer(TIMER, 0)
  275. GPIO.setup(29, GPIO.OUT)
  276. GPIO.output(29, GPIO.HIGH)
  277. def center_text(self, text, font, width, y, color):
  278. w,h = font.getsize(text)
  279. self.draw.text(((width-w)/2,(y-h)/2), text, font=font, fill=color)
  280. def center_block(self, message, font, bounding_box, color):
  281. x1, y1, x2, y2 = bounding_box
  282. w, h = self.draw.textsize(message, font=font)
  283. x = (x2 - x1 - w)/2 + x1
  284. y = (y2 - y1 - h)/2 + y1
  285. self.draw.text((x, y), message, align='center', font=font, fill=color)
  286. def apply_theme(self):
  287. self.grey = self.theme["grey"]
  288. self.light_grey = self.theme["light_grey"]
  289. self.dark_grey = self.theme["dark_grey"]
  290. self.blue = self.theme["blue"]
  291. self.pink = self.theme["pink"]
  292. self.olive = self.theme["olive"]
  293. def save_song(self):
  294. base_dir = self.song_dir
  295. title = self.sconf['title']
  296. fname = base_dir + self.sconf['title'] + '.sng'
  297. if os.path.exists(fname):
  298. os.utime(fname, None)
  299. else:
  300. open(fname, 'a').close()
  301. self.sconf = ConfigObj(fname)
  302. self.sconf['volume'] = self.volume
  303. self.sconf['song'] = self.song
  304. self.sconf['bpm'] = self.bpm
  305. self.sconf['title'] = title
  306. notes = []
  307. for n in self.soundSlots:
  308. notes.append([n.id, n.file, n.volume, n.pitch, n.notes])
  309. self.sconf['sounds'] = notes
  310. self.sconf.write()
  311. def save_config(self):
  312. base_dir = "/home/pi/zpc_ct/"
  313. self.mconf['song_dir'] = self.song_dir
  314. self.mconf.write()
  315. def load_config(self):
  316. base_dir = "/home/pi/zpc_ct/"
  317. self.sconf = ConfigObj(base_dir + 'config.txt')
  318. self.theme = ConfigObj("/home/pi/zpc_ct/user/themes/" + self.mconf['theme'] + ".thm")
  319. self.song_dir = self.mconf['song_dir']
  320. def load_song(self):
  321. self.quit_mixer()
  322. self.start_mixer()
  323. base_dir = self.song_dir
  324. self.sconf = ConfigObj(base_dir + self.mconf['default_song'] + '.sng')
  325. print(base_dir + self.mconf['default_song'] + '.sng')
  326. self.last_load_sound_id = None
  327. print('song bpm is ', self.sconf['bpm'])
  328. self.bpm = self.sconf.as_int('bpm')
  329. song = []
  330. for i in self.sconf['song']:
  331. song.append(int(i))
  332. obj_id = 0
  333. self.soundSlots = []
  334. _iter = 0
  335. while _iter < 64:
  336. snd = self.sconf.as_list('sounds')[_iter]
  337. snd_lst = ast.literal_eval(snd)
  338. obj_id = snd_lst[0]
  339. filename = snd_lst[1]
  340. volume = int(snd_lst[2])
  341. pitch = int(snd_lst[3])
  342. notes = snd_lst[4]
  343. p1 = self.SoundSlot(filename, obj_id, self)
  344. p1.volume = volume
  345. p1.pitch = pitch
  346. if pitch != 0:
  347. p1.set_pitch()
  348. self.soundSlots.append(p1)
  349. p1.notes = notes
  350. _iter += 1
  351. self.song = song
  352. self.songCycle = cycle(self.song)
  353. self.curPattern = next(self.songCycle)
  354. self.songStart = self.curPattern
  355. self.update_bpm()
  356. def update_display(self, x):
  357. try:
  358. self.disp.image(self.image, self.rotation)
  359. except:
  360. print('display failed')
  361. def ud(self, x):
  362. self.disp.image(self.image, self.rotation)
  363. def clear_notes_on(self):
  364. _iter = 0
  365. self.notes_on = []
  366. while _iter < 64:
  367. self.notes_on.append(0)
  368. _iter += 1
  369. def start_mixer(self):
  370. print('starting mixer')
  371. mixer.init()
  372. mixer.set_num_channels(32)
  373. def quit_mixer(self):
  374. if mixer.get_init() != None:
  375. print('quitting mixer')
  376. mixer.quit()
  377. def load_defender(self):
  378. s1 = pygame.mixer.Sound('/home/pi/zpc_ct/user/sounds/po-arc/05.wav')
  379. self.defender_sounds.append(s1)
  380. s2 = pygame.mixer.Sound('/home/pi/zpc_ct/user/sounds/po-arc/08.wav')
  381. self.defender_sounds.append(s2)
  382. s3 = pygame.mixer.Sound('/home/pi/zpc_ct/user/sounds/po-arc/11.wav')
  383. self.defender_sounds.append(s3)
  384. p = Prog()
  385. while not done:
  386. p.Execute()
  387. if pygame.event.get(pygame.USEREVENT + 1):
  388. p.playhead += 1
  389. if p.playhead == 16:
  390. p.playhead = 0
  391. p.curPattern = next(p.songCycle)
  392. #blink internal led
  393. if p.playhead % 4 == 0:
  394. GPIO.setup(29, GPIO.OUT)
  395. GPIO.output(29, GPIO.LOW)
  396. else:
  397. GPIO.setup(29, GPIO.OUT)
  398. GPIO.output(29, GPIO.HIGH)
  399. p.pub.dispatch('beat', [p.curPattern, p.playhead])
  400. clock.tick_busy_loop()
  401. #print(clock.get_fps())
  402. pygame.quit()