Page 2 of 5

Posted: Sat Feb 05, 2011 1:05 pm
by AlbertoG
Neil! it's good to see you :D

Seems you have been around here for some time, 429 posts wow! I have much to read :)

I'm writing some comments to the source code I posted before. With my turtle-slow rubbish English abilities it will take me some time to finish.

Cheers,
Alberto

Posted: Sun Feb 06, 2011 10:27 am
by Jarhmander
Er, what is your native language?

Posted: Sun Feb 06, 2011 11:21 am
by Bregalad
This is written in his profile information.

Posted: Sun Feb 06, 2011 11:23 am
by AlbertoG
My native language is Spanish.

Once it's posted doesn't seem that difficult, but I write English pretty slow, just to be sure I don't make many mistakes. Then I see how miserably I fail sometimes. :lol:

Posted: Sun Feb 06, 2011 11:53 am
by AlbertoG
OK, here is the commented source code.

I'm sure you all know that, but let me just remind you that in those times we had severe memory restrictions for both RAM and ROM, so the sound driver and the song data had to be designed to occupy the less bytes possible.

This is the initialization routine, very simple. It uses macros to set the song sound driver pointers to the data of this particular song. You can see how channels 1, 2 and 3 are set to GOMEL, GOCHRD and GOBASS tables.

Code: Select all

GAMEOVER	INCHA	GOMEL
	INCHB	GOCHRD
	INCHC	GOBASS
	RTS
This is the table of tracks for channel 1. Since it's a very short tune it only lists one track. In a longer song it would address many more tracks or “chunks” of data. This is a never ending tune, so the final data instructs the driver to repeat the same table again and again.

Code: Select all

GOMEL	DW	GOMEL1
	DW	0,GOMEL
Same for channel 2.

Code: Select all

GOCHRD	DW	GOCHRD1
	DW	0,GOCHRD
Same for channel 3.

Code: Select all

GOBASS	DW	GOBASS1
	DW	0,GOBASS
This is the “real” data of the song, and this concretely is what I used to call the Bass Track, because it would contain mostly the bass notes. With the NES you were almost forced to use the triangle wave channel always for the bass part.

The first command (VIB,0,4,5) tells the driver to set a vibrato with 0 initial delay, 4 depth and a speed of 5 frames. The second command (TRA,-2) sets this track to transpose the notes down 2 semitones.

The next line (PERC 10, GOPERC) initializes a drum track with a note length of 10 for each sound. Since drum sequences are usually cyclic, I used to “invoke” drum tracks and breaks from any of the other tracks (usually the bass track), at any time, instead of having a separated table with all the drum sequences. This reduced the amount of data in the song.

Next, there is a note length command (L40) which tells the driver to set the note length to 40 video frames.

The rest of the data are notes, and tied notes (__), which just leave the last note sounding for the last length set, in this case 40.

The last command (XM) marks the end of the track.

Code: Select all

GOBASS1	DB	VIB,0,4,5,TRA,-2
	PERC	10,GOPERC
	DB	L40
	DB	a1,__
	DB	__,g1
	DB	e1,__
	DB	__,e1

	DB	a1,__
	DB	__,g1
	DB	e1,__
	DB	__,g1

	DB	d1,__
	DB	__,d2
	DB	a1,__
	DB	__,a1
	DB	d1,__
	DB	__,d2
	DB	e1,__
	DB	gs1,B1
	DB	XM
This is what I used to call the Chord Track, because it would contain most of the chords of the song, in the form of those fast arpeggios that made European chip music so distinctive.

The first command (L10) sets the length note to 10 frames.

The second command (SENV,$D1,3,$64) sets a volume envelope for the sound. The volume envelope routines of my NES and Game Boy drivers were very similar, so I could share code between them, but in the Game Boy I used hardware envelopes (because of hardware restrictions, nasty noises when changing volume), and in the NES I used a simulation by software.

The next command (W1) instructs the channel to set the square waveform to 25% duty. W0 was 50% and W2 12.5% I think.

