ยปCore Development>Code coverage>Lib/sndhdr.py

Python code coverage for Lib/sndhdr.py

#countcontent
1n/a"""Routines to help recognizing sound files.
2n/a
3n/aFunction whathdr() recognizes various types of sound file headers.
4n/aIt understands almost all headers that SOX can decode.
5n/a
6n/aThe return tuple contains the following items, in this order:
7n/a- file type (as SOX understands it)
8n/a- sampling rate (0 if unknown or hard to decode)
9n/a- number of channels (0 if unknown or hard to decode)
10n/a- number of frames in the file (-1 if unknown or hard to decode)
11n/a- number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW
12n/a
13n/aIf the file doesn't have a recognizable type, it returns None.
14n/aIf the file can't be opened, OSError is raised.
15n/a
16n/aTo compute the total time, divide the number of frames by the
17n/asampling rate (a frame contains a sample for each channel).
18n/a
19n/aFunction what() calls whathdr(). (It used to also use some
20n/aheuristics for raw data, but this doesn't work very well.)
21n/a
22n/aFinally, the function test() is a simple main program that calls
23n/awhat() for all files mentioned on the argument list. For directory
24n/aarguments it calls what() for all files in that directory. Default
25n/aargument is "." (testing all files in the current directory). The
26n/aoption -r tells it to recurse down directories found inside
27n/aexplicitly given directories.
28n/a"""
29n/a
30n/a# The file structure is top-down except that the test program and its
31n/a# subroutine come last.
32n/a
33n/a__all__ = ['what', 'whathdr']
34n/a
35n/afrom collections import namedtuple
36n/a
37n/aSndHeaders = namedtuple('SndHeaders',
38n/a 'filetype framerate nchannels nframes sampwidth')
39n/a
40n/aSndHeaders.filetype.__doc__ = ("""The value for type indicates the data type
41n/aand will be one of the strings 'aifc', 'aiff', 'au','hcom',
42n/a'sndr', 'sndt', 'voc', 'wav', '8svx', 'sb', 'ub', or 'ul'.""")
43n/aSndHeaders.framerate.__doc__ = ("""The sampling_rate will be either the actual
44n/avalue or 0 if unknown or difficult to decode.""")
45n/aSndHeaders.nchannels.__doc__ = ("""The number of channels or 0 if it cannot be
46n/adetermined or if the value is difficult to decode.""")
47n/aSndHeaders.nframes.__doc__ = ("""The value for frames will be either the number
48n/aof frames or -1.""")
49n/aSndHeaders.sampwidth.__doc__ = ("""Either the sample size in bits or
50n/a'A' for A-LAW or 'U' for u-LAW.""")
51n/a
52n/adef what(filename):
53n/a """Guess the type of a sound file."""
54n/a res = whathdr(filename)
55n/a return res
56n/a
57n/a
58n/adef whathdr(filename):
59n/a """Recognize sound headers."""
60n/a with open(filename, 'rb') as f:
61n/a h = f.read(512)
62n/a for tf in tests:
63n/a res = tf(h, f)
64n/a if res:
65n/a return SndHeaders(*res)
66n/a return None
67n/a
68n/a
69n/a#-----------------------------------#
70n/a# Subroutines per sound header type #
71n/a#-----------------------------------#
72n/a
73n/atests = []
74n/a
75n/adef test_aifc(h, f):
76n/a import aifc
77n/a if not h.startswith(b'FORM'):
78n/a return None
79n/a if h[8:12] == b'AIFC':
80n/a fmt = 'aifc'
81n/a elif h[8:12] == b'AIFF':
82n/a fmt = 'aiff'
83n/a else:
84n/a return None
85n/a f.seek(0)
86n/a try:
87n/a a = aifc.open(f, 'r')
88n/a except (EOFError, aifc.Error):
89n/a return None
90n/a return (fmt, a.getframerate(), a.getnchannels(),
91n/a a.getnframes(), 8 * a.getsampwidth())
92n/a
93n/atests.append(test_aifc)
94n/a
95n/a
96n/adef test_au(h, f):
97n/a if h.startswith(b'.snd'):
98n/a func = get_long_be
99n/a elif h[:4] in (b'\0ds.', b'dns.'):
100n/a func = get_long_le
101n/a else:
102n/a return None
103n/a filetype = 'au'
104n/a hdr_size = func(h[4:8])
105n/a data_size = func(h[8:12])
106n/a encoding = func(h[12:16])
107n/a rate = func(h[16:20])
108n/a nchannels = func(h[20:24])
109n/a sample_size = 1 # default
110n/a if encoding == 1:
111n/a sample_bits = 'U'
112n/a elif encoding == 2:
113n/a sample_bits = 8
114n/a elif encoding == 3:
115n/a sample_bits = 16
116n/a sample_size = 2
117n/a else:
118n/a sample_bits = '?'
119n/a frame_size = sample_size * nchannels
120n/a if frame_size:
121n/a nframe = data_size / frame_size
122n/a else:
123n/a nframe = -1
124n/a return filetype, rate, nchannels, nframe, sample_bits
125n/a
126n/atests.append(test_au)
127n/a
128n/a
129n/adef test_hcom(h, f):
130n/a if h[65:69] != b'FSSD' or h[128:132] != b'HCOM':
131n/a return None
132n/a divisor = get_long_be(h[144:148])
133n/a if divisor:
134n/a rate = 22050 / divisor
135n/a else:
136n/a rate = 0
137n/a return 'hcom', rate, 1, -1, 8
138n/a
139n/atests.append(test_hcom)
140n/a
141n/a
142n/adef test_voc(h, f):
143n/a if not h.startswith(b'Creative Voice File\032'):
144n/a return None
145n/a sbseek = get_short_le(h[20:22])
146n/a rate = 0
147n/a if 0 <= sbseek < 500 and h[sbseek] == 1:
148n/a ratecode = 256 - h[sbseek+4]
149n/a if ratecode:
150n/a rate = int(1000000.0 / ratecode)
151n/a return 'voc', rate, 1, -1, 8
152n/a
153n/atests.append(test_voc)
154n/a
155n/a
156n/adef test_wav(h, f):
157n/a import wave
158n/a # 'RIFF' <len> 'WAVE' 'fmt ' <len>
159n/a if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ':
160n/a return None
161n/a f.seek(0)
162n/a try:
163n/a w = wave.openfp(f, 'r')
164n/a except (EOFError, wave.Error):
165n/a return None
166n/a return ('wav', w.getframerate(), w.getnchannels(),
167n/a w.getnframes(), 8*w.getsampwidth())
168n/a
169n/atests.append(test_wav)
170n/a
171n/a
172n/adef test_8svx(h, f):
173n/a if not h.startswith(b'FORM') or h[8:12] != b'8SVX':
174n/a return None
175n/a # Should decode it to get #channels -- assume always 1
176n/a return '8svx', 0, 1, 0, 8
177n/a
178n/atests.append(test_8svx)
179n/a
180n/a
181n/adef test_sndt(h, f):
182n/a if h.startswith(b'SOUND'):
183n/a nsamples = get_long_le(h[8:12])
184n/a rate = get_short_le(h[20:22])
185n/a return 'sndt', rate, 1, nsamples, 8
186n/a
187n/atests.append(test_sndt)
188n/a
189n/a
190n/adef test_sndr(h, f):
191n/a if h.startswith(b'\0\0'):
192n/a rate = get_short_le(h[2:4])
193n/a if 4000 <= rate <= 25000:
194n/a return 'sndr', rate, 1, -1, 8
195n/a
196n/atests.append(test_sndr)
197n/a
198n/a
199n/a#-------------------------------------------#
200n/a# Subroutines to extract numbers from bytes #
201n/a#-------------------------------------------#
202n/a
203n/adef get_long_be(b):
204n/a return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
205n/a
206n/adef get_long_le(b):
207n/a return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]
208n/a
209n/adef get_short_be(b):
210n/a return (b[0] << 8) | b[1]
211n/a
212n/adef get_short_le(b):
213n/a return (b[1] << 8) | b[0]
214n/a
215n/a
216n/a#--------------------#
217n/a# Small test program #
218n/a#--------------------#
219n/a
220n/adef test():
221n/a import sys
222n/a recursive = 0
223n/a if sys.argv[1:] and sys.argv[1] == '-r':
224n/a del sys.argv[1:2]
225n/a recursive = 1
226n/a try:
227n/a if sys.argv[1:]:
228n/a testall(sys.argv[1:], recursive, 1)
229n/a else:
230n/a testall(['.'], recursive, 1)
231n/a except KeyboardInterrupt:
232n/a sys.stderr.write('\n[Interrupted]\n')
233n/a sys.exit(1)
234n/a
235n/adef testall(list, recursive, toplevel):
236n/a import sys
237n/a import os
238n/a for filename in list:
239n/a if os.path.isdir(filename):
240n/a print(filename + '/:', end=' ')
241n/a if recursive or toplevel:
242n/a print('recursing down:')
243n/a import glob
244n/a names = glob.glob(os.path.join(filename, '*'))
245n/a testall(names, recursive, 0)
246n/a else:
247n/a print('*** directory (use -r) ***')
248n/a else:
249n/a print(filename + ':', end=' ')
250n/a sys.stdout.flush()
251n/a try:
252n/a print(what(filename))
253n/a except OSError:
254n/a print('*** not found ***')
255n/a
256n/aif __name__ == '__main__':
257n/a test()