The Definitive Guide to Composing NESmaker-Compatible Music and Sound

User avatar
jorotroid
Posts: 272
Joined: Wed Aug 08, 2018 7:48 pm
Location: California
Contact:

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by jorotroid » Fri Oct 09, 2020 1:29 pm

CutterCross wrote:
Sat Aug 10, 2019 6:41 pm
- Looping with the Bxx (must be present in all channels, using unique patterns) command **

** Bxx effect is NOT supported by default, and can only be re-implemented by using GGsound's official ft_txt_to_asm Python script or the GGsound ft_txt_to_asm executable on this forum.
So a couple of things about Bxx. The reason why you "need" to put it on every channel is that otherwise the loop will only apply to the channel that it is on. That might actually be useful in some cases, but also Bxx might not always be the right way of doing loops anyway. Again using my medieval track as an example, I used the Bxx command on it, so its suppose to have a later restart point and I can compare what it looks like with the NESmaker converter vs the ggsound converter. (I was sort of gambling that Bxx would be fixed for NESmaker 4.5's version of the converter, but that is sort of besides the point)

If you open up the AllSongs_WithSFX.asm file in the GameEngineData\Sound folder after importing the txt and exporting the rom, this is what the data for the frames of the first square channel would look like. I'll add some comments to make it what is going on clearer.

Code: Select all

MedievalTimes_square1:
	.db CAL,<(MedievalTimes_square1_0),>(MedievalTimes_square1_0)	; Each line is a frame for this channel.
	.db CAL,<(MedievalTimes_square1_0),>(MedievalTimes_square1_0)	; CAL is a ggsound opcode telling the engine to 
	.db CAL,<(MedievalTimes_square1_0),>(MedievalTimes_square1_0)	; "call" the address defined by the next 2 bytes.
	.db CAL,<(MedievalTimes_square1_0),>(MedievalTimes_square1_0)	; The 2 bytes are the lo and hi bytes for the
	.db CAL,<(MedievalTimes_square1_5),>(MedievalTimes_square1_5)	; address of the start of the pattern. You can 
	.db CAL,<(MedievalTimes_square1_2),>(MedievalTimes_square1_2)	; the pattern number and the end of the label
	.db CAL,<(MedievalTimes_square1_3),>(MedievalTimes_square1_3)	; for these bytes.
	.db CAL,<(MedievalTimes_square1_6),>(MedievalTimes_square1_6)
	.db CAL,<(MedievalTimes_square1_7),>(MedievalTimes_square1_7)
	.db CAL,<(MedievalTimes_square1_1),>(MedievalTimes_square1_1)
	.db CAL,<(MedievalTimes_square1_1),>(MedievalTimes_square1_1)
	.db CAL,<(MedievalTimes_square1_8),>(MedievalTimes_square1_8)
	.db GOT								; GOT is the ggsound opcode for "goto"
	.dw MedievalTimes_square1					; Note that this is the same label as the one at the top.
And here is what that part would look like in the asm file generated by the ggsound converter:

Code: Select all

_MedievalTimes_square1:
  .db CAL,<(_MedievalTimes_square1_0),>(_MedievalTimes_square1_0)
  .db CAL,<(_MedievalTimes_square1_0),>(_MedievalTimes_square1_0)
 _MedievalTimes_square1_loop:						; Note this label that wasn't in the NESmaker output.
  .db CAL,<(_MedievalTimes_square1_0),>(_MedievalTimes_square1_0)
  .db CAL,<(_MedievalTimes_square1_0),>(_MedievalTimes_square1_0)
  .db CAL,<(_MedievalTimes_square1_5),>(_MedievalTimes_square1_5)
  .db CAL,<(_MedievalTimes_square1_2),>(_MedievalTimes_square1_2)
  .db CAL,<(_MedievalTimes_square1_3),>(_MedievalTimes_square1_3)
  .db CAL,<(_MedievalTimes_square1_6),>(_MedievalTimes_square1_6)
  .db CAL,<(_MedievalTimes_square1_7),>(_MedievalTimes_square1_7)
  .db CAL,<(_MedievalTimes_square1_1),>(_MedievalTimes_square1_1)
  .db CAL,<(_MedievalTimes_square1_1),>(_MedievalTimes_square1_1)
  .db CAL,<(_MedievalTimes_square1_8),>(_MedievalTimes_square1_8)
  .db GOT
  .dw _MedievalTimes_square1_loop					; Note that this is the new label, not the start label
You can see in the NESmaker outputted file at the end of the frame sequence it defines a word that this is the address of the label for the start of the sequence of frames, MedievalTimes_square1. But in the ggsound outputted file, it has a different label defining the word at the end with "_loop" added to the end of the label, and the location of the label precedes the frame the song should be looping back to. This is how the ggsound engine handles looping. One nice thing about the way they designed it is that this means looping to a later point doesn't take any additional space than looping back to the beginning. Using Bxx can be fine in some situations, but what if you want the last frame of one or more of the tracks of your song to be a copy of another frame? You would have to make a copy of that frame and put the Bxx command at the end and having a copy of a frame would mean that you'll have a whole frame of redundant data that will get exported.

So to summarize, if the end of your song in completely unique, Bxx is fine to use. If the last frame reuses any frames, it's better to edit the outputted asm file from the ggsound converter after the fact. Just be sure to make the changes to all of the channels.

Additionally, I haven't done this yet but I think you could also use the Bxx command to make pickup notes or a shortened intro frame because Bxx does tell the ggsound converter to stop reading the frame of that channel after that point. You would probably also have to edit the file to make it work.

Also you can't edit the AllSongs_WithSFX.asm file because it gets overwritten by NESmaker on export. If anyone doesn't feel like downloading the ggsound converter, I suppose you could make a copy of the AllSongs_WithSFX.asm after an export, do some edits to the copy, and then import the copy.



Ok, one more thing while I'm here. Not sure if this has been mentioned before, but if you do use the ggsound converter, don't use the note release command (pressing the '\' key in the pattern editor that results in a big equal sign). This will give you an error when you go to export your rom. Using the NESmaker converter this will result in the same output as a note cut (pressing the '1' key giving you a big dash in your pattern). You probably should just always use note cuts anyway especially considering ggsound doesn't support the function of the note releases anyway.
User avatar
Andrew. Pierce.2032
Posts: 11
Joined: Fri Jul 05, 2019 8:46 pm
Location: Oklahaoma City
Contact:

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by Andrew. Pierce.2032 » Fri Oct 09, 2020 8:06 pm

jorotroid wrote:
Fri Oct 09, 2020 11:20 am
Andrew. Pierce.2032 wrote:
Tue Oct 06, 2020 11:22 pm
A couple of things I discovered that I didn't see mentioned:

Frames in the FamiTracker module have to progress together. If you want to loop a channel, you have to loop ALL the channels.

so your module can look like this:

00|00 00 00 00 00
01|01 01 01 01 01
02|01 01 01 01 01
03|02 02 02 02 02


But it can't look like this:

00|00 00 00 00 00
01|01 01 01 01 01
02|02 02 02 01 02
03|03 03 03 01 03
I've seen other people at times say that, but it's not true. Here are the frames from my medieval theme in the 4.5 Tutorial Sound Pack as an example.

00 : 00 00 03 00 00
01 : 00 00 01 01 00
02 : 00 04 00 02 00
03 : 00 04 01 01 00
04 : 05 01 01 02 00
05 : 02 02 00 04 00
06 : 03 03 02 03 00
07 : 06 05 05 01 00
08 : 07 06 06 01 00
09 : 01 01 02 02 00
0A : 01 01 02 02 00
0B : 08 07 04 05 00

If you're having an issue there must be another cause. What exactly is the issue you get?

Andrew. Pierce.2032 wrote:
Tue Oct 06, 2020 11:22 pm
Another problem I ran into is that NesMaker does not like empty frames. I had a song that used some polyrhthmic envelopes to make some ambient textures across a couple frames and it would it kept spitting back the error message "Object reference not set to an instance of an object" (Same error for the issue above. My guess is that famitracker tries to consolidate the blank and irregular frames in a way that either Nesmaker or the GG sound engine doesn't support.) As long as you have a note on at least one channel per a frame it shouldn't be a problem. I also learned that instrument trails don't carry between frames in the compiled rom the way they do in famitracker so I'll be needing to fill those frames in regardless.
Also another thing I have not experienced. Like pattern 00 of frame 00 of the noise channel in my example above is completely blank. But maybe that is because it's at the beginning? Maybe the issue is from a blank pattern in certain contexts?

I can't imagine what else it could be but I'm also not sure why yours would work and mine wouldn't. I loaded all of my instruments into an otherwise blank song and everything worked fine. If I make a song like this:

00|00 00 00 00 00
01|01 01 01 00 00

with a C5 at the start of every pattern, then when importing the famitracker txt to nesmaker I'll get the error message mentioned above of "Object reference not set to an instance of an object". Changing the 00 patterns in frame 01 to 01 patterns :
(01| 01 01 01 01 01) allows it to import fine. Likewise, if I take that same now-functioning song and I leave all the 01 patterns for each channel in frame 01 completely blank I'll get the same error message when trying to import the txt to nesmaker. Adding a single note on any channel in the otherwise blank frame fixes the issue.

If you have a solution I would genuinely love to hear it. It would be great to take back all of space used up by copy/pasted patterns.
Last edited by Andrew. Pierce.2032 on Fri Oct 09, 2020 8:44 pm, edited 1 time in total.
User avatar
jorotroid
Posts: 272
Joined: Wed Aug 08, 2018 7:48 pm
Location: California
Contact:

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by jorotroid » Fri Oct 09, 2020 8:37 pm

Andrew. Pierce.2032 wrote:
Fri Oct 09, 2020 8:06 pm
If you have a solution I would genuinely love to hear it. It would be great to take back all of space used up by copy/pasted patterns.
I'll try doing what you said you did after I get off work, but also if you would be willing to share your txt file, I could take a look and see if I can pinpoint the problem.
User avatar
Andrew. Pierce.2032
Posts: 11
Joined: Fri Jul 05, 2019 8:46 pm
Location: Oklahaoma City
Contact:

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by Andrew. Pierce.2032 » Fri Oct 09, 2020 8:53 pm

jorotroid wrote:
Fri Oct 09, 2020 8:37 pm
Andrew. Pierce.2032 wrote:
Fri Oct 09, 2020 8:06 pm
If you have a solution I would genuinely love to hear it. It would be great to take back all of space used up by copy/pasted patterns.
I'll try doing what you said you did after I get off work, but also if you would be willing to share your txt file, I could take a look and see if I can pinpoint the problem.
I've already reformatted my song layout to comply with what nesmaker would accept but I'll make a reverted version as well and dm them to you.

EDIT: After taking the time to go back and remove all of the duplicate patterns the txt file imported into nesmaker without a hitch. I don't know what I've done differently but I'll take it.
User avatar
jorotroid
Posts: 272
Joined: Wed Aug 08, 2018 7:48 pm
Location: California
Contact:

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by jorotroid » Sat Oct 10, 2020 3:01 am

Andrew. Pierce.2032 wrote:
Fri Oct 09, 2020 8:53 pm
EDIT: After taking the time to go back and remove all of the duplicate patterns the txt file imported into nesmaker without a hitch. I don't know what I've done differently but I'll take it.
Awesome!

I've been doing some tests on the blank frames error. I think what causes it is if all patterns with the same index are blank and at least one of those patterns is used. So if pattern 01 of your squares, triangle, and noise channels are all blank, but for whatever reason you didn't end up using any pattern 01 in your song at all, you would be fine. But if you use any pattern 01 with all of them being blank, you will get the error. If you put something in one of those patterns, it will work again. Surprisingly,
even if the one pattern that isn't blank is not actually used in the song, this will still allow you to import error free; but you probably don't want to do that because that unused pattern would just be a waste of space.

I think what is going on under the hood is that there is some kind of optimization in the converter to see when all patterns of an index are all blank and if so, skip creating data for that pattern. But this optimization neglects to check if any of the patterns are actually getting used. Then when NESmaker goes to import the audio after converting it, it see that there is a reference to a pattern in the pattern sequence, but there is no data for that pattern.

I can't help but to feel that there are more permutations that could lead to this error manifesting, but I'm not sure what other configurations to try next at this point.

Also somewhat related that might be good to know is what exactly does happen when you export a successful blank frame. One thing the exporters do is automatically generate one extra instrument called "silent." This instrument has a volume of 0 and gets played for note cuts, but also gets played at the beginning of any frame that doesn't already have another note designated for the beginning. This means that note do not sustain past the end of a frame like they do in NESmaker. So if you wanted to used a blank frame with a note sustaining over it that started from the previous frame, that won't be possible.
User avatar
baardbi
Posts: 169
Joined: Sun Jul 07, 2019 2:28 pm
Location: Norge

Re: The Definitive Guide to Composing NESmaker-Compatible Music and Sound

Post by baardbi » Thu Oct 15, 2020 3:01 pm

This is awesome! Thank you for doing this.
Post Reply