Jacob Vosmaer's blog

Dynacord ADS Disk Images

2024-09-14

I reverse-engineered some 1980's floppy disk images to extract WAV files from them. The code is in this repo.

The Dynacord ADS sampler

In the 1980s, German pro audio equipment manufacturer Dynacord also made electronic musical instruments. The company still exists but they seem to have gotten out of the musical instrument business.

I got curious about their 1986 ADD One digital drum brain and I wanted to find the audio samples used inside that machine. This led me to a German sequencer.de synthesizer forum thread where somebody shared disk images of a sound library for the 1990 Dynacord ADS. It appears the ADS is a lower-cost successor to the ADD One that uses some of the same samples.

Great, so that meant I could hear and use the samples that are probably also in the ADD One.

A mystery filesystem

The only problem was that the ADS seems to use a custom filesystem (they are not DOS floppies). People on sequencer.de mentioned you could play back the disk images themselves as raw 16-bit mono big-endian PCM audio data but then you hear lots of digital glitches in between the audio: the floppies contain data and audio after all.

non-audio data followed by audio Note how the non-audio data on the left is not centered around the zero line: it has a DC offset. The audio data on the right is centered around 0.

I could edit the digital "audio" by hand to remove the glitches but that seemed silly. Wouldn't it be a much better use of my time to reverse-engineer enough of the floppy filesystem to allow me to separate the audio from the non-audio data?

(To be honest, I like these sort of challenges way too much, and I did not try very hard to find out if more is known about the filesystem of these floppies. I just threw myself on the puzzle.)

Hexdumps

After staring at hex dumps for a long time (hexdump -C floppy.img | less) I finally started to see some patterns.

They all start like this, which is interesting but not helpful.


00000000  30 2e 30 30 20 44 79 6e  61 63 6f 72 64 20 41 64  |0.00 Dynacord Ad|
00000010  76 61 6e 63 65 64 20 44  69 67 74 61 6c 20 53 61  |vanced Digtal Sa|
00000020  6d 70 6c 65 72 2e 20 44  69 73 6b 20 6f 70 65 72  |mpler. Disk oper|
00000030  61 74 69 6e 67 20 73 79  73 74 65 6d 20 28 63 29  |ating system (c)|
00000040  20 31 39 38 38 20 46 61  73 74 20 46 6f 72 77 61  | 1988 Fast Forwa|
00000050  72 64 20 44 65 73 69 67  6e 73 20 70 72 6f 67 72  |rd Designs progr|
00000060  61 6d 20 62 79 20 4b 65  6c 76 69 6e 20 4d 63 4b  |am by Kelvin McK|
00000070  69 73 69 63 20 20 20 20  20 20 20 20 20 20 20 20  |isic            |
00000080  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |

This is hexdump -C output. Each line shows 16 bytes as hexadecimal, with an ASCII interpratation on the right hand side. The numbers on the left are file offsets in hexadecimal (so 00000010 means that that line starts 0x10=16 bytes into the file).

A bit further down you see a table of contents:


000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000200  43 48 52 42 45 4c 4c 41  4c 4c 01 00 00 00 01 00  |CHRBELLALL......|
00000210  43 48 52 42 45 4c 4c 50  41 4e 02 00 00 00 01 00  |CHRBELLPAN......|
00000220  42 45 4c 50 41 4e 46 4c  41 4e 03 00 00 00 01 00  |BELPANFLAN......|
00000230  42 45 4c 4c 20 53 49 4e  47 4c 04 00 00 00 01 00  |BELL SINGL......|
00000240  42 4c 41 4e 4b 20 4d 49  58 20 0a 00 00 00 01 00  |BLANK MIX ......|
00000250  20 48 4d 20 32 27 38 39  20 20 2d 00 00 00 01 00  | HM 2'89  -.....|
00000260  54 4f 4e 45 20 53 4f 55  4e 44 00 01 00 00 02 00  |TONE SOUND......|
00000270  43 48 52 42 45 4c 4c 41  4c 44 01 01 00 00 02 00  |CHRBELLALD......|
00000280  42 45 4c 4c 20 4c 4f 57  4e 44 02 01 00 00 02 00  |BELL LOWND......|
00000290  42 45 4c 4c 20 4c 4f 55  4e 44 03 01 00 00 02 00  |BELL LOUND......|
000002a0  42 45 4c 4c 20 4c 4f 4d  4e 44 04 01 00 00 02 00  |BELL LOMND......|
000002b0  42 45 4c 4c 20 4d 49 44  4e 44 05 01 00 00 02 00  |BELL MIDND......|
000002c0  42 45 4c 4c 20 48 49 4d  4e 44 06 01 00 00 02 00  |BELL HIMND......|
000002d0  42 45 4c 4c 20 48 49 47  4e 44 07 01 00 00 02 00  |BELL HIGND......|
000002e0  43 48 52 42 45 4c 50 41  4c 44 08 01 00 00 02 00  |CHRBELPALD......|
000002f0  42 45 4c 4c 20 4c 4f 53  4e 44 09 01 00 00 02 00  |BELL LOSND......|
00000300  c3 48 2e 42 45 4c 4c 20  4c 4f 63 03 00 01 ce 00  |.H.BELL LOc.....|
00000310  c3 48 2e 42 45 4c 4c 20  4c 4d 63 04 00 02 f9 00  |.H.BELL LMc.....|
00000320  c3 48 2e 42 45 4c 4c 20  4d 49 63 05 00 02 f7 00  |.H.BELL MIc.....|
00000330  c3 48 2e 42 45 4c 4c 20  48 4d 63 06 00 02 ee 00  |.H.BELL HMc.....|
00000340  c3 48 2e 42 45 4c 4c 20  48 49 63 07 00 01 a3 00  |.H.BELL HIc.....|
00000350  20 20 20 20 20 20 20 20  20 20 7f ff 00 00 00 00  |          ......|

