Music Programming with PMD

Written by Noyemi K. and published on 07 April 2017

A handy cookbook and reference for programming sound with the PMD Music Macro Language!

Introduction to PMD The OPN Series Sound Chips Hello, dear reader! If you're looking through this guidebook, you have some interest in programming the YM2203 and YM2608 sound chips using PMD for NEC PCs. Perhaps you're a fan of Ys, Snatcher, Popful Mail, or even Touhou and would like to discover and make the best use of a world of charming FM sound. To create music for this sound chip series for NEC FM boards, first you need to know the basics about these chips. The YM2203

The YM2203 is the first OPN series FM chip from Yamaha. It is monoaural with 3 4-operator FM channels, and includes an onboard YM2149F as its Sound Source Generator, conferring an additional 3 square wave PSG (Programmable Sound Generator). This gives the programmer 6 total sound channels to work with, with the FM channels being markedly more complex to program instruments for. The YM2608

The YM2608 or OPNA, is an expansion of the YM2203 chip—all the FM channels are stereo pannable thanks to an internal stereo DAC. Otherwise, programming FM tones is exactly the same. There exists a few other features that makes the OPNA a bit special though:

The number of FM channels available is increased by 3, for a total of 6 stereo FM channels.

An ADPCM channel is included, allowing the playback of small ADPCM samples

An additional sound source is supplied in the form of an ADPCM ROM that has stored drum samples, referred to as the Rhythm Sound Source for more rhythm options the programmer can use to free up FM channels

for more rhythm options the programmer can use to free up FM channels The 86 SoundBoard for PC-9801 includes a different PCM sound source, allowing for a somewhat more limited PCM channel in place of the ADPCM sound source on SpeakBoard/YM2608 Now that you've gotten to know the chips a little bit, dear reader, you have some context for actually making music with them. This guidebook will be broken up into multiple chapters for covering the various components of programming SSG envelopes, FM tone data, and special features (including PMD preprocessor macros, LFOs, and more). The Basic PMD Program A PMD MML program consists of several key parts, which are in no particular order besides the preprocessor and metadata coming first. They consist of:

The preprocessor macros and metadata FM tone data Macro definitions Song sequence data (where macros are expanded by the compiler and SSG envelopes can be specified in-line) A PMD program doesn't necessarily need all components of each piece specified, and macro definitions can be omitted entirely, though they confer various quality-of-life advantages that the savvy music programmer can't afford to ignore for long. More on that later. The metadata of the song program includes the composer, arranger, the output filetype (.M and .M2 are the focus of this guidebook, though PMD can certainly output programs for a variety of sound boards), optional memos and notes, detune and LFO parameters, tempo, and song title. LFOs can be specified on their own in-line, as can tempo. PMD players will generally take and display metadata and some can display the notes as well. FM tone data are special instrument definition sections, where all of the FM sound generation parameters are specified and organized into instruments, accessed by FM channels using the @ command. We'll discover those in more detail later. Macros are handy specially defined segments, which wrap up a piece of song sequence data to potentially be transplanted someplace else without having to copypaste the code, thus saving space and saving the programmer a massive headache if the song needs to be "debugged" in various parts where the same song sequence plays. It's also useful for defining percussion parts, since emulating percussion through the soundchip's various possible commands generally takes more than a single note. Song sequence data are the actual song sequence, delineated by channel declarations (ABCDEF 123) and containing notes, rests, macro commands, envelope switches, octave jumps, loops, and more. Most of the time spent writing a PMD program is generally spend creating beautiful song data. Below is an example of some song data you might have in your program: A [cde>f4.]2 B @1v12 abfdg<f8 ;The SSG part is below! GH v13E1,-3,6,1 [def]3 I [!k!c!s8]3 !kr!s!s Don't worry about what each of those commands above means—yet. And certainly, if you tried to paste this into a PMD MML file, what would compile would sound fairly awful because it just represents a gibberish assemblage of actual PMD commands as you'd see them in a program. The most common ones, of course. In the following section, we'll discuss the basic commands you'll be using most often with PMD, first with defining metadata and then with basic song sequence commands.

Basic PMD Programming Defining Metadata and Preprocessor Instructions

In the introductory chapter, we discussed the different parts that make up a basic PMD program. The first of which, is the defining of metadata and preprocessor instructions for the PMD compiler. There are a lot of these different instructions and metadata components, but being that this is a cookbook for getting music playing on an OPN series chip through PMD, we'll only cover the basics for now. Let's see an example! ;======================== ; My PMD Metadata template ;======================== #Title The Programmer - Investigation #Composer Noyemi Karlaite #Arranger Noyemi Karlaite #Memo OPN Version #Detune Extend #Filename .M2 In the above sequence, the metadata for the song HDB.M2 is defined. A semicolon denotes a comment line, which can be useful for separating song parts or PMD code components. I'll explain each of the other components in a list:

#Title is for defining the title of a song as it would appear in any PMD playback engine, such as FMPMD for windows

