Menu

Home

Paul Osmialowski

Retromidi Wiki

How it all started

Being a part-time fan of the Forth language, I was delighted to find out a decent Forth compiler (named DX-Forth) being under active development and targeting old machines working under the control of DOS and CP/M operating systems. I was particularly impressed by the existence of the CP/M version. After I tried some of its graphics capabilities (provided by xplgraph library for DOS that I managed to port to CP/M in somewhat cut-down version relying on CP/M's GSX interface), I started to look at the sound generating capabilities. I soon realized that in case of CP/M, nothing more than just a beep can be achieved. Although the hardware on which CP/M used to be run through the 80's (e.g. Amstrad CPC, Commodore C128, ZX Spectrum 128k +3) was often equipped with audio devices of one kind or another, no consisntent music programming interface (similar to GSX for graphics) was developed for CP/M. Even the only (AY-based) sound generator developed by DK'Tronics for CP/M-dedicated Amstrad PCW computers required direct talking to the hardware from the programs wanting to utilize it (and the only known example of software using it successfully was the Head Over Heels game, see https://www.youtube.com/watch?v=Q7HsI460U2U&t=6s). I came to a conclusion that the easiest way to 'play a tune' or to 'signal an event with a sound effect' would be to send MIDI commands over one of available peripheral interfaces.

And here, again, although there were CP/M running machines with MIDI interface (e.g. ZX Spectrum 128k +3), it was never exposed to the operating system in consistent form. Therefore I started to look for devices that could be connected through any of the interfaces widely available to the CP/M computers. The RS232C serial and Centronics parallel ports were obvious candidates. Eventually, I've found the one off-the-shelf device utilizing Centronics parallel port (DreamBlaster S2P by Serdaco) and a few web pages describing how to build proper RS232C to MIDI converter (e.g. http://midi-and-more.de/midiconverter.htm). At the end it turned out that this S2P device is pretty much DOS-oriented, not responding like a printer, hence it required direct (operating system bypassing) port-I/O communication (which I also succesfully attempted in my example C code for Turbo C and a Forth code for the DOS version of DX-Forth). To try RS232C solution, I've used plain null-modem serial cable (the proper one, with hardware flow control lines properly connected) in order to connect CP/M with Linux-running machine on which I've developed simple alsa-based MIDI proxy. It required Teunis van Beelen's rs232-console (see https://gitlab.com/Teuniz/rs232-console.git) to handle serial communication through a simple stdin/stdout pipe. On the receiving side of the proxy were the alsa-compatible devices: real or emulated. Using alsa-compatible USB MIDI adapter I could connect AY3 hardware synth by Twisted Electrons (which I initially planned to connect to the CP/M machine through the RS232C to MIDI converter). For software-only solution I used fluidsynth with one of its sound fonts with its output directed to pulseaudio.

Having simple Forth routines for emitting MIDI events, I could add soume sound to my CP/M programs.

The pipeline

My Forth routines are able to emit MIDI events to the standard printer device (usually, parallel port) of underlying operating system (DOS or CP/M). Hence the system controlled printer port must be redirected to a serial port. The whole of the pipeline looks as follows:
1. Set the serial communication parameters (adjust them to your needs) and redirect the printer port to a (selected) serial port:

  • A single command on CP/M (the sio device in the following example is a built-in RS232 serial port of the ZX Spectrum 128k +3 computer):
> device lst:=sio[noxon, 19200]
  • The two commands on DOS:
> mode COM1: 9600,N,8,1
> mode LPT1:=COM1:
  1. Either connect a hardware MIDI device to your Linux-running machine or start fluidsynth with one of its sound fonts, e.g.:
$ fluidsynth -a pulseaudio -m alsa_seq /usr/share/sounds/sf2/FluidR3_GM.sf2
  1. Examine a list of the available MIDI devices on your Linux-running machine:
$ aplaymidi -l

The example names of available MIDI port are 128:0, 28:1, etc.

  1. Run the passmidi proxy coupled with rs232-console (adjust the serial communication parameters accordingly!):
$ ./passmidi 128:0 rs232-console -- -p ttyUSB0 -b 19200 -m 8N1 -f hardware

Alternatively, you can start FUSE ZX Spectrum emulator (in the +3 mode) patched with my patch (see https://sourceforge.net/p/fuse-emulator/patches/426) redirecting +3's serial port to the stdin/stdout of the emulator. The emulator can be coupled with the passmidi proxy as such:

$ ./passmidi 128:0 fuse -- -m plus3 ZXCPM3A.DSK

The .mi and .mib formats

After I developed DX-Forth routines for emitting MIDI events, I started to wonder if I can write some player capable to just play some MIDI tunes from a file. Unfortunately, I've found the most of widely used MIDI file formats too heavy to handle effectively on the most of theCP/M-running machines. Thus I had to develop something light. I came up with the textual .mi format and its binary counterpart, the .mib format.

An human-readable .mi file is a sequence of MIDI events where a single MIDI event is expressed as a set of numeric values, one value per line, as such:

  • interval given in ticks (0 ticks = now),
  • MIDI command code (value in a range 128-224, multiplies of 16 only),
  • MIDI channel (0 to 15),
  • one or two lines of MIDI command parameters,
  • a 'star' terminator.

Following example depicts seven consecutive MIDI events copied from an example .mi file:

0
192
0
8
*
0
144
0
73
80
*
240
128
0
73
80
*
0
144
0
61
80
*
240
144
0
78
80
*
0
128
0
61
80
*
240
128
0
78
80
*

You can use mi2mib conversion program to convert an human-readable .mi file into a binary .mib file.

The .mib format encodes an MIDI event in a serie of bytes (each value is a single byte) as such:

  • interval given in ticks (0 ticks = now),
  • MIDI command code bitwise or'ed with MIDI channel number (values in a range 128-239),
  • parameter 1 of a MIDI command
  • parameter 2 (ONLY when this particular MIDI command accepts two parameters)

To construct a new textual .mi file (with an usual text editor), one can examine the contents of some .mid file. The .mid is a popular binary format, it needs to be converted to something human-readable first. A good soultion is to convert .mid binary file into a human-readable .csv spreadsheet, e.g. using John Walker's midicsv set of tools (see https://fourmilab.ch/webtools/midicsv). Note that the absolute tick values in so obtained .csv spreadsheet file need to be re-calculated into tick intervals.


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.