Skip to content

Commit 8218b32

Browse files
committed
1.0.1 version
1 parent 83fc406 commit 8218b32

File tree

1 file changed

+309
-0
lines changed

1 file changed

+309
-0
lines changed

bkbin2wav.py

+309
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
#!/usr/bin/python
2+
# The Utility allows to convert BK-0010 .BIN snapshots into WAV format to be loaded through cable to real device
3+
#
4+
# URL: https://github.com/raydac/bkbin2wav
5+
# Author: Igor Maznitsa (http://www.igormaznitsa.com)
6+
# Version: 1.01
7+
#
8+
# License: Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
9+
10+
__author__ = 'Igor Maznitsa (http://www.igormaznitsa.com)'
11+
__version__ = '1.0.1'
12+
__projecturl__ = 'https://github.com/raydac/bkbin2wav'
13+
14+
import wave
15+
import getopt
16+
import sys
17+
import os
18+
import struct
19+
20+
SND_PARTS = [
21+
bytearray([0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40]),
22+
bytearray(
23+
[0x80, 0xa0, 0xb7, 0xc0, 0xb7, 0xa0, 0x80, 0x5f, 0x48, 0x3f, 0x48, 0x5f, 0x80, 0xb7, 0xb7, 0x80, 0x48, 0x48]),
24+
bytearray(
25+
[0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40,
26+
0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40,
27+
0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40, 0x80, 0xbf, 0xbf, 0x80, 0x40, 0x40]),
28+
bytearray(
29+
[0x80, 0x90, 0x9d, 0xa4, 0xa6, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x6f, 0x62, 0x5b, 0x57, 0x56,
30+
0x55, 0x55, 0x55, 0x55, 0x55, 0x6e, 0x80, 0x9a, 0xa2, 0xa6, 0xa7, 0xa9, 0x80, 0x6f, 0x63, 0x5c, 0x59, 0x59,
31+
0x80, 0xb7, 0xb7, 0x80, 0x48, 0x48]),
32+
bytearray(
33+
[0x80, 0x8f, 0xa8, 0xb5, 0xbc, 0xbf, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
34+
0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbd, 0xbc, 0xb2, 0x80, 0x59, 0x48, 0x40, 0x3b,
35+
0x39, 0x38, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
36+
0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3e, 0x7a])
37+
]
38+
39+
SIGNAL_RESET = 0
40+
SIGNAL_SET = 1
41+
SIGNAL_START_SYNCHRO = 2
42+
SIGNAL_SYNCHRO = 3
43+
SIGNAL_END_MARKER = 4
44+
45+
46+
def amplify():
47+
"""Extends audio diapason in the WAV signals to 1.0 instead of 0.5"""
48+
min_l = 256
49+
max_l = 0
50+
for arr in SND_PARTS:
51+
for b in arr:
52+
if b < min_l:
53+
min_l = b
54+
if b > max_l:
55+
max_l = b
56+
57+
max_l -= 128
58+
min_l -= 128
59+
60+
c_max = 128.0 / max_l
61+
c_min = -127.0 / min_l
62+
63+
coeff = min(c_max, c_min)
64+
65+
for arr in SND_PARTS:
66+
for i in range(len(arr)):
67+
arr[i] = max(0, min(255, int(round((arr[i] - 128) * coeff)) + 128))
68+
69+
70+
def play(i, arr, num=1):
71+
"""Save wav sequence for num-times into the result"""
72+
for c in range(num):
73+
for x in SND_PARTS[i]:
74+
arr.append(x)
75+
76+
77+
def byte2arr(b, arr):
78+
"""Append wav representation of a sound to array"""
79+
for i in range(0, 8):
80+
for a in SND_PARTS[(b >> i) & 1]:
81+
arr.append(a)
82+
83+
84+
def txt2arr(txt, arr):
85+
"""Write text chars as bytes into sound array (with 16 char restrictions)"""
86+
ws = 0
87+
if len(txt) < 16:
88+
ws = 16 - len(txt)
89+
if len(txt) > 16:
90+
txt = txt[0:16]
91+
for c in txt:
92+
byte2arr(ord(c), arr)
93+
94+
while ws != 0:
95+
byte2arr(ord(' '), arr)
96+
ws -= 1
97+
98+
99+
def short2arr(num, arr):
100+
"""Write a short (0...FFFF) value into sound array)"""
101+
byte2arr(num & 0xFF, arr)
102+
byte2arr((num >> 8) & 0xFF, arr)
103+
104+
105+
def init_wav(turbo_mode, filename, data_length):
106+
r = wave.open(filename, 'w')
107+
r.setnchannels(1)
108+
r.setsampwidth(1)
109+
if turbo_mode:
110+
r.setframerate(22050)
111+
else:
112+
r.setframerate(11025)
113+
r.setnframes(data_length)
114+
r.setcomptype('NONE', 'PCM')
115+
return r
116+
117+
118+
def calc_crc(data):
119+
a = 0
120+
for i in data:
121+
a += i
122+
if a > 0xFFFF:
123+
a -= 0xFFFF
124+
return a
125+
126+
127+
def make_bk_wav(turbo_mode, filename, start, name, data, data_length):
128+
sound_buffer = []
129+
130+
play(SIGNAL_START_SYNCHRO, sound_buffer, 512)
131+
play(SIGNAL_SYNCHRO, sound_buffer)
132+
133+
play(SIGNAL_START_SYNCHRO, sound_buffer)
134+
play(SIGNAL_SYNCHRO, sound_buffer)
135+
136+
short2arr(start, sound_buffer)
137+
short2arr(data_length, sound_buffer)
138+
txt2arr(name, sound_buffer)
139+
140+
play(SIGNAL_START_SYNCHRO, sound_buffer)
141+
play(SIGNAL_SYNCHRO, sound_buffer)
142+
143+
for i in range(data_length):
144+
byte2arr(data[i], sound_buffer)
145+
146+
tap_check_code = calc_crc(data)
147+
short2arr(tap_check_code, sound_buffer)
148+
149+
play(SIGNAL_END_MARKER, sound_buffer)
150+
play(SIGNAL_START_SYNCHRO, sound_buffer, 64 if turbo_mode else 32)
151+
play(SIGNAL_SYNCHRO, sound_buffer)
152+
153+
wav_file = init_wav(turbo_mode, filename, len(sound_buffer))
154+
try:
155+
wav_file.writeframes(bytearray(sound_buffer))
156+
finally:
157+
wav_file.close()
158+
return tap_check_code
159+
160+
161+
def read_short(f):
162+
v = struct.unpack('BB', f.read(2))
163+
return (v[1] << 8) | v[0]
164+
165+
166+
def read_bin(bin_file, enforce_physical_length):
167+
physical_length = os.path.getsize(bin_file) - 4
168+
169+
if physical_length < 0:
170+
print("Wrong BIN snapshot format, its size less than 4")
171+
exit(1)
172+
173+
f = open(bin_file, 'rb')
174+
with open(bin_file, 'rb') as f:
175+
start_address = read_short(f)
176+
bin_data_length = read_short(f)
177+
data_length = bin_data_length
178+
if enforce_physical_length:
179+
data_length = physical_length
180+
181+
if data_length > physical_length:
182+
print("Wrong data size length in BIN snapshot %d" % (data_length))
183+
exit(1)
184+
arr = bytearray(f.read(data_length))
185+
186+
return start_address, data_length, bin_data_length, arr,
187+
188+
189+
def header():
190+
print("""
191+
BKBIN2WAV allows to convert .BIN snapshots (for BK-0010(01) emulators) into WAV format.
192+
193+
Project page : %s
194+
Author : %s
195+
Version : %s
196+
197+
It is a converter of .BIN files (a snapshot format for BK-0010(01) emulators) into sound WAV files which compatible with real BK-0010 TAP reader
198+
""" % (__projecturl__, __author__, __version__))
199+
200+
201+
def help():
202+
print(""" Usage:
203+
204+
bkbin2wav -i <binfile> [-a] [-o <wavfile>] [-n <name>] [-s addr] [-t]
205+
206+
Command line options:
207+
-h Print help
208+
-f Use file size instead of .BIN header size field value
209+
-a Amplify the audio signal in the result WAV file
210+
-i <file> The BIN file to be converted
211+
-o <file> The Result WAV file (by default the BIN file name with WAV extension)
212+
-n <name> The Name of the file in the TAP header (must be less or equals 16 chars)
213+
-s <addr> The Start address for the TAP header (by default the start address from the BIN will be used)
214+
-t Use the double frequency "turbo" mode
215+
""")
216+
217+
218+
header()
219+
220+
opts, args = getopt.getopt(sys.argv[1:], "atfhi:o:n:s:",
221+
['amplify', 'turbo', 'help', 'input', 'output', 'name', 'start'])
222+
223+
infile = ''
224+
outfile = ''
225+
bin_start_address = -1
226+
name = ''
227+
turbo = False
228+
amplifier_flag = False
229+
enforce_physical_size = False
230+
231+
for o, a in opts:
232+
if o in ('-h', '--help', '-?', '--h', '-help'):
233+
help()
234+
exit(1)
235+
elif o == '-i':
236+
infile = a
237+
elif o == '-s':
238+
bin_start_address = int(a) & 0xFFFF
239+
elif o == '-a':
240+
amplifier_flag = True
241+
amplify()
242+
elif o == '-o':
243+
outfile = a
244+
elif o == '-n':
245+
name = a
246+
elif o == '-t':
247+
turbo = True
248+
elif o == '-f':
249+
enforce_physical_size = True
250+
else:
251+
help()
252+
exit(1)
253+
254+
if len(opts) == 0:
255+
help()
256+
exit(1)
257+
258+
if infile == '':
259+
print("The Input BIN file is undefined")
260+
exit(1)
261+
262+
if not os.path.isfile(infile):
263+
print("Can't find the BIN file %s" % (infile))
264+
exit(1)
265+
266+
if outfile == '':
267+
outfile = os.path.abspath(os.path.dirname(infile)) + os.sep + os.path.basename(infile) + '.wav'
268+
269+
if name == '':
270+
name = os.path.splitext(os.path.basename(infile))[0].upper()[:16]
271+
272+
bin_file_size_without_header = os.path.getsize(infile) - 4;
273+
bin_start, bin_length, bin_hdr_len, bin_read_length = read_bin(infile, enforce_physical_size)
274+
275+
if bin_start_address == -1:
276+
bin_start_address = bin_start
277+
278+
if enforce_physical_size:
279+
print(
280+
"Detected flag to enforce physical file size (size defined inside of .BIN is %s byte(s), real size is %s byte(s))" % (
281+
bin_hdr_len, bin_file_size_without_header))
282+
else:
283+
if bin_hdr_len != bin_file_size_without_header:
284+
print(
285+
"Warning! Detected different size defined in BIN header, use -f to use file size instead of header size (%s != %s)" % (
286+
bin_hdr_len, bin_file_size_without_header))
287+
288+
if bin_start_address != bin_start:
289+
print("Warning! The Start address has been changed from %d(%s) to %d(%s)" % (
290+
bin_start, oct(bin_start), bin_start_address, oct(bin_start_address)))
291+
292+
print("""
293+
Input file : %s
294+
Output file : %s
295+
Name : %s
296+
Start address : %d (%s)
297+
Turbo mode : %s
298+
Amplifier : %s
299+
""" % (infile, outfile, name, bin_start_address, oct(bin_start_address), ('ON' if turbo else 'OFF'),
300+
('ON' if amplifier_flag else 'OFF')))
301+
302+
print(""" Fields of the BIN file header:
303+
Start : %d (%s)
304+
Length : %d (%s)
305+
""" % (bin_start, oct(bin_start), bin_length, oct(bin_length)))
306+
307+
crc_code = make_bk_wav(turbo, outfile, bin_start_address, name, bin_read_length, bin_length)
308+
309+
print("The WAV file has been saved successfully as '%s', the checksum is %s" % (outfile, hex(crc_code)))

0 commit comments

Comments
 (0)