midi.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. #!/usr/bin/env python
  2. """Contains an example of midi input, and a separate example of midi output.
  3. By default it runs the output example.
  4. python midi.py --output
  5. python midi.py --input
  6. """
  7. import sys
  8. import os
  9. import pygame
  10. import pygame.midi
  11. from pygame.locals import *
  12. try: # Ensure set available for output example
  13. set
  14. except NameError:
  15. from sets import Set as set
  16. def print_device_info():
  17. pygame.midi.init()
  18. _print_device_info()
  19. pygame.midi.quit()
  20. def _print_device_info():
  21. for i in range( pygame.midi.get_count() ):
  22. r = pygame.midi.get_device_info(i)
  23. (interf, name, input, output, opened) = r
  24. in_out = ""
  25. if input:
  26. in_out = "(input)"
  27. if output:
  28. in_out = "(output)"
  29. print ("%2i: interface :%s:, name :%s:, opened :%s: %s" %
  30. (i, interf, name, opened, in_out))
  31. def input_main(device_id = None):
  32. pygame.init()
  33. pygame.fastevent.init()
  34. event_get = pygame.fastevent.get
  35. event_post = pygame.fastevent.post
  36. pygame.midi.init()
  37. _print_device_info()
  38. if device_id is None:
  39. input_id = pygame.midi.get_default_input_id()
  40. else:
  41. input_id = device_id
  42. print ("using input_id :%s:" % input_id)
  43. i = pygame.midi.Input( input_id )
  44. pygame.display.set_mode((1,1))
  45. going = True
  46. while going:
  47. events = event_get()
  48. for e in events:
  49. if e.type in [QUIT]:
  50. going = False
  51. if e.type in [KEYDOWN]:
  52. going = False
  53. if e.type in [pygame.midi.MIDIIN]:
  54. print (e)
  55. if i.poll():
  56. midi_events = i.read(10)
  57. # convert them into pygame events.
  58. midi_evs = pygame.midi.midis2events(midi_events, i.device_id)
  59. for m_e in midi_evs:
  60. event_post( m_e )
  61. del i
  62. pygame.midi.quit()
  63. def output_main(device_id = None):
  64. """Execute a musical keyboard example for the Church Organ instrument
  65. This is a piano keyboard example, with a two octave keyboard, starting at
  66. note F3. Left mouse down over a key starts a note, left up stops it. The
  67. notes are also mapped to the computer keyboard keys, assuming an American
  68. English PC keyboard (sorry everyone else, but I don't know if I can map to
  69. absolute key position instead of value.) The white keys are on the second
  70. row, TAB to BACKSLASH, starting with note F3. The black keys map to the top
  71. row, '1' to BACKSPACE, starting with F#3. 'r' is middle C. Close the
  72. window or press ESCAPE to quit the program. Key velocity (note
  73. amplitude) varies vertically on the keyboard image, with minimum velocity
  74. at the top of a key and maximum velocity at bottom.
  75. Default Midi output, no device_id given, is to the default output device
  76. for the computer.
  77. """
  78. # A note to new pygamers:
  79. #
  80. # All the midi module stuff is in this function. It is unnecessary to
  81. # understand how the keyboard display works to appreciate how midi
  82. # messages are sent.
  83. # The keyboard is drawn by a Keyboard instance. This instance maps Midi
  84. # notes to musical keyboard keys. A regions surface maps window position
  85. # to (Midi note, velocity) pairs. A key_mapping dictionary does the same
  86. # for computer keyboard keys. Midi sound is controlled with direct method
  87. # calls to a pygame.midi.Output instance.
  88. #
  89. # Things to consider when using pygame.midi:
  90. #
  91. # 1) Initialize the midi module with a to pygame.midi.init().
  92. # 2) Create a midi.Output instance for the desired output device port.
  93. # 3) Select instruments with set_instrument() method calls.
  94. # 4) Play notes with note_on() and note_off() method calls.
  95. # 5) Call pygame.midi.Quit() when finished. Though the midi module tries
  96. # to ensure that midi is properly shut down, it is best to do it
  97. # explicitly. A try/finally statement is the safest way to do this.
  98. #
  99. GRAND_PIANO = 0
  100. CHURCH_ORGAN = 19
  101. instrument = CHURCH_ORGAN
  102. #instrument = GRAND_PIANO
  103. start_note = 53 # F3 (white key note), start_note != 0
  104. n_notes = 24 # Two octaves (14 white keys)
  105. bg_color = Color('slategray')
  106. key_mapping = make_key_mapping([K_TAB, K_1, K_q, K_2, K_w, K_3, K_e, K_r,
  107. K_5, K_t, K_6, K_y, K_u, K_8, K_i, K_9,
  108. K_o, K_0, K_p, K_LEFTBRACKET, K_EQUALS,
  109. K_RIGHTBRACKET, K_BACKSPACE, K_BACKSLASH],
  110. start_note)
  111. pygame.init()
  112. pygame.midi.init()
  113. _print_device_info()
  114. if device_id is None:
  115. port = pygame.midi.get_default_output_id()
  116. else:
  117. port = device_id
  118. print ("using output_id :%s:" % port)
  119. midi_out = pygame.midi.Output(port, 0)
  120. try:
  121. midi_out.set_instrument(instrument)
  122. keyboard = Keyboard(start_note, n_notes)
  123. screen = pygame.display.set_mode(keyboard.rect.size)
  124. screen.fill(bg_color)
  125. pygame.display.flip()
  126. background = pygame.Surface(screen.get_size())
  127. background.fill(bg_color)
  128. dirty_rects = []
  129. keyboard.draw(screen, background, dirty_rects)
  130. pygame.display.update(dirty_rects)
  131. regions = pygame.Surface(screen.get_size()) # initial color (0,0,0)
  132. keyboard.map_regions(regions)
  133. pygame.event.set_blocked(MOUSEMOTION)
  134. repeat = 1
  135. mouse_note = 0
  136. on_notes = set()
  137. while 1:
  138. update_rects = None
  139. e = pygame.event.wait()
  140. if e.type == pygame.MOUSEBUTTONDOWN:
  141. mouse_note, velocity, __, __ = regions.get_at(e.pos)
  142. if mouse_note and mouse_note not in on_notes:
  143. keyboard.key_down(mouse_note)
  144. midi_out.note_on(mouse_note, velocity)
  145. on_notes.add(mouse_note)
  146. else:
  147. mouse_note = 0
  148. elif e.type == pygame.MOUSEBUTTONUP:
  149. if mouse_note:
  150. midi_out.note_off(mouse_note)
  151. keyboard.key_up(mouse_note)
  152. on_notes.remove(mouse_note)
  153. mouse_note = 0
  154. elif e.type == pygame.QUIT:
  155. break
  156. elif e.type == pygame.KEYDOWN:
  157. if e.key == pygame.K_ESCAPE:
  158. break
  159. try:
  160. note, velocity = key_mapping[e.key]
  161. except KeyError:
  162. pass
  163. else:
  164. if note not in on_notes:
  165. keyboard.key_down(note)
  166. midi_out.note_on(note, velocity)
  167. on_notes.add(note)
  168. elif e.type == pygame.KEYUP:
  169. try:
  170. note, __ = key_mapping[e.key]
  171. except KeyError:
  172. pass
  173. else:
  174. if note in on_notes and note != mouse_note:
  175. keyboard.key_up(note)
  176. midi_out.note_off(note, 0)
  177. on_notes.remove(note)
  178. dirty_rects = []
  179. keyboard.draw(screen, background, dirty_rects)
  180. pygame.display.update(dirty_rects)
  181. finally:
  182. del midi_out
  183. pygame.midi.quit()
  184. def make_key_mapping(key_list, start_note):
  185. """Return a dictionary of (note, velocity) by computer keyboard key code"""
  186. mapping = {}
  187. for i in range(len(key_list)):
  188. mapping[key_list[i]] = (start_note + i, 127)
  189. return mapping
  190. class NullKey(object):
  191. """A dummy key that ignores events passed to it by other keys
  192. A NullKey instance is the left key instance used by default
  193. for the left most keyboard key.
  194. """
  195. def _right_white_down(self):
  196. pass
  197. def _right_white_up(self):
  198. pass
  199. def _right_black_down(self):
  200. pass
  201. def _right_black_up(self):
  202. pass
  203. null_key = NullKey()
  204. def key_class(updates, image_strip, image_rects, is_white_key=True):
  205. """Return a keyboard key widget class
  206. Arguments:
  207. updates - a set into which a key instance adds itself if it needs
  208. redrawing.
  209. image_strip - The surface containing the images of all key states.
  210. image_rects - A list of Rects giving the regions within image_strip that
  211. are relevant to this key class.
  212. is_white_key (default True) - Set false if this is a black key.
  213. This function automates the creation of a key widget class for the
  214. three basic key types. A key has two basic states, up or down (
  215. depressed). Corresponding up and down images are drawn for each
  216. of these two states. But to give the illusion of depth, a key
  217. may have shadows cast upon it by the adjacent keys to its right.
  218. These shadows change depending on the up/down state of the key and
  219. its neighbors. So a key may support multiple images and states
  220. depending on the shadows. A key type is determined by the length
  221. of image_rects and the value of is_white.
  222. """
  223. # Naming convention: Variables used by the Key class as part of a
  224. # closure start with 'c_'.
  225. # State logic and shadows:
  226. #
  227. # A key may cast a shadow upon the key to its left. A black key casts a
  228. # shadow on an adjacent white key. The shadow changes depending of whether
  229. # the black or white key is depressed. A white key casts a shadow on the
  230. # white key to its left if it is up and the left key is down. Therefore
  231. # a keys state, and image it will draw, is determined entirely by its
  232. # itself and the key immediately adjacent to it on the right. A white key
  233. # is always assumed to have an adjacent white key.
  234. #
  235. # There can be up to eight key states, representing all permutations
  236. # of the three fundamental states of self up/down, adjacent white
  237. # right up/down, adjacent black up/down.
  238. #
  239. down_state_none = 0
  240. down_state_self = 1
  241. down_state_white = down_state_self << 1
  242. down_state_self_white = down_state_self | down_state_white
  243. down_state_black = down_state_white << 1
  244. down_state_self_black = down_state_self | down_state_black
  245. down_state_white_black = down_state_white | down_state_black
  246. down_state_all = down_state_self | down_state_white_black
  247. # Some values used in the class.
  248. #
  249. c_down_state_initial = down_state_none
  250. c_down_state_rect_initial = image_rects[0]
  251. c_down_state_self = down_state_self
  252. c_updates = updates
  253. c_image_strip = image_strip
  254. c_width, c_height = image_rects[0].size
  255. # A key propagates its up/down state change to the adjacent white key on
  256. # the left by calling the adjacent key's _right_black_down or
  257. # _right_white_down method.
  258. #
  259. if is_white_key:
  260. key_color = 'white'
  261. else:
  262. key_color = 'black'
  263. c_notify_down_method = "_right_%s_down" % key_color
  264. c_notify_up_method = "_right_%s_up" % key_color
  265. # Images:
  266. #
  267. # A black key only needs two images, for the up and down states. Its
  268. # appearance is unaffected by the adjacent keys to its right, which cast no
  269. # shadows upon it.
  270. #
  271. # A white key with a no adjacent black to its right only needs three
  272. # images, for self up, self down, and both self and adjacent white down.
  273. #
  274. # A white key with both a black and white key to its right needs six
  275. # images: self up, self up and adjacent black down, self down, self and
  276. # adjacent white down, self and adjacent black down, and all three down.
  277. #
  278. # Each 'c_event' dictionary maps the current key state to a new key state,
  279. # along with corresponding image, for the related event. If no redrawing
  280. # is required for the state change then the image rect is simply None.
  281. #
  282. c_event_down = {down_state_none: (down_state_self, image_rects[1])}
  283. c_event_up = {down_state_self: (down_state_none, image_rects[0])}
  284. c_event_right_white_down = {
  285. down_state_none: (down_state_none, None),
  286. down_state_self: (down_state_self, None)}
  287. c_event_right_white_up = c_event_right_white_down.copy()
  288. c_event_right_black_down = c_event_right_white_down.copy()
  289. c_event_right_black_up = c_event_right_white_down.copy()
  290. if len(image_rects) > 2:
  291. c_event_down[down_state_white] = (
  292. down_state_self_white, image_rects[2])
  293. c_event_up[down_state_self_white] = (down_state_white, image_rects[0])
  294. c_event_right_white_down[down_state_none] = (down_state_white, None)
  295. c_event_right_white_down[down_state_self] = (
  296. down_state_self_white, image_rects[2])
  297. c_event_right_white_up[down_state_white] = (down_state_none, None)
  298. c_event_right_white_up[down_state_self_white] = (
  299. down_state_self, image_rects[1])
  300. c_event_right_black_down[down_state_white] = (
  301. down_state_white, None)
  302. c_event_right_black_down[down_state_self_white] = (
  303. down_state_self_white, None)
  304. c_event_right_black_up[down_state_white] = (
  305. down_state_white, None)
  306. c_event_right_black_up[down_state_self_white] = (
  307. down_state_self_white, None)
  308. if len(image_rects) > 3:
  309. c_event_down[down_state_black] = (
  310. down_state_self_black, image_rects[4])
  311. c_event_down[down_state_white_black] = (down_state_all, image_rects[5])
  312. c_event_up[down_state_self_black] = (down_state_black, image_rects[3])
  313. c_event_up[down_state_all] = (down_state_white_black, image_rects[3])
  314. c_event_right_white_down[down_state_black] = (
  315. down_state_white_black, None)
  316. c_event_right_white_down[down_state_self_black] = (
  317. down_state_all, image_rects[5])
  318. c_event_right_white_up[down_state_white_black] = (
  319. down_state_black, None)
  320. c_event_right_white_up[down_state_all] = (
  321. down_state_self_black, image_rects[4])
  322. c_event_right_black_down[down_state_none] = (
  323. down_state_black, image_rects[3])
  324. c_event_right_black_down[down_state_self] = (
  325. down_state_self_black, image_rects[4])
  326. c_event_right_black_down[down_state_white] = (
  327. down_state_white_black, image_rects[3])
  328. c_event_right_black_down[down_state_self_white] = (
  329. down_state_all, image_rects[5])
  330. c_event_right_black_up[down_state_black] = (
  331. down_state_none, image_rects[0])
  332. c_event_right_black_up[down_state_self_black] = (
  333. down_state_self, image_rects[1])
  334. c_event_right_black_up[down_state_white_black] = (
  335. down_state_white, image_rects[0])
  336. c_event_right_black_up[down_state_all] = (
  337. down_state_self_white, image_rects[2])
  338. class Key(object):
  339. """A key widget, maintains key state and draws the key's image
  340. Constructor arguments:
  341. ident - A unique key identifier. Any immutable type suitable as a key.
  342. posn - The location of the key on the display surface.
  343. key_left - Optional, the adjacent white key to the left. Changes in
  344. up and down state are propagated to that key.
  345. A key has an associated position and state. Related to state is the
  346. image drawn. State changes are managed with method calls, one method
  347. per event type. The up and down event methods are public. Other
  348. internal methods are for passing on state changes to the key_left
  349. key instance.
  350. """
  351. is_white = is_white_key
  352. def __init__(self, ident, posn, key_left = None):
  353. """Return a new Key instance
  354. The initial state is up, with all adjacent keys to the right also
  355. up.
  356. """
  357. if key_left is None:
  358. key_left = null_key
  359. rect = Rect(posn[0], posn[1], c_width, c_height)
  360. self.rect = rect
  361. self._state = c_down_state_initial
  362. self._source_rect = c_down_state_rect_initial
  363. self._ident = ident
  364. self._hash = hash(ident)
  365. self._notify_down = getattr(key_left, c_notify_down_method)
  366. self._notify_up = getattr(key_left, c_notify_up_method)
  367. self._key_left = key_left
  368. self._background_rect = Rect(rect.left, rect.bottom - 10,
  369. c_width, 10)
  370. c_updates.add(self)
  371. def down(self):
  372. """Signal that this key has been depressed (is down)"""
  373. self._state, source_rect = c_event_down[self._state]
  374. if source_rect is not None:
  375. self._source_rect = source_rect
  376. c_updates.add(self)
  377. self._notify_down()
  378. def up(self):
  379. """Signal that this key has been released (is up)"""
  380. self._state, source_rect = c_event_up[self._state]
  381. if source_rect is not None:
  382. self._source_rect = source_rect
  383. c_updates.add(self)
  384. self._notify_up()
  385. def _right_white_down(self):
  386. """Signal that the adjacent white key has been depressed
  387. This method is for internal propagation of events between
  388. key instances.
  389. """
  390. self._state, source_rect = c_event_right_white_down[self._state]
  391. if source_rect is not None:
  392. self._source_rect = source_rect
  393. c_updates.add(self)
  394. def _right_white_up(self):
  395. """Signal that the adjacent white key has been released
  396. This method is for internal propagation of events between
  397. key instances.
  398. """
  399. self._state, source_rect = c_event_right_white_up[self._state]
  400. if source_rect is not None:
  401. self._source_rect = source_rect
  402. c_updates.add(self)
  403. def _right_black_down(self):
  404. """Signal that the adjacent black key has been depressed
  405. This method is for internal propagation of events between
  406. key instances.
  407. """
  408. self._state, source_rect = c_event_right_black_down[self._state]
  409. if source_rect is not None:
  410. self._source_rect = source_rect
  411. c_updates.add(self)
  412. def _right_black_up(self):
  413. """Signal that the adjacent black key has been released
  414. This method is for internal propagation of events between
  415. key instances.
  416. """
  417. self._state, source_rect = c_event_right_black_up[self._state]
  418. if source_rect is not None:
  419. self._source_rect = source_rect
  420. c_updates.add(self)
  421. def __eq__(self, other):
  422. """True if same identifiers"""
  423. return self._ident == other._ident
  424. def __hash__(self):
  425. """Return the immutable hash value"""
  426. return self._hash
  427. def __str__(self):
  428. """Return the key's identifier and position as a string"""
  429. return ("<Key %s at (%d, %d)>" %
  430. (self._ident, self.rect.top, self.rect.left))
  431. def draw(self, surf, background, dirty_rects):
  432. """Redraw the key on the surface surf
  433. The background is redrawn. The altered region is added to the
  434. dirty_rects list.
  435. """
  436. surf.blit(background, self._background_rect, self._background_rect)
  437. surf.blit(c_image_strip, self.rect, self._source_rect)
  438. dirty_rects.append(self.rect)
  439. return Key
  440. def key_images():
  441. """Return a keyboard keys image strip and a mapping of image locations
  442. The return tuple is a surface and a dictionary of rects mapped to key
  443. type.
  444. This function encapsulates the constants relevant to the keyboard image
  445. file. There are five key types. One is the black key. The other four
  446. white keys are determined by the proximity of the black keys. The plain
  447. white key has no black key adjacent to it. A white-left and white-right
  448. key has a black key to the left or right of it respectively. A white-center
  449. key has a black key on both sides. A key may have up to six related
  450. images depending on the state of adjacent keys to its right.
  451. """
  452. my_dir = os.path.split(os.path.abspath(__file__))[0]
  453. strip_file = os.path.join(my_dir, 'data', 'midikeys.png')
  454. white_key_width = 42
  455. white_key_height = 160
  456. black_key_width = 22
  457. black_key_height = 94
  458. strip = pygame.image.load(strip_file)
  459. names = [
  460. 'black none', 'black self',
  461. 'white none', 'white self', 'white self-white',
  462. 'white-left none', 'white-left self', 'white-left black',
  463. 'white-left self-black', 'white-left self-white', 'white-left all',
  464. 'white-center none', 'white-center self',
  465. 'white-center black', 'white-center self-black',
  466. 'white-center self-white', 'white-center all',
  467. 'white-right none', 'white-right self', 'white-right self-white']
  468. rects = {}
  469. for i in range(2):
  470. rects[names[i]] = Rect(i * white_key_width, 0,
  471. black_key_width, black_key_height)
  472. for i in range(2, len(names)):
  473. rects[names[i]] = Rect(i * white_key_width, 0,
  474. white_key_width, white_key_height)
  475. return strip, rects
  476. class Keyboard(object):
  477. """Musical keyboard widget
  478. Constructor arguments:
  479. start_note: midi note value of the starting note on the keyboard.
  480. n_notes: number of notes (keys) on the keyboard.
  481. A Keyboard instance draws the musical keyboard and maintains the state of
  482. all the keyboard keys. Individual keys can be in a down (depressed) or
  483. up (released) state.
  484. """
  485. _image_strip, _rects = key_images()
  486. white_key_width, white_key_height = _rects['white none'].size
  487. black_key_width, black_key_height = _rects['black none'].size
  488. _updates = set()
  489. # There are five key classes, representing key shape:
  490. # black key (BlackKey), plain white key (WhiteKey), white key to the left
  491. # of a black key (WhiteKeyLeft), white key between two black keys
  492. # (WhiteKeyCenter), and white key to the right of a black key
  493. # (WhiteKeyRight).
  494. BlackKey = key_class(_updates,
  495. _image_strip,
  496. [_rects['black none'], _rects['black self']],
  497. False)
  498. WhiteKey = key_class(_updates,
  499. _image_strip,
  500. [_rects['white none'],
  501. _rects['white self'],
  502. _rects['white self-white']])
  503. WhiteKeyLeft = key_class(_updates,
  504. _image_strip,
  505. [_rects['white-left none'],
  506. _rects['white-left self'],
  507. _rects['white-left self-white'],
  508. _rects['white-left black'],
  509. _rects['white-left self-black'],
  510. _rects['white-left all']])
  511. WhiteKeyCenter = key_class(_updates,
  512. _image_strip,
  513. [_rects['white-center none'],
  514. _rects['white-center self'],
  515. _rects['white-center self-white'],
  516. _rects['white-center black'],
  517. _rects['white-center self-black'],
  518. _rects['white-center all']])
  519. WhiteKeyRight = key_class(_updates,
  520. _image_strip,
  521. [_rects['white-right none'],
  522. _rects['white-right self'],
  523. _rects['white-right self-white']])
  524. def __init__(self, start_note, n_notes):
  525. """Return a new Keyboard instance with n_note keys"""
  526. self._start_note = start_note
  527. self._end_note = start_note + n_notes - 1
  528. self._add_keys()
  529. def _add_keys(self):
  530. """Populate the keyboard with key instances
  531. Set the _keys and rect attributes.
  532. """
  533. # Keys are entered in a list, where index is Midi note. Since there are
  534. # only 128 possible Midi notes the list length is managable. Unassigned
  535. # note positions should never be accessed, so are set None to ensure
  536. # the bug is quickly detected.
  537. #
  538. key_map = [None] * 128
  539. start_note = self._start_note
  540. end_note = self._end_note
  541. black_offset = self.black_key_width // 2
  542. prev_white_key = None
  543. x = y = 0
  544. if is_white_key(start_note):
  545. is_prev_white = True
  546. else:
  547. x += black_offset
  548. is_prev_white = False
  549. for note in range(start_note, end_note + 1):
  550. ident = note # For now notes uniquely identify keyboard keys.
  551. if is_white_key(note):
  552. if is_prev_white:
  553. if note == end_note or is_white_key(note + 1):
  554. key = self.WhiteKey(ident, (x, y), prev_white_key)
  555. else:
  556. key = self.WhiteKeyLeft(ident, (x, y), prev_white_key)
  557. else:
  558. if note == end_note or is_white_key(note + 1):
  559. key = self.WhiteKeyRight(ident, (x, y), prev_white_key)
  560. else:
  561. key = self.WhiteKeyCenter(ident,
  562. (x, y),
  563. prev_white_key)
  564. is_prev_white = True
  565. x += self.white_key_width
  566. prev_white_key = key
  567. else:
  568. key = self.BlackKey(ident,
  569. (x - black_offset, y),
  570. prev_white_key)
  571. is_prev_white = False
  572. key_map[note] = key
  573. self._keys = key_map
  574. kb_width = key_map[self._end_note].rect.right
  575. kb_height = self.white_key_height
  576. self.rect = Rect(0, 0, kb_width, kb_height)
  577. def map_regions(self, regions):
  578. """Draw the key regions onto surface regions.
  579. Regions must have at least 3 byte pixels. Each pixel of the keyboard
  580. rectangle is set to the color (note, velocity, 0). The regions surface
  581. must be at least as large as (0, 0, self.rect.left, self.rect.bottom)
  582. """
  583. # First draw the white key regions. Then add the overlapping
  584. # black key regions.
  585. #
  586. cutoff = self.black_key_height
  587. black_keys = []
  588. for note in range(self._start_note, self._end_note + 1):
  589. key = self._keys[note]
  590. if key.is_white:
  591. fill_region(regions, note, key.rect, cutoff)
  592. else:
  593. black_keys.append((note, key))
  594. for note, key in black_keys:
  595. fill_region(regions, note, key.rect, cutoff)
  596. def draw(self, surf, background, dirty_rects):
  597. """Redraw all altered keyboard keys"""
  598. changed_keys = self._updates
  599. while changed_keys:
  600. changed_keys.pop().draw(surf, background, dirty_rects)
  601. def key_down(self, note):
  602. """Signal a key down event for note"""
  603. self._keys[note].down()
  604. def key_up(self, note):
  605. """Signal a key up event for note"""
  606. self._keys[note].up()
  607. def fill_region(regions, note, rect, cutoff):
  608. """Fill the region defined by rect with a (note, velocity, 0) color
  609. The velocity varies from a small value at the top of the region to
  610. 127 at the bottom. The vertical region 0 to cutoff is split into
  611. three parts, with velocities 42, 84 and 127. Everything below cutoff
  612. has velocity 127.
  613. """
  614. x, y, width, height = rect
  615. if cutoff is None:
  616. cutoff = height
  617. delta_height = cutoff // 3
  618. regions.fill((note, 42, 0),
  619. (x, y, width, delta_height))
  620. regions.fill((note, 84, 0),
  621. (x, y + delta_height, width, delta_height))
  622. regions.fill((note, 127, 0),
  623. (x, y + 2 * delta_height, width, height - 2 * delta_height))
  624. def is_white_key(note):
  625. """True if note is represented by a white key"""
  626. key_pattern = [True, False, True, True, False, True,
  627. False, True, True, False, True, False]
  628. return key_pattern[(note - 21) % len(key_pattern)]
  629. def usage():
  630. print ("--input [device_id] : Midi message logger")
  631. print ("--output [device_id] : Midi piano keyboard")
  632. print ("--list : list available midi devices")
  633. def main(mode='output', device_id=None):
  634. """Run a Midi example
  635. Arguments:
  636. mode - if 'output' run a midi keyboard output example
  637. 'input' run a midi event logger input example
  638. 'list' list available midi devices
  639. (default 'output')
  640. device_id - midi device number; if None then use the default midi input or
  641. output device for the system
  642. """
  643. if mode == 'input':
  644. input_main(device_id)
  645. elif mode == 'output':
  646. output_main(device_id)
  647. elif mode == 'list':
  648. print_device_info()
  649. else:
  650. raise ValueError("Unknown mode option '%s'" % mode)
  651. if __name__ == '__main__':
  652. try:
  653. device_id = int( sys.argv[-1] )
  654. except:
  655. device_id = None
  656. if "--input" in sys.argv or "-i" in sys.argv:
  657. input_main(device_id)
  658. elif "--output" in sys.argv or "-o" in sys.argv:
  659. output_main(device_id)
  660. elif "--list" in sys.argv or "-l" in sys.argv:
  661. print_device_info()
  662. else:
  663. usage()