Play .YM and .YMM Audio Files - Megatokio/kilipili GitHub Wiki

Lib kilipili contains class Audio::YMMusicPlayer for playback of AY-3-8912 audio files. These must be provided as .ymm files which is an improvement over .ym files which can be found widely on the internet and here on GitHub.

What are .ym Files?

.ym files contain register data for the 8-bit AY-3-8912 sound chip and were initially invented for the Atari ST. (i believe.)
Uncompressed .ym files are pretty large, they contain 50*16 = 800 register update values per second. Therefore they are normally compressed with LHA. In order to achieve really good compression the file contains all data for register 0, then register 1, 2, 3 and so on. The register data is not stored in the sequence in which it is read by the player.

The .ym format is only suitable for playback if you can load the entire decompressed file into ram because the data is stored per register and not per frame as it is needed by the player. In addition the LZH decoder requires a big chunk of memory too.

What are .ymm Files?

.ymm files are designed for playback in memory constrained environments. They are internally compressed. If compressed with a window size of winbits=14 <=> buffer size = 32 kByte then they are often better compressed than LHA compressed .ym files. But they also compress well with much smaller buffer size: The recommended buffer size is actually only between winbits=10 <=> 2 kBytes and winbits=12 <=> 8 kBytes buffer size. The allowed window size is between winbits=8 and winbits=14. You only need this small buffer hanging around but not the whole file. This frees almost all ram for video display.

class YMMFileConverter

class YMMFileConverter is part of the desktop_tools/RsrcFileWriter.
The RsrcFileWriter needs a command file.

The relevant entries in a command file for the RsrcFileWriter are the conversion type 'ymm' and the window size, e.g.:

*.ym   ymm   W12

This converts all files which match the pattern .ym into the ymm format using a buffer size of 2^12 = 4096 codes = 8192 bytes. See also the Wiki page for the RsrcFileWriter.

class YMMusicPlayer

The decoder is Audio::YMMusicPlayer. This class is a subclass of class AudioSource and designed to be added to the AudioController as a audio source. It has member functions to play a single file, a directory, pause audio, repeat a file or a directory.

It has a run() member function which is to be called by your main round-robin dispatcher loop.

The YMMusicPlayer object is currently 600 bytes in size which adds to the buffer size, which is allocated as needed by the played file.

ymm File Format

char[4]  magic       "ymm!"
char     variant     2
char     winbits     8 .. 14
char     frame_rate  50 .. 60
char     frame_size  16 = registers per audio frame
uint32   num_frames  little endian!
uint32   loop_frame  if file is played in a loop
uint32   ay_clock    2000000 for the Atari ST
char[]   title,0
char[]   author,0
char[]   comment,0
uint32   rbusz       buffer_assignment: 2 bits per register
char[]   bitstream

Each register has it's own LZ backref window. If the buffer is shared equally among all registers each register gets 1/16 of the overall buffer which is set with the winbits. But the buffer for each register can be reduced or increased or even set to 0 by the compressor to optimize buffer usage. This is indicated by 2 bytes for each register in rbusz.

Each register has it's own bitstream but these are interleaved in the total bitstream of the file.

YMMFileConverter Algorithm

  • The data for each register is RLE compressed
  • This is further LZ compressed with windowsize = total window/16 or winbits-4
  • This is encoded into a bitstream.
    So there are actually 16 compressed streams which each use 1/16 of the total buffer size.
  • Then the RLE data is also LZ compressed with windowsize/2 and windowsize*2.
  • Then the window sizes are traded between the registers to give registers which benefit the most from a larger buffer a larger buffer and give registers which suffer the least a smaller buffer or even no backref buffer at all.

Then the 16 choosen bitstreams are combined into a single bitstream, which is quite tricky.

Finally the final bitstream is decoded and compared against the original data. So the encoder is it's own unit test.

The .ym and the .ymm file contains a 'loop position'. In order to reach this position the bitstream must be rewound to the start and the frames of the intro must be skipped until the loop frame is reached. This is of course handled by the YMMusicPlayer.