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