└── main.py /main.py: -------------------------------------------------------------------------------- 1 | import audioop 2 | import ossaudiodev 3 | from collections import deque 4 | 5 | THRESHOLD_FACTOR = 3.5 6 | FIRST_PEAK_FACTOR = 0.8 7 | SECOND_PEAK_FACTOR = 0.5 8 | 9 | def get_chunk(src, bias): 10 | data = audioop.bias(src.read(10000), 2, bias) 11 | return data, audioop.maxpp(data, 2) 12 | 13 | def get_swipe(dev='/dev/audio'): 14 | audio = ossaudiodev.open(dev, 'r') 15 | audio.setparameters(ossaudiodev.AFMT_S16_LE, 1, 44100) 16 | 17 | baselines = deque([2**15] * 4) 18 | bias = 0 19 | while 1: 20 | data, power = get_chunk(audio, bias) 21 | 22 | baseline = sum(baselines) / len(baselines) * THRESHOLD_FACTOR 23 | print power, baseline, power / (baseline or 1) 24 | 25 | chunks = [] 26 | while power > baseline: 27 | print power, baseline, power / (baseline or 1), '*' 28 | chunks.append(data) 29 | data, power = get_chunk(audio, bias) 30 | 31 | if len(chunks) > 1: 32 | data = old_data + ''.join(chunks) + data 33 | while audioop.maxpp(data[:3000], 2) < baseline / 2: 34 | data = data[1000:] 35 | while audioop.maxpp(data[-3000:], 2) < baseline / 2: 36 | data = data[:-1000] 37 | 38 | return audioop.bias(data, 2, -audioop.avg(data, 2)) 39 | 40 | old_data = data 41 | 42 | bias = -audioop.avg(data, 2) 43 | 44 | baselines.popleft() 45 | baselines.append(power) 46 | 47 | def get_samples(data, width=2): 48 | return list(audioop.getsample(data, width, i) for i in xrange(len(data) / width)) 49 | 50 | def get_peaks(data): 51 | peak_threshold = audioop.maxpp(data[:1000], 2) * FIRST_PEAK_FACTOR 52 | 53 | samples = get_samples(data) 54 | 55 | i = 0 56 | old_i = 0 57 | sign = 1 58 | while i < len(samples): 59 | peak = 0 60 | while samples[i] * sign > peak_threshold: 61 | peak = max(samples[i] * sign, peak) 62 | i += 1 63 | 64 | if peak: 65 | if old_i: 66 | yield i - old_i 67 | old_i = i 68 | sign *= -1 69 | peak_threshold = peak * SECOND_PEAK_FACTOR 70 | 71 | i += 1 72 | 73 | def get_bits(peaks): 74 | peaks = list(peaks) 75 | 76 | # Discard first 5 peaks 77 | peaks = peaks[5:] 78 | 79 | # Clock next 4 peaks (should be zeros) 80 | clocks = deque([p / 2.0 for p in peaks[:4]]) 81 | 82 | i = 0 83 | while i < len(peaks)-2: 84 | peak = peaks[i] 85 | 86 | if peak > 1.5 * sum(clocks, 0.0) / len(clocks): 87 | yield 0 88 | i += 1 89 | clocks.append(peak / 2) 90 | else: 91 | yield 1 92 | i += 2 93 | clocks.append(peak) 94 | clocks.popleft() 95 | 96 | def get_bytes(bits, width=5): 97 | bits = list(bits) 98 | 99 | # Discard leading 0s 100 | while bits[0] == 0: 101 | bits = bits[1:] 102 | 103 | while 1: 104 | byte, bits = bits[:width], bits[width:] 105 | if len(byte) < width or sum(byte) % 2 != 1: 106 | return 107 | yield byte 108 | 109 | def bcd_chr(byte): 110 | return chr(int(''.join(map(str, byte[-2::-1])), 2) + 48) 111 | 112 | def get_bcd_chars(bytes): 113 | bytes = list(bytes) 114 | 115 | 116 | if bcd_chr(bytes[0]) != ';': 117 | # Try reversed 118 | bytes = [byte[::-1] for byte in reversed(bytes)] 119 | 120 | ibytes = iter(bytes) 121 | 122 | start = ibytes.next() 123 | if bcd_chr(start) != ';': 124 | raise DecodeError('No start sentinal') 125 | 126 | lrc = start 127 | try: 128 | while 1: 129 | byte = ibytes.next() 130 | char = bcd_chr(byte) 131 | 132 | for i in range(len(lrc) - 1): 133 | lrc[i] = (lrc[i] + byte[i]) % 2 134 | 135 | if char == '?': 136 | lrc[-1] = sum(lrc[:-1], 1) % 2 137 | real_lrc = ibytes.next() 138 | if real_lrc != lrc: 139 | raise DecodeError('Bad LRC') 140 | return 141 | 142 | yield char 143 | 144 | except StopIteration: 145 | raise DecodeError('No end sentinal') 146 | 147 | 148 | class DecodeError(Exception): 149 | pass 150 | 151 | if __name__ == '__main__': 152 | print 'READY' 153 | data = get_swipe() 154 | peaks = list(get_peaks(data)) 155 | bits = list(get_bits(peaks)) 156 | bytes = list(get_bytes(bits)) 157 | try: 158 | print ''.join(get_bcd_chars(bytes)) 159 | except DecodeError, e: 160 | print e 161 | --------------------------------------------------------------------------------