Next there is an arpeggio command (EF0 to EF15). The arpeggios were defined globally for all the songs and consisted basically on tables of sequential notes used to transpose the current note. One arpeggio would contain a major chord (C,E,G,RET), other a minor chord (C,Ds,G,RET), and others may not contain a chord at all, only transpositions to different octaves. The transposition tables could be cyclic or not, depending on the final command (RET or END). This was another technique to reduce song data, since you didn't need to specify all the notes for a chord, only the root note and the chord type (if different than the previous one).

The next command (DO2 to DO8) instructs the driver to repeat 2 times the block of data found between this and the next LOP command. This was used to reduce the size of the song data too.

Next there is a “procedure” command (P1 to P9). Those commands were used to execute a portion of assembler code which modified whatever was needed in the sound driver. In this case I'm using the procedures to change the “instrument” of the song (sort of hard coded program change).

Code: Select all

GOCHRD1	DB	L10,SENV,$D1,3,$64,W1,EF1

	DO2
	DB	P9,a2,P3,c3,e3,a3,c4,a3,e3,c3
	LOP
	DO2
	DB	P9,e2,P3,g2,b2,e3,g3,e3,b2,g2
	LOP
	DO2
	DB	P9,a2,P3,c3,e3,a3,c4,a3,e3,c3
	LOP
	DO2
	DB	P9,e2,P3,g2,b2,e3,g3,e3,b2,g2
	LOP
	DO2
	DB	P9,d2,P3,f2,a2,d3,f3,d3,a2,f2
	LOP
	DO2
	DB	P9,a2,P3,c3,e3,a3,c4,a3,e3,c3
	LOP
	DO2
	DB	P9,d2,P3,f2,a2,d3,f3,d3,a2,f2
	LOP
	DO2
	DB	P9,EF5,e2,P3,gs2,b2,e3,gs3,e3,b2,gs2
	LOP
	DB	XM
This is what I used to call the Melody Track, because usually it would contain the main melody of the song. There are some new commands here.
The first new command (REL,5,$02) is what I called the “release effect”. Literally, what this command instructs the driver is to set a decay envelope of $02 just 5 frames before the next note command is reached. This was used to shorten the sound of the notes, like if you released the key before pressing the next one (non-legato). This decay envelope means, in nibbles, no change in initial volume (0) and a decay time of 2, hence the $02 value.
The next new command (nn or NN = not note) is a simple command used to trigger the above mentioned “release effect”.

This particular track has lots of tied note commands, this is because it's a short tune and probably I had enough memory available. Usually I would pack the data so it occupied less bytes by using the note length commands. For example instead of leaving “L10,C3,__,__,__” I would changed it to “L40,C3”. This had to be done by hand at the end of the composition process for all the tracks. Every byte counted!!

Code: Select all

GOMEL1	DB	L10,SENV,$39,5,$90,VIB,20,1,4,W1,REL,5,$02
	DB	e3,__,a3,__,c4,__,__,__
	DB	__,NN,a3,b3,c4,__,d4,__
	DB	b3,__,__,g3,e3,__,__,__
	DB	__,__,__,__,__,f3,e3,d3
	DB	e3,__,a3,__,c4,__,__,__
	DB	__,NN,a3,b3,c4,__,d4,__
	DB	b3,__,__,a3,g3,a3,b3,__
	DB	__,__,e3,__,__,__,f3,g3

	DB	a3,__,__,g3,f3,__,e3,f3
	DB	__,__,e3,__,d3,__,c3,__
	DB	d3,__,e3,__,e3,__,d3,e3
	DB	__,__,__,__,__,nn,f3,g3
	DB	a3,__,__,g3,f3,__,e3,f3
	DB	__,__,e3,__,c4,__,b3,__
	DB	e3,__,e3,__,d4,__,c4,__
	DB	c4,__,__,__,B3,__,__,__
	DB	XM


Finally, this is the drum track, a pretty simple one. Each number means one different drum sound, except 0 which is no sound. The default note length is set by the channel that initializes the drum track. Setting the length inside the track was also possible using L commands, but this way I could use the same drum track on different songs that used different base note lengths, again to reduce song data.

As you may figure if you listen to the song, 1=bass drum, 2= snare drum, 6 = short hit hat and 5 = slightly different short hit hat.

Code: Select all

GOPERC	DB	1,0,6,5,2,0,6,5
	DB	1,0,6,5,2,5,1,5
	DB	END

