VOGONS

Common searches


Dune "HERAD" Ad Lib Music Hacking

Topic actions

Reply 60 of 167, by binarymaster

User metadata
Rank Newbie
Rank
Newbie
synamax wrote:

Finally added the Output level Scaling table on the wiki

http://www.vgmpf.com/Wiki/index.php/HERAD#Out … l_Scaling_Table

Good work SynaMax!

By the way, did you find somebody who can program the HERAD replayer for AdPlug?

I think I can do this job, however my last patches are not merged yet, and this demotivates me.

by Stas'M

Reply 61 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Thanks! Compiling that table was crazy, 🤣.

Unfortunately, I've haven't found anyone that would be interested.

I'm sure you can do it! It would greatly speed up loading music. As much as I like opening up RAM and injecting my music code in MegaRace, a standalone player would be nice. 😀

EDIT: I also finally added the look up table for the rarely used feedback scaling macro. http://www.vgmpf.com/Wiki/index.php/HERAD#Fee … k_Scaling_Table

It's a pretty easy pattern here as well, everything is multiples of 2.

Reply 62 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Okay, so I've been working on trying to figure out how HERAD handles MIDI pitch bend events and converts them to OPL F-Numbers.

After some initial tests, I've found correlation to the previous FNUM table that I posted a while back. As we already established, HERAD uses a full 8-bit value for the MIDI Pitch Bends, which means we can do a value range of 00 to FF, however all the music files I ever seen only went up to 80. Using that as a guide, I made a song where I played every MIDI note one at a time and added two pitch bend events after each NoteOn: one pitch bend event at 00 and one pitch bend event at 80.

Consistently, every pitch bend at value 80 matched exactly with the last value of each note in the Coarse Pitch Slide Macro FNUM table.

C 	 =343 	- Pitch bend at 80 =363
C# =364 - Pitch bend at 80 =384
D =385 - Pitch bend at 80 =405
D# =408 - Pitch bend at 80 =428
E =433 - Pitch bend at 80 =453
F =459 - Pitch bend at 80 =479
F# =486 - Pitch bend at 80 =510
G =515 - Pitch bend at 80 =539
G# =546 - Pitch bend at 80 =570
A =579 - Pitch bend at 80 =603
A# =614 - Pitch bend at 80 =638
B =650 - Pitch bend at 80 =674

Here's the FNUMs for when the pitch bend set at 00. At first, I didn't recognize these numbers from any other lookup table so initially, I didn't know how exactly HERAD was coming up with these numbers.

C 	 =343 	- Pitch bend at 00 =323
C# =364 - Pitch bend at 00 =344
D =385 - Pitch bend at 00 =365
D# =408 - Pitch bend at 00 =388
E =433 - Pitch bend at 00 =413
F =459 - Pitch bend at 00 =439
F# =486 - Pitch bend at 00 =462
G =515 - Pitch bend at 00 =491
G# =546 - Pitch bend at 00 =522
A =579 - Pitch bend at 00 =555
A# =614 - Pitch bend at 00 =590
B =650 - Pitch bend at 00 =626

Then, after pulling out the good ol' calculator, the relationship between the numbers became clear. As it turns out, depending on the note, there's two different ways on how HERAD handles pitch bends.

For notes C to F, the lowest and highest value of the pitch bend are -/+ 20 FNUMs from the root FNUM.

Note  00    ROOT   80

C 323 343 363
C# 344 364 384
D 365 385 405
D# 388 408 428
E 413 433 453
F 439 459 479

However, for notes F# to B, the lowest and highest value of the pitch bend are -/+ 24 FNUMs from the root FNUM.

Note  00    ROOT   80

F# 462 486 510
G 491 515 539
G# 522 546 570
A 555 579 603
A# 590 614 638
B 626 650 674

So that solves part of the puzzle...the other main problem is the FNUMs in between these values. What I need to do is write down all the FNUMs during a pitch bend from 00 to 80 and figure out the pattern.

Reply 63 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Every time I try to figure out how the pitch bend works, I get more and more confused. Anyways, here's a rough look up table of all the FNUMs that HERAD cycles through when doing a pitch bend. I have seen pitch bends that stray from these numbers before, (like when handling different pitches, etc) however it's usually off by just 1 FNUM which is really unnoticeable to us humans.

323
328
333
338
343

344
349
354
359
364

365
370
375
380
385

388
393
398
403
408

413
418
423
428
433

439
444
449
454
459

