midi_test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import unittest
  2. import os
  3. import sys
  4. import time
  5. import pygame
  6. import pygame.midi
  7. import pygame.compat
  8. from pygame.locals import *
  9. class MidiInputTest(unittest.TestCase):
  10. def setUp(self):
  11. pygame.midi.init()
  12. in_id = pygame.midi.get_default_input_id()
  13. if in_id != -1:
  14. self.midi_input = pygame.midi.Input(in_id)
  15. else:
  16. self.midi_input = None
  17. def tearDown(self):
  18. if self.midi_input:
  19. self.midi_input.close()
  20. pygame.midi.quit()
  21. def test_Input(self):
  22. """|tags: interactive|
  23. """
  24. i = pygame.midi.get_default_input_id()
  25. if self.midi_input:
  26. self.assertEqual(self.midi_input.device_id, i)
  27. # try feeding it an input id.
  28. i = pygame.midi.get_default_output_id()
  29. # can handle some invalid input too.
  30. self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, i)
  31. self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, 9009)
  32. self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, -1)
  33. self.assertRaises(TypeError, pygame.midi.Input, "1234")
  34. self.assertRaises(OverflowError, pygame.midi.Input, pow(2, 99))
  35. def test_poll(self):
  36. if not self.midi_input:
  37. self.skipTest('No midi Input device')
  38. self.assertFalse(self.midi_input.poll())
  39. # TODO fake some incoming data
  40. pygame.midi.quit()
  41. self.assertRaises(RuntimeError, self.midi_input.poll)
  42. # set midi_input to None to avoid error in tearDown
  43. self.midi_input = None
  44. def test_read(self):
  45. if not self.midi_input:
  46. self.skipTest('No midi Input device')
  47. read = self.midi_input.read(5)
  48. self.assertEqual(read, [])
  49. # TODO fake some incoming data
  50. pygame.midi.quit()
  51. self.assertRaises(RuntimeError, self.midi_input.read, 52)
  52. # set midi_input to None to avoid error in tearDown
  53. self.midi_input = None
  54. def test_close(self):
  55. if not self.midi_input:
  56. self.skipTest('No midi Input device')
  57. self.assertIsNotNone(self.midi_input._input)
  58. self.midi_input.close()
  59. self.assertIsNone(self.midi_input._input)
  60. class MidiOutputTest(unittest.TestCase):
  61. def setUp(self):
  62. pygame.midi.init()
  63. m_out_id = pygame.midi.get_default_output_id()
  64. if m_out_id != -1:
  65. self.midi_output = pygame.midi.Output(m_out_id)
  66. else:
  67. self.midi_output = None
  68. def tearDown(self):
  69. if self.midi_output:
  70. self.midi_output.close()
  71. pygame.midi.quit()
  72. def test_Output(self):
  73. """|tags: interactive|
  74. """
  75. i = pygame.midi.get_default_output_id()
  76. if self.midi_output:
  77. self.assertEqual(self.midi_output.device_id, i)
  78. # try feeding it an input id.
  79. i = pygame.midi.get_default_input_id()
  80. # can handle some invalid input too.
  81. self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, i)
  82. self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, 9009)
  83. self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, -1)
  84. self.assertRaises(TypeError, pygame.midi.Output,"1234")
  85. self.assertRaises(OverflowError, pygame.midi.Output, pow(2,99))
  86. def test_note_off(self):
  87. """|tags: interactive|
  88. """
  89. if self.midi_output:
  90. out = self.midi_output
  91. out.note_on(5, 30, 0)
  92. out.note_off(5, 30, 0)
  93. with self.assertRaises(ValueError) as cm:
  94. out.note_off(5, 30, 25)
  95. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  96. with self.assertRaises(ValueError) as cm:
  97. out.note_off(5, 30, -1)
  98. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  99. def test_note_on(self):
  100. """|tags: interactive|
  101. """
  102. if self.midi_output:
  103. out = self.midi_output
  104. out.note_on(5, 30, 0)
  105. out.note_on(5, 42, 10)
  106. with self.assertRaises(ValueError) as cm:
  107. out.note_on(5, 30, 25)
  108. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  109. with self.assertRaises(ValueError) as cm:
  110. out.note_on(5, 30, -1)
  111. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  112. def test_set_instrument(self):
  113. if not self.midi_output:
  114. self.skipTest('No midi device')
  115. out = self.midi_output
  116. out.set_instrument(5)
  117. out.set_instrument(42, channel=2)
  118. with self.assertRaises(ValueError) as cm:
  119. out.set_instrument(-6)
  120. self.assertEqual(str(cm.exception), "Undefined instrument id: -6")
  121. with self.assertRaises(ValueError) as cm:
  122. out.set_instrument(156)
  123. self.assertEqual(str(cm.exception), "Undefined instrument id: 156")
  124. with self.assertRaises(ValueError) as cm:
  125. out.set_instrument(5, -1)
  126. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  127. with self.assertRaises(ValueError) as cm:
  128. out.set_instrument(5, 16)
  129. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  130. def test_write(self):
  131. if not self.midi_output:
  132. self.skipTest('No midi device')
  133. out = self.midi_output
  134. out.write([[[0xc0, 0, 0], 20000]])
  135. # is equivalent to
  136. out.write([[[0xc0], 20000]])
  137. # example from the docstring :
  138. # 1. choose program change 1 at time 20000 and
  139. # 2. send note 65 with velocity 100 500 ms later
  140. out.write([
  141. [[0xc0, 0, 0], 20000],
  142. [[0x90, 60, 100], 20500]
  143. ])
  144. out.write([])
  145. verrry_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1024)]
  146. out.write(verrry_long)
  147. too_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1025)]
  148. self.assertRaises(IndexError, out.write, too_long)
  149. # test wrong data
  150. with self.assertRaises(TypeError) as cm:
  151. out.write('Non sens ?')
  152. error_msg = "unsupported operand type(s) for &: 'str' and 'int'"
  153. self.assertEqual(str(cm.exception), error_msg)
  154. with self.assertRaises(TypeError) as cm:
  155. out.write(["Hey what's that?"])
  156. self.assertEqual(str(cm.exception), error_msg)
  157. def test_write_short(self):
  158. """|tags: interactive|
  159. """
  160. if not self.midi_output:
  161. self.skipTest('No midi device')
  162. out = self.midi_output
  163. # program change
  164. out.write_short(0xc0)
  165. # put a note on, then off.
  166. out.write_short(0x90, 65, 100)
  167. out.write_short(0x80, 65, 100)
  168. out.write_short(0x90)
  169. def test_write_sys_ex(self):
  170. if not self.midi_output:
  171. self.skipTest('No midi device')
  172. out = self.midi_output
  173. out.write_sys_ex(pygame.midi.time(),
  174. [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7])
  175. def test_pitch_bend(self):
  176. # FIXME : pitch_bend in the code, but not in documentation
  177. if not self.midi_output:
  178. self.skipTest('No midi device')
  179. out = self.midi_output
  180. with self.assertRaises(ValueError) as cm:
  181. out.pitch_bend(5, channel=-1)
  182. self.assertEqual(str(cm.exception), "Channel not between 0 and 15.")
  183. with self.assertRaises(ValueError) as cm:
  184. out.pitch_bend(5, channel=16)
  185. with self.assertRaises(ValueError) as cm:
  186. out.pitch_bend(-10001, 1)
  187. self.assertEqual(str(cm.exception), "Pitch bend value must be between "
  188. "-8192 and +8191, not -10001.")
  189. with self.assertRaises(ValueError) as cm:
  190. out.pitch_bend(10665, 2)
  191. def test_close(self):
  192. if not self.midi_output:
  193. self.skipTest('No midi device')
  194. self.assertIsNotNone(self.midi_output._output)
  195. self.midi_output.close()
  196. self.assertIsNone(self.midi_output._output)
  197. def test_abort(self):
  198. if not self.midi_output:
  199. self.skipTest('No midi device')
  200. self.assertEqual(self.midi_output._aborted, 0)
  201. self.midi_output.abort()
  202. self.assertEqual(self.midi_output._aborted, 1)
  203. class MidiModuleTest(unittest.TestCase):
  204. def setUp(self):
  205. pygame.midi.init()
  206. def tearDown(self):
  207. pygame.midi.quit()
  208. def test_MidiException(self):
  209. def raiseit():
  210. raise pygame.midi.MidiException('Hello Midi param')
  211. with self.assertRaises(pygame.midi.MidiException) as cm:
  212. raiseit()
  213. self.assertEqual(cm.exception.parameter, 'Hello Midi param')
  214. def test_get_count(self):
  215. c = pygame.midi.get_count()
  216. self.assertIsInstance(c, int)
  217. self.assertTrue(c >= 0)
  218. def test_get_default_input_id(self):
  219. midin_id = pygame.midi.get_default_input_id()
  220. # if there is a not None return make sure it is an int.
  221. self.assertIsInstance(midin_id, int)
  222. self.assertTrue(midin_id >= -1)
  223. pygame.midi.quit()
  224. self.assertRaises(RuntimeError, pygame.midi.get_default_output_id)
  225. def test_get_default_output_id(self):
  226. c = pygame.midi.get_default_output_id()
  227. self.assertIsInstance(c, int)
  228. self.assertTrue(c >= -1)
  229. pygame.midi.quit()
  230. self.assertRaises(RuntimeError, pygame.midi.get_default_output_id)
  231. def test_get_device_info(self):
  232. an_id = pygame.midi.get_default_output_id()
  233. if an_id != -1:
  234. interf, name, input, output, opened = pygame.midi.get_device_info(an_id)
  235. self.assertEqual(output, 1)
  236. self.assertEqual(input, 0)
  237. self.assertEqual(opened, 0)
  238. an_in_id = pygame.midi.get_default_input_id()
  239. if an_in_id != -1:
  240. r = pygame.midi.get_device_info(an_in_id)
  241. # if r is None, it means that the id is out of range.
  242. interf, name, input, output, opened = r
  243. self.assertEqual(output, 0)
  244. self.assertEqual(input, 1)
  245. self.assertEqual(opened, 0)
  246. out_of_range = pygame.midi.get_count()
  247. for num in range(out_of_range):
  248. self.assertIsNotNone(pygame.midi.get_device_info(num))
  249. info = pygame.midi.get_device_info(out_of_range)
  250. self.assertIsNone(info)
  251. def test_init(self):
  252. pygame.midi.quit()
  253. self.assertRaises(RuntimeError, pygame.midi.get_count)
  254. # initialising many times should be fine.
  255. pygame.midi.init()
  256. pygame.midi.init()
  257. pygame.midi.init()
  258. pygame.midi.init()
  259. self.assertTrue(pygame.midi.get_init())
  260. def test_midis2events(self):
  261. midi_data = ([[0xc0, 0, 1, 2], 20000],
  262. [[0x90, 60, 100, 'blablabla'], 20000]
  263. )
  264. events = pygame.midi.midis2events(midi_data, 2)
  265. self.assertEqual(len(events), 2)
  266. for eve in events:
  267. # pygame.event.Event is a function, but ...
  268. self.assertEqual(eve.__class__.__name__, 'Event')
  269. self.assertEqual(eve.vice_id, 2)
  270. # FIXME I don't know what we want for the Event.timestamp
  271. # For now it accepts it accepts int as is:
  272. self.assertIsInstance(eve.timestamp, int)
  273. self.assertEqual(eve.timestamp, 20000)
  274. self.assertEqual(events[1].data3, 'blablabla')
  275. def test_quit(self):
  276. # It is safe to call this more than once.
  277. pygame.midi.quit()
  278. pygame.midi.init()
  279. pygame.midi.quit()
  280. pygame.midi.quit()
  281. pygame.midi.init()
  282. pygame.midi.init()
  283. pygame.midi.quit()
  284. self.assertFalse(pygame.midi.get_init())
  285. def test_get_init(self):
  286. # Already initialized as pygame.midi.init() was called in setUp().
  287. self.assertTrue(pygame.midi.get_init())
  288. def test_time(self):
  289. mtime = pygame.midi.time()
  290. self.assertIsInstance(mtime, int)
  291. # should be close to 2-3... since the timer is just init'd.
  292. self.assertTrue(0 <= mtime < 100)
  293. def test_conversions(self):
  294. """ of frequencies to midi note numbers and ansi note names.
  295. """
  296. from pygame.midi import (
  297. frequency_to_midi, midi_to_frequency, midi_to_ansi_note
  298. )
  299. self.assertEqual(frequency_to_midi(27.5), 21)
  300. self.assertEqual(frequency_to_midi(36.7), 26)
  301. self.assertEqual(frequency_to_midi(4186.0), 108)
  302. self.assertEqual(midi_to_frequency(21), 27.5)
  303. self.assertEqual(midi_to_frequency(26), 36.7)
  304. self.assertEqual(midi_to_frequency(108), 4186.0)
  305. self.assertEqual(midi_to_ansi_note(21), 'A0')
  306. self.assertEqual(midi_to_ansi_note(102), 'F#7')
  307. self.assertEqual(midi_to_ansi_note(108), 'C8')
  308. if __name__ == '__main__':
  309. unittest.main()