I would really like to find commented code of other veteran musicians, just to know what commands and techniques they used on their sound drivers and compositions. Please let me know if you know of any place with such stuff :)

Cheers!

Posted: Sun Feb 06, 2011 3:00 pm
by Drag
I'm not a veteran musician, but I still can outline how my music engine works. :P As far as I'm aware, Sivak is using it in Battle Kid 2. He also used an older version of my engine in Battle Kid 1.

First thing is to define the instruments. Instruments are defined globally, so they apply to all songs, and all sound effects.

Code: Select all

envelopes
 dc.w	env_blank, env_s4_ch, env_s4_sd, env_s4_oh, env_s4_lead1
 dc.w	env_s4_lead2
env_blank
 hex	00 FF
env_s4_ch
 hex	0F 0E 0C 09 05 00 FF 
env_s4_sd
 hex	0F 0E 0C 0A 08 06 05 04 03 03 02 02 02 01 01 01 01 00 FF
env_s4_oh
 hex	0C 0C 0B 0B 0A 0A 0A 09 09 09 09 08 FF
env_s4_lead1
 hex	4d 4d 4c 4b 4a 4a 49 49 48 48 48 47 46 45 44 44 44 44 44 43 FF
env_s4_lead2
 hex	4f 08 0c 08 09 08 07 06 06 05 05 04 FF
You have a table of instruments, and then the data for each instrument.

