Home | History | Annotate | Download | only in test
      1 from test import support
      2 support.requires('audio')
      3 
      4 from test.support import findfile
      5 
      6 ossaudiodev = 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 OSError as 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, AttributeError):
     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 (%s) > 10%% off of expected time (%s)" %
     89                         (elapsed_time, expected_time))
     90 
     91     def set_parameters(self, dsp):
     92         # Two configurations for testing:
     93         #   config1 (8-bit, mono, 8 kHz) should work on even the most
     94         #      ancient and crufty sound card, but maybe not on special-
     95         #      purpose high-end hardware
     96         #   config2 (16-bit, stereo, 44.1kHz) should work on all but the
     97         #      most ancient and crufty hardware
     98         config1 = (ossaudiodev.AFMT_U8, 1, 8000)
     99         config2 = (AFMT_S16_NE, 2, 44100)
    100 
    101         for config in [config1, config2]:
    102             (fmt, channels, rate) = config
    103             if (dsp.setfmt(fmt) == fmt and
    104                 dsp.channels(channels) == channels and
    105                 dsp.speed(rate) == rate):
    106                 break
    107         else:
    108             raise RuntimeError("unable to set audio sampling parameters: "
    109                                "you must have really weird audio hardware")
    110 
    111         # setparameters() should be able to set this configuration in
    112         # either strict or non-strict mode.
    113         result = dsp.setparameters(fmt, channels, rate, False)
    114         self.assertEqual(result, (fmt, channels, rate),
    115                          "setparameters%r: returned %r" % (config, result))
    116 
    117         result = dsp.setparameters(fmt, channels, rate, True)
    118         self.assertEqual(result, (fmt, channels, rate),
    119                          "setparameters%r: returned %r" % (config, result))
    120 
    121     def set_bad_parameters(self, dsp):
    122         # Now try some configurations that are presumably bogus: eg. 300
    123         # channels currently exceeds even Hollywood's ambitions, and
    124         # negative sampling rate is utter nonsense.  setparameters() should
    125         # accept these in non-strict mode, returning something other than
    126         # was requested, but should barf in strict mode.
    127         fmt = AFMT_S16_NE
    128         rate = 44100
    129         channels = 2
    130         for config in [(fmt, 300, rate),       # ridiculous nchannels
    131                        (fmt, -5, rate),        # impossible nchannels
    132                        (fmt, channels, -50),   # impossible rate
    133                       ]:
    134             (fmt, channels, rate) = config
    135             result = dsp.setparameters(fmt, channels, rate, False)
    136             self.assertNotEqual(result, config,
    137                              "unexpectedly got requested configuration")
    138 
    139             try:
    140                 result = dsp.setparameters(fmt, channels, rate, True)
    141             except ossaudiodev.OSSAudioError as err:
    142                 pass
    143             else:
    144                 self.fail("expected OSSAudioError")
    145 
    146     def test_playback(self):
    147         sound_info = read_sound_file(findfile('audiotest.au'))
    148         self.play_sound_file(*sound_info)
    149 
    150     def test_set_parameters(self):
    151         dsp = ossaudiodev.open("w")
    152         try:
    153             self.set_parameters(dsp)
    154 
    155             # Disabled because it fails under Linux 2.6 with ALSA's OSS
    156             # emulation layer.
    157             #self.set_bad_parameters(dsp)
    158         finally:
    159             dsp.close()
    160             self.assertTrue(dsp.closed)
    161 
    162     def test_mixer_methods(self):
    163         # Issue #8139: ossaudiodev didn't initialize its types properly,
    164         # therefore some methods were unavailable.
    165         with ossaudiodev.openmixer() as mixer:
    166             self.assertGreaterEqual(mixer.fileno(), 0)
    167 
    168     def test_with(self):
    169         with ossaudiodev.open('w') as dsp:
    170             pass
    171         self.assertTrue(dsp.closed)
    172 
    173     def test_on_closed(self):
    174         dsp = ossaudiodev.open('w')
    175         dsp.close()
    176         self.assertRaises(ValueError, dsp.fileno)
    177         self.assertRaises(ValueError, dsp.read, 1)
    178         self.assertRaises(ValueError, dsp.write, b'x')
    179         self.assertRaises(ValueError, dsp.writeall, b'x')
    180         self.assertRaises(ValueError, dsp.bufsize)
    181         self.assertRaises(ValueError, dsp.obufcount)
    182         self.assertRaises(ValueError, dsp.obufcount)
    183         self.assertRaises(ValueError, dsp.obuffree)
    184         self.assertRaises(ValueError, dsp.getptr)
    185 
    186         mixer = ossaudiodev.openmixer()
    187         mixer.close()
    188         self.assertRaises(ValueError, mixer.fileno)
    189 
    190 def test_main():
    191     try:
    192         dsp = ossaudiodev.open('w')
    193     except (ossaudiodev.error, OSError) as msg:
    194         if msg.args[0] in (errno.EACCES, errno.ENOENT,
    195                            errno.ENODEV, errno.EBUSY):
    196             raise unittest.SkipTest(msg)
    197         raise
    198     dsp.close()
    199     support.run_unittest(__name__)
    200 
    201 if __name__ == "__main__":
    202     test_main()
    203