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