462
468
474
480
486

491
497
503
509
515

522
528
534
540
546

555
561
567
573
579

Show last 16 lines
590
596
602
608
614

626
632
638
644
650

656
662
668
674

The pattern here seems to be that from every 5 notes from C to F, there's a difference of 5 between every FNUM. For every 5 notes from F# to B, the difference between every FNUM is 6. Note that each grouping on numbers ends with the frequency for the note on the scale. 343 = C, 364 = C#, etc. There's 64 FNUMs here so that fits perfectly with the 128 pitch bend range. When the last FNUM is reached, HERAD will then go to the next block/octave and repeat the pattern. Again, it seems that HERAD is generating these numbers so there's going to be times where these numbers vary due to rounding, but as I mentioned previously, they'll usually just be off by one FNUM.

Now, this is all fine and dandy when HERAD's Pitch bend flag is set to 1, which is the setting for coarse, but when it's set to fine, things get different. The pitch bend moves much slower, because this time the FNUMs mostly increment only by 1. Also, the FNUM doesn't always change with every MIDI pitch bend event either, unlike when the bend is set to coarse. Another difference I noticed is that the finer pitch bend starts off at FNUM 325 rather than 323. I've attached two DRO files to show the differences between the two pitch bends. Both files were recorded from the exact same MIDI information, I added a metronome like sound to denote every MIDI tick. There's one tick per pitch bend event. As you can see with the "bendit.dro" file, when the instrument is set to coarse, the FNUMs change with every tick. However, in the "bendfine.dro", the instrument is set to fine and as a result, the FNUMs don't always change with every MIDI tick. I'm trying to figure out if there's a recognizable pattern there.

I'll post more updates after Christmas. 😀

EDIT: I'm wondering if the last digits in the header of the HERAD Adlib driver are the tables for controlling the pitch bends. They start at offset 0x90.

00 05 0A 0F 14 00 06 0C 12 18

It makes sense as there's a group of multiples of 5 and a group of multiples of 6. While messing around with the pitch slide macro I had some weird FNUMs pop up that are outside of the currently known range. I had my duration set to 0x92 and my slide step set to 0x1, while in coarse mode. It cycled through the normal series of FNUMs and then, when it reached the end of the note it displayed the last FNUM, 674, then it threw up some weird FNUMs, 680, 692, 710, 734. What's the connection with these numbers? They share the same multiples of 6 as the driver header. 734-710 = 24, 710-692 = 18, 692-680 = 12, 680-674 = 6. Interesting stuff... 😳

EDIT: Those numbers indeed control the pitch bend FNUMs! Zeroing them out removed any modified FNUMs and the resulting pitch bend that was loaded simply went up the standard 12-note scale. Very cool!

Attachments

Reply 64 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

So I decided to really dive in and take a close look at the HERAD driver in MegaRace. Amazingly, I was able to figure out a lot of stuff!

0x15:Something to do with the song speed (Always 0x0002)
[0x2 bytes long]

0x17:?(Always 0x1941)
[0x2 bytes long]

0x19:Song length (first two bytes from music file)
[0x2 bytes long]

0x1B:?(Always 0x1941)
[0x2 bytes long]

0x1D:Most likely OPL Timer
[0x2 bytes long]

0x1F:MIDI Measure Counter (this counts up after every 96 MIDI ticks and is used for the song loop)
[0x2 bytes long]

0x21:MIDI Tick Measure Counter (Counts down from 0x60 or 96 MIDI Ticks)
[0x2 bytes long]

0X23:Song Repeat Counter (Counts down from 0xFFFF)
[0x2 bytes long]

0x35:Feedback Table?
[0x7 bytes long]

0x3D:Table?
[0xA bytes long]

0x47:Frequency Table
[0x18 bytes long]

0x5F:FNUM/Block Number Register Values
[0x12 bytes long]

0x71:Velocity Table
[0x9 bytes long]

0x7A:? (Fine-tune Pitch table?)
[0x16 bytes long]

0x90:Pitch Bend Table
[0xA bytes long]

0x9A:? (Always 0x0080)
[0x2 bytes long]

0x9C:Padding? (Always 0xEEEEEE)
[0x3 bytes long]

0x9F:Two byte "ping-pong" timer that counts up in bits (0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80)
[0x2 bytes long]

0xA1:Padding? (Always 0x90)
[0x1 byte long]

0xA2:NoteOff Midi Tick Counter (counts down until next note)
[0x12 bytes long]

