10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom test import test_support
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yitest_support.requires('audio')
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom test.test_support import findfile
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiossaudiodev = test_support.import_module('ossaudiodev')
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport errno
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport sys
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport sunau
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport time
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport audioop
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport unittest
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Arggh, AFMT_S16_NE not defined on all platforms -- seems to be a
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# fairly recent addition to OSS.
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yitry:
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    from ossaudiodev import AFMT_S16_NE
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiexcept ImportError:
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if sys.byteorder == "little":
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    else:
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef read_sound_file(path):
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    with open(path, 'rb') as fp:
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        au = sunau.open(fp)
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rate = au.getframerate()
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        nchannels = au.getnchannels()
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        encoding = au._encoding
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        fp.seek(0)
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        data = fp.read()
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if encoding != sunau.AUDIO_FILE_ENCODING_MULAW_8:
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        raise RuntimeError("Expect .au file with 8-bit mu-law samples")
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Convert the data to 16-bit signed.
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    data = audioop.ulaw2lin(data, 2)
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    return (data, rate, 16, nchannels)
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass OSSAudioDevTests(unittest.TestCase):
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def play_sound_file(self, data, rate, ssize, nchannels):
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            dsp = ossaudiodev.open('w')
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except IOError, msg:
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if msg.args[0] in (errno.EACCES, errno.ENOENT,
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                               errno.ENODEV, errno.EBUSY):
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                raise unittest.SkipTest(msg)
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # at least check that these methods can be invoked
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.bufsize()
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.obufcount()
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.obuffree()
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.getptr()
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.fileno()
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Make sure the read-only attributes work.
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertFalse(dsp.closed)
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertEqual(dsp.name, "/dev/dsp")
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertEqual(dsp.mode, "w", "bad dsp.mode: %r" % dsp.mode)
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # And make sure they're really read-only.
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for attr in ('closed', 'name', 'mode'):
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            try:
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                setattr(dsp, attr, 42)
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            except TypeError:
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                pass
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.fail("dsp.%s not read-only" % attr)
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Compute expected running time of sound sample (in seconds).
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        expected_time = float(len(data)) / (ssize//8) / nchannels / rate
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # set parameters based on .au file headers
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.setparameters(AFMT_S16_NE, nchannels, rate)
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time)
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t1 = time.time()
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.write(data)
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp.close()
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t2 = time.time()
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elapsed_time = t2 - t1
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertTrue(percent_diff <= 10.0,
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        "elapsed time > 10% off of expected time")
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def set_parameters(self, dsp):
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Two configurations for testing:
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #   config1 (8-bit, mono, 8 kHz) should work on even the most
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #      ancient and crufty sound card, but maybe not on special-
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #      purpose high-end hardware
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #   config2 (16-bit, stereo, 44.1kHz) should work on all but the
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #      most ancient and crufty hardware
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        config1 = (ossaudiodev.AFMT_U8, 1, 8000)
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        config2 = (AFMT_S16_NE, 2, 44100)
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for config in [config1, config2]:
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            (fmt, channels, rate) = config
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if (dsp.setfmt(fmt) == fmt and
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                dsp.channels(channels) == channels and
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                dsp.speed(rate) == rate):
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                break
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise RuntimeError("unable to set audio sampling parameters: "
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                               "you must have really weird audio hardware")
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # setparameters() should be able to set this configuration in
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # either strict or non-strict mode.
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        result = dsp.setparameters(fmt, channels, rate, False)
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertEqual(result, (fmt, channels, rate),
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         "setparameters%r: returned %r" % (config, result))
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        result = dsp.setparameters(fmt, channels, rate, True)
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.assertEqual(result, (fmt, channels, rate),
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         "setparameters%r: returned %r" % (config, result))
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def set_bad_parameters(self, dsp):
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Now try some configurations that are presumably bogus: eg. 300
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # channels currently exceeds even Hollywood's ambitions, and
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # negative sampling rate is utter nonsense.  setparameters() should
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # accept these in non-strict mode, returning something other than
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # was requested, but should barf in strict mode.
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        fmt = AFMT_S16_NE
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rate = 44100
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        channels = 2
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for config in [(fmt, 300, rate),       # ridiculous nchannels
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                       (fmt, -5, rate),        # impossible nchannels
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                       (fmt, channels, -50),   # impossible rate
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                      ]:
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            (fmt, channels, rate) = config
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            result = dsp.setparameters(fmt, channels, rate, False)
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.assertNotEqual(result, config,
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                             "unexpectedly got requested configuration")
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            try:
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                result = dsp.setparameters(fmt, channels, rate, True)
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            except ossaudiodev.OSSAudioError, err:
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                pass
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.fail("expected OSSAudioError")
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def test_playback(self):
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sound_info = read_sound_file(findfile('audiotest.au'))
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.play_sound_file(*sound_info)
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def test_set_parameters(self):
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp = ossaudiodev.open("w")
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.set_parameters(dsp)
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Disabled because it fails under Linux 2.6 with ALSA's OSS
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # emulation layer.
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            #self.set_bad_parameters(dsp)
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        finally:
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            dsp.close()
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.assertTrue(dsp.closed)
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef test_main():
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    try:
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dsp = ossaudiodev.open('w')
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    except (ossaudiodev.error, IOError), msg:
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if msg.args[0] in (errno.EACCES, errno.ENOENT,
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                           errno.ENODEV, errno.EBUSY):
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise unittest.SkipTest(msg)
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        raise
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    dsp.close()
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    test_support.run_unittest(__name__)
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiif __name__ == "__main__":
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    test_main()
175