H7's PPMCK is a third expansion of the famous MCK, a program that will convert your Music Markup Language (MML) text files into NES music instructions. You can play the generated file in various NSF players and plugins. With this new expansion, you get access to tons of tracks. The original NES only has a few sound-generating channels. Various third-party chips were included in some games to expand this. PPMCK, to my knowledge, takes advantage of all of them. This allows for rich, complex chiptunes not available in the original MCK.
I had trouble learning MML because the documentation out there was sparse and mostly in other languages. So this effort is to help everyone reference the different commands using MML under h7's PPMCK. After reading this I hope you gain a greater understanding of utilizing all of PPMCK's channels so you can make great chiptunes efficiently!
Track Letter | Description |
---|---|
A,B | 2A03 Square Wave |
C | 2A03 Triangle Wave |
D | 2A03 Noise |
E | 2A03 DPCM Sample Playback |
F | FDS Wavetable Synth |
G,H,I,J,K,L | VRC7 FM Synth |
M,N | VRC6 Square Wave |
O | VRC6 Sawtooth Wave |
P,Q,R,S,T,U,V,W | N106 Wavetable Synth |
X,Y,Z | FME7 Multisound Synth |
a,b | MMC5 Square Wave |
Download PPMCK
software from http://takamatsu.cool.ne.jp/dutycycle/ppmck.html.
<string>
The title of your tune. It will probably display in most players.
31 characters maximum.
You cannot comment anything out on this line.
<string>
The person who composed the actual music. This may also display in
players.
Save your musical pseudonym for #PROGRAMER
31 characters maximum.
You cannot comment anything out on this line.
<string>
The person who typed the MML. Will not necessarily display in players.
The typo "PROGRAMER" is actually correct usage.
31 characters maximum.
You cannot comment anything out on this line.
<num> (0 or 1)
If set to 0, a ">" will raise the track one octave. "<" will lower one
octave.
If set to 1, a "<" will raise the track one octave. ">" will lower one
octave.
If this instruction is left out, a 0 is assumed.
Enables the Famicom Disk System channel F.
Enables Namco106 channels P,Q,R,S,T,U,V, and W.
<num> (1 to 8)
This is the number of channels you want to enable simultaneously. For
example, entering 3 will enable P, Q and R only. Entering 8 will enable all
of its channels: P through W.
Enables VRC6 channels M, N and O.
Enables VRC7 channels G, H, I, J, K and L.
Enables MMC5 channels a and b.
Enables FME7 channels X, Y, and Z.
<num> (1 to 14)
The number of the track you'd like to enable bank-switching on. Entering a 1
will bankswitch track A, 2 for B, 3 for C, and so on.
If you're working on a complex piece, you might see this error from NESASM:
NESASM has to organize data into 8 kilobyte banks. If a track takes up more than 8K, a bank will overflow. If you get this error and see a track that has a lot of data, try bankswitching it so the data can continue into a another bank.
<num1> (0 to 2)
The bank you are switching into.
<num2> (1 to 14)
The track you'd like to switch into the bank specified by <num1>.
This is simply a more detailed usage of #BANK-CHANGE <num>. If you're using the DPCM track E, don't use 0 as DPCM samples are stored in banks 1 and 2.
WARNING! OPTIONAL! This will replace the file "effect.h" you allegedly wrote for special instructions in assembly. Usually it is automatically generated for you per ppmck. It defines various macros so don't mess with it unless you know what you are doing.
Disables bankswitching. When used, you will specify a specific bank using #SETBANK.
<letter> (A to Z, a, or b)
The track you'd like to bankswitch.
<num>
The bank you'd like to use for the track specified with <letter>.
Just calling #SETBANK with no parameters will assume you want all tracks to be on bank 0.
<num1> (Usually 1 - 3)
The bank you'd like to move.
<num1> (Any number)
The destination bank. Note that if you pick a bank number higher than the previous filesize divided by 8192 plus one, it will pad unused banks with $FF.
Example
Bank Number | Address Range | Data |
---|---|---|
0 | $0000 - $1FFF | Song Data 1 |
1 | $2000 - $3FFF | Song Data 2 |
2 | $4000 - $5FFF | Song Data 3 |
Bank Number | Address Range | Data |
---|---|---|
0 | $0000 - $1FFF | Song Data 1 |
1 | $2000 - $3FFF | Padded with $FF |
2 | $4000 - $5FFF | Song Data 3 |
3 | $6000 - $7FFF | Song Data 2 |
When used, it will cause the DPCM track E to stop playing instantaneously when using a rest note "r", instead of letting the sample play out until its end or until retriggered by another note.
Unless otherwise noted, You can use up to 512 different values in the { } total. Any values placed after a "|" will be looped, but the first set of values will be gone through first. A case where this does not apply is a wavetable definition for the FDS (See @FM<num>).
<num> (0 - 127)
A number you set for your reference when calling throughout your MML.
Valid parameter values for tracks A, B, a, b: (0 - 3)
Valid parameter values for tracks M and N: (0 - 7)
This will define a duty cycle envelope macro.
@@<num> to call.
For tracks A, B, a, and b, you must use integers 0 to 3 in the { }. They correspond to these duty cycles:
<num> | Duty Cycle |
---|---|
0 | 12.5% |
1 | 25% |
2 | 50% |
3 | 75% |
Tracks M and N can use 8 different duty cycles. Use integers 0 through 8.
<num> | Duty Cycle |
---|---|
0 | 6.25% |
1 | 12.5% |
2 | 18.75% |
3 | 25% |
4 | 31.25% |
5 | 37.5% |
6 | 43.75% |
7 | 50% |
Example
<num> (0 - 127)
A number you set for your reference when calling throughout your MML.
Valid parameter values: (varies per track/chip)
Track Letter | Volume Range |
---|---|
A,B | 0 - 15 |
C | N/A |
D | 0 - 15 |
E | N/A |
F | 0 - 63 |
G - L | 0 - 15 |
M,N | 0 - 15 |
O | 0 - 63 |
P - W | 0 - 15 |
X - Z | 0 - 15 |
a,b | 0 - 15 |
This will define a volume envelope macro.
@v<num> to call.
Example
<num> (0 - 127)
A number you set for your reference when calling throughout your MML. T
Valid parameter values: (-127 - 126)
This will define a pitch envelope macro.
EP<num> to call.
ENOF to turn off.
The pitch macro is useable on most channels except DPCM playback track E. It's a different kind of envelope really, in that each value will begin a note slide and will continue sliding in the same fashion until either the next value is read in the envelope or it is turned back off in the track. Parameters in the negatives will slide down, positives slide up. The higher the parameter value the faster it will slide.
Example
<num> (0 - 127)
A number you set for your reference when calling throughout your MML.
This will define an arpeggio envelope macro.
EN<num> to call.
ENOF to turn off.
The arpeggio macro will rapidly progress through the series of notes. You can in fact use this on the Triangle track C as well. Sometimes this is used to simulate a chord. The parameters you put in will step the note up or down in semitones relative to the note called on the track.
Example
<num> (0 - 63)
A number you set for your reference when calling throughout your MML.
<param1> (0 - 255)
Delay until vibrato starts.
<param2> (1 - 255)
Vibrato speed.
<param3> (0 - 255)
Vibrato depth.
This will define a vibrato macro.
MP<num> to call.
MPOF to turn off.
You can modulate the pitch with a sort of sine wave with the vibrato macro. The vibrato macro is useable on most channels except DPCM track E. <param1> will dictate the time to wait after the note attack to start the vibrato. <param2> dictates how quickly to vibrate the pitch. Finally, <param3> dictates how far from the root note to swing.
Example
<num> (0 - 63)
The note [semitone] to map the sample to in the E track. Starts with note c0.
Because it maps to semitones, it's recommended to use values that correspond
to the "white keys" of a keyboard (e.g. DPCM0, DPCM2, DPCM4 for C0, D0 and E0
respectively, and so on). It's easier to type and read.
<str>
The file path to the DMC sample file relative to the location of
ppmck_e.exe.
<param1> (0 - 15)
Changes the pitch of the sample. 15 is normal. 0 is slow/low. Steps are in
semitones.
<param2> (0 - 4081)
OPTIONAL. The playback size of the sample. Specifying 0 or leaving this
parameter out will make playback size equal filesize, which is ok. If the
sample filesize is less than the size you specify here, the sample will be
padded to it at playback.
<param3> (0 - 15)
OPTIONAL. To use, you must specify param1 and 2 also. This param3 is the
Initial Delta Counter value, which is written to $4011 on note trigger.
$ff is recommended. If you put in $00, you may get clicks.
Using a non-zero value may change the volume of Triangle track C and and
Noise Track D.
<param4> (0 - 2)
OPTIONAL. To use, you must specify param1, 2 and 3 also. DMC Playback mode.
0 = normal playback mode.
1 = looped playback mode.
2 = normal playback mode with IRQ (not recommended).
Defines the path to and paramaters for a delta pulse modulation channel (DPCM) sample to be played on track E. The DPCM channel will play back 1-bit samples. Useful for drums and other short sound effects. There is no volume control, so adjust your volumes accordingly before conversion to the DMC format.
Example
<num> (0 - 127)
A number you set for your reference when calling throughout your MML.
Valid parameter values: (0 - 63)
Must use 64 two-digit parameters.
This will define a wavetable for the FDS track F. A 0 corresponds to the lowest sample value, 63 the highest. You can create some cool wave patterns with 64 samples.
@@<num> to change to this instrument on track F. Be careful not to get confused. This is the same way to change a duty cycle on a square wave channel. You can use a utility like MUMEM to help you make a wave visually, then copy a list of sample values to the clipboard. Check out these example definitions.
<num> (0 - 127)
A number you set for your reference when calling throughout your MML.
<param1> (0 - 31*)
The buffer number for the N106 chip. *The maximum buffer number varies with
the number of parameters (samples) you are specifying. Short samples (few
parameters specified) can use more buffers. See below.
Valid parameter values: (00 - 15) or you can use hex ($00 - $0F)
You can specify 4 - 32 samples, in increments of 4. You have to use certain buffers (<param1>) with certain numbers of specified samples. Check this chart:
Number of samples specified | Useable Buffers |
---|---|
4 | 00 - 32 |
8 | 00 - 15 |
12 | 00 - 09 |
16 | 00 - 07 |
20 | 00 - 05 |
24 | 00 - 04 |
28 | 00 - 03 |
32 | 00 - 03 |
Valid parameter values: ($00 - $FF)
Must use 8 parameters.
e.g. { $00 $00 $00 $00 $00 $00 $00 $00 }
This will define the custom user instrument for the VRC7.
@@<num> to call up instrument for the track.
Special procedures are necessary to call up the custom user-defined
instrument.
@@1 - @@15 are built-in instruments listed below.
@@17 - @@31 are the same built-in instruments as @@1 - @@15.
Keep in mind that calling @@0 in the actual track is reserved: it's a shortcut for the last VRC7 instrument you called within the track. If you want to call on your user-defined VRC7 instrument, first use @@64. To call your second user-defined instrument (OP1) then use @@65 and so on. Calling a different instrument will override the last called. You can use the @@0 shortcut on other VRC7 tracks as long as you've selected one of the instruments as stated above.
The eight parameters you enter correspond to the VRC7's 8 registers for controlling its FM synthesis. You can read more about this in Kevin Horton's VRC7 Documentation (See references).
Macro | Description |
---|---|
@@1 | Buzzy bell |
@@2 | Jazz electric guitar |
@@3 | Fretless bass |
@@4 | Tuba/trombone |
@@5 | Recorder/Clarinet |
@@6 | Strange chime |
@@7 | Trumpet |
@@8 | Violin |
@@9 | Soft bell |
@@10 | Music Box |
@@11 | Vibraphone |
@@12 | Violin 2 |
@@13 | Organ/Wurly |
@@14 | Chimey Trumpet |
@@15 | Acoustic Guitar |
Example
<num> (0 - 7)
A number you set for your reference when calling throughout your MML.
Valid parameter values: (0 - 7)
You must use 32 parameters.
This is a 32 parameter wavetable-like envelope for modulating FDS track F's pitch. It's similar to the @EP macro, but the values don't correspond semitone-for-semitone. Check out this chart which shows how the values change the pitch per frame.
Parameter value | Adjustment in Semitones |
---|---|
0 | 0 st |
1 | +1 st |
2 | +2 st |
3 | +4 st |
4 | Reset to original pitch |
5 | -4 st |
6 | -2 st |
7 | -1 st |
You will not "call" this effect with @MW or MW. This definition merely sets up the actual effect, @MH, which needs its own definition for delay, speed and depth. @MW is an envelope for modulating the FDS pitch and @MH is a macro for modulating the amount that @MW envelope modulates the pitch. It's like taking a xerox of a xerox. Crazy stuff happens.
Example
<num> (0 - 15)
A number you set for your reference when calling throughout your MML. Needs
to have a corresponding @MW<num> definition to put into action.
<param1> (0 - 255)
Delay until corresponding @MW<num> pitch envelope kicks in.
<param2> (0 - 4095)
Speed at which corresponding @MW<num> pitch envelop progresses.
<param3> (0 - 63)
Depth at which corresponding @MW<num> pitch envelop swings.
0 = 0% of any possible swing defined in the @MW envelop,
63 = 100% of any possible swing defined in the @MW envelop.
<param4> (0 - 7)
Chooses which @MW<num> envelop definition to use for the effect. To use
@MW0, which you defined beforehand, you just put a 0 here.
This will define a "vibrato" modulator for the chosen @MW envelope you want to use (param4). Once you have both an @MW<num> and this @MH<num> defined, you're ready to apply it to the FDS F track.
MH<num> to call.
MHOF to turn off the effect. You can reenable with the same
one or another if you like, of course.
Example
This will treat everything in between these as commenting and will not be compilied with the assembler.
Anything after a ";" in a line will be commented out.
Example
<note> (c, d, e, f, g, a or b)
The note of the scale you'd like to play.
<sharp/flat> (+ or -)
OPTIONAL. Insert a "+" here to raise the note one semitone. Insert a "-" to
lower one. This is essentially "sharp" and "flat".
<len> (1 - 99999999999)
The length of the note you'd like to play. This number is the length of a
measure divided by this number. For example, a c4 will play a
quarter note C.
If no length is given, a the length you specified with the l<len> command is assumed.
Example
<len> (1 - 99999999999)
The length of the note you'd like to rest. This number is the length of a
measure divided by this number. For example, a c4 will play a
quarter note C.
If no length is given, a the length you specified with the l<len> command is assumed.
Example
<num> (1 - 255)
The tempo of the track, not the entire song.
You'll use this after a track header to set its tempo. It won't set all tracks simulataneously unless you type every track letter you're using before this command. Strange? Check the example.
120 bpm is assumed if you don't specify a tempo.
Example
<len> (1 - 99999999999)
Default note length per measure (see entering notes).
<num> (1 - 99999999999)
Number of frames per measure.
This is a more specific tempo assignment. It follows the following
formula:
14400/<num>/<len>
It's essentially <len> notes every <num> frames.
Example
<len> (1 - 99999999999)
The default note length for the track you specify. Same rules as entering a
note.
Leaving this out will assume you want quarter notes.
Example
<num1> (1 - 8)
Divides lengths of entered notes by this number per the formula listed below.
Won't affect attack times.
<num2> (1 - 8)
OPTIONAL. Adds to divisor per the formula below. If not specified, 0
assumed.
<num1> / (8 + <num2>) = Quantized Note Length
This will quantize the length of notes played, but not when they are played.
q4 is assumed if you don't specify any quantization for a track.
Example
<num> (0 - 65535)
Number of frames to cut off of each note.
Ties two of the same notes together.
Example
Ties two of the same notes together, but you only need to tie lengths, note completely written out notes. Recommended over &.
Example
<num> (varies per track/chip)
Sets default octave for a track.
Track Letter | Octaves Range |
---|---|
A - C | 2 - 7 |
D | 0 and high notes of -1 for low rumbles |
E | 0 - 3 |
F | 0 - 6 |
G - L | 0 - 7 |
M,N | 0 - 8 |
O | 1 - 8 |
P - W | 0 - 8 |
X - Z | 0 - 8 |
a,b | 2 - 8 |
Example
This will raise the notes following one octave, unless you messed with #OCTAVE-REV. You can use more than one in a row.
Example
This will lower the notes following one octave, unless you messed with #OCTAVE-REV. You can use more than one in a row.
Example
<num> (0 - 99)
A specific note number, unlike entering letters you're entering in semitones
from c0.
<len> (0 - 99999999999)
The same note length you'd use with regular notation
A different way to enter notes instead of letters, per the following chart:
Command | Note |
---|---|
n0 | C0 |
n1 | C#0 |
n2 | D0 |
n3 | D#0 |
n4 | E0 |
n5 | F0 |
n6 | F#0 |
n7 | G0 |
n8 | G#0 |
n9 | A0 |
10 | A#0 |
n11 | B0 |
n12 | C1 |
n13 | C#1 |
n# | and so on... |
Remember each tracks octave restrictions.
Example
<num> (8 - 2034) ($0008 - $07f2)
Value to write to the frequency register directly.
<len> (0 - 99999999999)
The same note length you'd use with regular notation
This will let you choose a note by writing directly to the frequency register. Why you'd want to do this I don't know. You could get some pretty strange pitches by tinkering. But here is a chart showing you which notes correspond roughly. The higher the <num>, the lower the pitch. You can get higher frequencies. For example, to get C3, just divide C2's value $06AE by two. $06AE/2 = $0357.
<num> | Note |
---|---|
$07F2 | A1 |
$0780 | A#1 |
$0714 | B1 |
$06AE | C2 |
$064E | C#2 |
$05F4 | D2 |
$059E | D#2 |
$054E | E2 |
$0501 | F2 |
$04B9 | F#2 |
$0476 | G2 |
$0436 | G#2 |
$#### | ...and so on. |
The FDS behaves differently. As <num> increases, pitch increases. To get C3, you'd just divide C2's value by two: $0983/2 = $04C1 = C3.
<num> | Note |
---|---|
$0983 | C6 |
$0A14 | C#6 |
$0AAE | D6 |
$0B50 | D#6 |
$0bFD | E6 |
$0CB3 | F6 |
$0D74 | F#6 |
$0E41 | G6 |
$0F1A | G#6 |
$1000 | A6 |
$10F4 | A#6 |
$11F6 | B6 |
$#### | ...and so on. |
The N106 doesn't work with this method.
<num> (-99 - 99)
Transposes the notes that follow <num> semitones relative to the notes
entered (remember your default octave).
<num> (Varies per track/chip)
Value that sets the track volume, overriding any volume envelopes you have.
You can call back any envelope you want, of course. Remember each track's
volume restrictions:
Track Letter | Volume Range |
---|---|
A,B | 0 - 15 |
C | N/A |
D | 0 - 15 |
E | N/A |
F | 0 - 63 |
G - L | 0 - 15 |
M,N | 0 - 15 |
O | 0 - 63 |
P - W | 0 - 15 |
X - Z | 0 - 15 |
a,b | 0 - 15 |
Example
<num> (Varies per track/chip)
The number of levels you want to increase the volume by. Keep in mind tracks'
volume minimum and maximum (see @v or v commands).
You must specify a static volume with v<num> before using this.
Example
<num> (Varies per track/chip)
The number of levels you want to decrease the volume by. Keep in mind tracks'
volume minimum and maximum (see @v or v commands).
You must specify a static volume with v<num> before using this.
Example
<fade> (0 or 1)
0 fades FDS Track F's volume downward.
1 fades it upward.
<fade> (0 - 63)
The speed at which the FDS Hardware envelope changes.
Doesn't appear to be retriggered with each note. It'll just keep looping through, so call and turn off carefully.
EH<fade>,<num> calls,
EHOF turns off.
<num> (0 or 1)
The envelope to retrigger at the time quantization happens.
This will launch @v<num> volume envelope when the note is cut off by the quantization you specify. Instead of cutting the note off like quantization should, it will instead launch @v<num> at that moment. By choosing a different volume envelope here, you can in effect have two volume envelopes per note.
You can choose a new "first" envelope with another @v<num> command. Calling a static volume using v<num> will deactivate the effect until you call it again.
Example
<len> (0 - 99999999999)
How much you want to shift the following notes forward in time, in the same
terms as notes. Leaving this out will assume you want to use your
l<len> value.
This is a way to create echoes in your songs using two tracks, where one is using a lower volume. Check out this example for a nice bounce echo effect commonly used in many chiptunes.
You might want to use a rest command, because this command seems to be permanent. Consider using rests and thinking how much a track is ahead if you want to adjust the echo back to unison.
Using this effect andrests can shift your echo effect.
Example
This will set the loop position in the song. In some cases, tracks may not syncronize at certain tempos because of minute differences in track length. Bracket loops appear not to have this problem.
<num>
Will loop the notation inside the [ ] <num> times. A space saver. You
can nest loops within loops.
<num>
Will loop the notation before the first\ <num> times, then loops the
next section <num> times and so on. A real space saver. You can nest
loops within loops.
Example
<num> (1 - ?)
The number of delays.
This is some self-delay effect on a track. I've never gotten it to sound like I want, but it is there. To those trying to achieve this effect it is a space saver I suppose.
SD<num> calls.
SDOF turns off.
Example
Letter | Description | Commands (May be incomplete) |
---|---|---|
A,B | 2A03 Square | l#, o#, q#, @##, @v#, v# @@#, @#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
C | 2A03 Triangle | l#, o#, q#, EP#, EPOF, MP#, MPOF, k, K |
D | 2A03 Noise | l#, o#, @#, @V#, v#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
E | 2A03 DPCM | l#, o#, k, K |
F | FDS Wavetable | l#, o#, q#, @v#, v#, EN#, ENOF, EP#, EPOF, MP#, MPOF @@#, @FM#, @MW, @MH, MH#, MHOF, k, K |
G,H,I,J,K,L | VRC7 FM Synth | l#, o#, q#, @v#, v#, @@#, EN#, ENOF, EP#, EPOF, MP#, MPOF, @OP#, k, K |
M,N | VRC6 Square | l#, o#, q#, @v#, v#, @@#, @#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
O | VRC6 Sawtooth | l#, o#, q#, @v#, v#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
P,Q,R,S,T,U,V,W | N106 Wavetable | l#, o#, q#, @v#, v#, EN#, ENOF, k, K |
X,Y,Z | FME7 Multisound | l#, o#, q#, @v#, v#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
a,b | MMC5 Square | l#, o#, q#, @##, @v#, v# @@#, @#, EN#, ENOF, EP#, EPOF, MP#, MPOF, k, K |
With PPMCK release 9, multisong nsf creation is built-in! Which means this tutorial was obsolete! Ho ho! All it takes is a change in command-line arguments. Needless to say, this tutorial will be short.
Basically, when calling ppmckc, just use the -u argument for multisong nsf creation, like so.
This is all you need to change. If you want 5 songs, go right on ahead and put 5 in. If it blows up, it's not my fault! Then, as usual, call nesasm to create the rom. The headers ppmckc produces will contain the necessary data. Just make sure you keep your paths straight.
That's it! Whew!