Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #!/usr/bin/env python3 | |
| # kcs_encode.py | |
| # | |
| # Author : David Beazley (http://www.dabeaz.com) | |
| # Copyright (C) 2010 | |
| # | |
| # Modified for binary files for use with cc65 executables for OSI | |
| # computers by Stephan Muehlstrasser | |
| # | |
| # Requires Python 3.1.2 or newer | |
| """ | |
| Takes the contents of a text file and encodes it into a Kansas | |
| City Standard WAV file, that when played will upload data via the | |
| cassette tape input on various vintage home computers. See | |
| http://en.wikipedia.org/wiki/Kansas_City_standard | |
| """ | |
| import wave | |
| # A few global parameters related to the encoding | |
| FRAMERATE = 9600 # Hz | |
| ONES_FREQ = 2400 # Hz (per KCS) | |
| ZERO_FREQ = 1200 # Hz (per KCS) | |
| AMPLITUDE = 128 # Amplitude of generated square waves | |
| CENTER = 128 # Center point of generated waves | |
| # Create a single square wave cycle of a given frequency | |
| def make_square_wave(freq,framerate): | |
| n = int(framerate/freq/2) | |
| return bytearray([CENTER-AMPLITUDE//2])*n + \ | |
| bytearray([CENTER+AMPLITUDE//2])*n | |
| # Create the wave patterns that encode 1s and 0s | |
| one_pulse = make_square_wave(ONES_FREQ,FRAMERATE)*8 | |
| zero_pulse = make_square_wave(ZERO_FREQ,FRAMERATE)*4 | |
| # Pause to insert after carriage returns (10 NULL bytes) | |
| null_pulse = ((zero_pulse * 9) + (one_pulse * 2))*10 | |
| # Take a single byte value and turn it into a bytearray representing | |
| # the associated waveform along with the required start and stop bits. | |
| def kcs_encode_byte(byteval): | |
| bitmasks = [0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80] | |
| # The start bit (0) | |
| encoded = bytearray(zero_pulse) | |
| # 8 data bits | |
| for mask in bitmasks: | |
| encoded.extend(one_pulse if (byteval & mask) else zero_pulse) | |
| # Two stop bits (1) | |
| encoded.extend(one_pulse) | |
| encoded.extend(one_pulse) | |
| return encoded | |
| # Write a WAV file with encoded data. leader and trailer specify the | |
| # number of seconds of carrier signal to encode before and after the data | |
| def kcs_write_wav(filename,data,leader,trailer): | |
| w = wave.open(filename,"wb") | |
| w.setnchannels(1) | |
| w.setsampwidth(1) | |
| w.setframerate(FRAMERATE) | |
| # Write the leader | |
| w.writeframes(one_pulse*(int(FRAMERATE/len(one_pulse))*leader)) | |
| # Encode the actual data | |
| for byteval in data: | |
| w.writeframes(kcs_encode_byte(byteval)) | |
| # Write the trailer | |
| w.writeframes(one_pulse*(int(FRAMERATE/len(one_pulse))*trailer)) | |
| w.close() | |
| if __name__ == '__main__': | |
| import sys | |
| if len(sys.argv) != 3: | |
| print("Usage : %s infile outfile" % sys.argv[0],file=sys.stderr) | |
| raise SystemExit(1) | |
| in_filename = sys.argv[1] | |
| out_filename = sys.argv[2] | |
| data = open(in_filename,"rb").read() | |
| kcs_write_wav(out_filename,data,5,5) |