Structures will be described much like C structures,
 but with explicit padding, and with some things C structures can't have like variable-sized arrays.
All values are little-endian.

Terminology:
 Beat: A specific column independent of track.
 Frame: A specific column on a specific track.

```
struct file {
    char magic[3]; // "PMD" in ASCII
    char writable; // q3hardcore says this is a "writable" flag. It's usually set to 0x80.
    int dataOffset; // File offset of track1[0].
    int beatTimeMs; // The amount of time between beats, in milliseconds.
    int loopStart; // Loop start beat.
    int loopEnd; // Loop end beat.
    int songLength; // The length of the song in beats.
    struct waveTrackHeader waveTrack[3]; // Wave tracks.
    int drumTrackVolume; // Volume of the drum track. Note this doesn't strictly match up to wave track volumes.
    char padding[dataOffset - 0x418]; // Some hypothetical amount of padding. The 0x418 here is the size of everything preceding this.
    // You can also think of this as: struct frame data[4 * songLength];
    // Which is the form used internally by PiyoPiyoJ.
    struct frame track1[songLength]; // Wave track 1's frames
    struct frame track2[songLength]; // Wave track 2's frames
    struct frame track3[songLength]; // Wave track 3's frames
    struct frame trackP[songLength]; // Wave track 4's frames
};

struct waveTrack {
    char octave;
    char icon;
    char unknown[2]; // Presumably padding
    int envLength; // Envelope length. This appears to be measured in 22050hz samples.
    int volume; // Volume. Calibration on this is hard, so don't stress too much if you have to fudge things to get it 'right'-ish.
    char unknown[8];
    char waveForm[256]; // 8-bit signed waveform.
    char envelope[64]; // Envelope. This is 8-bit signed but PiyoPiyo never lets you put in values below 0.
};

struct frame {
    char data[3]; // Bitfield of notes. Lowest bit/first byte is the lowest pitch.
    char pan; // 0 for no change, else 1 (left) to 7 (right). 4 is centre.
};
```

The calculation you need to turn the note position and octave into the relative pitch is:
First, pos must be 0-based, with higher pitches being higher values.
Then add the octave * 12 to pos.

Run it through this:
double relativePitch=8363d*Math.pow(2.0d,pos/12.0d)/sample_rate;
And you have the relative pitch(where 1.0d is to play at normal speed,2.0d is to play at double speed,0.5d is to play at half speed, you get the idea)!
(Thanks to liborganya, which is where I got that calculation from)

If you want more detail, PiyoPiyoJ's source has been shipped with this distribution.

 -- 20kdc