0xB4:MIDI Track Position (counts up until reaching the track end value specified in the music file header)
[0x12 bytes long]

0xC6:Start of MIDI Tracks (Absolute address in music file, rather than relative address in music file header)
[0x12 bytes long]

0xD8:List of MIDI tracks using drum keymap instrument. If MIDI track uses keymap instrument, the absolute offset of the instrument in the music file will be used. If the instrument changes back to a normal instrument, the value zeros out.
[0x12 bytes long]

0xEA:Current Instrument (first byte) / MIDI Pitch (second byte). The pitch here is after any transpose macros have been applied. Also, if the keymap instrument is used, the first byte will display the instrument(s) that are actually being played.
[0x12 byte long]

0xFC:Pitch Slide Range Flag (first byte) / Root Note Transpose (second byte) for each MIDI track.
[0x12 byte long]

0x10E:Pitch Slide Duration Counter (first byte) / Pitch Slide Duration (second byte) for each MIDI track.
[0x12 byte long]

0x120:Pitch Slide Counter (first byte) / Pitch Slide Range (second byte) for each MIDI track. The Pitch Slide Counter starts at 0x40 (unless it is transposed) and either goes up or down depending on Pitch Slide Range value.
[0x12 byte long]

0x156:Output level Registers for each MIDI track?
[0x12 byte long]

0x168:Feedback/Panning Registers for each MIDI track?
[0x12 byte long]

0x17A:Waveform Select Registers for each MIDI track?
[0x12 byte long]

0x1C3: Start of HSQ decompression routine?

------

So yeah, that definitely answers a lot of unknowns in the HERAD driver. The driver that was being loaded in my copy of MegaRace is DFADP.HSQ, so that's the one that I'm going to stick with when reverse engineering. It's really cool to look "under the hood" and see how all these hexadecimals all come together to make music. 😀

Reply 65 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Happy New Year!

I took some time today to focus on the HERAD driver in MegaRace a little bit more and I made some more discoveries.

For ease of reading, I posted the latest file structure on the VGMPF wiki since I can do tables there and it looks much nicer than on the forum. Here's the latest driver structure so far.

The values at offset 0x17A really puzzle me. They definitely have to do with something about songs loop points, because the song just ends if these values are removed. Tracks that don't have loop points and repeat end to end like Maeva and Terminal City do not use these values but other songs like Factory Land, Skyholder and Fractalian Space do. Changing some values can cause out of time playback issues, for example, affected MIDI channels that are several measures behind from the rest of the song. I'm not entirely sure where the driver is getting these values, so I'll have to dig in a bit deeper.

Next I'm going to look into the OPL2 and AdLib Gold Drivers for Dune and see how they differ from MegaRace's driver.

Reply 66 of 167, by binarymaster

User metadata
Rank Newbie
Rank
Newbie

Thank you for your work again!

synamax wrote:

Next I'm going to look into the OPL2 and AdLib Gold Drivers for Dune and see how they differ from MegaRace's driver.

This would be very interesting to find out differences in drivers! 😀

Happy New Year! 🤣

by Stas'M

Reply 67 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie
binarymaster wrote:
Thank you for your work again! […]
Show full quote

Thank you for your work again!

synamax wrote:

Next I'm going to look into the OPL2 and AdLib Gold Drivers for Dune and see how they differ from MegaRace's driver.

This would be very interesting to find out differences in drivers! 😀

Happy New Year! 🤣

You're welcome! I'm glad I can help, considering my lack of programming skills.

The good news is that Dune, KGB and MegaRace share all the same tables for their respective OPL2 drivers, so we now know for sure.

At first glance, it looks like there's a lot of similarities between the drivers for Dune/KGB and MegaRace, however there definitely are differences, for example, Dune's driver is missing the Song Repeat Counter. The biggest difference is how the driver handles loading the music file. In MegaRace, it's really nice because the song is loaded in RAM immediately after the driver and absolute addressing is used so you know exactly where you are in the music file. As of right now, I have DOSBox opened up in HxD and it's showing the Dune AdLib Gold Driver at offset 0xC6240B0 but the song is found WAY far away at 0xC750020. Also, all the values for MIDI Track Positions and the Start of MIDI Tracks don't make any sense so the driver for Dune is definitely doing something very different when it comes to loading the music file in memory.

After loading one of my test music files I figured out the pattern for how Dune handles MIDI Track Positions:

WATER.AGD
52 00 57 02 5C 03 A1 09 A6 0C 6B 11 F0 13 3D 14 42 16

