Jacob Vosmaer's blog

Converting the AdventureKid waveforms to Yamaha TX16W format

2025-02-22

I converted the audio sample files in a 2010 sample collection (the AdventureKid waveforms) to work with a 1988 sampler (the Yamaha TX16W with Typhoon OS).

You can download the latest converted files here.

After I initially published this post I got some helpful feedback that made me realize I could do a better job. I have mostly left the original post intact and I will use these notes in block quotes to comment about things I learned later.

The AdventureKid waveforms

The AdventureKid AKWF sample collection consists of over 4000 audio samples of single cycle waveforms. Most digital samplers are able to loop audio. The original reason for this is to mask how small the underlying audio samples are, but you can also make creative use of the looping facility. If you loop a very small fragment of audio then the sample playback engine becomes a digital oscillator. Because most samplers also have amplitude envelopes, filters, LFO's etc. you can then use your sampler as a synthesizer.

Making single cycle waveforms is finnicky. If you're not careful you get discontinuities (amplitude jumps) in the waveform which sound like unpleasant high-pitched buzzing. This is why it's valuable that AdventureKid made this collection of short samples that loop cleanly. All the samples are in exactly the same format (44.1kHz 16-bit WAV) and have the same length (600 samples).

If you want to see what the samples look like you can check out this visual index.

The Yamaha TX16W, Typhoon and Cyclone

Yamaha TX16W front panel Image source: Yamaha Black Boxes

The 1988 TX16W was the first sampler made by Yamaha. They were late to the market and the TX16W was not a big success, particularly because of its difficult-to-use operating system.

For some reason it was common for samplers of this time to store their operating system on a floppy disk. You would have to boot the sampler from its OS floppy first, and then after that you could swap in other floppies to read your sound data. (Other digital instruments of the time would have their operating system installed on ROM chips.)

In the 1990s a group of Swedish computer programmers took it on themselves to write a better operating system for the TX16W hardware. There was a market for this because the original operating system was unpopular and all their customers had to do to switch, was to boot from the Swedish floppy instead of the Japanese one. This alternative operating system was called Typhoon. Typhoon seems to have displaced the original operating system because it is so much better.

All this is pretty cool but it gets even better. One of the Typhoon developers later started a music software company called Sonic Charge. In 2013, Sonic Charge released Cyclone, a free software emulation of the TX16W that runs the orginal binary of the Typhoon OS. This means that the TX16W/Typhoon now has a digital twin that you can run as an audio plugin on your computer.

As you can imagine, all this adds up to a challenge for me to see if I can get the AKWF samples to work in Cyclone (and by extension, the TX16W).

Getting the audio files into Cyclone

Getting audio files into Cyclone is a retro experience. You need to make a floppy image, open the image in the VST, and then use the original TX16W interface to load data off the floppy (at the original speed!).

When it comes to audio data, the good news is that Typhoon supports AIFF files which are still commonly used today. The bad news is that the original hardware only supports the following, uncommon sample rates: 16.7kHz, 33kHz and 50kHz. That means that if you want to load an existing audio file you will almost always have to do a sample rate conversion first.

A user on the Sonic Charge forum pointed out that Typhoon can load 44.1kHz sample files just fine. This is really nice! But as we will see below it is still a good idea to not use 44.1 kHz for the AKWF waveforms.

First try

You would think that higher sample rate means higher quality, so let's convert the AKWF WAV files to 50kHz.

$ sox -G sample.wav sample.aiff rate -v 50k
Congratulations, now you have a 50kHz AIFF audio file.

I later found out that the lower the sample rate of the audio file, the higher up you can transpose the sound. The native sample rate of the TX16W is 400kHz. When you play back a 50kHz audio file at 400kHz its pitch is 400/50 = 8 times higher. Musically, this is 3 octaves (because log2 8 = 3). If you play back a 16.6kHz audio file at 400kHz then it is 400/16.6 = 24 times higher, i.e. log2 24 = 4.6 octaves. For this reason I used rate -v 16667 for the AIFF files in the end.

