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