Luckily for me, the entries in the table of contents are 16 bytes long and they start at a multiple of 16 bytes into the file so they align perfectly with the hexdump -C output format. This appears to be a variable length array of 16-byte blocks with a special terminator block (the last line in the example above). Each block starts with 10 ASCII characters which appear to be a filename, followed by 6 bytes of additional data. Of those 6 bytes the third and sixth are always 0x00 so you could say there are only 4 bytes of additional data. This pattern also applied to the other floppy images.

There is something funny going on with the last 5 entries (not counting the terminator). Their first character 0xc3 is not an ASCII symbol. This floppy contains church bell sounds so you'd expect a 'C' character which is 0x43. For whatever reason, the most significant bit is set to 1 resulting in 0xc3. If we look at the non-name data at the end of each block we also see that these 5 blocks have 0x63 right after the name each time.

When you listen to the entire floppy image then apart from the glitches you can hear 5 different church bell sounds. So it seems likely that these 5 entries with their first name character mangled and 0x63 after the name are the audio samples?

I will spare you the details but I eventually figured out that this is indeed a list of the files on the floppy. The first file is always at address 0x2e00. The 14th and 15th byte in the table of contents entry are the file size in 512-byte blocks. So in the example above, the file CHRBELLALL starts at 0x2e00 and is 1*512=512 bytes long. The second file CHRBELLPAN starts at 0x2e00 + 512 = 0x3000 and is also 512 bytes long. The last file \xc3H.BELL HI starts at 0x15ba00 and is 0x1a3 * 512 = 214528 bytes long. To get the offset of a file in the table of contents you need to first add up the lengths of all the files that come before it.

Writing the utilities

As I was figuring out this structure, the first thing I did was write a program that could read and parse the table of contents. I was manually verifying the results by looking in my audio editor and by playing fragments of the image extracted with dd. For example to play that last sample I would run:


dd bs=0x15ba00 skip=1 if=floppy.img | play -c1 -B -ts16 -r44100 -

This uses play from SoX to send the raw PCM data to my sound card.

Once this table-of-contents printer called toc was working well enough it produced output like this:


$ ./toc < floppy.img 
mix CHRBELLALL, p[10:15]=01 00 00 00 01, offset=00002e00, len=512
mix CHRBELLPAN, p[10:15]=02 00 00 00 01, offset=00003000, len=512
mix BELPANFLAN, p[10:15]=03 00 00 00 01, offset=00003200, len=512
mix BELL SINGL, p[10:15]=04 00 00 00 01, offset=00003400, len=512
mix BLANK MIX , p[10:15]=0a 00 00 00 01, offset=00003600, len=512
mix  HM 2'89  , p[10:15]=2d 00 00 00 01, offset=00003800, len=512
snd TONE SOUND, p[10:15]=00 01 00 00 02, offset=00003a00, len=1024
snd CHRBELLALD, p[10:15]=01 01 00 00 02, offset=00003e00, len=1024
snd BELL LOWND, p[10:15]=02 01 00 00 02, offset=00004200, len=1024
snd BELL LOUND, p[10:15]=03 01 00 00 02, offset=00004600, len=1024
snd BELL LOMND, p[10:15]=04 01 00 00 02, offset=00004a00, len=1024
snd BELL MIDND, p[10:15]=05 01 00 00 02, offset=00004e00, len=1024
snd BELL HIMND, p[10:15]=06 01 00 00 02, offset=00005200, len=1024
snd BELL HIGND, p[10:15]=07 01 00 00 02, offset=00005600, len=1024
snd CHRBELPALD, p[10:15]=08 01 00 00 02, offset=00005a00, len=1024
snd BELL LOSND, p[10:15]=09 01 00 00 02, offset=00005e00, len=1024
sam CH.BELL LO, p[10:15]=63 03 00 01 ce, offset=00006200, len=236544
sam CH.BELL LM, p[10:15]=63 04 00 02 f9, offset=0003fe00, len=389632
sam CH.BELL MI, p[10:15]=63 05 00 02 f7, offset=0009f000, len=388608
sam CH.BELL HM, p[10:15]=63 06 00 02 ee, offset=000fde00, len=384000
sam CH.BELL HI, p[10:15]=63 07 00 01 a3, offset=0015ba00, len=214528

At this stage I had figured out that the floppies contain 3 types of file: "mix", "sound" and "sample". I still don't know how to parse the mixes and sounds but I only cared about the samples for now.

The next step was to write a program that could extract the samples. To begin with I split toc.c into two parts: adsimg.c to parse the table of contents and toc.c to print it.

The splitting program is in split.c. It took me a while to figure out that each sample has 512 bytes of header and trailer data that is not part of the audio. (The header and trailer look like audio most of the time.) Another fun complication was the fact that apparently the ADS can split sample files across multiple floppies. This makes sense because the ADS has more than a floppy's worth of RAM. I managed to sort this out in split.c so that it merges samples that cross floppy boundaries back into a single WAV file.

And of course, because I want to do everything myself, I wrote my own function that turns the raw PCM data into WAV files. This is all part of the learning and the fun for me.

Conclusion

The floppy image collection from the forum contains 120 images. I did some Bash scripting to run my split program on each of them. The end result is over 1300 WAV files. As silly as it seems to have spent at least 2 days staring at hexdumps and writing my utilities, it was more fun to do that than to manually select and save 1300 audio file regions in an audio editor!

Addendum 2025-02-11

I was wrong about each audio file having a 512-byte trailer. My audio extractor split was truncating each file. You can read more about how the length offset is stored here.

Tags: music dynacord-ads

IndexContact