Work in progress
A Go package to play MOD and S3M files.
Back in the mid-90's I was active in the PC demoscene and always relied on other people's player code for the music. I never knew how they worked, so I decided many many years later to sit down and code one.
The package consists of two main parts, a Song
and a Player
. The Song
struct represents a parsed MOD or S3M file, use NewMODSongFromBytes
or NewS3MSongFromBytes
to parse a byte slice holding a music file into a Song
. Use the correct function for the file type. With a Song
you can create a Player
instance. Then call GenerateAudio
on the Player
instance to generate raw audio output which you send to an audio device (the modplay
command) or serialize to disk (the modwav
command).
There are three binaries provided, modwav
, modplay
and moddump
.
Generates audio output from MOD and S3M song files, and saves the output in RIFF WAVE format. This is pure Go code with no third party dependencies.
$ go run ./cmd/modwav -hz 22050 awesome.mod # Generate a 22.5Khz WAVE file from awesome.mod called awesome.wav
You can use the -hz
and -wav
command line options to affect quality (default 44.1Khz) and output file, by default the same filename with a .wav
extension in the current directory. The -boost
flag can be used to boost the output volume, but this can cause clipping.
Plays MOD and S3M files through your computers audio out. Go/CGo and uses PortAudio to play the audio. I've included the Windows DLL portaudio_x64.dll
, you will need to compile portaudio for other platforms. Good luck with that, it can be a bit of a hassle.
$ export PKG_CONFIG_PATH=$PORTAUDIO
$ export CGO_CFLAGS="-I $PORTAUDIO/include"
$ export CGO_LDFLAGS="-L $PORTAUDIO/lib/.libs"
$ go run ./cmd/modplay awesome.mod
Prints the interpreted and raw contents of MOD and S3M files to stdout. The output includes the pattern data and instrument definitions. A really useful tool when debugging.
$ go run ./cmd/moddump mods/caero.s3m
Name:
Channels: 13
Speed: 6
Tempo: 131
Orders: 58 [0 1 4 5 6 7 8 9 16 17 2 3 12 11 10 14 13 14 18 19 20 21 22 21 24 25 24 25 30 31 32 33 34 33 35 36 35 36 37 38 37 38 39 40 41 42 39 40 41 42 46 47 48 49 50 43 44 45]
Pan: [56 40 80 80 40 56 96 24 96 56 96 24 56 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64]
Raw: {Pad:26 Filetype:16 _:0 Length:60 NumInstruments:41 NumPatterns:51 Flags:0 Tracker:4896 SampleFormat:2 _:[0 0 0 0] GlobalVolume:64 Speed:6 Tempo:131 MasterVolume:176 _:0 Panning:252 _:[0 0 0 0 0 0 0 0] _:[0 0] ChannelSettings:[0 8 1 9 2 10 3 11 4 12 5 13 6 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255]}
Instrument 0 x00
Name: kohwq
Length: 37112
[...]
Pattern 0 (x00)
00: E-407..... C-50801... .....10... D#50801... .....13... .......... .......... .......... .......... .......... A-40901... .......... ..........
01: .......... .....03... .....10... .....03... .....12... .......... .......... .......... .......... .......... .....01... .......... ..........
02: .......... .....06... .....0F... .....06... .....11... .......... .......... .......... .......... .......... .....01... .......... ..........
03: .......... .....09... .....0F... .....09... .....10... .......... .......... .......... .......... .......... .....02... .......... ..........
04: .......... .....0B... .....0E... .....0B... .....10... .......... .......... .......... .......... .......... .....02... .......... ..........
[...]
Testing a music player is a little tricky because the output is audio data. There are unit tests for the note trigger logic, integrations tests which compare the player against golden output, and manual listening tests where I compare output to other players.
Currently there are unit tests for the note triggering logic in the player. I determined the logic by experimenting in ScreamTracker 3 and then codifying the behavior in unit tests. Over time I will increase the coverage of unit tests.
$ go test .
There are two scripts make_golden.sh
and check_against_golden.sh
. The first runs modwav
for each of the included songs to produce "golden" WAVE files. The second script re-runs modwav
to a temporary directory and compares the output to the corresponding golden file. The comparison uses the cmp
utility, so it's a trivial byte for byte comparison. These scripts are really only useful during refactors to verify that the output has not changed. Almost any other change affects the output so these tests will fail.
You can find tracker files at The Mod Archive but I included a small selection in the mods
folder that are used to test playback:
space_debris.mod
- one of the most popular MODs on The Mod Archive
dope.mod
- From the PC demo DOPE by Complex
believe.mod
- From the 64kb PC intro Believe by Valhalla
caero.s3m
- From the PC demo Caero by Plant & Electromotive Force
FireLight's MOD and S3M format documents were the most useful documents. I converted the box drawing characters from PC code page 437 in the original docs into Unicode. The official ScreamTracker 3 TECH.DOC was also handy.
I used micromod, MilkyTracker and libxmp as implementation guides.
(These notes mainly for myself, $MODPLAYER
and $PORTAUDIO
are the directory of this repo and portaudio respectively)
git clone https://github.com/PortAudio/portaudio $PORTAUDIO
cd $PORTAUDIO
./configure
# This will generate the static and dynamic library files that are needed
make
# Mac OSX installation instructions (is there a better way?)
sudo mkdir /usr/local/lib # Gross!
sudo cp $PORTAUDIO/lib/.libs/libportaudio.2.dylib /usr/local/lib
- Finish S3M support
- Increase unit test coverage
- Add sample interpolation into mixer for improved sound quality