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