blob: d3766e580b984392fdf496282131515c0e7c2f39 [file] [log] [blame]
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001from test import support
Hai Shi598a9512020-08-07 23:18:38 +08002from test.support import import_helper
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003support.requires('audio')
Guido van Rossum4507ec72003-02-14 19:29:22 +00004
Benjamin Petersone549ead2009-03-28 21:42:05 +00005from test.support import findfile
Greg Ward36dacfa2002-12-10 16:24:21 +00006
Hai Shi598a9512020-08-07 23:18:38 +08007ossaudiodev = import_helper.import_module('ossaudiodev')
R. David Murraya21e4ca2009-03-31 23:16:50 +00008
Greg Ward36dacfa2002-12-10 16:24:21 +00009import errno
Greg Ward36dacfa2002-12-10 16:24:21 +000010import sys
Brett Cannonabd9a592008-07-25 17:56:47 +000011import sunau
Greg Ward36dacfa2002-12-10 16:24:21 +000012import time
13import audioop
Guido van Rossumd8faa362007-04-27 19:54:29 +000014import unittest
Greg Ward36dacfa2002-12-10 16:24:21 +000015
Greg Ward8a709b32003-06-03 00:32:44 +000016# Arggh, AFMT_S16_NE not defined on all platforms -- seems to be a
17# fairly recent addition to OSS.
18try:
19 from ossaudiodev import AFMT_S16_NE
20except ImportError:
21 if sys.byteorder == "little":
22 AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
23 else:
24 AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
25
26
Greg Ward55a87902002-12-10 16:27:35 +000027def read_sound_file(path):
Brett Cannonabd9a592008-07-25 17:56:47 +000028 with open(path, 'rb') as fp:
29 au = sunau.open(fp)
30 rate = au.getframerate()
31 nchannels = au.getnchannels()
32 encoding = au._encoding
33 fp.seek(0)
34 data = fp.read()
Greg Ward36dacfa2002-12-10 16:24:21 +000035
Brett Cannonabd9a592008-07-25 17:56:47 +000036 if encoding != sunau.AUDIO_FILE_ENCODING_MULAW_8:
Guido van Rossumd8faa362007-04-27 19:54:29 +000037 raise RuntimeError("Expect .au file with 8-bit mu-law samples")
Greg Ward36dacfa2002-12-10 16:24:21 +000038
Greg Ward55a87902002-12-10 16:27:35 +000039 # Convert the data to 16-bit signed.
40 data = audioop.ulaw2lin(data, 2)
41 return (data, rate, 16, nchannels)
42
Guido van Rossumd8faa362007-04-27 19:54:29 +000043class OSSAudioDevTests(unittest.TestCase):
Greg Ward55a87902002-12-10 16:27:35 +000044
Guido van Rossumd8faa362007-04-27 19:54:29 +000045 def play_sound_file(self, data, rate, ssize, nchannels):
46 try:
47 dsp = ossaudiodev.open('w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +020048 except OSError as msg:
Guido van Rossumd8faa362007-04-27 19:54:29 +000049 if msg.args[0] in (errno.EACCES, errno.ENOENT,
50 errno.ENODEV, errno.EBUSY):
Benjamin Petersone549ead2009-03-28 21:42:05 +000051 raise unittest.SkipTest(msg)
Guido van Rossumd8faa362007-04-27 19:54:29 +000052 raise
53
54 # at least check that these methods can be invoked
55 dsp.bufsize()
56 dsp.obufcount()
57 dsp.obuffree()
58 dsp.getptr()
59 dsp.fileno()
60
61 # Make sure the read-only attributes work.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000062 self.assertFalse(dsp.closed)
Guido van Rossumd8faa362007-04-27 19:54:29 +000063 self.assertEqual(dsp.name, "/dev/dsp")
64 self.assertEqual(dsp.mode, "w", "bad dsp.mode: %r" % dsp.mode)
65
66 # And make sure they're really read-only.
67 for attr in ('closed', 'name', 'mode'):
68 try:
69 setattr(dsp, attr, 42)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +000070 except (TypeError, AttributeError):
Guido van Rossumd8faa362007-04-27 19:54:29 +000071 pass
72 else:
73 self.fail("dsp.%s not read-only" % attr)
74
75 # Compute expected running time of sound sample (in seconds).
76 expected_time = float(len(data)) / (ssize/8) / nchannels / rate
77
78 # set parameters based on .au file headers
79 dsp.setparameters(AFMT_S16_NE, nchannels, rate)
Barry Warsaw83d89982010-05-05 16:18:31 +000080 self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time)
Victor Stinner2cf4c202018-12-17 09:36:36 +010081 t1 = time.monotonic()
Guido van Rossumd8faa362007-04-27 19:54:29 +000082 dsp.write(data)
83 dsp.close()
Victor Stinner2cf4c202018-12-17 09:36:36 +010084 t2 = time.monotonic()
Guido van Rossumd8faa362007-04-27 19:54:29 +000085 elapsed_time = t2 - t1
86
87 percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000088 self.assertTrue(percent_diff <= 10.0,
Guido van Rossume7606312007-08-26 20:03:04 +000089 "elapsed time (%s) > 10%% off of expected time (%s)" %
Neal Norwitz2bf4d5b2007-08-26 22:16:55 +000090 (elapsed_time, expected_time))
Guido van Rossumd8faa362007-04-27 19:54:29 +000091
92 def set_parameters(self, dsp):
93 # Two configurations for testing:
94 # config1 (8-bit, mono, 8 kHz) should work on even the most
95 # ancient and crufty sound card, but maybe not on special-
96 # purpose high-end hardware
97 # config2 (16-bit, stereo, 44.1kHz) should work on all but the
98 # most ancient and crufty hardware
99 config1 = (ossaudiodev.AFMT_U8, 1, 8000)
100 config2 = (AFMT_S16_NE, 2, 44100)
101
102 for config in [config1, config2]:
103 (fmt, channels, rate) = config
104 if (dsp.setfmt(fmt) == fmt and
105 dsp.channels(channels) == channels and
106 dsp.speed(rate) == rate):
107 break
108 else:
109 raise RuntimeError("unable to set audio sampling parameters: "
110 "you must have really weird audio hardware")
111
112 # setparameters() should be able to set this configuration in
113 # either strict or non-strict mode.
114 result = dsp.setparameters(fmt, channels, rate, False)
115 self.assertEqual(result, (fmt, channels, rate),
116 "setparameters%r: returned %r" % (config, result))
117
118 result = dsp.setparameters(fmt, channels, rate, True)
119 self.assertEqual(result, (fmt, channels, rate),
120 "setparameters%r: returned %r" % (config, result))
121
122 def set_bad_parameters(self, dsp):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000123 # Now try some configurations that are presumably bogus: eg. 300
124 # channels currently exceeds even Hollywood's ambitions, and
125 # negative sampling rate is utter nonsense. setparameters() should
126 # accept these in non-strict mode, returning something other than
127 # was requested, but should barf in strict mode.
128 fmt = AFMT_S16_NE
129 rate = 44100
130 channels = 2
131 for config in [(fmt, 300, rate), # ridiculous nchannels
132 (fmt, -5, rate), # impossible nchannels
133 (fmt, channels, -50), # impossible rate
134 ]:
135 (fmt, channels, rate) = config
136 result = dsp.setparameters(fmt, channels, rate, False)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertNotEqual(result, config,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000138 "unexpectedly got requested configuration")
139
140 try:
141 result = dsp.setparameters(fmt, channels, rate, True)
142 except ossaudiodev.OSSAudioError as err:
143 pass
144 else:
145 self.fail("expected OSSAudioError")
146
147 def test_playback(self):
148 sound_info = read_sound_file(findfile('audiotest.au'))
149 self.play_sound_file(*sound_info)
150
151 def test_set_parameters(self):
152 dsp = ossaudiodev.open("w")
153 try:
154 self.set_parameters(dsp)
155
156 # Disabled because it fails under Linux 2.6 with ALSA's OSS
157 # emulation layer.
158 #self.set_bad_parameters(dsp)
159 finally:
160 dsp.close()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000161 self.assertTrue(dsp.closed)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000162
Antoine Pitrou39b35432010-03-23 00:25:54 +0000163 def test_mixer_methods(self):
164 # Issue #8139: ossaudiodev didn't initialize its types properly,
165 # therefore some methods were unavailable.
Georg Brandl1e908af2010-10-23 17:31:52 +0000166 with ossaudiodev.openmixer() as mixer:
Antoine Pitrou39b35432010-03-23 00:25:54 +0000167 self.assertGreaterEqual(mixer.fileno(), 0)
Georg Brandl1e908af2010-10-23 17:31:52 +0000168
169 def test_with(self):
170 with ossaudiodev.open('w') as dsp:
171 pass
172 self.assertTrue(dsp.closed)
Antoine Pitrou39b35432010-03-23 00:25:54 +0000173
Charles-François Natalia5293082011-06-11 18:58:24 +0200174 def test_on_closed(self):
175 dsp = ossaudiodev.open('w')
176 dsp.close()
177 self.assertRaises(ValueError, dsp.fileno)
178 self.assertRaises(ValueError, dsp.read, 1)
179 self.assertRaises(ValueError, dsp.write, b'x')
180 self.assertRaises(ValueError, dsp.writeall, b'x')
181 self.assertRaises(ValueError, dsp.bufsize)
182 self.assertRaises(ValueError, dsp.obufcount)
183 self.assertRaises(ValueError, dsp.obufcount)
184 self.assertRaises(ValueError, dsp.obuffree)
185 self.assertRaises(ValueError, dsp.getptr)
186
187 mixer = ossaudiodev.openmixer()
188 mixer.close()
189 self.assertRaises(ValueError, mixer.fileno)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000190
191def test_main():
Greg Ward36dacfa2002-12-10 16:24:21 +0000192 try:
Greg Ward080c1102003-05-29 00:23:17 +0000193 dsp = ossaudiodev.open('w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200194 except (ossaudiodev.error, OSError) as msg:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000195 if msg.args[0] in (errno.EACCES, errno.ENOENT,
196 errno.ENODEV, errno.EBUSY):
Benjamin Petersone549ead2009-03-28 21:42:05 +0000197 raise unittest.SkipTest(msg)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000198 raise
Guido van Rossum23cfc982007-08-21 22:49:52 +0000199 dsp.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000200 support.run_unittest(__name__)
Greg Ward36dacfa2002-12-10 16:24:21 +0000201
Guido van Rossumd8faa362007-04-27 19:54:29 +0000202if __name__ == "__main__":
203 test_main()