PPG Wave synthesizer range is undoubtedly very unique. It is not very popular, but I’ve seen some people looking for its wavetables or waveform samples on different forums. Recently, I managed to extract those from EPROM dumps I found online, so I just wanted to share my findings with all of you, who may be looking for it.

Getting the EPROM image

The first challenge I was facing was getting the EPROM images. Fortunately, that’s not as hard as I initially thought, and there are actually two ways of obtaining those.

The easy way means downloading the image from website such as this one. I got PPG EVU version 1.0.zip and merged all the files inside into one called eprom.bin.

The hard way relies on extracting resources from VST DLL file with a proper tool (check NirSoft ResourcesExtract). If you’re lucky, you might get something_EPROM.bin in your output.

I recommend you stick with the easy way.

Inspecting the EPROM contents

This may sound silly, but believe it or not - Audacity is actually great for inspecting binary files, especially when you’re looking for waveforms. You can also see (and hear) if parts of binary file are machine code or data relatively well. Anyway, that’s where I started.

After we import the EPROM file as raw data into Audacity (File -> Import -> Raw data (PCM 8-bit, unsigned, little-endian)) and zoom in on the beginning of the track, we see this interesting pattern:

The first part certainly isn’t machine code, but we will come back to that. Now, let’s focus on the part that follows right after it. You can immediately say that it looks like waveforms we’re looking for, but where exactly do they start and end?

Fortunately, I’ve found description of PPG’s waveforms and wavetables. It’s available here, and it’s going to help us a lot. It contains tables showing us how all the waveforms look. For example, these are the waveforms 0 to 7:

At this point, it’s very easy to see the matching part. Just remember that the PPG Wave only stores the first half of a cycle of each waveform. If you position the cursor just right, you will see that the waveform table starts exactly at sample (byte) 768 - a lovely round number.

Knowing that each waveform consists of 64 samples, and there are 256 waveforms total, we can determine that the waveform table is 16KiB long and ends at byte 17152. You might, however, notice that waveform data seems to go past that point. I don’t know the exact layout of EPROM and can’t say if that part is being used at all. Fortunately, the waveform description implies that the first 256 waves are all we need.

On Linux, the data can be extracted to another file with dd command: dd if=eprom.bin of=waveforms.bin skip=768 count=16384 bs=1

I converted the data to a C array, so it can be accessed easily in code. It’s available as a Gist here (be careful, it’s quite long). You can also convert it into separate WAV files, but I haven’t tried that yet.

Reverse engineering the wavetable format

It’s great that we managed to extract the waveform data, but unfortunately it’s not very useful on its own. We also need the wavetable data in order to know how to combine the waveforms properly.

Earlier, I said that the first 768 bytes of EPROM look like data. That’s why I decided to examine them more closely - that was a good decision. Please take a look at this excerpt of xxd eprom.bin:

00000000: 0065 0045 0846 1047 1848 2049 284a 304b  .e.E.F.G.H I(J0K
00000010: 3c01 4d00 4e08 4f10 5018 5120 5228 5330  <.M.N.O.P.Q R(S0
00000020: 543c 0265 002f 1e83 2684 2e8d 368e 3c03  T<.e./..&...6.<.
00000030: 6500 5e08 5f10 6018 6120 6228 643c 0402  e.^._.`.a b(d<..
00000040: 0001 1800 3001 3c05 8300 8408 8d10 8e18  ....0.<.........

Initially, let’s treat each byte as waveform index and try to work out if it matches layout of any of the wavetables.

And here’s the wavetable 0:

Keep in mind, that the waveforms with red text over them are results of interpolation and are not stored anywhere in the waveform bank. That means, here only waves 0, 8, 16 (and so on) are stored in the waveform table. I’m going to call them ‘key’ waveforms for the lack of a better name.

Anyway, did you notice that first ‘key’ waveform in the wavetable 0 is exactly the same as wave 65h? The second one looks exactly like waveform 45h. That gives us an important clue. It seems that every second byte denotes waveform index. Moreover, you can notice that each index byte is followed by wave position (slot) in the wavetable.

This pattern extends and we eventually get to seemingly the last ‘key’ wave in wavetable 0 - wave 4Ah on position 30h.

Wavetable 0

The interpolated waveforms, however, go on further - up to the slot 3Bh, after which the set of waveforms common for all wavetables follows. Examining the next two bytes of hexdump reveals that in reality there’s one more ‘key’ wave stored - 4Bh in the slot 3Ch. It seems to be used only to allow further interpolation, because it’s overridden with the always present triangle wave.

The next byte is 01h and it’s followed by information about wavetable 1. That would mean that each wavetable data entry is preceded with the wavetable number. While this pattern works for most of the wavetables, it’s broken for some. Anyway, this information is not really useful and this byte can apparently be ignored.

It’s also important to notice that wavetable data length is not constant. Each wavetable could have any (up to 64 or 61) amount of ‘key’ waves. End of each wavetable entry seems to be marked with wave position (slot number) equal to 3Ch (or greater?).

In conclusion, we can express the wavetable data format with this horrible mixture of pseudocode and C:

1b: ignore (Wavetable number?)
{
	1b: waveform
    1b: slot
} while (slot < 0x3c)

Extracting the wavetable data

I decided to write a little C program to extract the wavetable data from the EPROM and output it in a more human-readable format.

These are my results.

Keep in mind, that it only makes sense to run this program on the first 768b of the EPROM image. Even then it extracts more data than it should, what leads me to think that there’s more configuration data preceding the waveforms (program data perhaps?).

The PDF file with wavetable description tells us that the last ‘real’ wavetable is the wavetable 27, because 28 and 29 are entirely calculated by the synthesizer. There’s however a thing called ‘Upper Wavetable’ and it seems to reside in EPROM right after wavetable 27, technically making it the physical wavetable 28.

Summary

I hope you found this ‘article’ useful or at least interesting. Also, if you have any thoughts, please let me know in the comments section or email me.

Here are some important links:

Thank you for reading :)

A follow-up post