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.

test.py 9.2KB

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