1 """Routines to help recognizing sound files.
3 Function whathdr() recognizes various types of sound file headers.
4 It understands almost all headers that SOX can decode.
6 The 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
13 If the file doesn't have a recognizable type, it returns None.
14 If the file can't be opened, IOError is raised.
16 To compute the total time, divide the number of frames by the
17 sampling rate (a frame contains a sample for each channel).
19 Function what() calls whathdr(). (It used to also use some
20 heuristics for raw data, but this doesn't work very well.)
22 Finally, the function test() is a simple main program that calls
23 what() for all files mentioned on the argument list. For directory
24 arguments it calls what() for all files in that directory. Default
25 argument is "." (testing all files in the current directory). The
26 option -r tells it to recurse down directories found inside
27 explicitly given directories.
30 # The file structure is top-down except that the test program and its
31 # subroutine come last.
33 __all__
= ["what","whathdr"]
36 """Guess the type of a sound file"""
37 res
= whathdr(filename
)
41 def whathdr(filename
):
42 """Recognize sound headers"""
43 f
= open(filename
, 'rb')
52 #-----------------------------------#
53 # Subroutines per sound header type #
54 #-----------------------------------#
64 elif h
[8:12] == 'AIFF':
70 a
= aifc
.openfp(f
, 'r')
71 except (EOFError, aifc
.Error
):
73 return (fmt
, a
.getframerate(), a
.getnchannels(), \
74 a
.getnframes(), 8*a
.getsampwidth())
76 tests
.append(test_aifc
)
82 elif h
[:4] in ('\0ds.', 'dns.'):
88 data_size
= f(h
[8:12])
89 encoding
= f(h
[12:16])
91 nchannels
= f(h
[20:24])
92 sample_size
= 1 # default
102 frame_size
= sample_size
* nchannels
103 return type, rate
, nchannels
, data_size
/frame_size
, sample_bits
105 tests
.append(test_au
)
109 if h
[65:69] != 'FSSD' or h
[128:132] != 'HCOM':
111 divisor
= get_long_be(h
[128+16:128+20])
112 return 'hcom', 22050/divisor
, 1, -1, 8
114 tests
.append(test_hcom
)
118 if h
[:20] != 'Creative Voice File\032':
120 sbseek
= get_short_le(h
[20:22])
122 if 0 <= sbseek
< 500 and h
[sbseek
] == '\1':
123 ratecode
= ord(h
[sbseek
+4])
124 rate
= int(1000000.0 / (256 - ratecode
))
125 return 'voc', rate
, 1, -1, 8
127 tests
.append(test_voc
)
131 # 'RIFF' <len> 'WAVE' 'fmt ' <len>
132 if h
[:4] != 'RIFF' or h
[8:12] != 'WAVE' or h
[12:16] != 'fmt ':
134 style
= get_short_le(h
[20:22])
135 nchannels
= get_short_le(h
[22:24])
136 rate
= get_long_le(h
[24:28])
137 sample_bits
= get_short_le(h
[34:36])
138 return 'wav', rate
, nchannels
, -1, sample_bits
140 tests
.append(test_wav
)
144 if h
[:4] != 'FORM' or h
[8:12] != '8SVX':
146 # Should decode it to get #channels -- assume always 1
147 return '8svx', 0, 1, 0, 8
149 tests
.append(test_8svx
)
154 nsamples
= get_long_le(h
[8:12])
155 rate
= get_short_le(h
[20:22])
156 return 'sndt', rate
, 1, nsamples
, 8
158 tests
.append(test_sndt
)
163 rate
= get_short_le(h
[2:4])
164 if 4000 <= rate
<= 25000:
165 return 'sndr', rate
, 1, -1, 8
167 tests
.append(test_sndr
)
170 #---------------------------------------------#
171 # Subroutines to extract numbers from strings #
172 #---------------------------------------------#
175 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
178 return (ord(s
[3])<<24) |
(ord(s
[2])<<16) |
(ord(s
[1])<<8) |
ord(s
[0])
181 return (ord(s
[0])<<8) |
ord(s
[1])
184 return (ord(s
[1])<<8) |
ord(s
[0])
187 #--------------------#
188 # Small test program #
189 #--------------------#
194 if sys
.argv
[1:] and sys
.argv
[1] == '-r':
199 testall(sys
.argv
[1:], recursive
, 1)
201 testall(['.'], recursive
, 1)
202 except KeyboardInterrupt:
203 sys
.stderr
.write('\n[Interrupted]\n')
206 def testall(list, recursive
, toplevel
):
209 for filename
in list:
210 if os
.path
.isdir(filename
):
211 print filename
+ '/:',
212 if recursive
or toplevel
:
213 print 'recursing down:'
215 names
= glob
.glob(os
.path
.join(filename
, '*'))
216 testall(names
, recursive
, 0)
218 print '*** directory (use -r) ***'
220 print filename
+ ':',
225 print '*** not found ***'
227 if __name__
== '__main__':