import wave import struct # the format of BBC Micro tape files is: # - a 0 bit is 1200 Hz (sampled at 44100Hz, wavelength is 36 samples) # - a 1 bit is 2400 Hz (ditto, 18 samples) # Leader tone is 2400 Hz # # # ref http://beebwiki.mdfs.net/Acorn_cassette_format def physical_layer_read(filename): thames = wave.open(filename, 'rb') freq = thames.getframerate() result = [] if thames.getsampwidth()!=2: raise Exception('Sorry, sample width is weird: %d' % (thames.getsampwidth(),)) contents = thames.readframes(thames.getnframes()) prev = None count = 0 # Because 2400Hz tones fill two cycles in the space # where 1200Hz tones fill one, we will see them appear # twice. If this flag is set, we just output a 1 (for # the 2400Hz tone), so if we see another 2400Hz cycle # we shouldn't output another 1. in_2400 = False for i in range(0, len(contents)/2): value = struct.unpack('0: current = 'high' else: continue if prev=='low' and current=='high': if count < 27: # Short wavelength = high frequency if not in_2400: result.append(1) in_2400 = True else: in_2400 = False else: # Long wavelength = low frequency result.append(0) in_2400 = False count = 0 else: count += 1 prev = current return result def data_layer_read(bits): skipcount = 0 result = [] i = 0 while i 32: # there's been a lead tone, # so start a new block result.append('') skipcount = 0 value = 0 for k in range(i+1, i+9): value = value >> 1 value += bits[k]*128 result[-1] += chr(value) i += 10 else: i += 1 skipcount += 1 return result def block_layer_read(block): result = { 'filename': '', } if block[0]!='*': # don't print error for glitches if len(block)>32: print 'warning: magic number missing' return i = 1 while block[i]!=chr(0): result['filename'] += block[i] i += 1 i += 1 # ? off by one ? i += 4 # skip load address i += 4 # skip exec address result['serial'] = ord(block[i+1])<<8 | ord(block[i]) i += 2 result['length'] = ord(block[i+1])<<8 | ord(block[i]) i += 2 result['flags'] = ord(block[i]) i += 1 i += 2 # skip CRC i += 4 # skip something else (?) result['data'] = block[i:-2] return result def main(): bits = physical_layer_read('bbc2.wav') blocks = data_layer_read(bits) files = [] for block in blocks: details = block_layer_read(block) if details is None: continue print details print details['filename'], details['serial'] if details['serial'] == 0: files.append('') files[-1]+=details['data'] for i in range(len(files)): filename = 'thames%d.dat' % (i,) open(filename, 'wb').write(files[i]) if __name__=='__main__': main()