blob: 624fbf21ba7d88f7f3a485c5fdd201c0b3567d8e [file] [log] [blame]
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001from test import support
2support.requires('audio')
Guido van Rossum4507ec72003-02-14 19:29:22 +00003
Benjamin Petersone549ead2009-03-28 21:42:05 +00004from test.support import findfile
Greg Ward36dacfa2002-12-10 16:24:21 +00005
R. David Murraya21e4ca2009-03-31 23:16:50 +00006ossaudiodev = support.import_module('ossaudiodev')
7
Greg Ward36dacfa2002-12-10 16:24:21 +00008import errno
Greg Ward36dacfa2002-12-10 16:24:21 +00009import sys
Brett Cannonabd9a592008-07-25 17:56:47 +000010import sunau
Greg Ward36dacfa2002-12-10 16:24:21 +000011import time
12import audioop
Guido van Rossumd8faa362007-04-27 19:54:29 +000013import unittest
Greg Ward36dacfa2002-12-10 16:24:21 +000014
Greg Ward8a709b32003-06-03 00:32:44 +000015# Arggh, AFMT_S16_NE not defined on all platforms -- seems to be a
16# fairly recent addition to OSS.
17try:
18 from ossaudiodev import AFMT_S16_NE
19except 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
Greg Ward55a87902002-12-10 16:27:35 +000026def read_sound_file(path):
Brett Cannonabd9a592008-07-25 17:56:47 +000027 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()
Greg Ward36dacfa2002-12-10 16:24:21 +000034
Brett Cannonabd9a592008-07-25 17:56:47 +000035 if encoding != sunau.AUDIO_FILE_ENCODING_MULAW_8:
Guido van Rossumd8faa362007-04-27 19:54:29 +000036 raise RuntimeError("Expect .au file with 8-bit mu-law samples")
Greg Ward36dacfa2002-12-10 16:24:21 +000037
Greg Ward55a87902002-12-10 16:27:35 +000038 # Convert the data to 16-bit signed.
39 data = audioop.ulaw2lin(data, 2)
40 return (data, rate, 16, nchannels)
41
Guido van Rossumd8faa362007-04-27 19:54:29 +000042class OSSAudioDevTests(unittest.TestCase):
Greg Ward55a87902002-12-10 16:27:35 +000043
Guido van Rossumd8faa362007-04-27 19:54:29 +000044 def play_sound_file(self, data, rate, ssize, nchannels):
45 try:
46 dsp = ossaudiodev.open('w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +020047 except OSError as msg:
Guido van Rossumd8faa362007-04-27 19:54:29 +000048 if msg.args[0] in (errno.EACCES, errno.ENOENT,
49 errno.ENODEV, errno.EBUSY):
Benjamin Petersone549ead2009-03-28 21:42:05 +000050 raise unittest.SkipTest(msg)
Guido van Rossumd8faa362007-04-27 19:54:29 +000051 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.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000061 self.assertFalse(dsp.closed)
Guido van Rossumd8faa362007-04-27 19:54:29 +000062 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)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +000069 except (TypeError, AttributeError):
Guido van Rossumd8faa362007-04-27 19:54:29 +000070 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)
Barry Warsaw83d89982010-05-05 16:18:31 +000079 self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time)
Victor Stinner2cf4c202018-12-17 09:36:36 +010080 t1 = time.monotonic()
Guido van Rossumd8faa362007-04-27 19:54:29 +000081 dsp.write(data)
82 dsp.close()
Victor Stinner2cf4c202018-12-17 09:36:36 +010083 t2 = time.monotonic()
Guido van Rossumd8faa362007-04-27 19:54:29 +000084 elapsed_time = t2 - t1
85
86 percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000087 self.assertTrue(percent_diff <= 10.0,
Guido van Rossume7606312007-08-26 20:03:04 +000088 "elapsed time (%s) > 10%% off of expected time (%s)" %
Neal Norwitz2bf4d5b2007-08-26 22:16:55 +000089 (elapsed_time, expected_time))
Guido van Rossumd8faa362007-04-27 19:54:29 +000090
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):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000122 # 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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000136 self.assertNotEqual(result, config,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000137 "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()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000160 self.assertTrue(dsp.closed)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161
Antoine Pitrou39b35432010-03-23 00:25:54 +0000162 def test_mixer_methods(self):
163 # Issue #8139: ossaudiodev didn't initialize its types properly,
164 # therefore some methods were unavailable.
Georg Brandl1e908af2010-10-23 17:31:52 +0000165 with ossaudiodev.openmixer() as mixer:
Antoine Pitrou39b35432010-03-23 00:25:54 +0000166 self.assertGreaterEqual(mixer.fileno(), 0)
Georg Brandl1e908af2010-10-23 17:31:52 +0000167
168 def test_with(self):
169 with ossaudiodev.open('w') as dsp:
170 pass
171 self.assertTrue(dsp.closed)
Antoine Pitrou39b35432010-03-23 00:25:54 +0000172
Charles-François Natalia5293082011-06-11 18:58:24 +0200173 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)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000189
190def test_main():
Greg Ward36dacfa2002-12-10 16:24:21 +0000191 try:
Greg Ward080c1102003-05-29 00:23:17 +0000192 dsp = ossaudiodev.open('w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200193 except (ossaudiodev.error, OSError) as msg:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000194 if msg.args[0] in (errno.EACCES, errno.ENOENT,
195 errno.ENODEV, errno.EBUSY):
Benjamin Petersone549ead2009-03-28 21:42:05 +0000196 raise unittest.SkipTest(msg)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000197 raise
Guido van Rossum23cfc982007-08-21 22:49:52 +0000198 dsp.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000199 support.run_unittest(__name__)
Greg Ward36dacfa2002-12-10 16:24:21 +0000200
Guido van Rossumd8faa362007-04-27 19:54:29 +0000201if __name__ == "__main__":
202 test_main()