Since this runs on the NES, these are just sequences of bytes that get written to the volume/duty register for each channel. The exception being the triangle channel; I have the envelope code set up to write a constant instead. "FF" signals the end of the envelope, it just stops. "10 xx" is a "goto" command, for looping (I don't use that here).

Next, if you want to use any pitch envelopes (like vibrato, for instance), you define those.

Code: Select all

pitch_envelopes
 dc.w	penv_1
penv_1
 hex 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 hex 01 FF 00 00 00 FF 01 00 00 00 01 00 FF 00 00
 hex FF 00 01 00 01 00 01 FF FF FF 00 FF 01 01 8022
This is a little different, this is a table of differences, and these are signed bytes. That 80xx is the "goto" command again. As an extra point of interest, I copied this particular vibrato out of Dr. Mario. :P

Now comes the actual song data.

Code: Select all

songs
 dc.w	silence, song4

silence
 dc.w	$0000, $0000, $0000, $0000
 dc	$00

song4
 dc.w	song4_sq1, song4_sq2, song4_tri, song4_nse
 dc $57
Like with the instruments and the pitch envelopes, you start with a table that defines each song. I usually start with a "silence" song that basically sets a tempo of 00, and then just clears the channels. After that is an actual song. There's a pointer for each channel (use $0000 for unused channels), and then a tempo byte.

For the sake of simplicity, here's just one of the channels:

Code: Select all

song4_sq1
 hex ee 01 e4 04 c0 82 92 c3 a2 d1 13 e1 EB 0A c2 62 e0 c0 42 ca 52
 hex e4 05 EB 0F c2 42 52 EB 07 52 EB 0F c0 52 c1 e0 EB 0F
 hex e4 04 c2 33 13 a2 92 82 62 c5 32 82 c2 62 72 
 hex e4 05 c2 42 52 EB 07 52 EB 0F E3 05 c5 52 e0 52 E3 00 EB 07 c5 52 c1 52 c0 e0
 hex e4 04 EB 0F c5 e0 c2 13 e0 e0 03 e0 e0 a2 c8 e0 e4 05 EB 08 c2 a2 a2 e4 04 EB 0F c5 32
 dc		$e5
 dc.w	song4_sq1
  • EE 01: set pitch envelope to 1 (EE 00 turns it off)
  • E4 04: set instrument to 4 (which, in reality, is the fifth instrument defined in the table)
  • C0: set a note length to 1. C0-DF is actually a one-byte alias for the two-byte note length command E2 xx. So for example, putting C6 is the same as putting E2 06.
  • 82 92 are notes (F-2 and F#2). This is probably where I differ from a lot of engines, my notes are defined as (<semitone><octave>), so 33 53 73 83 a3 04 24 34 is a full scale (C, D, E, ... B, C). Note that 0x is A, instead of C. I'll explain that in a bit.

    I went with <semitone><octave> (versus a linear table) for programming reasons. <semitone> is an index into my pitch table, and <octave> is how many times to shift it right. Basically, if you have a pitch, you can raise it to the next octave by halving it, which is the same as shifting it right. This is very easy (and quick) for the CPU to decode, while also being easily human-readable.

    Next, the lowest pitch available to the NES APU is an A, which is why I chose to start my pitch table with A. Theoretically, this maximizes the amount of notes possible with the engine, because A, A#, and B are given 1 extra bit of resolution, rather than if I had started with C instead.
  • C3: note length is set to 4, and then a G is played.
  • D1: note length is set to 18 ($12)
  • 13: A#2 plays for 18 ticks. (Remember what I explained above, even though it's written as A#3, it's actually A#2)
  • E1: This is the "note tie" command, so that A# will continue for another 18 ticks.
  • EB 0A: Set volume to 10 ($A). 0 is silence, F is full volume.
  • C2: Note length is set to 3
  • 62: D#2 plays for 3 ticks
  • E0: Rest for 3 ticks
  • C0, 42: C#2 for 1 tick
  • CA, 52: D2 for 11 ticks.
Basically, stuff like this continues until that $e5 down there, which is the "goto" command. Here, it just loops the track over and over.

Sound effects follow the same format, except they have a slightly different header, and sound effects always play at tempo FF, regardless of the tempo of the current song.

The next cool thing is that you can have a track play sound effects instead of notes. That's actually how you do drums. :P

I'm still working on things though. Lately, I've been playing around with the Nintendo SPC engine. If you want to know something crazy, it works very similarly to this. :P

Posted: Mon Feb 07, 2011 3:20 am
by Bregalad
This is the “real” data of the song, and this concretely is what I used to call the Bass Track, because it would contain mostly the bass notes. With the NES you were almost forced to use the triangle wave channel always for the bass part.
Well, why ? There is no reason Triangle can't be used in it's higer tones ! The only restriction is that you can't go really high either it sounds off-tune, but you can go pretty high before that happens anyways.
Once it's posted doesn't seem that difficult, but I write English pretty slow, just to be sure I don't make many mistakes. Then I see how miserably
I fail sometimes.
Or do it like me I write everything fast, do a lot of mistakes and don't give a damn about it. :roll:
I'm not a veteran musician, but I still can outline how my music engine works. As far as I'm aware, Sivak is using it in Battle Kid 2. He also used an older version of my engine in Battle Kid 1.
And you're ok about him making profit using your engine ? Man I don't understand you.
Since this runs on the NES, these are just sequences of bytes that get written to the volume/duty register for each channel. The exception being the triangle channel; I have the envelope code set up to write a constant instead. "FF" signals the end of the envelope, it just stops. "10 xx" is a "goto" command, for looping (I don't use that here).
This is a flexible system (i.e. it can allow anything), but can be a bit wasteful of bytes, especially since apparently duty cycle and volumes envelope comes together so it's impossible for example to use the same volume enveloppe with a different duty cycle without re-coding it.

Also, if you wanted for example the volume to fade out really slowly, it would take a whole **** of bytes to get it done, when it could be otherwise done by software.
So I guess if I ever do that "ultimate" sound engine I wanted to do, I'd allow both software fades (when doing it slow) or hard-wired volume enveloppes like the ones you showed (when doing it fast).


I'm still working on things though. Lately, I've been playing around with the Nintendo SPC engine. If you want to know something crazy, it works very similarly to this.
I guess it's not so surprising. You don't change a forumla that works :wink:
However, the hardware is very different on SNES, and all volume envelopes can be done by hardware. Also the use of instrument samples makes arpeggios mostly useless (unless you're imitating a chiptune).

Posted: Mon Feb 07, 2011 9:18 am
by AlbertoG
@Drag
That's a very interesting way of programming tunes. I couldn't do with so much HEX though, first I would do is define a bunch of macros to manage that driver :D
I mean, I can understand Rob Hubbard doing it that way in 1983 because of not having proper compilers and editors, but not now!
I was very lucky to have the PDS since I programmed my first line of assembler code. It was an absolutely amazing and powerful editor and compiler / debugger. I used it for all my Spectrum / Amstrad / MSX, NES, GB and Master System / Game Gear soundtracks

The cyclic envelopes in your driver are very cool. I used a similar approach for certain effects in my Spectrum sound driver. The main theme of Light Corridor used a modulation envelope on top of the main volume envelope to simulate a tremolo, and I used them in other unreleased works to simulate delays.

My NES driver was quite simple, but I was pretty proud of it at the time. I had to learn 6502 (which I hated at first for having only 3 registers) and also draw the sprites of the games, so I didn't had much time to spend on the driver. But since it was written from scratch I did some things better than before. In the end I loved the 6502, and the sound of the NES too.
Instead of having the properties of the instruments defined in a table, I used that "procedure" approach to change the sound of each one, by poking directly the driver variables as needed. I also did this on the Master System driver, but all my other sound drivers worked in a different way: instrument definitions were just like any other chunk of music data, with the same commands, and the sound effects worked the same way. The problem with this method is that changing the instrument was much more expensive in CPU cycles.

Code: Select all

;instruments P9 and P3 in Asterix

SDAT MACRO
    LDY	#@1
    STA	(CHL),Y
ENDM

_P9 LDA  #<EEF6
    SDAT CHORDH
    LDA   #>EEF6
    SDAT CHORDL
    LDA  #0
    SDAT CHORDP
    LDA  #$F1
    SDAT ENVELOPE
    LDA  #5
    SDAT SENVCNT
    SDAT SENVCNTR
    LDA  #$92
    SDAT SENV2ENV
    JP   NEWNOTE2


_P3 LDA  #<EEF13
    SDAT CHORDH
    LDA  #>EEF13
    SDAT CHORDL
    LDA  #0
    SDAT CHORDP
    LDA  #$A0
    SDAT ENVELOPE
    LDA  #5
    SDAT SENVCNT
    SDAT SENVCNTR
    LDA  #$52
    SDAT SENV2ENV
    JP   NEWNOTE2
@Bregalad
Well, why ? There is no reason Triangle can't be used in it's higer tones ! The only restriction is that you can't go really high either it sounds off-tune, but you can go pretty high before that happens anyways.
Yes I know, but since it had that big bass sound it was best suited for it, and drums. I used it once for melody, though, in the ending theme of Asterix.
Or do it like me I write everything fast, do a lot of mistakes and don't give a damn about it. Rolling Eyes
That's something I should apply to more than one thing in my life...

Cheers

Posted: Tue Feb 08, 2011 12:16 pm
by cartlemmy
Not sure if anyone cares, but here's the instruction set of my sound engine thus far:

Code: Select all

Instruction        Code       Param A   Param B
Start Note         %1AABBBBB  Channel   Note to play
Octave Set         %000AABBB  Channel   Octave (0-7)
Envelope           %0010AABB  Channel   Envelope # (0-3)
Note Mode          %0011AABB  Channel   0 = Normal
									    1 = Arpeggio
                                        2 = Duty Cycle Shift
                                        
Wait               %0100AAAA  (A + 1) 16th notes to wait            
Loop To            %01010AAA  Loop times (0 = infinite) + Next two bytes is location to loop to
SqWave Duty Cycle  %01011ABB  Channel   Duty Cycle
Play DMC           %01100AAA  DMC # 
Stop Note          %011010AA  Channel
Currently Unused   %011011XX
Start Arpeggio     %01110000
Currently Unused   %01110001
Stream 2 Jump To   %01110010
Stream 2 Restart   %01110011
Stream 2 Start     %01110100
Stream 2 Stop      %01110101
Describe Arpeggio  %01110110
Currently Unused   %01110111-%01111111
I found that creating an underlying loop (Stream 2) and playing notes on top of that saves a lot of data. My data format is by no means easily human readable, nor does it lend itself to hard coded opcodes, so I created a separate script to parse a more human readable format to this.

Posted: Tue Feb 08, 2011 12:42 pm
by Bregalad
Yes I know, but since it had that big bass sound it was best suited for it, and drums. I used it once for melody, though, in the ending theme of Asterix.
Well, it sounds good I think ! Squares in the lower tones sounds good as well, I think it's best to explore different possibilities instead of relying on a fixed patter all the time.
My NES driver was quite simple, but I was pretty proud of it at the time.
Well it probably was quite complex in fact. Apparently it supports complex arpeggio effects and drums on triangle that interrupts the actual note triangle plays (I think you understand what I mean). My driver, which I designed to be very compact and simple, doesn't support any of this the only effect I do is extremely simple volume decay and duty-cycle switch on the first frame.

On a side note I really like this "short Triangle plugs" (combined with noise) way to make drums, that both Alberto and Neil used in their works. I think it sounds more accurate than DPCM ironically and it doesn't eat ROM space stupidly.
However I have no idea how to implement this, so for now I go with noise-only drums (or another channel, but that is fully used for drums and never plays some melody and drums at the same time).

The best I could attempt is for some kind of techno to have a note sliding downwards on beats (simulate a loud electronic bass drum) and a normal tone for counter-beats.

I guess to fully implement this I'd need to have internally 2 triangle channels (one who plays fast percussion stuff and the other a melody) and have one take over another when needed, like how I have internally multiple channels for music and sound effects, and the the latter take over the former. So I'd end up with 3 triangle channels :wink:

Posted: Tue Feb 08, 2011 1:12 pm
by tepples
The music engine in LJ65 and Concentration Room uses the sound effects system for percussion. So a kick drum would be stored as a sound effect and activated whenever the drum channel calls for it. In fact, the game might even reuse the kick drum as a sound effect proper, such as when an object lands.

It also limits the pitches in a phrase to the two octaves above the base pitch at which the phrase is played. This frees three bits for duration: a note is either 1, 2, 3, 4, 6, 8, 12, or 16 divisions long, with in-between durations handled by the "tie" command. The base pitch allows for transpositions, like the way the Comic Bakery theme is played twice in different keys in the first outdoor level of Jurassic Park, or like a bass line can be played at multiple pitches depending on what chord is on top.

Posted: Tue Feb 08, 2011 2:18 pm
by Bregalad
In fact, the game might even reuse the kick drum as a sound effect proper, such as when an object lands.
The method you use is interesitng, but in my opinion it is a very bad idea to reuse a sound effect like that.
Because the player will think a sound effect is playing when in fact it's just the music. This is very disturbing.

This has just happened to me when playing Mega Man Battle Network 6 this afternoon and listening this music, there seems to be a randomly high pitched sound effect, which sounds almost identical to the sound effect in the game when you move the cursor.

Posted: Tue Feb 08, 2011 2:41 pm
by tepples
Bregalad wrote:
In fact, the game might even reuse the kick drum as a sound effect proper, such as when an object lands.
in my opinion it is a very bad idea to reuse a sound effect like that. Because the player will think a sound effect is playing when in fact it's just the music. This is very disturbing.
Occasionally, it's supposed to be disturbing. Hip Tanaka composed much of the music of Metroid to sound like sound effects, on purpose.

But in practice, does it end up disturbing when pieces land using a kick drum sound in this video? Your brain separates them out: kick drum in the sound effects happens a quarter second after you press up on the Control Pad, while kick drum in the music happens at predictable times compared to the bass line.

Posted: Fri Oct 21, 2011 8:27 am
by Doommaster1994
I recently got in touch with Michelle Simon, who composed Roundball 2-on-2 Challenge for the NES. She told me that the game's music was composed as MIDI files in Cakewalk for DOS. I'd like to say that this is probably the millionth NES game developer (Park Place Productions) to use converted MIDI files, so I don't think converting MIDI to NES/NSF should be THAT hard.
A long time ago, I also contacted Doug Brandon, who composed Great Waldo Search and Rollerblade Racer. He said the music was composed on CuBase for the Atari ST.
While I'm still on the same subject, I said that Paul Wilkinson used hex code and Cakewalk. He told me that at first he composed in hex code (Terminator was one of them), then he wrote a MIDI conversion tool and converted his MIDI files to NES and he did the rest of his NES music that way.
For David Whittaker, I think he just wrote in hex code.

Posted: Fri Oct 21, 2011 8:29 am
by Shiru
It is not hard, you just going to get crap as output.