Dune AdLib Gold Driver
54 40 59 42 5E 43 A3 49 A8 4C 6D 51 F2 53 3F 54 44 56

Dune is still using absolute addressing for the Start of the MIDI Track positions but it's adding 0x4000 to the value, so what should be 0x0054, turns into 0x4054. It does this as well on the OPL2 driver.

I'm so glad I looked at MegaRace's driver first or otherwise I would be completely lost! From comparing the two drivers, you can definitely get the impression that MegaRace's driver is a lot more cleaned up and straight forward.

OPL3 of course has 18 channels so instead of the usual 0x12 bytes for each section, the individual track sections for OPL3 driver are now expanded to 0x24 bytes.

I haven't confirmed all the numbers yet, but past that 0x90 padding byte, it looking very much like it's the same positioning for all the counters and values for the various instrument macros and MIDI track positions like MegaRace's driver.

Reply 68 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Hey guys! Got some new updates for you!

I made two new videos that showcase the Dune driver and MegaRace driver running inside of a hex editor so you can see all the FNUMs, Registers, MIDI ticks, counters and various bytes do their thing while playing the song.

YouTube - Dune Driver playing MORNING.HSQ
YouTube - MegaRace Driver playing NEWSAN.HSQ (With Driver Structure Breakdown)

I figured out one of the remaining unknowns at the end of the driver. 0x17A is the values that the MIDI tick counters load when the song loops. They represent how many ticks until the next MIDI event at the loop point. Tracks that have a long pause until the next MIDI event when the song loops, have bigger values.

I also noticed a difference with how Dune and MegaRace handle Note Off events. We already know that MegaRace uses a truncated NoteOff event that's incompatible with the MIDI standard.

Dune/MIDI                  MegaRace
[tick] 80 3C 40 [tick] 80 3C

Well, the driver handles them differently as well. For the Dune driver, this occurs at 0xE2; 0xEA for the MegaRace driver. When a NoteOff occurs in Dune, the MIDI Pitch byte just switches to 0x00. But, when the NoteOff event happens in MegaRace, the pitch still stays the same, but the highest bit is turned on.

As soon as I find more time in my schedule I'll start working on the OPL3 driver for Dune...it shouldn't be much more difficult, I'm assuming it will be just more of the same.

Reply 69 of 167, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
synamax wrote:

As soon as I find more time in my schedule I'll start working on the OPL3 driver for Dune...it shouldn't be much more difficult, I'm assuming it will be just more of the same.

I might be able to help you get started with that, as I have only looked at the Dune Gold driver. It sounds similar, arrays with track start offsets, track delay values, track current playing offsets and the like.

Reply 71 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

Theoretically, yes. However, it would take a bit if effort to convert the the drums or any other instruments that are using the key map instrument, since that functionality isn't in the driver for Dune. Also, the transpose macro works slightly differently in MegaRace than in Dune, but now that I know how it works and understand the driver a lot more, it shouldn't be as trial and error as preivously.

The last thing to worry about would be the registers for the Surround Module, but we can start off with just copying the values from any of the Dune Gold songs and see what happens. If anyone has a Gold Surround Module, I would absolutely love to hear MegaRace's music played on it, so I would be more than happy to provide the music files if anyone can record the results.

Reply 72 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

This took a lot longer than I was hoping but here it is: PAGA (aka Skyholder) from MegaRace converted to HERAD 1 to work with Dune's AdLib Gold driver.

While I converted the main drum notes a while back, the additional percussion instruments on Channel 6 needed constant tweaking to finally get them to play correctly since there were so many of them. To give you a size difference between the original song and the converted file, 8 kilobytes of MIDI events were added to the file in order to get it to play correctly in Dune. The transpose macro for the percussion instruments had to be modified as well but that was probably the easiest part, now that I'm know what I'm doing.

To play the song in Dune, replace the song of your choice in the directory (be sure to have a backup!) with PAGA_COMPRESSED.AGD. Rename this file with the song you're replacing. Note this won't work with the CD version.

I'm eager to see if anyone can record this song on an actual AdLib Gold card. 😀

Attachments

  • Filename
    PAGA_for_Dune.zip
    File size
    11.35 KiB
    Downloads
    108 downloads
    File license
    Fair use/fair dealing exception

Reply 73 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

I should be finishing up my reverse engineering of the Dune drivers soon. Feedback is acting really weird in Dune and I need to figure out what exactly is going on in the driver, but other than that, everything else is documented.

