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