1# -*- coding: utf8 -*- 2 3import sys 4import os 5import unittest 6import platform 7 8from pygame.tests.test_utils import example_path, AssertRaisesRegexMixin 9 10import pygame 11from pygame import mixer 12from pygame.compat import unicode_, as_bytes, bytes_ 13 14try: 15 import pathlib 16except ImportError: 17 pathlib = None 18 19IS_PYPY = "PyPy" == platform.python_implementation() 20 21################################### CONSTANTS ################################## 22 23FREQUENCIES = [11025, 22050, 44100, 48000] 24SIZES = [-16, -8, 8, 16] 25if pygame.get_sdl_version()[0] >= 2: 26 SIZES.append(32) 27 28CHANNELS = [1, 2] 29BUFFERS = [3024] 30 31CONFIGS = [ 32 {"frequency": f, "size": s, "channels": c} 33 for f in FREQUENCIES 34 for s in SIZES 35 for c in CHANNELS 36] 37# Using all CONFIGS fails on a Mac; probably older SDL_mixer; we could do: 38# if platform.system() == 'Darwin': 39# But using all CONFIGS is very slow (> 10 sec for example) 40# And probably, we don't need to be so exhaustive, hence: 41 42CONFIG = {"frequency": 22050, "size": -16, "channels": 2} # base config 43if pygame.get_sdl_version()[0] >= 2: 44 # base config 45 CONFIG = {"frequency": 44100, "size": 32, "channels": 2, "allowedchanges": 0} 46 47 48class InvalidBool(object): 49 """To help test invalid bool values.""" 50 51 __nonzero__ = None 52 __bool__ = None 53 54 55############################## MODULE LEVEL TESTS ############################## 56 57 58class MixerModuleTest(unittest.TestCase): 59 def tearDown(self): 60 mixer.quit() 61 mixer.pre_init(0, 0, 0, 0) 62 63 def test_init__keyword_args(self): 64 # note: this test used to loop over all CONFIGS, but it's very slow.. 65 mixer.init(**CONFIG) 66 mixer_conf = mixer.get_init() 67 68 self.assertEqual(mixer_conf[0], CONFIG["frequency"]) 69 # Not all "sizes" are supported on all systems, hence "abs". 70 self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) 71 self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) 72 73 def test_pre_init__keyword_args(self): 74 # note: this test used to loop over all CONFIGS, but it's very slow.. 75 mixer.pre_init(**CONFIG) 76 mixer.init() 77 78 mixer_conf = mixer.get_init() 79 80 self.assertEqual(mixer_conf[0], CONFIG["frequency"]) 81 # Not all "sizes" are supported on all systems, hence "abs". 82 self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) 83 self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) 84 85 def test_pre_init__zero_values(self): 86 # Ensure that argument values of 0 are replaced with 87 # default values. No way to check buffer size though. 88 mixer.pre_init(22050, -8, 1) # Non default values 89 mixer.pre_init(0, 0, 0) # Should reset to default values 90 mixer.init(allowedchanges=0) 91 self.assertEqual(mixer.get_init()[0], 44100) 92 self.assertEqual(mixer.get_init()[1], -16) 93 self.assertGreaterEqual(mixer.get_init()[2], 2) 94 95 def test_init__zero_values(self): 96 # Ensure that argument values of 0 are replaced with 97 # preset values. No way to check buffer size though. 98 mixer.pre_init(44100, 8, 1, allowedchanges=0) # None default values 99 mixer.init(0, 0, 0) 100 self.assertEqual(mixer.get_init(), (44100, 8, 1)) 101 102 @unittest.skip("SDL_mixer bug") 103 def test_get_init__returns_exact_values_used_for_init(self): 104 # fix in 1.9 - I think it's a SDL_mixer bug. 105 106 # TODO: When this bug is fixed, testing through every combination 107 # will be too slow so adjust as necessary, at the moment it 108 # breaks the loop after first failure 109 110 for init_conf in CONFIGS: 111 frequency, size, channels 112 if (frequency, size) == (22050, 16): 113 continue 114 mixer.init(frequency, size, channels) 115 116 mixer_conf = mixer.get_init() 117 118 self.assertEqual(init_conf, mixer_conf) 119 mixer.quit() 120 121 def test_get_init__returns_None_if_mixer_not_initialized(self): 122 self.assertIsNone(mixer.get_init()) 123 124 def test_get_num_channels__defaults_eight_after_init(self): 125 mixer.init() 126 self.assertEqual(mixer.get_num_channels(), 8) 127 128 def test_set_num_channels(self): 129 mixer.init() 130 131 default_num_channels = mixer.get_num_channels() 132 for i in range(1, default_num_channels + 1): 133 mixer.set_num_channels(i) 134 self.assertEqual(mixer.get_num_channels(), i) 135 136 def test_quit(self): 137 """ get_num_channels() Should throw pygame.error if uninitialized 138 after mixer.quit() """ 139 mixer.init() 140 mixer.quit() 141 self.assertRaises(pygame.error, mixer.get_num_channels) 142 143 # TODO: FIXME: appveyor fails here sometimes. 144 @unittest.skipIf(sys.platform.startswith("win"), "See github issue 892.") 145 def test_sound_args(self): 146 def get_bytes(snd): 147 return snd.get_raw() 148 149 mixer.init() 150 151 sample = as_bytes("\x00\xff") * 24 152 wave_path = example_path(os.path.join("data", "house_lo.wav")) 153 uwave_path = unicode_(wave_path) 154 bwave_path = uwave_path.encode(sys.getfilesystemencoding()) 155 snd = mixer.Sound(file=wave_path) 156 self.assertTrue(snd.get_length() > 0.5) 157 snd_bytes = get_bytes(snd) 158 self.assertTrue(len(snd_bytes) > 1000) 159 160 self.assertEqual(get_bytes(mixer.Sound(wave_path)), snd_bytes) 161 162 self.assertEqual(get_bytes(mixer.Sound(file=uwave_path)), snd_bytes) 163 self.assertEqual(get_bytes(mixer.Sound(uwave_path)), snd_bytes) 164 arg_emsg = "Sound takes either 1 positional or 1 keyword argument" 165 166 with self.assertRaises(TypeError) as cm: 167 mixer.Sound() 168 self.assertEqual(str(cm.exception), arg_emsg) 169 with self.assertRaises(TypeError) as cm: 170 mixer.Sound(wave_path, buffer=sample) 171 self.assertEqual(str(cm.exception), arg_emsg) 172 with self.assertRaises(TypeError) as cm: 173 mixer.Sound(sample, file=wave_path) 174 self.assertEqual(str(cm.exception), arg_emsg) 175 with self.assertRaises(TypeError) as cm: 176 mixer.Sound(buffer=sample, file=wave_path) 177 self.assertEqual(str(cm.exception), arg_emsg) 178 179 with self.assertRaises(TypeError) as cm: 180 mixer.Sound(foobar=sample) 181 self.assertEqual(str(cm.exception), "Unrecognized keyword argument 'foobar'") 182 183 snd = mixer.Sound(wave_path, **{}) 184 self.assertEqual(get_bytes(snd), snd_bytes) 185 snd = mixer.Sound(*[], **{"file": wave_path}) 186 187 with self.assertRaises(TypeError) as cm: 188 mixer.Sound([]) 189 self.assertEqual(str(cm.exception), "Unrecognized argument (type list)") 190 191 with self.assertRaises(TypeError) as cm: 192 snd = mixer.Sound(buffer=[]) 193 emsg = "Expected object with buffer interface: got a list" 194 self.assertEqual(str(cm.exception), emsg) 195 196 ufake_path = unicode_("12345678") 197 self.assertRaises(IOError, mixer.Sound, ufake_path) 198 self.assertRaises(IOError, mixer.Sound, "12345678") 199 200 with self.assertRaises(TypeError) as cm: 201 mixer.Sound(buffer=unicode_("something")) 202 emsg = "Unicode object not allowed as buffer object" 203 self.assertEqual(str(cm.exception), emsg) 204 self.assertEqual(get_bytes(mixer.Sound(buffer=sample)), sample) 205 if type(sample) != str: 206 somebytes = get_bytes(mixer.Sound(sample)) 207 # on python 2 we do not allow using string except as file name. 208 self.assertEqual(somebytes, sample) 209 self.assertEqual(get_bytes(mixer.Sound(file=bwave_path)), snd_bytes) 210 self.assertEqual(get_bytes(mixer.Sound(bwave_path)), snd_bytes) 211 212 snd = mixer.Sound(wave_path) 213 with self.assertRaises(TypeError) as cm: 214 mixer.Sound(wave_path, array=snd) 215 self.assertEqual(str(cm.exception), arg_emsg) 216 with self.assertRaises(TypeError) as cm: 217 mixer.Sound(buffer=sample, array=snd) 218 self.assertEqual(str(cm.exception), arg_emsg) 219 snd2 = mixer.Sound(array=snd) 220 self.assertEqual(snd.get_raw(), snd2.get_raw()) 221 222 def test_sound_unicode(self): 223 """test non-ASCII unicode path""" 224 mixer.init() 225 import shutil 226 227 ep = unicode_(example_path("data")) 228 temp_file = os.path.join(ep, u"你好.wav") 229 org_file = os.path.join(ep, u"house_lo.wav") 230 shutil.copy(org_file, temp_file) 231 try: 232 with open(temp_file, "rb") as f: 233 pass 234 except IOError: 235 raise unittest.SkipTest("the path cannot be opened") 236 237 try: 238 sound = mixer.Sound(temp_file) 239 del sound 240 finally: 241 os.remove(temp_file) 242 243 @unittest.skipIf( 244 os.environ.get("SDL_AUDIODRIVER") == "disk", 245 "this test fails without real sound card", 246 ) 247 def test_array_keyword(self): 248 try: 249 from numpy import ( 250 array, 251 arange, 252 zeros, 253 int8, 254 uint8, 255 int16, 256 uint16, 257 int32, 258 uint32, 259 ) 260 except ImportError: 261 self.skipTest("requires numpy") 262 263 freq = 22050 264 format_list = [-8, 8, -16, 16] 265 channels_list = [1, 2] 266 267 a_lists = dict((f, []) for f in format_list) 268 a32u_mono = arange(0, 256, 1, uint32) 269 a16u_mono = a32u_mono.astype(uint16) 270 a8u_mono = a32u_mono.astype(uint8) 271 au_list_mono = [(1, a) for a in [a8u_mono, a16u_mono, a32u_mono]] 272 for format in format_list: 273 if format > 0: 274 a_lists[format].extend(au_list_mono) 275 a32s_mono = arange(-128, 128, 1, int32) 276 a16s_mono = a32s_mono.astype(int16) 277 a8s_mono = a32s_mono.astype(int8) 278 as_list_mono = [(1, a) for a in [a8s_mono, a16s_mono, a32s_mono]] 279 for format in format_list: 280 if format < 0: 281 a_lists[format].extend(as_list_mono) 282 a32u_stereo = zeros([a32u_mono.shape[0], 2], uint32) 283 a32u_stereo[:, 0] = a32u_mono 284 a32u_stereo[:, 1] = 255 - a32u_mono 285 a16u_stereo = a32u_stereo.astype(uint16) 286 a8u_stereo = a32u_stereo.astype(uint8) 287 au_list_stereo = [(2, a) for a in [a8u_stereo, a16u_stereo, a32u_stereo]] 288 for format in format_list: 289 if format > 0: 290 a_lists[format].extend(au_list_stereo) 291 a32s_stereo = zeros([a32s_mono.shape[0], 2], int32) 292 a32s_stereo[:, 0] = a32s_mono 293 a32s_stereo[:, 1] = -1 - a32s_mono 294 a16s_stereo = a32s_stereo.astype(int16) 295 a8s_stereo = a32s_stereo.astype(int8) 296 as_list_stereo = [(2, a) for a in [a8s_stereo, a16s_stereo, a32s_stereo]] 297 for format in format_list: 298 if format < 0: 299 a_lists[format].extend(as_list_stereo) 300 301 for format in format_list: 302 for channels in channels_list: 303 try: 304 mixer.init(freq, format, channels) 305 except pygame.error: 306 # Some formats (e.g. 16) may not be supported. 307 continue 308 try: 309 __, f, c = mixer.get_init() 310 if f != format or c != channels: 311 # Some formats (e.g. -8) may not be supported. 312 continue 313 for c, a in a_lists[format]: 314 self._test_array_argument(format, a, c == channels) 315 finally: 316 mixer.quit() 317 318 def _test_array_argument(self, format, a, test_pass): 319 from numpy import array, all as all_ 320 321 try: 322 snd = mixer.Sound(array=a) 323 except ValueError: 324 if not test_pass: 325 return 326 self.fail("Raised ValueError: Format %i, dtype %s" % (format, a.dtype)) 327 if not test_pass: 328 self.fail( 329 "Did not raise ValueError: Format %i, dtype %s" % (format, a.dtype) 330 ) 331 a2 = array(snd) 332 a3 = a.astype(a2.dtype) 333 lshift = abs(format) - 8 * a.itemsize 334 if lshift >= 0: 335 # This is asymmetric with respect to downcasting. 336 a3 <<= lshift 337 self.assertTrue(all_(a2 == a3), "Format %i, dtype %s" % (format, a.dtype)) 338 339 def _test_array_interface_fail(self, a): 340 self.assertRaises(ValueError, mixer.Sound, array=a) 341 342 def test_array_interface(self): 343 mixer.init(22050, -16, 1, allowedchanges=0) 344 snd = mixer.Sound(buffer=as_bytes("\x00\x7f") * 20) 345 d = snd.__array_interface__ 346 self.assertTrue(isinstance(d, dict)) 347 if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: 348 typestr = "<i2" 349 else: 350 typestr = ">i2" 351 self.assertEqual(d["typestr"], typestr) 352 self.assertEqual(d["shape"], (20,)) 353 self.assertEqual(d["strides"], (2,)) 354 self.assertEqual(d["data"], (snd._samples_address, False)) 355 356 @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") 357 @unittest.skipIf(IS_PYPY, "pypy2 no likey") 358 def test_newbuf__one_channel(self): 359 mixer.init(22050, -16, 1) 360 self._NEWBUF_export_check() 361 362 @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") 363 @unittest.skipIf(IS_PYPY, "pypy2 no likey") 364 def test_newbuf__twho_channel(self): 365 mixer.init(22050, -16, 2) 366 self._NEWBUF_export_check() 367 368 def _NEWBUF_export_check(self): 369 freq, fmt, channels = mixer.get_init() 370 ndim = 1 if (channels == 1) else 2 371 itemsize = abs(fmt) // 8 372 formats = { 373 8: "B", 374 -8: "b", 375 16: "=H", 376 -16: "=h", 377 32: "=I", 378 -32: "=i", # 32 and 64 for future consideration 379 64: "=Q", 380 -64: "=q", 381 } 382 format = formats[fmt] 383 from pygame.tests.test_utils import buftools 384 385 Exporter = buftools.Exporter 386 Importer = buftools.Importer 387 is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN 388 fsys, frev = ("<", ">") if is_lil_endian else (">", "<") 389 shape = (10, channels)[:ndim] 390 strides = (channels * itemsize, itemsize)[2 - ndim :] 391 exp = Exporter(shape, format=frev + "i") 392 snd = mixer.Sound(array=exp) 393 buflen = len(exp) * itemsize * channels 394 imp = Importer(snd, buftools.PyBUF_SIMPLE) 395 self.assertEqual(imp.ndim, 0) 396 self.assertTrue(imp.format is None) 397 self.assertEqual(imp.len, buflen) 398 self.assertEqual(imp.itemsize, itemsize) 399 self.assertTrue(imp.shape is None) 400 self.assertTrue(imp.strides is None) 401 self.assertTrue(imp.suboffsets is None) 402 self.assertFalse(imp.readonly) 403 self.assertEqual(imp.buf, snd._samples_address) 404 imp = Importer(snd, buftools.PyBUF_WRITABLE) 405 self.assertEqual(imp.ndim, 0) 406 self.assertTrue(imp.format is None) 407 self.assertEqual(imp.len, buflen) 408 self.assertEqual(imp.itemsize, itemsize) 409 self.assertTrue(imp.shape is None) 410 self.assertTrue(imp.strides is None) 411 self.assertTrue(imp.suboffsets is None) 412 self.assertFalse(imp.readonly) 413 self.assertEqual(imp.buf, snd._samples_address) 414 imp = Importer(snd, buftools.PyBUF_FORMAT) 415 self.assertEqual(imp.ndim, 0) 416 self.assertEqual(imp.format, format) 417 self.assertEqual(imp.len, buflen) 418 self.assertEqual(imp.itemsize, itemsize) 419 self.assertTrue(imp.shape is None) 420 self.assertTrue(imp.strides is None) 421 self.assertTrue(imp.suboffsets is None) 422 self.assertFalse(imp.readonly) 423 self.assertEqual(imp.buf, snd._samples_address) 424 imp = Importer(snd, buftools.PyBUF_ND) 425 self.assertEqual(imp.ndim, ndim) 426 self.assertTrue(imp.format is None) 427 self.assertEqual(imp.len, buflen) 428 self.assertEqual(imp.itemsize, itemsize) 429 self.assertEqual(imp.shape, shape) 430 self.assertTrue(imp.strides is None) 431 self.assertTrue(imp.suboffsets is None) 432 self.assertFalse(imp.readonly) 433 self.assertEqual(imp.buf, snd._samples_address) 434 imp = Importer(snd, buftools.PyBUF_STRIDES) 435 self.assertEqual(imp.ndim, ndim) 436 self.assertTrue(imp.format is None) 437 self.assertEqual(imp.len, buflen) 438 self.assertEqual(imp.itemsize, itemsize) 439 self.assertEqual(imp.shape, shape) 440 self.assertEqual(imp.strides, strides) 441 self.assertTrue(imp.suboffsets is None) 442 self.assertFalse(imp.readonly) 443 self.assertEqual(imp.buf, snd._samples_address) 444 imp = Importer(snd, buftools.PyBUF_FULL_RO) 445 self.assertEqual(imp.ndim, ndim) 446 self.assertEqual(imp.format, format) 447 self.assertEqual(imp.len, buflen) 448 self.assertEqual(imp.itemsize, 2) 449 self.assertEqual(imp.shape, shape) 450 self.assertEqual(imp.strides, strides) 451 self.assertTrue(imp.suboffsets is None) 452 self.assertFalse(imp.readonly) 453 self.assertEqual(imp.buf, snd._samples_address) 454 imp = Importer(snd, buftools.PyBUF_FULL_RO) 455 self.assertEqual(imp.ndim, ndim) 456 self.assertEqual(imp.format, format) 457 self.assertEqual(imp.len, buflen) 458 self.assertEqual(imp.itemsize, itemsize) 459 self.assertEqual(imp.shape, exp.shape) 460 self.assertEqual(imp.strides, strides) 461 self.assertTrue(imp.suboffsets is None) 462 self.assertFalse(imp.readonly) 463 self.assertEqual(imp.buf, snd._samples_address) 464 imp = Importer(snd, buftools.PyBUF_C_CONTIGUOUS) 465 self.assertEqual(imp.ndim, ndim) 466 self.assertTrue(imp.format is None) 467 self.assertEqual(imp.strides, strides) 468 imp = Importer(snd, buftools.PyBUF_ANY_CONTIGUOUS) 469 self.assertEqual(imp.ndim, ndim) 470 self.assertTrue(imp.format is None) 471 self.assertEqual(imp.strides, strides) 472 if ndim == 1: 473 imp = Importer(snd, buftools.PyBUF_F_CONTIGUOUS) 474 self.assertEqual(imp.ndim, 1) 475 self.assertTrue(imp.format is None) 476 self.assertEqual(imp.strides, strides) 477 else: 478 self.assertRaises(BufferError, Importer, snd, buftools.PyBUF_F_CONTIGUOUS) 479 480 def todo_test_fadeout(self): 481 482 # __doc__ (as of 2008-08-02) for pygame.mixer.fadeout: 483 484 # pygame.mixer.fadeout(time): return None 485 # fade out the volume on all sounds before stopping 486 # 487 # This will fade out the volume on all active channels over the time 488 # argument in milliseconds. After the sound is muted the playback will 489 # stop. 490 # 491 492 self.fail() 493 494 def test_find_channel(self): 495 # __doc__ (as of 2008-08-02) for pygame.mixer.find_channel: 496 497 # pygame.mixer.find_channel(force=False): return Channel 498 # find an unused channel 499 mixer.init() 500 501 filename = example_path(os.path.join("data", "house_lo.wav")) 502 sound = mixer.Sound(file=filename) 503 504 num_channels = mixer.get_num_channels() 505 506 if num_channels > 0: 507 found_channel = mixer.find_channel() 508 self.assertIsNotNone(found_channel) 509 510 # try playing on all channels 511 channels = [] 512 for channel_id in range(0, num_channels): 513 channel = mixer.Channel(channel_id) 514 channel.play(sound) 515 channels.append(channel) 516 517 # should fail without being forceful 518 found_channel = mixer.find_channel() 519 self.assertIsNone(found_channel) 520 521 # try forcing without keyword 522 found_channel = mixer.find_channel(True) 523 self.assertIsNotNone(found_channel) 524 525 # try forcing with keyword 526 found_channel = mixer.find_channel(force=True) 527 self.assertIsNotNone(found_channel) 528 529 for channel in channels: 530 channel.stop() 531 found_channel = mixer.find_channel() 532 self.assertIsNotNone(found_channel) 533 534 535 536 537 def todo_test_get_busy(self): 538 539 # __doc__ (as of 2008-08-02) for pygame.mixer.get_busy: 540 541 # pygame.mixer.get_busy(): return bool 542 # test if any sound is being mixed 543 # 544 # Returns True if the mixer is busy mixing any channels. If the mixer 545 # is idle then this return False. 546 # 547 548 self.fail() 549 550 def todo_test_pause(self): 551 552 # __doc__ (as of 2008-08-02) for pygame.mixer.pause: 553 554 # pygame.mixer.pause(): return None 555 # temporarily stop playback of all sound channels 556 # 557 # This will temporarily stop all playback on the active mixer 558 # channels. The playback can later be resumed with 559 # pygame.mixer.unpause() 560 # 561 562 self.fail() 563 564 def test_set_reserved(self): 565 566 # __doc__ (as of 2008-08-02) for pygame.mixer.set_reserved: 567 568 # pygame.mixer.set_reserved(count): return count 569 mixer.init() 570 default_num_channels = mixer.get_num_channels() 571 572 # try reserving all the channels 573 result = mixer.set_reserved(default_num_channels) 574 self.assertEqual(result, default_num_channels) 575 576 # try reserving all the channels + 1 577 result = mixer.set_reserved(default_num_channels + 1) 578 # should still be default 579 self.assertEqual(result, default_num_channels) 580 581 # try unreserving all 582 result = mixer.set_reserved(0) 583 # should still be default 584 self.assertEqual(result, 0) 585 586 # try reserving half 587 result = mixer.set_reserved(int(default_num_channels/2)) 588 # should still be default 589 self.assertEqual(result, int(default_num_channels/2)) 590 591 def todo_test_stop(self): 592 593 # __doc__ (as of 2008-08-02) for pygame.mixer.stop: 594 595 # pygame.mixer.stop(): return None 596 # stop playback of all sound channels 597 # 598 # This will stop all playback of all active mixer channels. 599 600 self.fail() 601 602 def todo_test_unpause(self): 603 604 # __doc__ (as of 2008-08-02) for pygame.mixer.unpause: 605 606 # pygame.mixer.unpause(): return None 607 # resume paused playback of sound channels 608 # 609 # This will resume all active sound channels after they have been paused. 610 611 self.fail() 612 613 def test_get_sdl_mixer_version(self): 614 """Ensures get_sdl_mixer_version works correctly with no args.""" 615 expected_length = 3 616 expected_type = tuple 617 expected_item_type = int 618 619 version = pygame.mixer.get_sdl_mixer_version() 620 621 self.assertIsInstance(version, expected_type) 622 self.assertEqual(len(version), expected_length) 623 624 for item in version: 625 self.assertIsInstance(item, expected_item_type) 626 627 def test_get_sdl_mixer_version__args(self): 628 """Ensures get_sdl_mixer_version works correctly using args.""" 629 expected_length = 3 630 expected_type = tuple 631 expected_item_type = int 632 633 for value in (True, False): 634 version = pygame.mixer.get_sdl_mixer_version(value) 635 636 self.assertIsInstance(version, expected_type) 637 self.assertEqual(len(version), expected_length) 638 639 for item in version: 640 self.assertIsInstance(item, expected_item_type) 641 642 def test_get_sdl_mixer_version__kwargs(self): 643 """Ensures get_sdl_mixer_version works correctly using kwargs.""" 644 expected_length = 3 645 expected_type = tuple 646 expected_item_type = int 647 648 for value in (True, False): 649 version = pygame.mixer.get_sdl_mixer_version(linked=value) 650 651 self.assertIsInstance(version, expected_type) 652 self.assertEqual(len(version), expected_length) 653 654 for item in version: 655 self.assertIsInstance(item, expected_item_type) 656 657 def test_get_sdl_mixer_version__invalid_args_kwargs(self): 658 """Ensures get_sdl_mixer_version handles invalid args and kwargs.""" 659 invalid_bool = InvalidBool() 660 661 with self.assertRaises(TypeError): 662 version = pygame.mixer.get_sdl_mixer_version(invalid_bool) 663 664 with self.assertRaises(TypeError): 665 version = pygame.mixer.get_sdl_mixer_version(linked=invalid_bool) 666 667 def test_get_sdl_mixer_version__linked_equals_compiled(self): 668 """Ensures get_sdl_mixer_version's linked/compiled versions are equal. 669 """ 670 linked_version = pygame.mixer.get_sdl_mixer_version(linked=True) 671 complied_version = pygame.mixer.get_sdl_mixer_version(linked=False) 672 673 self.assertTupleEqual(linked_version, complied_version) 674 675 676############################## CHANNEL CLASS TESTS ############################# 677 678 679class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): 680 @classmethod 681 def setUpClass(cls): 682 # Initializing the mixer is slow, so minimize the times it is called. 683 mixer.init() 684 685 @classmethod 686 def tearDownClass(cls): 687 mixer.quit() 688 689 def setUp(cls): 690 # This makes sure the mixer is always initialized before each test (in 691 # case a test calls pygame.mixer.quit()). 692 if mixer.get_init() is None: 693 mixer.init() 694 695 def test_channel(self): 696 """Ensure Channel() creation works.""" 697 channel = mixer.Channel(0) 698 699 self.assertIsInstance(channel, mixer.ChannelType) 700 self.assertEqual(channel.__class__.__name__, "Channel") 701 702 def test_channel__without_arg(self): 703 """Ensure exception for Channel() creation with no argument.""" 704 with self.assertRaises(TypeError): 705 mixer.Channel() 706 707 def test_channel__invalid_id(self): 708 """Ensure exception for Channel() creation with an invalid id.""" 709 with self.assertRaises(IndexError): 710 mixer.Channel(-1) 711 712 def test_channel__before_init(self): 713 """Ensure exception for Channel() creation with non-init mixer.""" 714 mixer.quit() 715 716 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 717 mixer.Channel(0) 718 719 def todo_test_fadeout(self): 720 721 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.fadeout: 722 723 # Channel.fadeout(time): return None 724 # stop playback after fading channel out 725 # 726 # Stop playback of a channel after fading out the sound over the given 727 # time argument in milliseconds. 728 # 729 730 self.fail() 731 732 def test_get_busy(self): 733 """Ensure an idle channel's busy state is correct.""" 734 expected_busy = False 735 channel = mixer.Channel(0) 736 737 busy = channel.get_busy() 738 739 self.assertEqual(busy, expected_busy) 740 741 def todo_test_get_busy__active(self): 742 """Ensure an active channel's busy state is correct.""" 743 self.fail() 744 745 def todo_test_get_endevent(self): 746 747 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_endevent: 748 749 # Channel.get_endevent(): return type 750 # get the event a channel sends when playback stops 751 # 752 # Returns the event type to be sent every time the Channel finishes 753 # playback of a Sound. If there is no endevent the function returns 754 # pygame.NOEVENT. 755 # 756 757 self.fail() 758 759 def todo_test_get_queue(self): 760 761 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_queue: 762 763 # Channel.get_queue(): return Sound 764 # return any Sound that is queued 765 # 766 # If a Sound is already queued on this channel it will be returned. 767 # Once the queued sound begins playback it will no longer be on the 768 # queue. 769 # 770 771 self.fail() 772 773 def todo_test_get_sound(self): 774 775 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_sound: 776 777 # Channel.get_sound(): return Sound 778 # get the currently playing Sound 779 # 780 # Return the actual Sound object currently playing on this channel. If 781 # the channel is idle None is returned. 782 # 783 784 self.fail() 785 786 def test_get_volume(self): 787 """Ensure a channel's volume can be retrieved.""" 788 expected_volume = 1.0 # default 789 channel = mixer.Channel(0) 790 791 volume = channel.get_volume() 792 793 self.assertAlmostEqual(volume, expected_volume) 794 795 def todo_test_get_volume__while_playing(self): 796 """Ensure a channel's volume can be retrieved while playing.""" 797 self.fail() 798 799 def todo_test_pause(self): 800 801 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.pause: 802 803 # Channel.pause(): return None 804 # temporarily stop playback of a channel 805 # 806 # Temporarily stop the playback of sound on a channel. It can be 807 # resumed at a later time with Channel.unpause() 808 # 809 810 self.fail() 811 812 def todo_test_play(self): 813 814 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.play: 815 816 # Channel.play(Sound, loops=0, maxtime=0, fade_ms=0): return None 817 # play a Sound on a specific Channel 818 # 819 # This will begin playback of a Sound on a specific Channel. If the 820 # Channel is currently playing any other Sound it will be stopped. 821 # 822 # The loops argument has the same meaning as in Sound.play(): it is 823 # the number of times to repeat the sound after the first time. If it 824 # is 3, the sound will be played 4 times (the first time, then three 825 # more). If loops is -1 then the playback will repeat indefinitely. 826 # 827 # As in Sound.play(), the maxtime argument can be used to stop 828 # playback of the Sound after a given number of milliseconds. 829 # 830 # As in Sound.play(), the fade_ms argument can be used fade in the sound. 831 832 self.fail() 833 834 def todo_test_queue(self): 835 836 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.queue: 837 838 # Channel.queue(Sound): return None 839 # queue a Sound object to follow the current 840 # 841 # When a Sound is queued on a Channel, it will begin playing 842 # immediately after the current Sound is finished. Each channel can 843 # only have a single Sound queued at a time. The queued Sound will 844 # only play if the current playback finished automatically. It is 845 # cleared on any other call to Channel.stop() or Channel.play(). 846 # 847 # If there is no sound actively playing on the Channel then the Sound 848 # will begin playing immediately. 849 # 850 851 self.fail() 852 853 def todo_test_set_endevent(self): 854 855 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_endevent: 856 857 # Channel.set_endevent(): return None 858 # Channel.set_endevent(type): return None 859 # have the channel send an event when playback stops 860 # 861 # When an endevent is set for a channel, it will send an event to the 862 # pygame queue every time a sound finishes playing on that channel 863 # (not just the first time). Use pygame.event.get() to retrieve the 864 # endevent once it's sent. 865 # 866 # Note that if you called Sound.play(n) or Channel.play(sound,n), the 867 # end event is sent only once: after the sound has been played "n+1" 868 # times (see the documentation of Sound.play). 869 # 870 # If Channel.stop() or Channel.play() is called while the sound was 871 # still playing, the event will be posted immediately. 872 # 873 # The type argument will be the event id sent to the queue. This can 874 # be any valid event type, but a good choice would be a value between 875 # pygame.locals.USEREVENT and pygame.locals.NUMEVENTS. If no type 876 # argument is given then the Channel will stop sending endevents. 877 # 878 879 self.fail() 880 881 def todo_test_set_volume(self): 882 883 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_volume: 884 885 # Channel.set_volume(value): return None 886 # Channel.set_volume(left, right): return None 887 # set the volume of a playing channel 888 # 889 # Set the volume (loudness) of a playing sound. When a channel starts 890 # to play its volume value is reset. This only affects the current 891 # sound. The value argument is between 0.0 and 1.0. 892 # 893 # If one argument is passed, it will be the volume of both speakers. 894 # If two arguments are passed and the mixer is in stereo mode, the 895 # first argument will be the volume of the left speaker and the second 896 # will be the volume of the right speaker. (If the second argument is 897 # None, the first argument will be the volume of both speakers.) 898 # 899 # If the channel is playing a Sound on which set_volume() has also 900 # been called, both calls are taken into account. For example: 901 # 902 # sound = pygame.mixer.Sound("s.wav") 903 # channel = s.play() # Sound plays at full volume by default 904 # sound.set_volume(0.9) # Now plays at 90% of full volume. 905 # sound.set_volume(0.6) # Now plays at 60% (previous value replaced). 906 # channel.set_volume(0.5) # Now plays at 30% (0.6 * 0.5). 907 908 self.fail() 909 910 def todo_test_stop(self): 911 912 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.stop: 913 914 # Channel.stop(): return None 915 # stop playback on a Channel 916 # 917 # Stop sound playback on a channel. After playback is stopped the 918 # channel becomes available for new Sounds to play on it. 919 # 920 921 self.fail() 922 923 def todo_test_unpause(self): 924 925 # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.unpause: 926 927 # Channel.unpause(): return None 928 # resume pause playback of a channel 929 # 930 # Resume the playback on a paused channel. 931 932 self.fail() 933 934 935############################### SOUND CLASS TESTS ############################## 936 937 938class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): 939 @classmethod 940 def tearDownClass(cls): 941 mixer.quit() 942 943 def setUp(cls): 944 # This makes sure the mixer is always initialized before each test (in 945 # case a test calls pygame.mixer.quit()). 946 if mixer.get_init() is None: 947 mixer.init() 948 949 # See MixerModuleTest's methods test_sound_args(), test_sound_unicode(), 950 # and test_array_keyword() for additional testing of Sound() creation. 951 def test_sound(self): 952 """Ensure Sound() creation with a filename works.""" 953 filename = example_path(os.path.join("data", "house_lo.wav")) 954 sound1 = mixer.Sound(filename) 955 sound2 = mixer.Sound(file=filename) 956 957 self.assertIsInstance(sound1, mixer.Sound) 958 self.assertIsInstance(sound2, mixer.Sound) 959 960 def test_sound__from_file_object(self): 961 """Ensure Sound() creation with a file object works.""" 962 filename = example_path(os.path.join("data", "house_lo.wav")) 963 964 # Using 'with' ensures the file is closed even if test fails. 965 with open(filename, "rb") as file_obj: 966 sound = mixer.Sound(file_obj) 967 968 self.assertIsInstance(sound, mixer.Sound) 969 970 def test_sound__from_sound_object(self): 971 """Ensure Sound() creation with a Sound() object works.""" 972 filename = example_path(os.path.join("data", "house_lo.wav")) 973 sound_obj = mixer.Sound(file=filename) 974 975 sound = mixer.Sound(sound_obj) 976 977 self.assertIsInstance(sound, mixer.Sound) 978 979 @unittest.skipIf(pathlib is None, "no pathlib") 980 def test_sound__from_pathlib(self): 981 """Ensure Sound() creation with a pathlib.Path object works.""" 982 path = pathlib.Path(example_path(os.path.join("data", "house_lo.wav"))) 983 sound1 = mixer.Sound(path) 984 sound2 = mixer.Sound(file=path) 985 self.assertIsInstance(sound1, mixer.Sound) 986 self.assertIsInstance(sound2, mixer.Sound) 987 988 def todo_test_sound__from_buffer(self): 989 """Ensure Sound() creation with a buffer works.""" 990 self.fail() 991 992 def todo_test_sound__from_array(self): 993 """Ensure Sound() creation with an array works.""" 994 self.fail() 995 996 def test_sound__without_arg(self): 997 """Ensure exception raised for Sound() creation with no argument.""" 998 with self.assertRaises(TypeError): 999 mixer.Sound() 1000 1001 def test_sound__before_init(self): 1002 """Ensure exception raised for Sound() creation with non-init mixer.""" 1003 mixer.quit() 1004 filename = example_path(os.path.join("data", "house_lo.wav")) 1005 1006 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1007 mixer.Sound(file=filename) 1008 1009 @unittest.skipIf(IS_PYPY, "pypy skip") 1010 def test_samples_address(self): 1011 """Test the _samples_address getter.""" 1012 try: 1013 from ctypes import pythonapi, c_void_p, py_object 1014 1015 try: 1016 Bytes_FromString = pythonapi.PyBytes_FromString # python 3 1017 except: 1018 Bytes_FromString = pythonapi.PyString_FromString # python 2 1019 1020 Bytes_FromString.restype = c_void_p 1021 Bytes_FromString.argtypes = [py_object] 1022 samples = as_bytes("abcdefgh") # keep byte size a multiple of 4 1023 sample_bytes = Bytes_FromString(samples) 1024 1025 snd = mixer.Sound(buffer=samples) 1026 1027 self.assertNotEqual(snd._samples_address, sample_bytes) 1028 finally: 1029 pygame.mixer.quit() 1030 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1031 snd._samples_address 1032 1033 def todo_test_fadeout(self): 1034 1035 # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.fadeout: 1036 1037 # Sound.fadeout(time): return None 1038 # stop sound playback after fading out 1039 # 1040 # This will stop playback of the sound after fading it out over the 1041 # time argument in milliseconds. The Sound will fade and stop on all 1042 # actively playing channels. 1043 # 1044 1045 self.fail() 1046 1047 def test_get_length(self): 1048 """Tests if get_length returns a correct length.""" 1049 try: 1050 for size in SIZES: 1051 pygame.mixer.quit() 1052 pygame.mixer.init(size=size) 1053 filename = example_path(os.path.join("data", "punch.wav")) 1054 sound = mixer.Sound(file=filename) 1055 # The sound data is in the mixer output format. So dividing the 1056 # length of the raw sound data by the mixer settings gives 1057 # the expected length of the sound. 1058 sound_bytes = sound.get_raw() 1059 mix_freq, mix_bits, mix_channels = pygame.mixer.get_init() 1060 mix_bytes = abs(mix_bits) / 8 1061 expected_length = float(len(sound_bytes)) / mix_freq / mix_bytes / mix_channels 1062 self.assertAlmostEqual(expected_length, sound.get_length()) 1063 finally: 1064 pygame.mixer.quit() 1065 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1066 sound.get_length() 1067 1068 def test_get_num_channels(self): 1069 """ 1070 Tests if Sound.get_num_channels returns the correct number 1071 of channels playing a specific sound. 1072 """ 1073 try: 1074 filename = example_path(os.path.join("data", "house_lo.wav")) 1075 sound = mixer.Sound(file=filename) 1076 1077 self.assertEqual(sound.get_num_channels(), 0) 1078 sound.play() 1079 self.assertEqual(sound.get_num_channels(), 1) 1080 sound.play() 1081 self.assertEqual(sound.get_num_channels(), 2) 1082 sound.stop() 1083 self.assertEqual(sound.get_num_channels(), 0) 1084 finally: 1085 pygame.mixer.quit() 1086 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1087 sound.get_num_channels() 1088 1089 def test_get_volume(self): 1090 """Ensure a sound's volume can be retrieved.""" 1091 try: 1092 expected_volume = 1.0 # default 1093 filename = example_path(os.path.join("data", "house_lo.wav")) 1094 sound = mixer.Sound(file=filename) 1095 1096 volume = sound.get_volume() 1097 1098 self.assertAlmostEqual(volume, expected_volume) 1099 finally: 1100 pygame.mixer.quit() 1101 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1102 sound.get_volume() 1103 1104 def todo_test_get_volume__while_playing(self): 1105 """Ensure a sound's volume can be retrieved while playing.""" 1106 self.fail() 1107 1108 def todo_test_play(self): 1109 1110 # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.play: 1111 1112 # Sound.play(loops=0, maxtime=0, fade_ms=0): return Channel 1113 # begin sound playback 1114 # 1115 # Begin playback of the Sound (i.e., on the computer's speakers) on an 1116 # available Channel. This will forcibly select a Channel, so playback 1117 # may cut off a currently playing sound if necessary. 1118 # 1119 # The loops argument controls how many times the sample will be 1120 # repeated after being played the first time. A value of 5 means that 1121 # the sound will be played once, then repeated five times, and so is 1122 # played a total of six times. The default value (zero) means the 1123 # Sound is not repeated, and so is only played once. If loops is set 1124 # to -1 the Sound will loop indefinitely (though you can still call 1125 # stop() to stop it). 1126 # 1127 # The maxtime argument can be used to stop playback after a given 1128 # number of milliseconds. 1129 # 1130 # The fade_ms argument will make the sound start playing at 0 volume 1131 # and fade up to full volume over the time given. The sample may end 1132 # before the fade-in is complete. 1133 # 1134 # This returns the Channel object for the channel that was selected. 1135 1136 self.fail() 1137 1138 def test_set_volume(self): 1139 """Ensure a sound's volume can be set.""" 1140 try: 1141 float_delta = 1.0 / 128 # SDL volume range is 0 to 128 1142 filename = example_path(os.path.join("data", "house_lo.wav")) 1143 sound = mixer.Sound(file=filename) 1144 current_volume = sound.get_volume() 1145 1146 # (volume_set_value : expected_volume) 1147 volumes = ( 1148 (-1, current_volume), # value < 0 won't change volume 1149 (0, 0.0), 1150 (0.01, 0.01), 1151 (0.1, 0.1), 1152 (0.5, 0.5), 1153 (0.9, 0.9), 1154 (0.99, 0.99), 1155 (1, 1.0), 1156 (1.1, 1.0), 1157 (2.0, 1.0), 1158 ) 1159 1160 for volume_set_value, expected_volume in volumes: 1161 sound.set_volume(volume_set_value) 1162 1163 self.assertAlmostEqual( 1164 sound.get_volume(), expected_volume, delta=float_delta 1165 ) 1166 finally: 1167 pygame.mixer.quit() 1168 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1169 sound.set_volume(1) 1170 1171 def todo_test_set_volume__while_playing(self): 1172 """Ensure a sound's volume can be set while playing.""" 1173 self.fail() 1174 1175 def test_stop(self): 1176 """Ensure stop can be called while not playing a sound.""" 1177 try: 1178 expected_channels = 0 1179 filename = example_path(os.path.join("data", "house_lo.wav")) 1180 sound = mixer.Sound(file=filename) 1181 1182 sound.stop() 1183 1184 self.assertEqual(sound.get_num_channels(), expected_channels) 1185 finally: 1186 pygame.mixer.quit() 1187 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1188 sound.stop() 1189 1190 def todo_test_stop__while_playing(self): 1191 """Ensure stop stops a playing sound.""" 1192 self.fail() 1193 1194 def test_get_raw(self): 1195 """Ensure get_raw returns the correct bytestring.""" 1196 try: 1197 samples = as_bytes("abcdefgh") # keep byte size a multiple of 4 1198 snd = mixer.Sound(buffer=samples) 1199 1200 raw = snd.get_raw() 1201 1202 self.assertIsInstance(raw, bytes_) 1203 self.assertEqual(raw, samples) 1204 finally: 1205 pygame.mixer.quit() 1206 with self.assertRaisesRegex(pygame.error, "mixer not initialized"): 1207 snd.get_raw() 1208 1209 1210##################################### MAIN ##################################### 1211 1212if __name__ == "__main__": 1213 unittest.main() 1214