Related tags
History
← osdk-dot-orgPreprocessor abuses →
  Let's make noise
Sat 28th February 2015   
This article was first published in the CEOMag 296.

Our beloved Oric computer is equipped with a sound generator compatible with the one found on the Atari ST, Amstrad CPC, MSX and some models of Sinclair Spectrum, so theoretically there’s no real reason why most of the earlier Oric games had no music at all or perhaps just a game over tune.

Since the 90ies, most of the Oric games and demos had musics written by Jonathan Bristow1, using either Sonix, Soundtracker or Wave2, but unfortunately he was the only one to really master these tools, which means that all this knowledge has been lost.

So, what are the solutions?

A first solution would be obviously to try to get the entire Sonix/Wave technology working again, and finding people who can make music with it, which may prove difficult.

A second solution would be to make an Oric player for some other music editing tool, like for example the Vortex Tracker3, a Windows program used in the Spectrum universe.

An alternative would be to write a player for MaxYMizer4, an Atari ST program used by some of the most famous YM musicians.

Both these solution require to compose music.

A third solution is to exploit the huge number of musics already available on the other platforms that use the same soundchip!5

Register dumps

A French demomaker called Arnaud Carré6, wanted to listen to Atari ST musics when he was working on his PC. That was long before YouTube or Spotify, the computers were barely able to run emulators for the Atari ST at a decent speed, so he decided to use a very CPU efficient method: the register dump.

Back in the days, Atari ST music player routines were not very efficient, which made it very difficult to play musics in situation were CPU time was critical. What demo maker did was to trade CPU time against memory usage, and basically “pre-play” the entire music in memory in some virtual sound chip registers, and then during the demo just read back these values and send them to the real sound generator.

Obviously, if you have the entire music in memory, you can just save it and replay it later on any device that has a register compatible chip, so it’s what Arnaud did: He wrote a simple YM replaying routine and used these memory dumps as source file. ST-Sound7 was born.

The main problem with these files is that they tend to be large.

The typical Atari ST music would take from 1 to 5 kilobytes of memory, including the actual 68000 code to play the music, the note information and also the sound instrument description.

After being converted to memory dump format, the used memory would just explode: On a machine connected to a PAL television you try to update the effects in synchronisation with the video display, which means you have to update the sound registers 50 times per second if you want also to synchronize the audio.

The YM chip has 14 registers dedicated to the audio generation, this means that to accurately replay one second of music you use 14*50 = 700 bytes of sound data.

For one minute of music, that would be 14*50*60 = 42 000 bytes. And for ten minutes of music that’s about 400 kilobytes.

Obviously that’s quite larger than the size taken by the original file.

Data compression

The solution Arnaud used was to compress the data using the commonly used LHA method.

Musics are naturally made of repeated elements, that make them natural candidates for compression.

In order to improve the packing ratio, Arnaud decided to store the values by register number instead of frame, so if the value of a particular register does not change it will pack nicely because of the Run Length Encoding method.

So to summarize, a YM file looks like that:
[Some header information] 
[All register 0 values]
[All register 1 values]
[All register 2 values]
[All register 3 values]
(…)
[All register 12 values]
[All register 13 values]
All that being compressed with LHA.

The result file is not as small as the original, but fits nicely in the 3 to 20 kilobyte range, which makes it a decent way to store YM files in a portable way.

The YM format has evolved over time to accommodate the special effects and features used in some Atari ST musics such as “SID” sound, SyncBuzz or DigiDrums, but these would be hard to play on the Oric because they require multiple timers running at varying frequency.

How can be play these files on the Oric ?

YM vs MYM

The main problem we face on the Oric is the available memory.

Because YM files are stored by register number, and then compressed, they cannot be unpacked on the fly: To be able to access the register information the entire file need to be decompressed in memory, so we are back to our original memory usage problem.

Fortunately, we can store the register information whatever way we want, and we can use any compression method we want, as long as the end result can be processed on our 1mhz 6502.

One such format is the MYM one, designed by a Finnish MSX user called Marq.

This format is not as efficient as the YM storage wise, but if is good enough to play most Atari ST songs without too much problems.

This format is natively supported by the OSDK’s tool called Ym2Mym, and was used in the Buggy Boy, Pushing the Envelope and Zerofx demos.

It was also used with a custom variant of the replay routine in the game Athanor.

So, what makes MYM usable on the Oric?

The main change is that the registers are packed by blocks of 128 frames, which means you only have to decompress 1792 bytes at a time (14 registers * 128 frames). The compression ratio is not as good as straight YM files, but at least you can play the music on the Oric.

The main problem is to balance the CPU usage.

128 frames is roughly 2.5 seconds, which means that every 2.5 seconds you have to get a new batch of 1792 bytes ready to use.

The way I handled that in my player, was to actually reserve memory for 256 frames, pre-decompress 128 frames at the start of the program, and then spread the decompression of the new data over multiple frames to avoid huge cpu usage spikes. (I process one register every 9 frames)

The main drawback is that now we have 3584 bytes used just for storing the uncompressed frames, plus the size of the player, plus the size of the music data.

CPU wise, the algorithm was originally designed to be running on a Z80 processor, so not everything is optimal on the 6502. I’m pretty sure the code could be optimized by just doing some minor changes to the MYM file format: Reading bit encoded streams is not a strong point of the 6502!8

How to use from BASIC

I’m not going to detail how to use the MYM player from assembler, all my recent demos are using it and the source code is available, that should be easy enough to understand.

Oric Arcade Music Greatest Hits
Oric Arcade Music Greatest Hits

Instead I’m going to explain the BASIC compatible player, because it’s an easy way to increase the quality of a BASIC game, just add some simple music in the intro :)

