Permalink
Fetching contributors…
Cannot retrieve contributors at this time
85 lines (70 sloc) 2.63 KB
#!/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)