#!/usr/bin/env python # coding: utf8 import wave as wave_lib from array import array from functools import partial READ_FRAMES_PER_BLOCK = 64 * 1024 OSCILLATOR_RATE = 14318180 SAM_CPU_SLOW_DIVISOR = 16 BIT0_LENGTH = 813 * SAM_CPU_SLOW_DIVISOR BIT1_LENGTH = 435 * SAM_CPU_SLOW_DIVISOR AVERAGE_BIT_LENGTH = (BIT0_LENGTH + BIT1_LENGTH) / 2 class TapeError(Exception): pass def iter_samples(wave): samplewidth = wave.getsampwidth() try: typecode = {1: 'b', 2: 'h', 4: 'i'}[samplewidth] except KeyError: raise ValueError('Unsupported samplewidth: {0}'.format(samplewidth)) for frames in iter(partial(wave.readframes, READ_FRAMES_PER_BLOCK), ''): for value in array(typecode, frames): yield value class PushbackIterator(object): NOTHING = object() def __init__(self, iterable): self.iterator = iter(iterable) self.pushed_value = self.NOTHING def __iter__(self): return self def next(self): result = self.pushed_value if result is self.NOTHING: return next(self.iterator) else: self.pushed_value = self.NOTHING return result def push_value(self, value): self.pushed_value = value class Tape(object): def __init__(self, filename): self.wave = wave_lib.open(filename) if self.wave.getnchannels() != 1: raise TapeError( 'unsupported number of channels: {0}'.format( self.wave.getnchannels() ) ) self.cycles_per_frame = OSCILLATOR_RATE / self.wave.getframerate() self.samples = PushbackIterator(iter_samples(self.wave)) def __enter__(self): return self def __exit__(self, *_args): self.close() def close(self): self.wave.close() def pulse_in(self): length = self.cycles_per_frame sign = next(self.samples) < 0 for sample in self.samples: if (sample < 0) != sign: self.samples.push_value(sample) break length += self.cycles_per_frame if length > (OSCILLATOR_RATE / 2): break return sign, length def bit_in(self): _, pulse1_width = self.pulse_in() while True: pulse0_width = pulse1_width phase, pulse1_width = self.pulse_in() cycle_width = pulse0_width + pulse1_width if phase and (BIT1_LENGTH / 2) <= cycle_width <= (BIT0_LENGTH * 2): break return int(cycle_width < AVERAGE_BIT_LENGTH) def byte_in(self): result = 0 for _ in xrange(8): result = (result >> 1) | (0x80 if self.bit_in() else 0) return result def block_sync(self): byte = 0 while True: byte = (byte >> 1) | (0x80 if self.bit_in() else 0) # print '{0:08b}'.format(byte) if byte == 0x3c: break def block_in(self): self.block_sync() block = [self.byte_in(), self.byte_in()] block.extend(self.byte_in() for _ in xrange(block[1])) expected_sum = self.byte_in() block_sum = sum(block) & 0xff if expected_sum != block_sum: print block raise TapeError( 'Wrong block checksum: expected {0}, got {1}'.format( expected_sum, block_sum ) ) block.append(expected_sum) return block def main(): #filename = 'HelloWorld1 xroar.wav' filename = 'HelloWorld1 origin.wav' tape = Tape(filename) print tape.block_in() print tape.block_in() print tape.block_in() if __name__ == '__main__': main()