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