First, you need to fetch a recent version of the OSDK, so you get the latest version of YM2MYM.

Then you need to get the code of the mym player project.

Make sure your OSDK is correctly setup (everything is indicated in the documentation), and then build the player project (using osdk_build.bat) and then run it (osdk_execute.bat)

If everything is working nicely, you should now have a WELCOME TO THE ORIC ARCADE MUSIC GREATEST HITS page with a selection of 11 musics to choose from. Play a bit with it so you can have a rough idea of the kind of music you can add to your game or demo :)

So, how does that work?

The BASIC player

First take a look at the .BAS program:

10 REM Simple music player:
20 REM - Music files are loaded in $7600
30 REM - Register buffer in $6800
40 REM - Player binary code in $6500
50 REM Oric 1 compatible version
60 REM

70 HIMEM #6000
80 TEXT:PAPER4:INK6:CLS
100 PRINT "Loading music player"
110 LOAD"MYMPLAYER.BIN"
120 CLS:GOSUB 1000:PRINT:PRINT"Number:Select music Space:Quit"
130 GET A$
140 IF A$=" " THEN END
145 IF (A$>="0" AND A$<="9") OR (A$="A") THEN GOTO 200
150 GOTO 120

160 REM
170 REM Loading music
180 REM
200 PRINT "Loading "+A$
210 LOAD"MUSIC"+A$+".BIN"
200 PRINT "Currently playing music "+A$
220 CALL#6500
230 PRINT"Press a key to continue"
240 GET A$
250 CALL#6503
270 GOTO 120

1000 REM
1001 REM MENU
1002 REM
1010 PRINT" WELCOME TO THE ORIC "
1020 PRINT"ARCADE MUSIC GREATEST HITS"
1030 PRINT
1040 PRINT"CHOOSE THE MUSIC:"
1050 PRINT"1-Bubble Bobble"
1060 PRINT"2-Great Giana Sisters"
1070 PRINT"3-Rainbow Islands"
1080 PRINT"4-Pac Mania"
1090 PRINT"5-Tetris"
1100 PRINT"6-Speedball"
1110 PRINT"7-Nebulus"
1120 PRINT"8-Outrun"
1130 PRINT"9-Commando"
1140 PRINT"0-Ghostbusters"
1150 PRINT"A-Supercars"
1500 RETURN

The most important thing, is to make sure that nothing will overwrite the memory used by the music player, so you have to think about your memory mapping.

In this particular example, the instruction HIMEM #6000 is there to make sure that the BASIC will not use any memory location above #6000, which means that the entire memory area from #6000 to #B400 is free to use by the music player (in TEXT mode).

Of course, you will need to check how much memory you really need to reserve based on which music you decided to use.
  • at line 110, the code loads the file MYMPLAYER.BIN which contains the assembled music player.
  • at line 210, is the loading of the selected music.
  • the line 220 is where the music player is started (CALL #6500)
  • and line 250 where the music player is stopped (CALL #6503).
As you can see in the top first comments, the “Music files are loaded in $7600”, “Register buffer in $6800” and “Player binary code in $6500”. These values are important to remember.

Converting the music files

Let’s start by the musics.

If you open the Data folder, you will see a number of .ym files, the largest is Ghostbusters (5 kb) and the smaller is Bubble Bobble (1 kb).

If you now look at the osdk_builder.bat, you will find that these files are converted to the MYM format using the following command:

SET YM2MYM=%osdk%\Bin\ym2mym.exe -h1 -m15872
%YM2MYM% "data\Bubble Bobble 1.ym" build\BubbleBobble.tap $7600 "Music1"

What it means, is that the music “Bubble Bobble” should be converted as a tape file starting at address $7600, called “Music1”, using YM2MYM so the file cannot be larger than 15872 bytes.

The music player

Still in osdk_build.bat is the following sequence:
ECHO Assembling music player
%osdk%\bin\xa mymplayer.s -o build\mymplayer.o
%OSDK%\bin\header -h1 -a0 build\mymplayer.o build\mymplayer.tap $6500

What it does, is to assemble the file mymplayer.s into a tape file called mymplayer.tap starting at address $6500.

If you open the mymplayer.s file, you sill see the following:

*=$6500 ; Actual start address of the player

MymPlayerStart
jmp StartMusic ; Call #6500 to start the music
jmp EndMusic ; Call #6503 to stop the music

This just means that the music player will be assembled at the address $6500, and that the two first instructions are jmps to the functions that start and stop the music: This is what the CALL #6500 and CALL #6503 are actually calling.

The code also contains this:

#define _PlayerBuffer $6800 ; .dsb 256*14 (About 3.5 kilobytes)
#define _PlayerBufferEnd $7600

; Loads between the player buffer and the redefined character sets
#define _MusicData $7600 ; Musics are loaded in $7600

all it does is to define the location of the decompression buffer and the location of the music files.

Final details

There’s something important I need to address: This music player is not designed to be used on a TAPE based system, it assumes you are running using a Microdisc9.

This is not a limitation of the system, you could indeed make it run without the microdisc, it’s just a matter of changing the way the IRQ is handled.

The reason I think it is not worth the hassle is that these files are already large, and honestly you don’t want to inflict more minutes of unreliable tape loading to the user just so he can listen to a music.

If you really want to make it run on TAPE system, then all you have to do is to modify the code so it accepts to be called from the BASIC irq handler10 instead of the CPU handler.


1. Aka Twilighte
5. A large archive of YM files to download
6. Aka Leonard of Oxygene
8. Also I wrote the decoder more than 10 years ago, so bear with me in regard to the quality of the code
9. Or Cumulus, all it needs is access to the overlay
10. Which happens to be a different address on the Oric 1 and Atmos

Coverity Scan Build Status