This document describes how to build and use the Desktop JVM implementation of the ModPlayer interface.
The Desktop implementation follows a similar architecture to the Android version:
┌─────────────────────────────────────────────────────────────────┐
│ DesktopModPlayer.kt │
│ (High-level Kotlin implementation, handles JavaSound audio) │
└────────────────────────────┬────────────────────────────────────┘
│ uses
┌────────────────────────────▼────────────────────────────────────┐
│ DesktopModPlayerNative.kt │
│ (JNI wrapper with external native functions for libopenmpt) │
└────────────────────────────┬────────────────────────────────────┘
│ JNI calls
┌────────────────────────────▼────────────────────────────────────┐
│ desktop_mod_player_jni.cpp │
│ (JNI bridge - wraps libopenmpt C API, no audio handling) │
└────────────────────────────┬────────────────────────────────────┘
│ calls
┌────────────────────────────▼────────────────────────────────────┐
│ libopenmpt.so │
│ (Pre-compiled libopenmpt library) │
└─────────────────────────────────────────────────────────────────┘
| Aspect | Android | Desktop |
|---|---|---|
| Audio Output | Oboe (native) | JavaSound (SourceDataLine) |
| Native Lib Build | CMake via Gradle | CMake standalone |
| Lib Loading | System.loadLibrary | Extract from resources |
- JDK with JNI headers - JDK 11+ recommended
- CMake 3.16+ - For building the native library
- C++ compiler - GCC on Linux, Clang on macOS, MSVC on Windows
- libopenmpt - Pre-compiled library and headers
shared/src/desktopMain/
├── cpp/
│ ├── CMakeLists.txt # Build configuration
│ ├── desktop_mod_player_jni.cpp # JNI wrapper code
│ └── build_native.sh # Build script
├── kotlin/com/beyondeye/openmpt/core/
│ ├── DesktopModPlayer.kt # Main implementation
│ ├── DesktopModPlayerNative.kt # JNI interface
│ ├── NativeLibraryLoader.kt # Cross-platform library loader
│ └── ModPlayerFactory.desktop.kt
└── resources/native/
├── linux-x64/
│ ├── libopenmpt.so
│ └── libmodplayer_desktop.so
├── linux-arm64/
│ └── ...
├── macos-x64/
│ ├── libopenmpt.dylib
│ └── libmodplayer_desktop.dylib
├── macos-arm64/
│ └── ...
└── windows-x64/
├── openmpt.dll
└── modplayer_desktop.dll
cd shared/src/desktopMain/cpp
./build_native.shcd shared/src/desktopMain/cpp
mkdir build && cd build
# Configure (auto-detect paths)
cmake ..
# Or specify paths explicitly
cmake .. \
-DLIBOPENMPT_INCLUDE_DIR=/path/to/libopenmpt/include \
-DLIBOPENMPT_LIBRARY=/path/to/libopenmpt.so
# Build
cmake --build . --config ReleaseThe built library will be placed in:
shared/src/desktopMain/resources/native/{platform}/libmodplayer_desktop.so
Where {platform} is one of:
linux-x64linux-arm64macos-x64macos-arm64windows-x64
For each platform, you need two libraries in the resources directory:
-
libopenmpt - The MOD player library
- Linux:
libopenmpt.so - macOS:
libopenmpt.dylib - Windows:
openmpt.dll
- Linux:
-
modplayer_desktop - The JNI wrapper (built from this project)
- Linux:
libmodplayer_desktop.so - macOS:
libmodplayer_desktop.dylib - Windows:
modplayer_desktop.dll
- Linux:
# Debian/Ubuntu
sudo apt install libopenmpt-dev
# Fedora
sudo dnf install libopenmpt-devel
# Arch
sudo pacman -S libopenmptlibopenmpt source is included in this project at libopenmpt/src/main/cpp/.
For macOS (recommended - no external dependencies):
cd libopenmpt/src/main/cpp/macos
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release -j8This builds libopenmpt WITHOUT external dependencies (no MP3, OGG, Vorbis support). All native tracker formats (MOD, XM, IT, S3M, etc.) work perfectly.
The built library will be at: libopenmpt/src/main/cpp/macos/build/lib/libopenmpt.dylib
For macOS with external dependencies (MP3, OGG, Vorbis support):
cd libopenmpt/src/main/cpp
./build/download_externals.sh # Downloads mpg123, libogg, libvorbis
cd build/xcode-macosx
xcodebuild -workspace libopenmpt.xcworkspace -scheme libopenmpt -configuration Release -arch arm64See README_macos_build.md for detailed macOS build instructions.
For Linux:
cd libopenmpt/src/main/cpp
make CONFIG=standard sharedVisit https://lib.openmpt.org/libopenmpt/ for pre-built binaries.
The NativeLibraryLoader class handles cross-platform library loading:
- Detection - Detects current OS (Linux/macOS/Windows) and architecture (x64/arm64)
- Extraction - Extracts libraries from JAR resources to a temp directory
- Loading - Loads libraries using
System.load()in dependency order
Libraries are loaded from: /native/{platform}/ within the resources.
// Create player
val player = DesktopModPlayer()
// Load a module
val moduleData = File("song.xm").readBytes()
player.loadModule(moduleData)
// Play
player.play()
// Control playback
player.pause()
player.seek(30.0) // Seek to 30 seconds
player.setRepeatCount(-1) // Infinite loop
// Get info
println("Title: ${player.getMetadata().title}")
println("Duration: ${player.durationSeconds}s")
// Cleanup
player.release()The desktop implementation uses JavaSound with these defaults:
| Parameter | Value |
|---|---|
| Sample Rate | 48000 Hz |
| Channels | 2 (Stereo) |
| Bit Depth | 16-bit |
| Buffer Size | 2048 frames (~42ms) |
- Check that libraries are in the correct resources directory
- Verify the platform directory name matches your system
- Run with
-Djava.library.pathto debug:java -Djava.library.path=/path/to/libs -jar app.jar
- Ensure libopenmpt is loaded before modplayer_desktop
- Check library dependencies with
ldd(Linux) orotool -L(macOS) - Verify the library was built with the same architecture
- Check that JavaSound is available:
AudioSystem.isLineSupported() - Verify audio format is supported on your system
- Try different audio buffer sizes
- "Could not find libopenmpt": Set
LIBOPENMPT_INCLUDE_DIRandLIBOPENMPT_LIBRARY - "JNI.h not found": Set
JAVA_HOMEor ensure JDK is in PATH - "Undefined reference": Check library linking order
To build for multiple platforms, you'll need to build on each target platform or use cross-compilation:
cmake .. \
-DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"Use the macos-arm64 directory:
mkdir -p shared/src/desktopMain/resources/native/macos-arm64
cp libopenmpt/src/main/cpp/macos/build/lib/libopenmpt.dylib shared/src/desktopMain/resources/native/macos-arm64/Use the macos-x64 directory:
mkdir -p shared/src/desktopMain/resources/native/macos-x64
# Build with -DCMAKE_OSX_ARCHITECTURES=x86_64For distribution, you may need to sign the dylibs:
codesign --force --sign - libopenmpt.dylib
codesign --force --sign - libmodplayer_desktop.dylib- Gradle integration for automatic native build
- Pre-built binaries for all platforms in CI
- Windows support testing
- macOS arm64 support
- macOS code signing automation