A native OPENSTEP 4.2 / NeXTSTEP (i386) GUI MP3 player, written from
scratch. It decodes MP3 with libmpg123 linked in-process — not by
driving an external mpg123 binary — and plays through the NeXT sound
system with a code-only AppKit interface.
Verified on real hardware: an i386 OPENSTEP 4.2 machine with an Ensoniq ES1371 (Creative AudioPCI) sound card driven by the SoundBlaster16PCI DriverKit driver. Every supported sample rate, bit depth, and channel count plays at correct pitch, both at the source rate and at a user-forced output format.
Platform status. OnionPlayer currently runs on OPENSTEP 4.2 for Intel (i386) only — that is the sole configuration it has been built and tested on. The other NeXT architectures (m68k, SPARC, HP PA-RISC) are a future goal, not a supported target yet: the code is portable C / Objective-C, but it has not been compiled or run there, and the prebuilt bundle in
dist/is an i386 Mach-O binary.
The main window with the green LED dot-matrix readout (a scrolling track title over the elapsed/total time), the Output Format panel, and the separate playlist window.
- In-process libmpg123 decode. The decoder sits behind an
AudioDecoderprotocol, so FLAC/Ogg back-ends can be added later without touching the engine or UI. - Formats: 22.05 / 44.1 / 48 kHz (and the other MPEG rates), 8- and 16-bit, mono and stereo.
- Output format: automatic or forced. By default the output matches the source; you can also force a rate / bit depth / channel count from the Output Format panel. The ES1371's hardware sample-rate converter plays the forced format natively — useful for lower-powered targets (e.g. m68k) where decoding to a smaller format saves CPU.
- LED dot-matrix display. A hand-drawn 5×7 dot-matrix panel (no bitmap font) shows two lines: the track title (from the ID3 tag — artist/title — or the filename), which ping-pong scrolls when it is wider than the panel, over the time. Click the panel to toggle the time between elapsed / total and elapsed / remaining. A smaller LED strip shows the live kbps / kHz / channels.
- VU meter in its own window: stereo vertical LED bars (green → yellow → red) with peak-hold markers, kept in sync with the audio.
- Transport: play / pause / stop / seek, plus system-volume control.
- Playlist in a separate window, with previous / next, shuffle and repeat, and automatic advance at end of track.
- Drag and drop: drop one or more
.mp3files to reset the default playlist and start playing; drop a.m3uor.plsto load it. - Playlist files: File ▸ Load / Save / Save As read and write
.m3uplaylists. - Menus: File (playlist load/save), Play (transport), and Window (raise the Main Player, Playlist, VU Meter, or Output Format window).
- Session persistence: on quit the app remembers the current playlist and which windows were open (and where), and restores them on the next launch. Missing files are dropped from the restored playlist.
- Real
.appbundle that launches from the Workspace and shows its icon (both in the browser and the process dock).
- OPENSTEP 4.2 (i386) / NeXTSTEP.
- A DriverKit audio driver. Tested with SoundBlaster16PCI (ES1371); any driver that advertises the standard sample rates works, because the player hands the device the audio format directly and lets the hardware's sample-rate converter do the rest.
Installer package (GUI). Unpack dist/OnionPlayer.pkg.tar on the
machine and double-click OnionPlayer.pkg — Installer.app opens and
installs OnionPlayer.app into /LocalApps:
gnutar xf .../openstep-mp3player/dist/OnionPlayer.pkg.tar
open OnionPlayer.pkg
Plain bundle. Or unpack the .app straight into an apps directory:
cd /LocalApps
gnutar xzf .../openstep-mp3player/dist/OnionPlayer.app.tar.gz
Then double-click OnionPlayer.app in the Workspace, or open /LocalApps/OnionPlayer.app.
The build has two steps: build the ported libmpg123 static library, then compile the app against it.
# 1. Build libmpg123 for OPENSTEP cc 2.7.2.1 (produces libmpg123.a)
build/build-libmpg123.sh
# 2. Compile and link the app (run in the persistent build dir):
cc -O -I. -Iinclude -o OnionPlayer \
src/PlaybackEngine.m src/Mpg123Decoder.m \
app/OnionApp.m app/DotMatrixView.m app/VUMeterView.m \
libmpg123.a -lm -framework AppKit -framework Foundation -framework SoundKit \
-sectcreate __ICON __header app/OnionPlayer.iconheader \
-sectcreate __ICON app app/OnionPlayer.app/Resources/OnionPlayer.tiff
The two -sectcreate sections are what let the Workspace launch the
bundle and render its icon: a tab-separated icon-header binding table in
__ICON __header, and the icon TIFF (72 DPI) in __ICON app.
libmpg123 is compiled with OPT_GENERIC and REAL_IS_FLOAT; one file,
lfs_wrap.c, must be built at -O0 to dodge a code-generation bug in
cc 2.7.2.1 when it joins two long long comparisons with &&/||.
See NOTES.ko.md for the full development log and every trap found along
the way, and PLAN.md for the phased plan.
To build the OPENSTEP Installer package on the machine, given a built
OnionPlayer.app:
sh pkg/build-pkg.sh /LocalApps/OnionPlayer.app dist
It stages the app, runs /NextAdmin/Installer.app/package against
pkg/OnionPlayer.info (title, version, /LocalApps default location),
and writes OnionPlayer.pkg plus an uncompressed OnionPlayer.pkg.tar
for transport.
src/AudioDecoder.h— the decoder protocol (open, format, read, seek, length).src/Mpg123Decoder— the libmpg123 implementation. Output is signed native-endian PCM; the engine byte-swaps 16-bit to big-endian for the NeXT sound system.src/PlaybackEngine— a bounded, streaming engine. A background cthread decodes 0.25 s chunks into a fixed pool ofSNDSoundStructbuffers, keeps a few enqueued (SNDStartPlaying,preempt = 0), and paces itself withSNDWaiton the oldest outstanding tag. Handles seek / pause / resume / stop and reports the play position.app/OnionApp.m— the code-only AppKit UI (no.nib): menus, the main transport window, playlist window, output-format panel, drag-and-drop, and the session save/restore.app/DotMatrixView— the 5×7 LED dot-matrix view: multi-line text, ping-pong title scroll, and click-to-toggle, drawn withNSRectFill.app/VUMeterView— the stereo vertical LED VU bars. It polls the engine's per-chunk peak level (keyed to the currently-playing chunk, so it stays in sync despite the decode lookahead) on its own ~20 Hz timer, with peak-hold and decay.
GPL v2 or later. libmpg123 is LGPL 2.1 (compatible); the port lives under
port/, patches/, and build/. The ES1371 sample-rate-converter
reference sources consulted during development are BSD/GPL and listed in
NOTES.ko.md.