is for defining the title of a song as it would appear in any PMD playback engine, such as FMPMD for windows #Composer and #Arranger detail who composed the song and who is responsible for that arrangement, respectively

and detail who composed the song and who is responsible for that arrangement, respectively #Memo is a note field that can be accessed by more advanced PMD players, most likely used for liner notes on music disks

is a note field that can be accessed by more advanced PMD players, most likely used for liner notes on music disks #Detune specifies that there is an extended range of detune from the default, which is by semitone and not cents

specifies that there is an extended range of detune from the default, which is by semitone and not cents #Filename specifies the output format that the compiler will render from your code. .M, .M2, .OVI, and others are available, though I recommend .M2 I'd recommend following a standard metadata definition section template across all your songs, so it's easy to correct mistakes without having to also correct layout. There exists a few additional instructions that you may find useful that I will cover in later chapters on advanced programming. Song Sequence Data, Macros, and Channel Control The meat of any PMD sound instruction program is the song sequence data—even beyond defining FM instruments, the bulk of your time will be spent wrangling OPN or OPNA's channels and making them sing. Let's begin with a small example: ;=============== ; Song Sequence ;=============== ABCGHI t52 A @0 v14 o4 l16 A L A o3 [d8ar >dr8. cr8<g r8c8 d8ar >d8c8gd8f8<c8r]2 BC @1v10 l16 C r16 D-3 BC L BC o6 [r1]8 GHI l16 H r GHI L GH o5 [r1]8 GH f1.r2 d+1d2.<g4 o4 GH [d8ar >dr8. cr8<g r8c8 d8ar >d8c8gd8f8<c8r]2 ;=============== ; SSG Rhythm Sequence ;=============== I [!kr!o8 !sr!c!c !c!s!o8 !sr!kr]2 [!k!c!o!c !s!c!c!c !c!s!o8 !s!c!kr]2 In the above example, you have bits and pieces of a full song. This is not as pleasing to the ear as it could be, but it serves as a useful example for explaining the different commands in play. Defining Channels: ABCDEF are FM channels, and GHI are the SSG channels. In the previous example, it is meant to be OPN only, so FM4-FM6 are unused. All commands that follow one or more channel declarations will be performed in sequence and by all channels declared, so for instance, the line here will actually be played by SSG1 and SSG2:

GH [d8ar >dr8. cr8<g r8c8 d8ar >d8c8gd8f8<c8r]2

If the code above was copied into a file, you'll note that SSG2 plays a short moment after SSG1, making an echo of sorts. It's a very common technique to have multiple channels playing the same part, but with some detune or delay for a little depth. Essentially, beginning a line with A, B, etc. or any combination will tell the compiler "Hey! Channel(s) X should all play this part after they've done whatever they were just doing." Notes and Rests: Note commands are quite simple, and any modifiers that notes can use will also apply to rests. Notes are lowercase "abcdefg" and rests are "r". Putting a number (such as 16, 8, 4, 2, 1) after a note or rest specifies its length, corresponding to a ratio of a whole note (1). So, r4 is a quarter rest, a8 an eighth note playing a, and so on. A dotted note (that is, a note extended by half its defined length for some nice intermediate lengths) is represented by a period (.) following length (ex: r1. will be a whole rest and a half) Octaves, Sharp, and Flat: Octaves are declared as a lowercase "o" followed by a number. o3 a8 is an eighth note playing on the third octave. Sharp and flat are represented by a + or - following the tone specified, and before its length. So, a+16 is a 16th note playing A sharp.

You can move between octaves without specifying an octave in the song sequence data using greater than/less than signs (). > moves up an octave from the previous note, and < moves down an octave. You can picture a sequence such as "abc>a bc < resting on top of a hill in the song, if that helps! Miscellany: In the example of song sequence data earlier in this chapter, you might have noticed some weird commands that appear to have nothing to do with the score, followed by numbers. There isn't a whole lot to these, so I'll just list them:

t is the Tempo definition command. It uses an integer and appears to be half the compiled song's actual BPM.

is the Tempo definition command. It uses an integer and appears to be half the compiled song's actual BPM. v is the Volume definition command, which ranges from 0-15 for 16 discrete levels, unless volume is extended in the preprocessor or a different volume command is used.

is the Volume definition command, which ranges from 0-15 for 16 discrete levels, unless volume is extended in the preprocessor or a different volume command is used. l is the default Length of a note, when a note declaration isn't followed by a length. So "l16 r" would be a 16th rest.

is the default Length of a note, when a note declaration isn't followed by a length. So "l16 r" would be a 16th rest. @ is the timbre selection for FM channels. When FM timbres are defined in the song, they are assigned an ID by the programmer and @ simply points to the ID of the timbre you'd like to use. So, say you have a bass instrument as @1... well, declaring @1 in an FM channel part will switch to that timbre!

