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.
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