Reply 74 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

The results from my latest venture in reverse engineering Dune's driver yielded some interesting results!

Here's the breakdown: http://www.vgmpf.com/Wiki/index.php/UserWiki: … oppy_Version.29

I figured out how HERAD triggers the fade outs in Dune! At offset 0xA6, there's several bytes that control playback of the music. The first byte is set to 0x80, and it used to play a song, however if you set it to 0xC0, it fades out the song and switches to 0x00 to reset the song from the beginning, before finally switching back to 0x80 to play the song again. Now, if the byte next to it is set to 0x1, during playback then a new song plays after the current song is finished or when a fade out occurs.

MegaRace could have used this functionality as well but only the first byte remains (to start and stop songs) and the rest is written over with 0xEEEEEE padding.

I really want to know what the bytes at the beginning of the driver are. If anyone can help point me in the right direction, that would be greatly appreciated.

Beginning of MegaRace's driver:

E9 E8 01 E9 BE 02 E9 08 02 E9 A3 02 E9 5F 02 E9 73 03 E9 46 02

Beginning of Dune's OPL2 driver:

E9 DD 01 E9 EF 01 E9 51 02 E9 ED 01 E9 40 02 E9 F4 01 E9 E0 02 E9 7C 02 E9 31 02 E9 2F 02 E9 2D 02

Reply 75 of 167, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

The bytes at the beginning are an array of near jump instructions, one for each function of the driver. Near jumps are 3 bytes: E9 opcode byte followed by a word offset. So, function 0 jumps to offset 0 and follows the jump there to that function's code; function 1 jumps to offset 3, function 2 to offset 6, and so forth. Disassembly should make this kind of thing more obvious...

Reply 76 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie
ripsaw8080 wrote:

The bytes at the beginning are an array of near jump instructions, one for each function of the driver. Near jumps are 3 bytes: E9 opcode byte followed by a word offset. So, function 0 jumps to offset 0 and follows the jump there to that function's code; function 1 jumps to offset 3, function 2 to offset 6, and so forth. Disassembly should make this kind of thing more obvious...

Awesome!! You're a genius, ripsaw. I really need to start learning x86 opcodes and figuring out disassembly. I have a copy of IDA, so now that I know what's at the beginning of the drivers perhaps disassembly will be much easier now (I hope).

The other main unknown in the beginning of the driver is this set of 0x10 bytes, both of them found right after the MIDI Measure and Tick counters. I'm assuming these are also opcodes as well?

MegaRace driver:

CF 06 5D 06 E5 08 E5 08 D2 05 2E 07 DD 07 F5 06

Dune driver:

E2 05 B3 05 68 08 68 08 0D 05 2F 06 71 07 F6 05

Reply 78 of 167, by superfury

User metadata
Rank l33t++
Rank
l33t++
Jepael wrote:

I think that's just data. Megarace certainly not code. Which Dune driver, Adlib or Gold, I can't find those bytes.

You're probably right: CF is an IRET instruction, which wouldn't make much sense at the start of a block of code, except when the rest is just data. Those 08 opcodes(including next 2 or 4 bytes) would just be a bunch of OR instructions on memory and registers at first glance(without fully disassembling).
(Assuming real mode)
CF IRET
06 5D 06 E5 OR [DS:DI+08E5],SI
E5 (08 D2 05 2E 07 DD 07 F5 06) ??? Don't know the E5 opcode just like that. One of the string instructions perhaps? LOSDB?
08 D2 OR DX,DX
05 2E 07 DD AND AX,DD07
07 F5 OR SI,BP
06 OR [ModR/M to follow]

Doesn't make much sense. (Just disassembled using my own memory without looking anything up)

Edit: whoops. E5 08 is IN AX,08. That's one of the DMA 8-bit transfer registers.

D2: Don't know the opcode just like that.

Have you tried disassembling the code in real mode(16-bit operand&address size)? It might be starting in real mode(or 16-bit protected mode) to initialize itself.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 79 of 167, by synamax

User metadata
Rank Newbie
Rank
Newbie

So I disassmbled the code in 486 real most in IDA and it looks like it found the jumps in the beginning of the driver as well as a lot of other subroutines. I can't make heads or tails from any of it since I don't know anything about disassembly but the very last subroutine looks like it's calling the 388 port for the Adlib card. I attached a txt file with the disassembled code, see if that gets us anywhere.

Attachments