The base pitch of the AKWF samples is 44100/600 = 73.5Hz. By converting them to 16667Hz sample rate, the maximum pitch when playing back on the TX16W becomes 24 * 73.5 = 1764Hz.

If you like drag and drop you can use this online tool from Sonic Charge to create an image file. If like me you dislike drag and drop, you start by downloading an empty image from the online tool. Then you install GNU Mtools and you can copy files onto the floppy image on the command line.


$ mformat -i helloworld.img -f 720 -c 2 -C
$ mcopy -i helloworld.img sample.aiff ::/

Now we have a floppy image with our AIFF file and we can use the virtual buttons in the Cyclone VST to load the file from the virtual floppy. If we try to play it we get a short blip. That is expected because the AKWF files are 600 samples long at the original 44.1kHz sample rate, which works out to 13.6ms. So we go into the TX16W wave editor and we enable looping by setting the start and end point of the loop. Now it turns out that you cannot loop from the first sample; the earliest you can start your loop is at sample 64.

Second try

OK! We go back to sox and we add 64 samples of silence at the beginning.


$ sox -G sample.wav sample.aiff rate -v 50k pad 64s

Then copy with mcopy, load the virtual floppy and do the whole retro dance again. And it works! If you set the loop points to 64 and 744 you get a cleanly looping single cycle waveform. Why 744? The length is now 680 samples because we increased the sampe rate to 50kHz.

We can do better by putting two cycles of the waveform back to back, and putting the loop markers around the second cycle. In fact I ended up using three cycles, then doing sample rate conversion, and then trimming off the last cycle because the sample rate conversion sometimes messed up the end of the file, leading to loop glitches.

$ sox -G sample.wav sample.wav sample.wav sample.aiff rate -v 16667 trim 0 454s
At 16667Hz, the AKWF cycle length is 277 samples. The trim 0 454s SoX command cuts off the final audio after exactly two cycles (2 * 277 = 454).

The next problem is the pitch of the waveform. It is customary for MIDI instruments to be tuned to the A440 standard. The Typhoon OS has built-in pitch detection but it does not work well on short samples such as those from AKWF, so we have to enter the pitch manually on the TX16W into the sample metadata. This is the same pitch setting for each AKWF sample (D2+2) but you have to set it in the TX16W every time.

Conclusion so far: it works but it is too cumbersome. Let's see if we can do better.

hexdump to the rescue

The Typhoon OS can not only read AIFF files but also save them. And reloading the AIFF files shows that the loop points and pitch information get stored in the updated AIFF. Let's look at how the files changed.

This is what the header of an AIFF file looked like before I enabled looping and pitch:


00000000  46 4f 52 4d 00 00 06 20  41 49 46 46 43 4f 4d 54  |FORM... AIFFCOMT|
00000010  00 00 00 1a 00 01 e3 dd  47 6c 00 00 00 10 50 72  |........Gl....Pr|
00000020  6f 63 65 73 73 65 64 20  62 79 20 53 6f 58 43 4f  |ocessed by SoXCO|
00000030  4d 4d 00 00 00 12 00 01  00 00 02 e8 00 10 40 0e  |MM............@.|
00000040  c3 50 00 00 00 00 00 00  53 53 4e 44 00 00 05 d8  |.P......SSND....|
00000050  00 00 00 00 00 00 00 00  ff ff 00 00 ff ff ff ff  |................|
00000060  00 00 00 00 ff ff 00 00  00 00 00 01 00 01 00 01  |................|
(... rest of sample data omitted ...)

Then after I added metadata and saved in Typhoon, we get:


00000000  46 4f 52 4d 00 00 06 72  41 49 46 46 41 50 50 4c  |FORM...rAIFFAPPL|
00000010  00 00 00 24 73 74 6f 63  07 54 79 70 68 6f 6f 6e  |...$stoc.Typhoon|
00000020  56 49 6e 66 00 00 00 10  b4 a8 f8 28 a8 c6 d1 86  |VInf.......(....|
00000030  7e 42 4f cd 3a af d9 c9  43 4f 4d 4d 00 00 00 12  |~BO.:...COMM....|
00000040  00 01 00 00 02 e8 00 10  40 0e c3 50 00 00 00 00  |........@..P....|
00000050  00 00 49 4e 53 54 00 00  00 14 32 02 00 7f 00 7f  |..INST....2.....|
00000060  00 00 00 01 00 01 00 02  00 01 00 01 00 02 4d 41  |..............MA|
00000070  52 4b 00 00 00 24 00 02  00 01 00 00 00 40 0a 6c  |RK...$.......@.l|
00000080  6f 6f 70 20 73 74 61 72  74 00 00 02 00 00 02 e8  |oop start.......|
00000090  08 6c 6f 6f 70 20 65 6e  64 00 53 53 4e 44 00 00  |.loop end.SSND..|
000000a0  05 d8 00 00 00 00 00 00  00 00 ff f0 00 00 ff f0  |................|
(... rest of sample data omitted ...)

Time to learn more about how AIFF files work. I found this description of the standard online.

The files are organized into chunks with a 4-byte identifier and a 4-byte length prefix. There is a container chunk of type FORM. Nested inside the FORM container there is the magic string AIFF followed by a variable number of other chunks. In the first file we see COMT, COMM and SSND chunks. The COMM and SSND chunks are mandatory and contain audio metadata (length, samplerate, channel count) and raw audio data respectively.

If you click through to the standard document above you see that it is from 1988/1989, i.e. it is from the same vintage as the TX16W itself. It actually looks like the AIFF file format was designed for samplers. The standard describes optional metadata chunks for markers (MARK) and for using the audio data as an instrument (INST). The INST chunk has fields for base pitch, loop points, loop modes, MIDI key range and MIDI velocity range.

Once we know this we can make some sense of the second hexdump. There is an application specific chunk (APPL) which contains the string Typhoon so it must be Typhoon metadata. Then we see both an INST chunk and a MARK chunk. Let's look at the INST chunk.

INST chunk description Source: AIFF standard

This is what it looks like in the file saved by Typhoon:


49 4e 53 54 00 00 00 14  32 02 00 7f 00 7f 00 00
00 01 00 01 00 02 00 01  00 01 00 02

It starts with a 4-byte ID 0x494e5354 which is ASCII INST followed by the length of the remainder of the chunk: 0x00000014. Then we get the base pitch: MIDI note 0x32 (D2) detuned up by 0x02 semitones. The key and velocity ranges are both from 0x00 to 0x7f, i.e. the full MIDI range. There is 0x0000 gain (i.e. none) applied to the sample. Then we get the descriptions of the sustain loop and the release loop, both of which are 00 01 00 01 00 02. That means forward looping (0x0001) from marker 0x0001 to marker 0x0002.

I went over the MARK chunk too but I will spare you the details; it defines the loop start and end markers as expected at 64 and 744.

Now my first thought as an enthusiastic programmer was that I could write a program that parses and deconstructs the AIFF files, inserts the INST and MARK chunks, and reconstructs them. But then on second thought I realized that for each of the 4000+ AKWF files the INST and MARK chunks look exactly the same. All the files are exactly the same length and sample rate, the loop points are exactly the same etc. So all I really had to do was:

I wrote a program in my favourite scripting language that does this. You may wonder if I could have simplified it further by hard-coding the FORM container size but I found out after the fact that a few of the samples are stereo and because my program does the sums, it handled that correctly by accident. This is because the markers specify positions in frames, so in the stereo files the loop still runs from frame 64 to frame 744.

Source code


/* addloop.c

Adds AIFF loop points to AKWF sample files to be used in the TX16W with Typhoon
OS.

sox -G AKWF_xxxx.wav AKWF_xxxx.wav AKWF_xxxx.wav AKWF_xxx.aiff \
  rate -v 16667 trim 0 454s
./addloop 38 2 227 454 < AKWF_xxx.aiff > AKWF_xxx.aiff.tmp
mv AKWF_xxx.aiff.tmp AKWF_xxx.aiff
*/

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void fail(char *fmt, ...) {
  va_list ap;

  fflush(stdout);

  fputs("error: ", stderr);
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
  fputc('\n', stderr);

  exit(1);
}

char magic[] = {
    0x49, 0x4e, 0x53, 0x54, 0x00, 0x00, 0x00, 0x14, 0x26, 0x02, 0x00, 0x7f,
    0x00, 0x7f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01,
    0x00, 0x01, 0x00, 0x02, 0x4d, 0x41, 0x52, 0x4b, 0x00, 0x00, 0x00, 0x24,
    0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x02, 0xa8, 0x0a, 0x6c, 0x6f, 0x6f,
    0x70, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x05, 0x50, 0x08, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x65, 0x6e, 0x64, 0x00,
};

int main(int argc, char **argv) {
  unsigned char buf[12];
  int size, c, off;

  if (argc != 5) {
    fputs("Usage: addloop ROOT DETUNE LOOPSTART LOOPEND\n", stderr);
    exit(1);
  }

  magic[8] = atoi(argv[1]);
  magic[9] = atoi(argv[2]);
  off = atoi(argv[3]);
  magic[42] = off >> 8;
  magic[43] = off;
  off = atoi(argv[4]);
  magic[60] = off >> 8;
  magic[61] = off;

  if (fread(buf, 1, sizeof(buf), stdin) != sizeof(buf))
    fail("short read");
  if (memcmp(buf, "FORM\x00\x00", 6) || memcmp(buf + 8, "AIFF", 4))
    fail("bad header: %12.12s", buf);

  size = ((int)buf[6] << 8) + (int)buf[7];
  size += sizeof(magic);
  fputs("FORM", stdout);
  putchar(0);
  putchar(0);
  putchar(size >> 8);
  putchar(size);

  fputs("AIFF", stdout);
  size -= 4;

  fwrite(magic, 1, sizeof(magic), stdout);
  size -= sizeof(magic);
  while (c = getchar(), c != EOF) {
    putchar(c);
    size--;
  }

  if (size)
    fail("input file size %d bytes too large", -size);
}

And then I had to do some shell scripting to convert all the files.


(
  set -e
  (cd AKWF && ls */*.wav) | while read w
  do
    aiff=AKWF--TX16W-Typhoon-Cyclone/${w%.wav}.aiff
    sox -G AKWF/$w $aiff rate -v 50k pad 64s
    ./addloop < $aiff > ${aiff}.tmp
    mv ${aiff}.tmp $aiff
  done
)

Here is an example of a file that went through this process.

Example of processed waveform Note how the audio editor recognizes the loop points. The digital silence at the beginning is a clumsy workaround for the inability of the TX16W to loop back to the beginning of a file. I got rid of this silence later by putting two cycles back to back.

I have submitted a pull request to the AKWF GitHub repository with the converted sample files.

Conclusion

This was fun, and easier than I expected because the sampler metadata is natively supported by the AIFF file format and Typhoon does the right thing by using AIFF metadata as the standard intended it.

Now I have a new problem, namely that I can only fit 37 files in the root directory of a floppy (some sort of limit on the number of directory entries?) and I have 4000+ files to try out. This will take a while.

That 37 file limit was due to VFAT long filenames eating up directory slots. By renaming the files to 8.3 format I managed to fit 99 AKWF samples on a TX16W-compatible floppy.

Tags: music c tx16w yamaha akwf

IndexContact