A few years ago I worked on a format for storing ripped Sega Genesis music. It worked, and I ripped around 30 games to get an idea of the different types of music drivers used.
I never quite finished the project as writing the user interface always held me up. But there's definitely a need to get this completed, as GYM (being good for what it was) isn't too ideal.
The main problem with GYM is that storing DAC samples eats up huge amounts of file space. By using ripped code and data you only have to store the minimum amount of data (typically a few hundered kilobytes at the most, and then you have *all* music and SFX in one file)
So I'd like to discuss this project and get input from other people about how to continue.
A large number of games work like this, with three main subroutines to control sound output:
- init: 68000 copies Z80 driver to sound RAM and resets Z80
- vblank (or 'update' if you prefer): 68000 does nothing (Z80 runs off internal timers or vblank)
- request: 68000 writes sound code into sound RAM which the Z80 checks eventually and plays new music or sfx
To support this kind of music driver, all you have to do is locate and rip the init/request routines, and then the ROM data blocks used by the Z80 (typically for DAC samples). It is trivial to modify an emulator to report sound RAM access and bank selection to figure these parts out.
The less common types are:
- 68000 and Z80 work in tandem. E.g. Gauntlet has the 68000 run the PSG while the Z80 does everything else. Treasure games let the Z80 do DAC samples and the 68000 handles FM+PSG sound.
- 68000 does everything, Z80 idles.
For these you need to rip the 68000 music driver which is difficult but not impossible. A lot of games conveniently bundle the 68K, Z80 and music data in the same block of ROM so it's easy to rip out a chunk and have all the important bits.
Some games like Thunder Force IV have a lot of interaction where the 68000 dynamically copies sections of code and RAM out of the Z80 memory space, so this required a lot of the original game logic to be analyzed and reproduced, because it was tightly integrated with the game code. I think this is about the only way such games can be handled.
Now, on to playing back the ripped bits and pieces:
The original player I developed fit in a few kilobytes of a Genesis ROM that had joystick control to change a sound number. It would call the 'init' subroutine, the 'update' subroutine on each VBlank if necessary, and the 'request' subroutine when a new sound number was needed. For most games I could call the original subroutines directly, or make some minor modifications.
The original ROM data was loaded into ROM space at the same addresses the game used (e.g. if a bank of DAC samples were at $70000, that's where they went into the player ROM) This is because the 68000 code cannot be easily relocated (the init or request ones can, but a full 68000 driver cannot) and the Z80 code would have to be changed to specify relocated ROM bank addresses that are fed to the bank selection shift register. In a few cases my player code had to be moved around to accomodate the original game's used locations.
I'll discuss a proposed file format next. Because the ripping process basically involves a lot of hex editing, 68000 coding, and sorting through emulator output logs, you end up with various odds and ends that need to be combined into a single file. I think the best way to handle this is to have the user create a plain text file divided into sections that contains this information, which a tool would compile into a binary file. Something like this:
(sonic.txt)
[game]
rom = "/roms/sonic.bin" // where the source ROM is that the tool rips data blocks from
vmode = NTSC; // tell the player what the refresh rate is
[interface] // tells the player where the functions are to interact with the music driver
init = 0x0abcd; // address of init subroutine
request = 0x00abcd; // address of sound request subroutine
update = 0x00abcd; // address of vblank update routine, not always used
[data] // list of offsets and sizes of data blocks to extract
0x70000, 0x8000 // data block, 32K from $70000
0x10000, 0x10000 // another data block, 64K from $10000
[info] // textual information about the game that somebody listening to the music would want to see
composer = "foobar"
copyright = "1992 Sega"
[tracklist] // each entry is request code, duration, loop point, name
0x8A, 2:34, 0:20, "Title Screen"
0x8B, 3:34, 2:10, "Stage 3"
/* end */
This would be combined into a binary file format, using the source ROM specified. You could then do things like:
- Play music in an emulated environment like a WinAMP plugin. Each 'track' would have a name, associated sound code passed to the request function, and optionally duration and loop point information.
- Play music on the Genesis, using a converter tool to make a ROM out of the ripped data. Maybe some profiling to determine what portions of 68K RAM and ROM were used by the music driver would be necessary to place the player code without interfering, but it's all possible to do.
Random thoughts:
Maybe the tracklist should have a BGM and SFX indicator so a player can exclude SFX or place them after all the background music since most people only want to hear that.
The binary file format could easily be reversed to give the textual description file (with the source ROM instead perhaps being a CRC of the original one used) and the data blocks saved individually. So a bad rip could be later worked on by someone else in the future instead of having to keep the original specification file around.
Some way to specify bits and pieces of data that aren't part of the source ROM is needed, for when fragments of 68000 code or replacement data are custom made for a particular rip.
So, what are people's thoughts about this? The things I've outlined have worked in the rips I've done, but to proceed with an 'official' format I want to include other features people want so we don't have a million different formats floating around.
As for the actual discussion of the ripped music file's structure and header, I'll discuss that later depending on the level of interest involved. 🙂 I did want to keep it in individual sections with a ID tag and version number so older players can skip over sections that they don't understand.
Also any ideas about supporting 32X and/or Sega CD sound emulation (if only for Snatcher and Darxide) are welcome.
Here is the original pack of rips in a Genesis ROM format that work on genecyst. Some have problems becausing the starting sound code doesn't match the original game (you'll have to cycle through a bunch of numbers first) and others have RAM sharing issues with the player so the joystick input or playback might be a bit weird.
http://cgfm2.emuviews.com/segaxtreme/gsfpack.rar (2.8MB)
Have fun. 😀
I never quite finished the project as writing the user interface always held me up. But there's definitely a need to get this completed, as GYM (being good for what it was) isn't too ideal.
The main problem with GYM is that storing DAC samples eats up huge amounts of file space. By using ripped code and data you only have to store the minimum amount of data (typically a few hundered kilobytes at the most, and then you have *all* music and SFX in one file)
So I'd like to discuss this project and get input from other people about how to continue.
A large number of games work like this, with three main subroutines to control sound output:
- init: 68000 copies Z80 driver to sound RAM and resets Z80
- vblank (or 'update' if you prefer): 68000 does nothing (Z80 runs off internal timers or vblank)
- request: 68000 writes sound code into sound RAM which the Z80 checks eventually and plays new music or sfx
To support this kind of music driver, all you have to do is locate and rip the init/request routines, and then the ROM data blocks used by the Z80 (typically for DAC samples). It is trivial to modify an emulator to report sound RAM access and bank selection to figure these parts out.
The less common types are:
- 68000 and Z80 work in tandem. E.g. Gauntlet has the 68000 run the PSG while the Z80 does everything else. Treasure games let the Z80 do DAC samples and the 68000 handles FM+PSG sound.
- 68000 does everything, Z80 idles.
For these you need to rip the 68000 music driver which is difficult but not impossible. A lot of games conveniently bundle the 68K, Z80 and music data in the same block of ROM so it's easy to rip out a chunk and have all the important bits.
Some games like Thunder Force IV have a lot of interaction where the 68000 dynamically copies sections of code and RAM out of the Z80 memory space, so this required a lot of the original game logic to be analyzed and reproduced, because it was tightly integrated with the game code. I think this is about the only way such games can be handled.
Now, on to playing back the ripped bits and pieces:
The original player I developed fit in a few kilobytes of a Genesis ROM that had joystick control to change a sound number. It would call the 'init' subroutine, the 'update' subroutine on each VBlank if necessary, and the 'request' subroutine when a new sound number was needed. For most games I could call the original subroutines directly, or make some minor modifications.
The original ROM data was loaded into ROM space at the same addresses the game used (e.g. if a bank of DAC samples were at $70000, that's where they went into the player ROM) This is because the 68000 code cannot be easily relocated (the init or request ones can, but a full 68000 driver cannot) and the Z80 code would have to be changed to specify relocated ROM bank addresses that are fed to the bank selection shift register. In a few cases my player code had to be moved around to accomodate the original game's used locations.
I'll discuss a proposed file format next. Because the ripping process basically involves a lot of hex editing, 68000 coding, and sorting through emulator output logs, you end up with various odds and ends that need to be combined into a single file. I think the best way to handle this is to have the user create a plain text file divided into sections that contains this information, which a tool would compile into a binary file. Something like this:
(sonic.txt)
[game]
rom = "/roms/sonic.bin" // where the source ROM is that the tool rips data blocks from
vmode = NTSC; // tell the player what the refresh rate is
[interface] // tells the player where the functions are to interact with the music driver
init = 0x0abcd; // address of init subroutine
request = 0x00abcd; // address of sound request subroutine
update = 0x00abcd; // address of vblank update routine, not always used
[data] // list of offsets and sizes of data blocks to extract
0x70000, 0x8000 // data block, 32K from $70000
0x10000, 0x10000 // another data block, 64K from $10000
[info] // textual information about the game that somebody listening to the music would want to see
composer = "foobar"
copyright = "1992 Sega"
[tracklist] // each entry is request code, duration, loop point, name
0x8A, 2:34, 0:20, "Title Screen"
0x8B, 3:34, 2:10, "Stage 3"
/* end */
This would be combined into a binary file format, using the source ROM specified. You could then do things like:
- Play music in an emulated environment like a WinAMP plugin. Each 'track' would have a name, associated sound code passed to the request function, and optionally duration and loop point information.
- Play music on the Genesis, using a converter tool to make a ROM out of the ripped data. Maybe some profiling to determine what portions of 68K RAM and ROM were used by the music driver would be necessary to place the player code without interfering, but it's all possible to do.
Random thoughts:
Maybe the tracklist should have a BGM and SFX indicator so a player can exclude SFX or place them after all the background music since most people only want to hear that.
The binary file format could easily be reversed to give the textual description file (with the source ROM instead perhaps being a CRC of the original one used) and the data blocks saved individually. So a bad rip could be later worked on by someone else in the future instead of having to keep the original specification file around.
Some way to specify bits and pieces of data that aren't part of the source ROM is needed, for when fragments of 68000 code or replacement data are custom made for a particular rip.
So, what are people's thoughts about this? The things I've outlined have worked in the rips I've done, but to proceed with an 'official' format I want to include other features people want so we don't have a million different formats floating around.
As for the actual discussion of the ripped music file's structure and header, I'll discuss that later depending on the level of interest involved. 🙂 I did want to keep it in individual sections with a ID tag and version number so older players can skip over sections that they don't understand.
Also any ideas about supporting 32X and/or Sega CD sound emulation (if only for Snatcher and Darxide) are welcome.
Here is the original pack of rips in a Genesis ROM format that work on genecyst. Some have problems becausing the starting sound code doesn't match the original game (you'll have to cycle through a bunch of numbers first) and others have RAM sharing issues with the player so the joystick input or playback might be a bit weird.
http://cgfm2.emuviews.com/segaxtreme/gsfpack.rar (2.8MB)
Have fun. 😀