-
Notifications
You must be signed in to change notification settings - Fork 1
/
Rmid2bin.bas
632 lines (431 loc) · 12.8 KB
/
Rmid2bin.bas
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
' This program extracts the firmware binary from Roland's SP-808EX update
' http://www.users.on.net/~fzabkar/temp/Rmid2bin.bas
#include "vbcompat.bi"
Dim infil As String
Dim outfil As String
' Dim f1 As Long
Common Shared f1 As Long
Dim f2 As Long
Dim i As UShort
Dim j As UShort
Dim str4var As String * 4
Dim MThd As Const String = "MThd"
Dim RmanfID As UByte = &H41
Dim MdevID As UByte = &H10
Dim RmodID As UByte = &H2B
Dim Mdataset1 As UByte = &H12
Dim bytvar As UByte
Dim wdvar As UShort
Dim dwvar As ULong
Dim uintvar As UInteger
Dim filsiz As UInteger
Dim MTrk As Const String = "MTrk"
Dim UARTclk As ULong
Dim deltaT As UByte
Dim event As UByte
Dim eventtyp As UByte
' Dim reclen As UShort
Common Shared reclen As UShort
Dim filofst As ULong
Dim eventadd( 1 To 3 ) As UByte
Dim memadd As ULong
Dim memstart As ULong
Dim cksm As UByte
Dim datapos As ULong
Dim datalen As UShort
Dim f7pos As ULong
Dim filnum As UByte
Dim totalfiles As UByte
Dim fileflag As UByte
Dim dwvar1 As ULong
Dim dwvar2 As ULong
Dim memsize As ULong
Dim octet( 1 To 7 ) As Ubyte
Dim maskbyt As UByte
' subroutine to calculate length of sysex records -- assumes max length of 0x3FFF (two bytes)
Declare Sub Sreclen()
Sub Sreclen()
Dim bytvar As UByte
reclen = 0
Get #f1,, bytvar
If bytvar < &H80 Then
reclen = bytvar
Exit Sub
End If
reclen = reclen + (bytvar And &H7F) shl 7
Get #f1,, bytvar
If bytvar > &H7F Then
Print "Unexpected record length (> 0x3FFF)"
reclen = &H4000
Exit Sub
End If
reclen = reclen + bytvar
End Sub
If Command( 3 ) <> "" Then
Print "Too many arguments"
Goto usage
End If
infil = Command( 1 )
If Left( infil, 6 ) <> "infil=" Then
Print "Input file not specified"
Goto usage
End If
infil = Mid( infil, 7 )
If infil ="" Then Goto usage End If
If Not FileExists( infil ) Then
Print "File not found: "; infil
End
End If
outfil = Command( 2 )
If Left( outfil, 7 ) <> "outfil=" Then
Print "Output file not specified"
Goto usage
End If
outfil = Mid( outfil, 8 )
If outfil ="" Then Goto usage End If
' If FileExists( outfil ) Then
' Print "Output file already exists: "; outfil
' End
' End If
f1 = FreeFile
Open infil For Binary As #f1
f2 = FreeFile
Open outfil For Binary As #f2
Print "Extracting binary firmware data from "; infil; " and writing to "; outfil; " ..."
Print
' Check for MIDI marker "MThd"
Get #f1,, str4var
If str4var <> MThd Then
Print "Unrecognised marker in header chunk -- should be """; MThd; """."
Goto aborted
End If
' Check for header length = 00 00 00 06
Get #f1,, uintvar
If uintvar <> &H06000000 Then
Print "Unexpected data length in header chunk (0x"; Hex( uintvar, 8 ); " -- should be 00 00 00 06"
Goto aborted
End If
' Check for SMF type = 0x0000
Get #f1,, wdvar
If wdvar <> 0 Then
Print "Unexpected SMF type in header chunk (0x"; Hex( wdvar, 4 ); " -- should be 0x0000"
Goto aborted
End If
' Check for Number of Tracks = 0x0001
Get #f1,, wdvar
If wdvar <> &H0100 Then
Print "Unexpected Number of Tracks in header chunk (0x"; Hex( wdvar, 4 ); " -- should be 0x0001"
Goto aborted
End If
' Skip past PPQ and ignore it
Get #f1,, wdvar
' Check for Track marker "MTrk"
Get #f1,, str4var
If str4var <> MTrk Then
Print "Unrecognised marker in track chunk -- should be """; MTrk; """."
Goto aborted
End If
' Check for correct file size -- big-endian
filsiz = 0
For i = 1 To 4
Get #f1,, bytvar
filsiz = (filsiz Shl 8) + bytvar
Next i
uintvar = Lof( f1 )
If filsiz <> ( uintvar - &H16 ) Then
Print "Length of MIDI file (0x"; Hex( uintvar ); ") is inconsistent with track chunk (0x"; Hex( filsiz, 8 ); ") + 0x16"
Goto aborted
End If
' Parse <delta_time> <event> records
' 00 FF 03 <len> <firmware model/version/date>
' 00 FF 02 <len> <copyright notice>
' 00 FF 51 <len> <UART clock rate>
' 10 F0 <len> 41 10 00 2B 12 <01 02 00> 00 00 00 00 00 <00 07 00> <cksm> F7 - start of file 1 of 8
' 10 F0 <len> 41 10 00 2B 12 <01 02 00> 00 00 00 00 00 <07 07 00> <cksm> F7 - start of file 8 of 8
' 10 F0 <len> 41 10 00 2B 12 <01 03 00> 00 00 00 00 00 <firmware model/version/date> <cksm> F7
' 10 F0 <len> 41 10 00 2B 12 <01 00 00> 00 00 00 00 00 ...
' ... <00 00 01> <00 00 00 00 00> <00 00 01> <0C 00 00 00 00> <cksm> F7
' <-- memstart --> <---memsize --->
' (nibble mode) (nibble mode)
' 10 F0 <len> 41 10 00 2B 12 <00 00 01> <5-byte mem addr (nibble mode)> <firmware payload> <cksm> F7
' 10 F0 <len> 41 10 00 2B 12 <01 01 00> 00 00 00 00 00 <4E> <cksm> F7 - only at end of file8
' 10 F0 <len> 41 10 00 2B 12 <01 02 00> 00 00 00 00 00 <00 07 7F> <cksm> F7 - end of file 1 of 8
' 10 F0 <len> 41 10 00 2B 12 <01 02 00> 00 00 00 00 00 <07 07 7F> <cksm> F7 - end of file 8 of 8
' 00 FF 2F 00 - end of track
Do Until EOF( f1 )
Get #f1,, deltaT
' Expecting delta_time of 0x00 or 0x10
Select Case As Const deltaT
Case &H10
' Delta_time = 0x10 (16 UART clock ticks) -- now expecting Roland 0xF0 sysex Event
Get #f1,, event
If event <> &HF0 Then
Print "Unrecognised Sysex Event value (0x"; Hex( event, 2 ); ") -- was expecting 0xF0"
Goto aborted
Else
' Get length of sysex data
Sreclen
If reclen > &H3FFF Then Goto aborted End If
' Now expecting Roland's manufacturer ID (0x41)
Get #f1,, bytvar
If bytvar <> RmanfID Then
Print "Unrecognised manufacturer ID (0x"; Hex( bytvar, 2 ); ") -- was expecting 0x"; Hex( RmanfID, 2)
Goto aborted
End If
' Now expecting default MIDI Device_ID (0x10)
Get #f1,, bytvar
If bytvar <> MdevID Then
Print "Unrecognised Device ID (0x"; Hex( bytvar, 2 ); ") -- was expecting 0x"; Hex( MdevID, 2)
Goto aborted
End If
' Now expecting 0x00
Get #f1,, bytvar
If bytvar <> 0 Then
Print "Unrecognised sysex event format"
Goto aborted
End If
' Now expecting Model ID (0x2B)
Get #f1,, bytvar
If bytvar <> RmodID Then
Print "Unrecognised Model ID (0x"; Hex( bytvar, 2 ); ") -- was expecting 0x"; Hex( RmodID, 2)
Goto aborted
End If
' Now expecting "Data Set 1" command ID (0x12)
Get #f1,, bytvar
If bytvar <> Mdataset1 Then
Print "Unrecognised command code -- (0x"; Hex( bytvar, 2 ); ") was expecting 0x12 (Data Set 1)"
Goto aborted
End If
' Checksum is computed from this point until 0xF7
cksm = 0
' Get 3 event address bytes
For i = 1 To 3
Get #f1,, eventadd( i )
cksm = cksm + eventadd( i )
Next i
' Get 5-byte memory address in nibble mode
memadd = 0
For i = 1 To 5
Get #f1,, bytvar
cksm = cksm + bytvar
memadd = (memadd Shl 4) + bytvar
Next i
' Save current file position
datapos = Seek( f1 )
' Now check for 0xF7 at end of packet
f7pos = datapos + (reclen - &H0E)
Get #f1, f7pos, bytvar
If bytvar <> &HF7 Then
Print "Unrecognised end-of-packet byte (0x"; Hex( bytvar, 2 ); ") -- was expecting 0xF7"
Goto aborted
End If
' Confirm that 7-bit checksum = 0x00 (from start of address bytes to checksum byte, inclusive)
Seek f1, datapos
datalen = reclen - &HF
For i = 1 To datalen + 1
Get #f1,, bytvar
cksm = cksm + bytvar
Next i
cksm = cksm And &H7F
If cksm <> 0 Then
Print "Non-zero checksum for sysex data."
Goto aborted
End If
' Assume this model has 3 sysex event address bytes
' Now parse the address bytes and decode Roland's sysex commands
' eventadd( 1 ) = 0x00 -> data
' eventadd( 1 ) = 0x01 -> metadata
Select Case As Const eventadd( 1 )
Case &H00
' Check for firmware binary data
' sysex event address = 00 00 01, otherwise abort
If (eventadd( 2 ) <> 0) Or (eventadd( 3 ) <> 1) Then
Print "Unrecognised sysex event address"
Goto aborted
End If
' Example showing how 7-bit octet encodes seven 8-bit bytes
' 00 16 5E 0A 4E 53 20 18 -- 7 data bytes + 1 mask byte
'
' 00/00 16/16 5E/DE 0A/8A 4E/4E 53/53 20/20 -- 7-bit / 8-bit
' 0000000 0010110 1011110 0001010 1001110 1010011 0100000 -- 7-bit data bytes
' 00000000 00010110 11011110 10001010 01001110 01010011 00100000 -- 8-bit data bytes
' : : : : : : :
' : .------' : : : : :
' : : .-------------' : : : :
' : : : .--------------------' : : :
' : : : : .---------------------------' : :
' : : : : : .----------------------------------' :
' : : : : : : .-----------------------------------------'
' : : : : : : :
' 0 0 1 1 0 0 0 -- 7-bit mask byte
' 18
Seek f1, datapos
' Check for integral number of octets
' seven 8-bit bytes are encoded as eight 7-bit bytes
If (datalen Mod 8) <> 0 Then
Print "Firmware packet size is not a multiple of 8"
Goto aborted
End If
' Set file pointer in output file to target memory address
Seek #f2, (memadd + 1)
For i = 1 To datalen Step 8
For j = 1 To 7
Get #f1,, octet( j )
Next j
Get #f1,, maskbyt
For j = 1 To 7
maskbyt = maskbyt Shl 1
bytvar = octet( j ) Or ( maskbyt And &H80 )
Put #f2,, bytvar
Next j
Next i
Case &H01
' Check for metadata
If eventadd( 3 ) <> 0 Then
Print "Unrecognised sysex event address3"
Goto aborted
End If
If memadd <> 0 Then
Print "Unrecognised sysex event format"
Goto aborted
End If
Select Case As Const eventadd( 2 )
Case &H00
' sysex event address = 01 00 00 -- <00 00 01> <-- memstart --> <00 00 01> <---memsize --->
' Specifies memory start address and memory size -- 5-byte (nibble mode)
Seek f1, datapos
dwvar1 = 0
For i = 1 To 3
Get #f1,, bytvar
dwvar1 = (dwvar1 Shl 8) + bytvar
Next i
memstart = 0
For i = 1 To 5
Get #f1,, bytvar
memstart = (memstart Shl 4) + bytvar
Next i
dwvar2 = 0
For i = 1 To 3
Get #f1,, bytvar
dwvar2 = (dwvar2 Shl 8) + bytvar
Next i
memsize = 0
For i = 1 To 5
Get #f1,, bytvar
memsize = (memsize Shl 4) + bytvar
Next i
If (dwvar1 <> 1) Or (dwvar2 <> 1) Or (memsize = 0) Then
Print "Unrecognised sysex memory parameters"
Goto aborted
End If
Print "Memory start address = 0x"; Hex( memstart )
Print "Memory size = 0x"; Hex( memsize )
Case &H01
' sysex event address = 01 01 00 -- single byte of data at end of last file ???
' don't know how to handle this, so do nothing
Seek f1, (datapos + datalen)
Case &H02
' sysex event address = 01 02 00 -- <current file number - 1> <total files - 1> <start/end of file>
Seek f1, datapos
Get #f1,, filnum
Get #f1,, totalfiles
Get #f1,, fileflag
If fileflag = &H00 Then
Print "Begin ";
ElseIf fileflag = &H7F Then
Print "Finished ";
Else
Print "Unrecognised sysex event format"
Goto aborted
End If
Print "converting file #"; filnum + 1; " of"; totalfiles + 1
Case &H03
' sysex event address = 01 03 00 -- firmware model/version/date sysex metadata
Seek f1, datapos
For i = 1 To datalen
Get #f1,, bytvar
Print Chr( bytvar );
Next i
Print
Case Else
Print "Unrecognised sysex event address2"
Goto aborted
End Select
Case Else
Print "Unrecognised sysex event address1"
Goto aborted
End Select
' skip over the checksum and 0xF7 bytes
Seek #f1, (Seek( f1 ) + 2)
End If
Case &H00
' Delta_time = 0x00 -- now expecting 0xFF Meta Event
Get #f1,, event
If event <> &HFF Then
Print "Unrecognised Meta Event value (0x"; Hex( event, 2 ); ") -- was expecting 0xFF"
Goto aborted
Else
Get #f1,, eventtyp
Sreclen
If reclen > &H3FFF Then Goto aborted End If
Select Case As Const eventtyp
Case &H03, &H02
' 0x03 = Firmware model/version/date
' 0x02 = Copyright notice
For i = 1 To reclen
Get #f1,, bytvar
Print Chr( bytvar );
Next i
Print
Case &H51
' 0x51 = UART clock rate (3 bytes)
If reclen <> 3 Then
Print "Unrecognised UART clock rate."
Goto aborted
Else
UARTclk = 0
For i = 1 To 3
Get #f1,, bytvar
UARTclk = (UARTclk Shl 8) + bytvar
Next i
Print "UART clock rate = "; UARTclk; " ticks per second"
End If
Case &H2F
' 0x2F = End of Track
If reclen = 0 Then
Print "End of Track"
Else
Print "Unrecognised Meta Event type."
Goto aborted
End If
Case Else
Print "Unrecognised Meta Event type."
Goto aborted
End Select
End If
Case Else
Print "Unrecognised delta_time value (0x"; Hex( deltaT, 2 ); ") -- was expecting 0x00 or 0x10"
Goto aborted
End Select
Loop
Print "Conversion completed."
Close
End
usage:
Print
Print "Convert Roland SP-808EX MIDI firmware files to binary format."
Print
Print "Usage: RMID2BIN infil=[""input filename""] outfil=[""output filename""]"
Print
Print "Example: RMID2BIN infil=SP8EX#1.mid outfil=SP8EXall.bin"
Print
Print "To extract firmware from all MIDI files into a single binary file ..."
Print
Print "for %i in (SP8EX#?.mid) do Rmid2bin.exe infil=%i outfil=SP8EXall.bin"
End
aborted:
filofst = Loc( f1 ) - 1
Print "Offset = 0x"; Hex( filofst )
Print "Conversion aborted."
Close
End