test_ossaudiodev.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from test import test_support
  2. test_support.requires('audio')
  3. from test.test_support import findfile
  4. ossaudiodev = test_support.import_module('ossaudiodev')
  5. import errno
  6. import sys
  7. import sunau
  8. import time
  9. import audioop
  10. import unittest
  11. # Arggh, AFMT_S16_NE not defined on all platforms -- seems to be a
  12. # fairly recent addition to OSS.
  13. try:
  14. from ossaudiodev import AFMT_S16_NE
  15. except ImportError:
  16. if sys.byteorder == "little":
  17. AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
  18. else:
  19. AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
  20. def read_sound_file(path):
  21. with open(path, 'rb') as fp:
  22. au = sunau.open(fp)
  23. rate = au.getframerate()
  24. nchannels = au.getnchannels()
  25. encoding = au._encoding
  26. fp.seek(0)
  27. data = fp.read()
  28. if encoding != sunau.AUDIO_FILE_ENCODING_MULAW_8:
  29. raise RuntimeError("Expect .au file with 8-bit mu-law samples")
  30. # Convert the data to 16-bit signed.
  31. data = audioop.ulaw2lin(data, 2)
  32. return (data, rate, 16, nchannels)
  33. class OSSAudioDevTests(unittest.TestCase):
  34. def play_sound_file(self, data, rate, ssize, nchannels):
  35. try:
  36. dsp = ossaudiodev.open('w')
  37. except IOError, msg:
  38. if msg.args[0] in (errno.EACCES, errno.ENOENT,
  39. errno.ENODEV, errno.EBUSY):
  40. raise unittest.SkipTest(msg)
  41. raise
  42. # at least check that these methods can be invoked
  43. dsp.bufsize()
  44. dsp.obufcount()
  45. dsp.obuffree()
  46. dsp.getptr()
  47. dsp.fileno()
  48. # Make sure the read-only attributes work.
  49. self.assertFalse(dsp.closed)
  50. self.assertEqual(dsp.name, "/dev/dsp")
  51. self.assertEqual(dsp.mode, "w", "bad dsp.mode: %r" % dsp.mode)
  52. # And make sure they're really read-only.
  53. for attr in ('closed', 'name', 'mode'):
  54. try:
  55. setattr(dsp, attr, 42)
  56. except TypeError:
  57. pass
  58. else:
  59. self.fail("dsp.%s not read-only" % attr)
  60. # Compute expected running time of sound sample (in seconds).
  61. expected_time = float(len(data)) / (ssize//8) / nchannels / rate
  62. # set parameters based on .au file headers
  63. dsp.setparameters(AFMT_S16_NE, nchannels, rate)
  64. self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time)
  65. t1 = time.time()
  66. dsp.write(data)
  67. dsp.close()
  68. t2 = time.time()
  69. elapsed_time = t2 - t1
  70. percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100
  71. self.assertTrue(percent_diff <= 10.0,
  72. "elapsed time > 10% off of expected time")
  73. def set_parameters(self, dsp):
  74. # Two configurations for testing:
  75. # config1 (8-bit, mono, 8 kHz) should work on even the most
  76. # ancient and crufty sound card, but maybe not on special-
  77. # purpose high-end hardware
  78. # config2 (16-bit, stereo, 44.1kHz) should work on all but the
  79. # most ancient and crufty hardware
  80. config1 = (ossaudiodev.AFMT_U8, 1, 8000)
  81. config2 = (AFMT_S16_NE, 2, 44100)
  82. for config in [config1, config2]:
  83. (fmt, channels, rate) = config
  84. if (dsp.setfmt(fmt) == fmt and
  85. dsp.channels(channels) == channels and
  86. dsp.speed(rate) == rate):
  87. break
  88. else:
  89. raise RuntimeError("unable to set audio sampling parameters: "
  90. "you must have really weird audio hardware")
  91. # setparameters() should be able to set this configuration in
  92. # either strict or non-strict mode.
  93. result = dsp.setparameters(fmt, channels, rate, False)
  94. self.assertEqual(result, (fmt, channels, rate),
  95. "setparameters%r: returned %r" % (config, result))
  96. result = dsp.setparameters(fmt, channels, rate, True)
  97. self.assertEqual(result, (fmt, channels, rate),
  98. "setparameters%r: returned %r" % (config, result))
  99. def set_bad_parameters(self, dsp):
  100. # Now try some configurations that are presumably bogus: eg. 300
  101. # channels currently exceeds even Hollywood's ambitions, and
  102. # negative sampling rate is utter nonsense. setparameters() should
  103. # accept these in non-strict mode, returning something other than
  104. # was requested, but should barf in strict mode.
  105. fmt = AFMT_S16_NE
  106. rate = 44100
  107. channels = 2
  108. for config in [(fmt, 300, rate), # ridiculous nchannels
  109. (fmt, -5, rate), # impossible nchannels
  110. (fmt, channels, -50), # impossible rate
  111. ]:
  112. (fmt, channels, rate) = config
  113. result = dsp.setparameters(fmt, channels, rate, False)
  114. self.assertNotEqual(result, config,
  115. "unexpectedly got requested configuration")
  116. try:
  117. result = dsp.setparameters(fmt, channels, rate, True)
  118. except ossaudiodev.OSSAudioError, err:
  119. pass
  120. else:
  121. self.fail("expected OSSAudioError")
  122. def test_playback(self):
  123. sound_info = read_sound_file(findfile('audiotest.au'))
  124. self.play_sound_file(*sound_info)
  125. def test_set_parameters(self):
  126. dsp = ossaudiodev.open("w")
  127. try:
  128. self.set_parameters(dsp)
  129. # Disabled because it fails under Linux 2.6 with ALSA's OSS
  130. # emulation layer.
  131. #self.set_bad_parameters(dsp)
  132. finally:
  133. dsp.close()
  134. self.assertTrue(dsp.closed)
  135. def test_main():
  136. try:
  137. dsp = ossaudiodev.open('w')
  138. except (ossaudiodev.error, IOError), msg:
  139. if msg.args[0] in (errno.EACCES, errno.ENOENT,
  140. errno.ENODEV, errno.EBUSY):
  141. raise unittest.SkipTest(msg)
  142. raise
  143. dsp.close()
  144. test_support.run_unittest(__name__)
  145. if __name__ == "__main__":
  146. test_main()