0001 """Routines to help recognizing sound files. 0002 0003 Function whathdr() recognizes various types of sound file headers. 0004 It understands almost all headers that SOX can decode. 0005 0006 The return tuple contains the following items, in this order: 0007 - file type (as SOX understands it) 0008 - sampling rate (0 if unknown or hard to decode) 0009 - number of channels (0 if unknown or hard to decode) 0010 - number of frames in the file (-1 if unknown or hard to decode) 0011 - number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW 0012 0013 If the file doesn't have a recognizable type, it returns None. 0014 If the file can't be opened, IOError is raised. 0015 0016 To compute the total time, divide the number of frames by the 0017 sampling rate (a frame contains a sample for each channel). 0018 0019 Function what() calls whathdr(). (It used to also use some 0020 heuristics for raw data, but this doesn't work very well.) 0021 0022 Finally, the function test() is a simple main program that calls 0023 what() for all files mentioned on the argument list. For directory 0024 arguments it calls what() for all files in that directory. Default 0025 argument is "." (testing all files in the current directory). The 0026 option -r tells it to recurse down directories found inside 0027 explicitly given directories. 0028 """ 0029 0030 # The file structure is top-down except that the test program and its 0031 # subroutine come last. 0032 0033 __all__ = ["what","whathdr"] 0034 0035 def what(filename): 0036 """Guess the type of a sound file""" 0037 res = whathdr(filename) 0038 return res 0039 0040 0041 def whathdr(filename): 0042 """Recognize sound headers""" 0043 f = open(filename, 'rb') 0044 h = f.read(512) 0045 for tf in tests: 0046 res = tf(h, f) 0047 if res: 0048 return res 0049 return None 0050 0051 0052 #-----------------------------------# 0053 # Subroutines per sound header type # 0054 #-----------------------------------# 0055 0056 tests = [] 0057 0058 def test_aifc(h, f): 0059 import aifc 0060 if h[:4] != 'FORM': 0061 return None 0062 if h[8:12] == 'AIFC': 0063 fmt = 'aifc' 0064 elif h[8:12] == 'AIFF': 0065 fmt = 'aiff' 0066 else: 0067 return None 0068 f.seek(0) 0069 try: 0070 a = aifc.openfp(f, 'r') 0071 except (EOFError, aifc.Error): 0072 return None 0073 return (fmt, a.getframerate(), a.getnchannels(), \ 0074 a.getnframes(), 8*a.getsampwidth()) 0075 0076 tests.append(test_aifc) 0077 0078 0079 def test_au(h, f): 0080 if h[:4] == '.snd': 0081 f = get_long_be 0082 elif h[:4] in ('\0ds.', 'dns.'): 0083 f = get_long_le 0084 else: 0085 return None 0086 type = 'au' 0087 hdr_size = f(h[4:8]) 0088 data_size = f(h[8:12]) 0089 encoding = f(h[12:16]) 0090 rate = f(h[16:20]) 0091 nchannels = f(h[20:24]) 0092 sample_size = 1 # default 0093 if encoding == 1: 0094 sample_bits = 'U' 0095 elif encoding == 2: 0096 sample_bits = 8 0097 elif encoding == 3: 0098 sample_bits = 16 0099 sample_size = 2 0100 else: 0101 sample_bits = '?' 0102 frame_size = sample_size * nchannels 0103 return type, rate, nchannels, data_size/frame_size, sample_bits 0104 0105 tests.append(test_au) 0106 0107 0108 def test_hcom(h, f): 0109 if h[65:69] != 'FSSD' or h[128:132] != 'HCOM': 0110 return None 0111 divisor = get_long_be(h[128+16:128+20]) 0112 return 'hcom', 22050/divisor, 1, -1, 8 0113 0114 tests.append(test_hcom) 0115 0116 0117 def test_voc(h, f): 0118 if h[:20] != 'Creative Voice File\032': 0119 return None 0120 sbseek = get_short_le(h[20:22]) 0121 rate = 0 0122 if 0 <= sbseek < 500 and h[sbseek] == '\1': 0123 ratecode = ord(h[sbseek+4]) 0124 rate = int(1000000.0 / (256 - ratecode)) 0125 return 'voc', rate, 1, -1, 8 0126 0127 tests.append(test_voc) 0128 0129 0130 def test_wav(h, f): 0131 # 'RIFF' <len> 'WAVE' 'fmt ' <len> 0132 if h[:4] != 'RIFF' or h[8:12] != 'WAVE' or h[12:16] != 'fmt ': 0133 return None 0134 style = get_short_le(h[20:22]) 0135 nchannels = get_short_le(h[22:24]) 0136 rate = get_long_le(h[24:28]) 0137 sample_bits = get_short_le(h[34:36]) 0138 return 'wav', rate, nchannels, -1, sample_bits 0139 0140 tests.append(test_wav) 0141 0142 0143 def test_8svx(h, f): 0144 if h[:4] != 'FORM' or h[8:12] != '8SVX': 0145 return None 0146 # Should decode it to get #channels -- assume always 1 0147 return '8svx', 0, 1, 0, 8 0148 0149 tests.append(test_8svx) 0150 0151 0152 def test_sndt(h, f): 0153 if h[:5] == 'SOUND': 0154 nsamples = get_long_le(h[8:12]) 0155 rate = get_short_le(h[20:22]) 0156 return 'sndt', rate, 1, nsamples, 8 0157 0158 tests.append(test_sndt) 0159 0160 0161 def test_sndr(h, f): 0162 if h[:2] == '\0\0': 0163 rate = get_short_le(h[2:4]) 0164 if 4000 <= rate <= 25000: 0165 return 'sndr', rate, 1, -1, 8 0166 0167 tests.append(test_sndr) 0168 0169 0170 #---------------------------------------------# 0171 # Subroutines to extract numbers from strings # 0172 #---------------------------------------------# 0173 0174 def get_long_be(s): 0175 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) 0176 0177 def get_long_le(s): 0178 return (ord(s[3])<<24) | (ord(s[2])<<16) | (ord(s[1])<<8) | ord(s[0]) 0179 0180 def get_short_be(s): 0181 return (ord(s[0])<<8) | ord(s[1]) 0182 0183 def get_short_le(s): 0184 return (ord(s[1])<<8) | ord(s[0]) 0185 0186 0187 #--------------------# 0188 # Small test program # 0189 #--------------------# 0190 0191 def test(): 0192 import sys 0193 recursive = 0 0194 if sys.argv[1:] and sys.argv[1] == '-r': 0195 del sys.argv[1:2] 0196 recursive = 1 0197 try: 0198 if sys.argv[1:]: 0199 testall(sys.argv[1:], recursive, 1) 0200 else: 0201 testall(['.'], recursive, 1) 0202 except KeyboardInterrupt: 0203 sys.stderr.write('\n[Interrupted]\n') 0204 sys.exit(1) 0205 0206 def testall(list, recursive, toplevel): 0207 import sys 0208 import os 0209 for filename in list: 0210 if os.path.isdir(filename): 0211 print filename + '/:', 0212 if recursive or toplevel: 0213 print 'recursing down:' 0214 import glob 0215 names = glob.glob(os.path.join(filename, '*')) 0216 testall(names, recursive, 0) 0217 else: 0218 print '*** directory (use -r) ***' 0219 else: 0220 print filename + ':', 0221 sys.stdout.flush() 0222 try: 0223 print what(filename) 0224 except IOError: 0225 print '*** not found ***' 0226 0227 if __name__ == '__main__': 0228 test() 0229
Generated by PyXR 0.9.4