is the timbre selection for FM channels. When FM timbres are defined in the song, they are assigned an ID by the programmer and @ simply points to the ID of the timbre you'd like to use. So, say you have a bass instrument as @1... well, declaring @1 in an FM channel part will switch to that timbre! L is the Loop macro. Everything after L in a channel sequence will loop infinitely. If you'd like to set up echo channels, it's best to have the rest declared in the song sequence in a little "setup" declaration before the loop, as I have above. Otherwise, the rest will keep applying and the echo will eventually be thrown completely off timing!

is the Loop macro. Everything after L in a channel sequence will loop infinitely. If you'd like to set up echo channels, it's best to have the rest declared in the song sequence in a little "setup" declaration before the loop, as I have above. Otherwise, the rest will keep applying and the echo will eventually be thrown completely off timing! [ and ] , followed by an integer, encloses a looped section. So, [ abcdef ] 4 will loop the sequence "abcdef" 4 times.

, followed by an integer, encloses a looped section. So, 4 will loop the sequence "abcdef" 4 times. : escapes from the looped section as noted above. If you have a sequence that loops 4 times, you will escape wherever this macro is placed on the 4th iteration. Now you're probably wondering. "Well I've got all that, but do I have to type it in every time I want a section to repeat?" Well, you can do that. But you can also enclose sequences of commonly used commands, or song sections, into macros. There's no hard-and-fast rule about keeping them tidy, but generally I follow the convention of using lowercase letters for single-note or command switch macros for automation, and uppercase letters for song sections. To define a macro, just use an exclamation point on its own line ( ! ) followed immediately by an upper or lowercase letter. Then, hit space or tab and write in the song sequence you'd like to enclose in the macro, like so: !B abcd bcd>e< ;Lead part B !k @10o3v15{c<c} ;Kick drum Tips and Tricks: You will often be using rhythm lines, because drums are important. Instead of defining an instrument switch in the song sequence, simply use a macro to enclose these instrument switches and notes, but leave the length for later.

In PMD, you can put a length after a macro if it ends on a note; this will affect the length of the note if it's a one-note macro being expanded. This is, again, useful for drums.

You can enclose macros within macros, because the compiler expands them in the driver instruction file when it does its work. This is useful for having a rhythm line base which is ended on bar 4 or bar 8 with a nice fill that you define.

Using the SSG Channels Envelopes and Quantization step Controlling the SSG is pretty simple. If no envelope is defined, the note commands will simply trigger the SSG and generate a square wave for the duration of the note time. G o5 a8 ;This causes SSG1 to generate an 8th note at A5... a "blocky" timbre! If we want to go into more detailed control of the SSG channels (G, H, and I) we will do so with the help of SSG Envelopes. This is not to be confused with the SSG-EG feature which works with FM channels, mind. Envelopes take the form of a small array of parameters, either 4 or 5, which controls the volume of the channel during a note event. You can store an envelope for the channel(s) to play by defining it in the song sequence. I like to do it on its own line, before parts are switched or, in most cases, before the channel begins to play. ;======================== ;Example of some Envelopes GH v15 E1,-1,2,1 ;short, fast attack envelope for G and H I v13 E3,2,7,3 ;for I here, we have an envelope with a longer curve and slower attack It's fairly simple! After calling the envelope "constructor" with E, you supply a sequence of values (Attack, Decay, Sustain, Release). This will control the volume of the channel, and lower numbers means a faster time. But how do we control the envelopes over a larger frame of reference without constructing new ones? Easy, by changing the envelope quantization step. Change envelope quantization in-line during the song sequence with the "q" command, followed by a note length to quantize to. q16 for 16th note quantization of the SSG envelope, q8 for eighth note, and so on! Extra Controls For you control freaks out there, you can control your SSG channels in even more ways with two additional commands. D and P are commands you might have seen in some of my MML source files, though you may not have seen P actually used for the SSG (as for FM, the lowercase p is the pan command!) P selects a tone generation mode for your SSG. Normally, mode 1 is used ( P1 ). This is the regular square wave generator mode. Mode 2 will generate pseudorandom noise, and mode 3 will generate periodic noise, which is noise with a subtle tonality due to its periodicity. These are useful to know, as they can allow you to create SSG drumkits for use when you don't have any free FM channels to work with, or you simply like the style of SSG rhythm. ;=============== ; SSG Rhythm Macros ;=============== !k P3 q8 v15E31,-1,0,1 o3{c<c} !s P3 q16 v15E31,2,2,1 o4{c<c} !c P2 v14E31,4,4,0 q16 o4c !o P3 v13E0,8,4,8 q4 o8g+



D is a simple but important command, as it does detune. This is actually the reason some SSG parts in my works have the kind of "bright" quality they do—two SSG channels playing the same note detuned, or echoed with slight detune, really adds some extra dimension to the sound. If you have a channel free to do it, try it out! GH v13E12,2,4,1 GHI l16 H r D2 GHI L GH o5 GH f1.r2 d+1d2.<g4 o4 [d8ar >dr8. cr8<g r8c8 d8ar >d8c8gd8f8<c8r]2 GH o5 [d8<d8 g8a>c r4 r<g>c<a >dr<d8 g8ar >g8rf rdc8]2 In the next part I'll talk about creating FM tones to use your FM channels on!