Question about making my own music tracker for NES games?

Started by Dracula X, December 26, 2016, 11:42:56 PM

Previous topic - Next topic

Dracula X

I'd like to know which program do I need to get start, Visual Basic, C#, C++ or MFC? Thanks! I'm a fast learner at this type of stuff.
Back to Thunder Force II hacking again.

FAST6191

You want to make your own music for the NES or play back NSF, or some other format that does the same thing, files on the PC?

For the former though I imagine you can build something on the PC in a high level language to output to the NES I should say a lot of really good stuff is ground up NES 6502 assembly. You will want to know a bit of it to make the fragments that your PC program uses too.

For the latter then most would probably start with a NES emulator sound core and figure out how they want to interact with that.

Dracula X

1. Yes! Make music or my own from other NSF files like the CAPCOM 1990 Music editor for example. And other nes games like Predator, Moon Crystal, Kirby's Adventer, Skate or Die 1 & 2 and Ski or Die. I know most of the music data from most of the games.

2. Yes! I've done that manually in ca65 from scratch and it's easier than ASM6.

3. I'll give that a try.
Back to Thunder Force II hacking again.

Disch

Language stuff
MFC isn't a language, but is a library for use with C++.
I generally don't recommend C++ for GUI programs.
Nor would I recommend VB when C# is on the table.

So of the languages provided, C# would probably be my choice.  However you might want to consider Python instead, as that is what I'd probably end up using.

Maybe Rust if you're feeling frisky, but I don't know how complete its GUI libs are... so I can't really recommend it.  Or maybe I just want to push Rust because I've heard nothing but great things and I really want to learn it.  :P


Tracker stuff

Generally trackers are build to work with an existing music engine.  For most, the music engine is custom, so that features and whatnot can be added in parallel to the tracker as well as the engine.

First thing you should figure out is what engine(s) you want to support.  Do you want to support the Capcom engine?  Or do you want something custom?  Or is there a specific game you want?

Whatever you chose, you have to understand the format of the music data first.  That is, you should be able to edit an NSF file by hand with a hex editor first.  Granted it'd be tedious, but you'd have to know how to do it -- after all, how can you make your program do it if you don't know how to yourself?

If you want to make something custom -- that'd be where I start.  Write your music engine first, then write the tracker to fill in the chunks of data.

Emulator stuff

Writing an emulator and/or an NSF player will absolutely teach you everything you would ever want to know about NES architecture and how its sound works -- but it might be overkill for this goal.  I'm not sure I would recommend it.

And, unless you're planning to write your own custom music engine, I would actually recommend against it.  (This is coming from a guy who loves making emulators).  If you're writing a tracker to modify an existing engine, you don't need to know how the NES works as much as you need to know how that specific engine works.

Dracula X

Tracker Stuff:
The only that I have so far is Moon Crystal music engine data format and this is what I have so far:

Track 3 Test:

Track Location - $8E39

Square 1 - $8E41 - $00
Square 2 - $8E94 - $01
Triangle - $8ED9 - $02
Noise - $8F37 - $03

Effect List:

A0 - ; ? Jump to location yy xx
A2 - ; ?
A3 - ; ? Jump to location A3 xx yy yy
A4 - ; ? Jump to location yy xx
A6 - ; Speed
A7 - ; Automatic release time
AF - ; Base octave
B4 - ; ? xx xx
Back to Thunder Force II hacking again.

Disch

You have a long way to go.   :P

No point in writing a program to edit something if you don't know how to edit it.  If Moon Crystal is a game you're targetting, break out a disassembler and start dissecting the music engine to figure out where all the data is stored and what all the data does.

Dracula X

#6
Ok! I'll try that right now.


Tracker stuff:
Can I change it to CAPCOM 1990 Music Engine? Because it have all the info that I need to hack music.
Back to Thunder Force II hacking again.

Disch

If you know how to edit the music by hand, then yes, you should be able to make a program to do it.

HertzDevil

I have a trace logger Lua script specially designed for NSF files, that displays the first time each byte in the ROM is read by each song; the output looks like this (warning: large text file) on a modified JoshW NSF rip of Moon Crystal. The script saves me a lot of time figuring out the music format for any NES game whatsoever, since the timing information is included in the logs and reflects exactly what the song sounds like in virtually all cases.

The next step I do is to place a read breakpoint on any convenient music data byte, e.g. the first byte of track 3's Pulse 1 data (where it says "$000EC1:A8 first accessed on frame    0" in the log), after which the effect dispatcher can be easily found:

; $F9EC
lda ($5D), y
sta $0701
bpl @NoteCommand
inc $0708
iny
jmp @EffectDispatch
@NoteCommand:
; ...

; $FA7F
@EffectDispatch:
cmp #$A0
bcs :+
jmp @LengthCommand
: asl
tax
lda $FA55, x
sta $5F
lda $FA56, x
sta $60
jmp ($5F)

; $FAD5
@LengthCommand:
and #$0F
tax
lda $FF16, x
; ...


The command lookup table resides in $FA95 - $FAD4, so bytes A0 to BF all correspond to commands (but there are duplicates); bytes 80 to 8A use a note length table at $FF16 - $FF20. It suffices to directly inspect the code for each individual command first, checking the number of bytes they consume. The log also reveals that the global song pointer table starts from $8100, and that each song definition simply consists of the four pointers to each track's data stream. Even though the effects of the commands are still unknown, there is already enough information to build a music compiler where controlled trial-and-error music data can be inserted. There is no need to edit the NSF "by hand in a hex editor" from this point.

All of this took around 20 minutes. A full analysis of the sound engine may take 2 or 3 hours. The disassembly part is unnecessary, but saves a fair chunk of time writing the interim music compiler.




A few months ago, someone asked me about the idea of making a generic ROM hacking tracker, since I already maintain one using Music Macro Language instead. I made some comments that would apply whether targeting a single sound engine or many of them:

  • Not all note lengths are necessarily representable by the sound engine, but a tracker almost always makes no assumptions about them because tracker modules trigger and halt notes using separate entities;
  • Trackers usually synchronize all the tracks available, and the ones that don't (e.g. Goat Tracker, LSDj) have a much higher difficulty curve for the end users;
  • Said desync can occur because the tempo settings can be local to each track instead of global, and some games rely on this to access more note lengths (e.g. triplets by locally tripling the tempo);
  • Said desync can also occur because many music engines have richer control flow than tracker modules, such as track-local loops with breaks, nested loops, inline patterns, and global patterns with variable length. More importantly it is difficult to represent them in conventional tracker interfaces;
  • The insertion tool must be able to represent and insert any well-formed music data; in particular, it should be able to recreate exactly the contents of the original music, except possibly for pointer locations. This is the reason such a tracker cannot optimize away the commands that desynchronize tracks;
  • Commands can take any number of parameters, but trackers rarely implement effect commands that take more than one;
  • Sometimes instruments do not exist as an abstraction at all, and different aspects are controlled by different commands, in which case the tracker ought to also be able to use each of them individually.
Those quirks do not happen with Music Macro Language dialects because they are designed to map directly to the compiled data; anything not supported by the target sound engine is simply a syntax error of the MML dialect.
Constructing Chiptune; Construing 8-Bit. Makes a generic MML compiler in Lua.

Disch

Very nice @ HertzDevil.

Though I did want to clarify one thing:

Quote from: HertzDevil on January 04, 2017, 02:10:58 AM
There is no need to edit the NSF "by hand in a hex editor" from this point.

I didn't mean to say you should manually edit it with a hex editor.  Just that you should know how to.