e8555c89 Andy McCall 1 month ago
1
namespace eval vgm {
2
variable active false
3
4
variable psg_register
5
variable opll_register
6
variable y8950_register
7
variable opl4_register_wave
8
variable opl4_register
9
variable active_fm_register -1
10
variable opl3_register
11
variable opl3_port
12
13
variable start_time 0
14
variable tick_time 0
15
variable ticks
16
variable music_data
17
variable file_name
18
variable original_filename
19
variable directory [ file normalize $ ::env(OPENMSX_USER_DATA) /../vgm_recordings]
20
21
variable psg_logged false
22
variable fm_logged false
23
variable y2151_logged false
24
variable y8950_logged false
25
variable moonsound_logged false
26
variable scc_logged false
27
variable opl3_logged false
28
29
variable scc_plus_used
30
31
variable watchpoints [ list ]
32
33
variable loop_amount 0
34
variable position 0
35
36
variable auto_next false
37
38
variable mbwave_title_hack false
39
variable mbwave_loop_hack false
40
variable mbwave_basic_title_hack false
41
42
variable supported_chips [ list MSX-Music PSG MoonSound MSX-Audio SCC SFG OPL3]
43
44
set_help_proc vgm_rec [ namespace code vgm_rec_help]
45
proc vgm_rec_help { args } {
46
switch - - [ lindex $ args 1 ] {
47
" start" { return {VGM recording will be initialised, specify one or more soundchips to record.
48
49
Syntax: vgm_rec start <MSX-Audio|MSX-Music|Moonsound|PSG|SCC|SFG|OPL3>
50
51
Actual recording will start when audio is detected to avoid silence at the beginning of the recording. This mechanism will only work if the MSX and/or playback routine does not send data to the soundchip when not playing, recording will start immediately in those cases.
52
}}
53
" stop" { return {Stop recording and save the data to the openMSX user directory in vgm_recordings. By default the filename will be music0001.vgm, when this exists music0002.vgm etc...
54
55
Syntax: vgm_rec stop
56
}}
57
" abort" { return {Abort an active recording without saving the data.
58
59
Syntax: vgm_rec abort
60
}}
61
" next" { return {Stops current recording and starts recording the next track, remembering the chips to record for.
62
63
Syntax: vgm_rec next
64
}}
65
" auto_next" { return {Enables the auto_next recording; if no data is being sent to the chip for more than 2 seconds, the next recording will be started. Optional argument true/false, defaults to true.
66
67
Syntax: vgm_rec auto_next
68
}}
69
" prefix" { return {Specify the prefix of the VGM files, instead of the default music.
70
71
Syntax: vgm_rec prefix
72
}}
73
" enable_hack" { return {Enables one of three hacks; MBWave_basic_title, MBWave_title or MBWave_loop.
74
75
Syntax: vgm_rec enable_hack <MBWave_basic_title|MBWave_title|MBWave_loop>
76
77
MBWave_basic_title will change the filename written to the track title detected in the openMSX memory when using the MBWave basic driver. MBWave_title will do the same when using MBWave itself. MBWave_loop will insert a vgm pokey command when the tracks loops for the second time. This can be useful when it's hard to find the loop point using the vgmlpfnd tool from vgm_tools. When using vgm_cmp this otherwise useless command will be optimised away.
78
}}
79
" disable_hacks" { return {Disable all hacks.
80
81
Syntax: vgm_rec disable_hacks
82
}}
83
default { return {Record a vgm file from audio playing in openMSX.
84
85
Syntax: vgm_rec <sub-command> [arguments if needed]
86
87
Where sub-command is one of:
88
start, stop, abort, next, auto_next, prefix, enable_hack or disable_hacks.
89
90
Use 'help vgm_rec <sub-command>' to get more help on specific sub-commands.
91
}}
92
}
93
}
94
95
proc little_endian_32 { value } {
96
binary format i $ value
97
}
98
99
proc zeros { value } {
100
string repeat " \0 " $ value
101
}
102
103
set_tabcompletion_proc vgm_rec [ namespace code tab_vgmrec]
104
105
proc tab_vgmrec { args } {
106
variable supported_chips
107
if { [ lsearch -exact $ args " enable_hack" ] > = 0 } {
108
concat MBWave_title MBWave_loop MBWave_basic_title
109
} else {
110
if { [ lsearch -exact $ args " start" ] > = 0 } {
111
concat $ supported_chips
112
} else {
113
concat start stop abort next auto_next prefix enable_hack disable_hacks
114
}
115
}
116
}
117
118
proc set_next_filename { } {
119
variable original_filename
120
variable directory
121
variable file_name [ utils::get_next_numbered_filename $ directory $ original_filename " .vgm" ]
122
}
123
124
proc vgm_rec_set_filename { filename } {
125
variable original_filename
126
127
if { [ file extension $ filename] eq " .vgm" } {
128
set filename [ file rootname $ filename]
129
}
130
set original_filename $ filename
131
set_next_filename
132
}
133
134
vgm_rec_set_filename " music"
135
136
proc vgm_rec { args } {
137
variable active
138
variable auto_next
139
140
variable mbwave_title_hack
141
variable mbwave_loop_hack
142
variable mbwave_basic_title_hack
143
144
variable psg_logged
145
variable fm_logged
146
variable y2151_logged
147
variable y8950_logged
148
variable moonsound_logged
149
variable scc_logged
150
variable opl3_logged
151
152
set prefix_index [ lsearch -exact $ args " prefix" ]
153
if { $ prefix_index > = 0 } {
154
if { $ prefix_index = = ([ llength $ args] - 1)} {
155
error " Please specify prefix to use, see 'help vgm_rec prefix'."
156
}
157
vgm_rec_set_filename [ lindex $ args $ prefix_index +1]
158
return
159
}
160
161
set hack_index [ lsearch -exact $ args " enable_hack" ]
162
if { $ hack_index > = 0 } {
163
if { $ hack_index = = ([ llength $ args] - 1)} {
164
error " Please specify hack to activate, use tab completion."
165
}
166
foreach a [ lrange $ args $ hack_index +1 end] {
167
if { [ string compare -nocase $ a " MBWave_title" ] = = 0 } { set mbwave_title_hack true} \
168
elseif { [ string compare -nocase $ a " MBWave_loop" ] == 0 } { set mbwave_loop_hack true} \
169
elseif { [ string compare -nocase $ a " MBWave_basic_title" ] == 0 } { set mbwave_basic_title_hack true} \
170
else { error " Hack '$ a ' not recognized, use tab completion." }
171
}
172
if { ($ mbwave_title_hack | | $ mbwave_loop_hack ) & & ($ mbwave_basic_title_hack ) } {
173
vgm::vgm_disable_hacks
174
error " Don't combine 'MBWave_basic_title' with other hacks... all hacks disabled to avoid problems."
175
}
176
return " Enabled hack(s): $ args ."
177
}
178
179
if { [ lsearch -exact $ args " disable_hacks" ] > = 0 } {
180
vgm::vgm_disable_hacks
181
return " Disabled all hacks."
182
}
183
184
set auto_next_index [ lsearch -exact $ args " auto_next" ]
185
if { $ auto_next_index > = 0 } {
186
set param [ lindex $ args $ auto_next_index +1] ;
187
set paramBool [ expr { ($ param eq " " ) ? true : bool($ param )} ]
188
if { $ active & & $ paramBool} {
189
error " Auto_next can't be actived during recording, abort/stop the current recording and try again."
190
}
191
set auto_next $ paramBool
192
return " [ expr { $ auto_next ? " Enabled" : " Disabled" } ] auto_next feature."
193
}
194
195
if { [ lsearch -exact $ args " abort" ] > = 0 } {
196
return [ vgm::vgm_rec_end true]
197
}
198
199
if { [ lsearch -exact $ args " stop" ] > = 0 } {
200
return [ vgm::vgm_rec_end false]
201
}
202
203
if { [ lsearch -exact $ args " next" ] > = 0 } {
204
if { ! $ active} {
205
error " Not recording now..."
206
}
207
return [ vgm::vgm_rec_next ]
208
}
209
210
set index [ lsearch -exact $ args " start" ]
211
if { $ index > = 0 } {
212
if { $ active} {
213
error " Already recording, please stop it before running start again."
214
}
215
if { $ index = = ([ llength $ args] - 1)} {
216
error " Please choose at least one chip to record for, use tab completion."
217
}
218
set psg_logged false
219
set fm_logged false
220
set y2151_logged false
221
set y8950_logged false
222
set moonsound_logged false
223
set scc_logged false
224
set opl3_logged false
225
226
foreach a [ lrange $ args $ index +1 end] {
227
if { [ string compare -nocase $ a " PSG" ] = = 0 } { set psg_logged true} \
228
elseif { [ string compare -nocase $ a " MSX-Music" ] == 0 } { set fm_logged true} \
229
elseif { [ string compare -nocase $ a " SFG" ] == 0 } { set y2151_logged true} \
230
elseif { [ string compare -nocase $ a " MSX-Audio" ] == 0 } { set y8950_logged true} \
231
elseif { [ string compare -nocase $ a " MoonSound" ] == 0 } { set moonsound_logged true} \
232
elseif { [ string compare -nocase $ a " SCC" ] == 0 } { set scc_logged true} \
233
elseif { [ string compare -nocase $ a " OPL3" ] == 0 } { set opl3_logged true} \
234
else {
235
error " Invalid chip to record for specified, use tab completion"
236
return
237
}
238
}
239
return [ vgm::vgm_rec_start ]
240
}
241
242
error " Invalid input detected, use tab completion"
243
}
244
245
proc vgm_disable_hacks { } {
246
variable mbwave_title_hack false
247
variable mbwave_loop_hack false
248
variable mbwave_basic_title_hack false
249
}
250
251
proc vgm_rec_start { } {
252
variable active true
253
254
variable auto_next
255
if { $ auto_next} {
256
vgm::vgm_check_audio_data_written
257
}
258
259
variable mbwave_loop_hack
260
if { $ mbwave_loop_hack} {
261
vgm::vgm_log_loop_point
262
}
263
264
set_next_filename
265
variable directory
266
file mkdir $ directory
267
268
variable psg_register -1
269
variable fm_register -1
270
variable y2151_register -1
271
variable y8950_register -1
272
variable opl4_register_wave -1
273
variable opl4_register -1
274
variable opl3_register -1
275
276
variable ticks 0
277
variable music_data " "
278
variable temp_music_data " "
279
280
variable scc_plus_used false
281
282
variable watchpoints
283
284
variable file_name
285
set recording_text " VGM recording initiated, start playback now, data will be recorded to $ file_name for the following sound chips:"
286
287
variable psg_logged
288
if { $ psg_logged} {
289
lappend watchpoints [ debug set_watchpoint write_io 0xA0 { } { vgm::write_psg_address} ] \
290
[ debug set_watchpoint write_io 0xA1 { } { vgm::write_psg_data} ]
291
append recording_text " PSG"
292
}
293
294
variable fm_logged
295
if { $ fm_logged} {
296
lappend watchpoints [ debug set_watchpoint write_io 0x7C { } { vgm::write_opll_address} ] \
297
[ debug set_watchpoint write_io 0x7D { } { vgm::write_opll_data} ]
298
append recording_text " MSX-Music"
299
}
300
301
variable y2151_logged
302
if { $ y2151_logged} {
303
foreach { ps ss} [ find_all_sfg ] {
304
lappend watchpoints [ debug set_watchpoint write_mem 0x3FF0 " \[ watch_in_slot $ ps $ ss\] " { vgm::write_y2151_address} ] \
305
[ debug set_watchpoint write_mem 0x3FF1 " \[ watch_in_slot $ ps $ ss\] " { vgm::write_y2151_data} ]
306
}
307
append recording_text " SFG"
308
}
309
variable y8950_logged
310
if { $ y8950_logged} {
311
lappend watchpoints [ debug set_watchpoint write_io 0xC0 { } { vgm::write_y8950_address} ] \
312
[ debug set_watchpoint write_io 0xC1 { } { vgm::write_y8950_data} ]
313
314
315
316
set y8950_ram [ concat [ lindex [ machine_info output_port 0xC0 ] 0 ] RAM]
317
if { [ lsearch -exact [ debug list] $ y8950_ram] > = 0 } {
318
set y8950_ram_size [ debug size $ y8950_ram]
319
if { $ y8950_ram_size > 0 } {
320
append temp_music_data [ binary format ccc 0x67 0x66 0x88 ] \
321
[ little_endian_32 [ expr { $ y8950_ram_size + 8 } ] ] \
322
[ little_endian_32 $ y8950_ram_size] \
323
[ zeros 4 ] \
324
[ debug read_block $ y8950_ram 0 $ y8950_ram_size]
325
}
326
}
327
append recording_text " MSX-Audio"
328
}
329
330
331
332
333
334
335
336
337
variable moonsound_logged
338
if { $ moonsound_logged} {
339
lappend watchpoints [ debug set_watchpoint write_io 0x7E { } { vgm::write_opl4_address_wave} ] \
340
[ debug set_watchpoint write_io 0x7F { } { vgm::write_opl4_data_wave} ] \
341
[ debug set_watchpoint write_io 0xC4 { } { vgm::write_opl4_address_1} ] \
342
[ debug set_watchpoint write_io 0xC5 { } { vgm::write_opl4_data} ] \
343
[ debug set_watchpoint write_io 0xC6 { } { vgm::write_opl4_address_2} ] \
344
[ debug set_watchpoint write_io 0xC7 { } { vgm::write_opl4_data} ]
345
346
347
348
set moonsound_ram [ concat [ lindex [ machine_info output_port 0x7E ] 0 ] { wave RAM} ]
349
if { [ lsearch -exact [ debug list] $ moonsound_ram] > = 0 } {
350
set moonsound_ram_size [ debug size $ moonsound_ram]
351
if { $ moonsound_ram_size > 0 } {
352
append temp_music_data [ binary format ccc 0x67 0x66 0x87 ] \
353
[ little_endian_32 [ expr { $ moonsound_ram_size + 8 } ] ] \
354
[ little_endian_32 $ moonsound_ram_size] \
355
[ zeros 4 ] \
356
[ debug read_block $ moonsound_ram 0 $ moonsound_ram_size]
357
358
359
append temp_music_data [ binary format cccc 0xD0 0x01 0x05 0x03 ]
360
}
361
}
362
append recording_text " MoonSound"
363
}
364
variable opl3_logged
365
if { $ opl3_logged} {
366
lappend watchpoints [ debug set_watchpoint write_io 0xC0 { } { vgm::write_opl3_address_1} ] \
367
[ debug set_watchpoint write_io 0xC1 { } { vgm::write_opl3_data} ] \
368
[ debug set_watchpoint write_io 0xC2 { } { vgm::write_opl3_address_2} ] \
369
[ debug set_watchpoint write_io 0xC3 { } { vgm::write_opl3_data} ] \
370
[ debug set_watchpoint write_io 0xC4 { } { vgm::write_opl3_address_1} ] \
371
[ debug set_watchpoint write_io 0xC5 { } { vgm::write_opl3_data} ] \
372
[ debug set_watchpoint write_io 0xC6 { } { vgm::write_opl3_address_2} ] \
373
[ debug set_watchpoint write_io 0xC7 { } { vgm::write_opl3_data} ]
374
append recording_text " OPL3"
375
}
376
377
variable scc_logged
378
if { $ scc_logged} {
379
foreach { ps ss plus} [ find_all_scc ] {
380
if { $ plus} {
381
lappend watchpoints [ debug set_watchpoint write_mem { 0xB800 0xB8AF } " \[ watch_in_slot $ ps $ ss\] " { vgm::scc_plus_data} ]
382
} else {
383
lappend watchpoints [ debug set_watchpoint write_mem { 0x9800 0x988F } " \[ watch_in_slot $ ps $ ss\] " { vgm::scc_data} ]
384
}
385
}
386
append recording_text " SCC"
387
}
388
389
message $ recording_text
390
return $ recording_text
391
}
392
393
proc find_all_scc { } {
394
set result [ list ]
395
for { set ps 0 } { $ ps < 4 } { incr ps} {
396
for { set ss 0 } { $ ss < 4 } { incr ss} {
397
set device_list [ machine_info slot $ ps $ ss 2 ]
398
if { [ llength $ device_list] ! = 0 } {
399
set device [ lindex $ device_list 0 ]
400
set device_info_dict [ machine_info device $ device]
401
set device_type [ dict get $ device_info_dict " type" ]
402
if { [ string match -nocase *scc* $ device_type] } {
403
lappend result $ ps $ ss 1
404
} elseif { [ dict exists $ device_info_dict " mappertype" ] } {
405
set mapper_type [ dict get $ device_info_dict " mappertype" ]
406
if { [ string match -nocase *scc* $ mapper_type] | |
407
[ string match -nocase manbow2 $ mapper_type] | |
408
[ string match -nocase KonamiUltimateCollection $ mapper_type] } {
409
lappend result $ ps $ ss 0
410
}
411
}
412
}
413
if { ! [ machine_info issubslotted $ ps] } break
414
}
415
}
416
return $ result
417
}
418
419
proc find_all_sfg { } {
420
set result [ list ]
421
for { set ps 0 } { $ ps < 4 } { incr ps} {
422
for { set ss 0 } { $ ss < 4 } { incr ss} {
423
set device_list [ machine_info slot $ ps $ ss 0 ]
424
if { [ llength $ device_list] ! = 0 } {
425
set device [ lindex $ device_list 0 ]
426
set device_info_dict [ machine_info device $ device]
427
set device_type [ dict get $ device_info_dict " type" ]
428
429
if { [ string match -nocase *sfg* $ device_type] } {
430
lappend result $ ps $ ss
431
}
432
}
433
if { ! [ machine_info issubslotted $ ps] } break
434
}
435
}
436
return $ result
437
}
438
439
proc write_psg_address { } {
440
variable psg_register $ ::wp_last_value
441
}
442
443
proc write_psg_data { } {
444
variable psg_register
445
if { $ psg_register > = 0 & & $ psg_register < 14 } {
446
update_time
447
variable music_data
448
append music_data [ binary format ccc 0xA0 $ psg_register $ ::wp_last_value]
449
}
450
}
451
452
proc write_opll_address { } {
453
variable opll_register $ ::wp_last_value
454
}
455
456
proc write_opll_data { } {
457
variable opll_register
458
if { $ opll_register > = 0 } {
459
update_time
460
variable music_data
461
append music_data [ binary format ccc 0x51 $ opll_register $ ::wp_last_value]
462
}
463
}
464
465
proc write_y2151_address { } {
466
variable y2151_register $ ::wp_last_value
467
}
468
proc write_y2151_data { } {
469
variable y2151_register
470
if { $ y2151_register > = 0 } { # initialised to -1
471
update_time
472
variable music_data
473
append music_data [ binary format ccc 0x54 $ y2151_register $ ::wp_last_value]
474
}
475
}
476
477
proc write_y8950_address { } {
478
variable y8950_register $ ::wp_last_value
479
}
480
481
proc write_y8950_data { } {
482
variable y8950_register
483
if { $ y8950_register > = 0 } {
484
update_time
485
variable music_data
486
append music_data [ binary format ccc 0x5C $ y8950_register $ ::wp_last_value]
487
}
488
}
489
490
proc write_opl4_address_wave { } {
491
variable opl4_register_wave $ ::wp_last_value
492
}
493
494
proc write_opl4_data_wave { } {
495
variable opl4_register_wave
496
if { $ opl4_register_wave > = 0 } {
497
update_time
498
499
500
variable music_data
501
append music_data [ binary format cccc 0xD0 0x2 $ opl4_register_wave $ ::wp_last_value]
502
}
503
}
504
505
proc write_opl4_address_1 { } {
506
variable opl4_register $ ::wp_last_value
507
variable active_fm_register 0
508
}
509
510
proc write_opl4_address_2 { } {
511
variable opl4_register $ ::wp_last_value
512
variable active_fm_register 1
513
}
514
515
proc write_opl4_data { } {
516
variable opl4_register
517
variable active_fm_register
518
if { $ opl4_register > = 0 } {
519
update_time
520
variable music_data
521
append music_data [ binary format cccc 0xD0 $ active_fm_register $ opl4_register $ ::wp_last_value]
522
}
523
}
524
525
proc write_opl3_address_1 { } {
526
variable opl3_register $ ::wp_last_value
527
variable opl3_port 0xC0
528
}
529
530
proc write_opl3_address_2 { } {
531
variable opl3_register $ ::wp_last_value
532
variable opl3_port 0xC2
533
}
534
535
proc write_opl3_data { } {
536
variable opl3_register
537
if { $ opl3_register > = 0 } {
538
update_time
539
variable opl3_port
540
variable music_data
541
switch $ opl3_port {
542
0xC0 { append music_data [ binary format ccc 0x5E $ opl3_register $ ::wp_last_value] }
543
0xC2 { append music_data [ binary format ccc 0x5F $ opl3_register $ ::wp_last_value] }
544
}
545
}
546
}
547
548
proc scc_data { } {
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
update_time
576
577
variable music_data
578
if { 0x9800 < = $ ::wp_last_address & & $ ::wp_last_address < 0x9880 } {
579
append music_data [ binary format cccc 0xD2 0x0 [ expr { $ ::wp_last_address - 0x9800 } ] $ ::wp_last_value]
580
} elseif { 0x9880 < = $ ::wp_last_address & & $ ::wp_last_address < 0x988A } {
581
append music_data [ binary format cccc 0xD2 0x1 [ expr { $ ::wp_last_address - 0x9880 } ] $ ::wp_last_value]
582
} elseif { 0x988A < = $ ::wp_last_address & & $ ::wp_last_address < 0x988F } {
583
append music_data [ binary format cccc 0xD2 0x2 [ expr { $ ::wp_last_address - 0x988A } ] $ ::wp_last_value]
584
} elseif { $ ::wp_last_address = = 0x988F } {
585
append music_data [ binary format cccc 0xD2 0x3 0x0 $ ::wp_last_value]
586
}
587
}
588
589
proc scc_plus_data { } {
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
update_time
616
617
variable music_data
618
if { 0xB800 < = $ ::wp_last_address & & $ ::wp_last_address < 0xB8A0 } {
619
append music_data [ binary format cccc 0xD2 0x4 [ expr { $ ::wp_last_address - 0xB800 } ] $ ::wp_last_value]
620
} elseif { 0xB8A0 < = $ ::wp_last_address & & $ ::wp_last_address < 0xb8aa } {
621
append music_data [ binary format cccc 0xD2 0x1 [ expr { $ ::wp_last_address - 0xB8A0 } ] $ ::wp_last_value]
622
} elseif { 0xB8AA < = $ ::wp_last_address & & $ ::wp_last_address < 0xB8AF } {
623
append music_data [ binary format cccc 0xD2 0x2 [ expr { $ ::wp_last_address - 0xB8AA } ] $ ::wp_last_value]
624
} elseif { $ ::wp_last_address = = 0xB8AF } {
625
append music_data [ binary format cccc 0xD2 0x3 0x0 $ ::wp_last_value]
626
}
627
628
variable scc_plus_used true
629
}
630
631
proc update_time { } {
632
variable start_time
633
if { $ start_time = = 0 } {
634
set start_time [ machine_info time]
635
message " VGM recording started, data was written to one of the sound chips recording for."
636
}
637
638
variable tick_time
639
set tick_time [ machine_info time]
640
set new_ticks [ expr { int(($ tick_time - $ start_time ) * 44100)} ]
641
642
variable ticks
643
if { $ new_ticks < $ ticks} {
644
puts " VGM warning: Tick backstep ($ new_ticks -> $ ticks )"
645
set ticks [ expr { $ new_ticks - 65535 } ]
646
}
647
648
variable music_data
649
while { $ new_ticks > $ ticks} {
650
set difference [ expr { $ new_ticks - $ ticks} ]
651
set step [ expr { $ difference > 65535 ? 65535 : $ difference} ]
652
incr ticks $ step
653
append music_data [ binary format cs 0x61 $ step]
654
}
655
}
656
657
proc vgm_rec_end { abort } {
658
variable active
659
if { ! $ active} {
660
error " Not recording currently..."
661
}
662
663
664
variable watchpoints
665
foreach watch $ watchpoints {
666
if { [ catch {
667
debug remove_watchpoint $ watch
668
} errorText] } {
669
puts " Failed to remove watchpoint $ watch ... using savestates maybe? Continue anyway."
670
}
671
}
672
set watchpoints [ list ]
673
674
if { ! $ abort} {
675
update_time
676
677
variable tick_time 0
678
variable music_data
679
variable temp_music_data
680
681
append temp_music_data $ music_data [ binary format c 0x66 ]
682
683
set header " Vgm "
684
685
append header [ little_endian_32 [ expr { [ string length $ temp_music_data] + 0x100 - 4 } ] ]
686
687
append header [ little_endian_32 0x161 ] [ zeros 4 ]
688
689
690
variable fm_logged
691
if { $ fm_logged} {
692
append header [ little_endian_32 3579545 ]
693
} else {
694
append header [ zeros 4 ]
695
}
696
697
append header [ zeros 4 ]
698
699
variable ticks
700
append header [ little_endian_32 $ ticks]
701
set ticks 0
702
append header [ zeros 20 ]
703
704
variable y2151_logged
705
if { $ y2151_logged} {
706
append header [ little_endian_32 3579545 ]
707
} else {
708
append header [ zeros 4 ]
709
}
710
711
712
append header [ little_endian_32 [ expr { 0x100 - 0x34 } ] ] [ zeros 32 ]
713
714
715
variable y8950_logged
716
if { $ y8950_logged} {
717
append header [ little_endian_32 3579545 ]
718
} else {
719
append header [ zeros 4 ]
720
}
721
722
723
variable opl3_logged
724
if { $ opl3_logged} {
725
append header [ little_endian_32 14318182 ]
726
} else {
727
append header [ zeros 4 ]
728
}
729
730
731
variable moonsound_logged
732
if { $ moonsound_logged} {
733
append header [ little_endian_32 33868800 ]
734
} else {
735
append header [ zeros 4 ]
736
}
737
738
append header [ zeros 16 ]
739
740
741
variable psg_logged
742
if { $ psg_logged} {
743
append header [ little_endian_32 1789773 ]
744
} else {
745
append header [ zeros 4 ]
746
}
747
748
append header [ zeros 36 ]
749
750
751
variable scc_logged
752
if { $ scc_logged} {
753
set scc_clock 1789773
754
variable scc_plus_used
755
if { $ scc_plus_used} {
756
757
758
set scc_clock [ expr { $ scc_clock | 1 < < 31 } ]
759
}
760
append header [ little_endian_32 $ scc_clock]
761
} else {
762
append header [ zeros 4 ]
763
}
764
765
append header [ zeros 96 ]
766
767
variable file_name
768
variable directory
769
770
771
variable mbwave_title_hack
772
variable mbwave_basic_title_hack
773
if { $ mbwave_title_hack | | $ mbwave_basic_title_hack} {
774
set title_address [ expr { $ mbwave_title_hack ? 0xffc6 : 0xc0dc } ]
775
set file_name [ string map { / -} [ debug read_block " Main RAM" $ title_address 0x32 ] ]
776
set file_name [ string trim $ file_name]
777
set file_name [ format %s%s%s%s $ directory " /" $ file_name " .vgm" ]
778
}
779
780
set file_handle [ open $ file_name " w" ]
781
fconfigure $ file_handle -encoding binary -translation binary
782
puts -nonewline $ file_handle $ header
783
puts -nonewline $ file_handle $ temp_music_data
784
close $ file_handle
785
786
set stop_message " VGM recording stopped, wrote data to $ file_name ."
787
} else {
788
set stop_message " VGM recording aborted, no data written..."
789
}
790
791
set active false
792
variable start_time 0
793
variable loop_amount 0
794
795
message $ stop_message
796
return $ stop_message
797
}
798
799
proc vgm_rec_next { } {
800
variable active
801
if { ! $ active} {
802
variable original_filename " music"
803
} else {
804
vgm_rec_end false
805
}
806
vgm_rec_start
807
}
808
809
810
proc vgm_check_audio_data_written { } {
811
variable active
812
if { ! $ active} return
813
814
variable tick_time
815
variable auto_next
816
set now [ machine_info time]
817
if { $ tick_time = = 0 | | $ now - $ tick_time < 1 } {
818
after time 1 vgm::vgm_check_audio_data_written
819
} else {
820
vgm::vgm_rec_end false
821
if { $ auto_next} {
822
message " auto_next feature active, starting next recording"
823
vgm::vgm_rec_start
824
}
825
}
826
}
827
828
829
proc vgm_log_loop_point { } {
830
variable watchpoints
831
lappend watchpoints [ debug set_watchpoint write_mem 0x51f7 { } { vgm::vgm_check_loop_point} ]
832
}
833
834
proc vgm_check_loop_point { } {
835
variable start_time
836
if { $ start_time = = 0 } return
837
838
variable position
839
set position_new [ expr { $ ::wp_last_value = = 255 ? 0 : $ ::wp_last_value} ]
840
if { $ position_new < $ position} {
841
after time 1 vgm::vgm_log_loop_in_music_data
842
}
843
set position $ position_new
844
}
845
846
proc vgm_log_loop_in_music_data { } {
847
variable start_time
848
if { $ start_time = = 0 } return
849
850
variable loop_amount
851
incr loop_amount
852
variable music_data
853
append music_data [ binary format ccc 0xbb 0xbb 0xbb ]
854
if { $ loop_amount = = 1 } {
855
message " First loop: Track-length in seconds (if not using transposing..): [ expr { [ machine_info time] - $ start_time} ] . Marker inserted in VGM file."
856
}
857
if { $ loop_amount = = 2 } {
858
message " Second loop. Marker inserted in VGM file."
859
}
860
if { $ loop_amount > 2 } {
861
keymatrixdown 7 4
862
message " Third loop occurred, stop playback."
863
variable position 0
864
set loop_amount 0
865
after time 1 {keymatrixup 7 4}
866
}
867
}
868
869
namespace export vgm_rec
870
871
}
872
873
namespace import vgm::*