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