A cross-platform Nintendo DS music player built with Kotlin Multiplatform. Load any .nds ROM and browse, search, and
play its full soundtrack — on iOS, Android, and desktop.
| Platform | Link |
|---|---|
| iOS (App Store) | Download on the App Store |
| Android (Google Play) | Get it on Google Play |
| Desktop (macOS / Linux / Windows) | GitHub Releases |
- Open any
.ndsROM — drag-and-drop or use the file picker on every platform - Browse the full soundtrack — tracks are grouped by SDAT sound archive within the ROM
- Search — filter tracks instantly by name across all archives
- Full playback controls — play, pause, resume, stop, next, and previous
- Configurable loop count — choose how many times each track loops before advancing
- Auto-advance — plays the next track automatically when one finishes; silently skips unplayable tracks
- Lock screen & Now Playing — artwork-free Now Playing card with remote controls on iOS
- Background audio — keeps playing when the screen is off (iOS)
- High-quality audio — powered by FluidSynth for faithful SF2 soundfont rendering
| Layer | Library |
|---|---|
| UI | Compose Multiplatform 1.10 |
| Architecture | Kotlin Multiplatform + ViewModel + Clean Architecture |
| NDS parsing & MIDI generation | kotlinds |
| Audio engine | fluidsynth-kmp — FluidSynth wrapped for KMP |
| Dependency injection | Koin |
- Parse —
kotlindsreads the ROM binary, extracts all SDAT sound archives, and exposes the track list - Convert — each track is converted on-demand to a standard MIDI file + SF2 soundfont
- Render —
fluidsynth-kmpfeeds the MIDI and SF2 to FluidSynth, producing high-quality PCM audio on all platforms
Prerequisites: JDK 17+, Android SDK, Xcode 16+ (for iOS)
git clone https://github.com/nathanfallet/nds-music-player
cd nds-music-player./gradlew :composeApp:assembleDebug./gradlew :composeApp:runOpen iosApp/iosApp.xcodeproj in Xcode and run on a simulator or device.
Or via Gradle:
./gradlew :composeApp:embedAndSignAppleFrameworkForXcodecomposeApp/
├── commonMain/ # Shared UI, ViewModel, domain logic, AudioPlayer
├── iosMain/ # iOS audio session, Now Playing, lock screen controls
├── androidMain/ # Android entry point
└── jvmMain/ # Desktop entry point
iosApp/ # Xcode project / Swift entry point