Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nada — Local Music Player for Nintendo Switch

Nada is a homebrew music player for Nintendo Switch running Atmosphère CFW. It plays FLAC and MP3 files straight from your SD card in the background, while a game is running — you never have to close the game to change tracks.

Think of it as a small offline music app that lives underneath the system: audio keeps playing while you play, and a slide-in overlay gives you transport controls, cover art, a seek bar, and a folder browser.

How it works

Nada is not a single app. It is two pieces that talk to each other:

Piece File What it does
Sysmodule sys-nada (exefs.nsp) A background service that starts with the console. It reads files from the SD card, decodes audio, mixes it into the system audio output, and owns all playback state (queue, position, volume, shuffle/repeat).
Overlay ovl-nada.ovl The UI. Summoned on top of whatever is running via the Tesla/Ultrahand overlay menu. It draws the interface and sends commands to the sysmodule; it holds no playback state of its own.

They communicate over a custom IPC service. This split is what makes background playback possible: closing the overlay does not stop the music, because the overlay was never playing it in the first place. It also means the UI stays responsive — the overlay never opens an audio file or decodes an image itself.

This follows the same architecture as sys-tune and streamfin-switch, but scoped deliberately narrower: local files only, no streaming, no database.

Features

Playback

  • FLAC and MP3 decoding (via dr_flac / dr_mp3)
  • Audio output through audren/audrv, so playback survives sleep mode
  • Keeps playing in the background while a game runs, and while the overlay is closed
  • Play/pause, next/previous, seek, shuffle, and repeat (off / one / all)
  • Volume control as a PCM multiplier in the sysmodule; the level persists across overlay sessions

Library

  • Automatic queue per folder, sorted alphabetically by filename
  • Hierarchical browser: Albums → Tracks → Now Playing
  • Metadata (title, artist, album) parsed from Vorbis comments (FLAC) and ID3v2 (MP3)
  • Metadata and cover art are loaded lazily — only for what is visible or currently playing — so browsing a library of hundreds of files stays fast

Cover art

  • Embedded artwork from FLAC PICTURE blocks and MP3 APIC frames
  • JPEG (baseline and progressive) and PNG
  • Decoded inside the sysmodule and streamed to the overlay as a small bitmap, never decoded by the overlay
  • Fallback chain when embedded art is missing or too large to decode:
    1. embedded picture
    2. cover.jpg in the track's folder
    3. cover.png in the track's folder
    4. <trackname>.jpg next to the track (per-track cover, useful for mixed folders)

Resilience

  • Per-game blacklist: music auto-pauses when a blacklisted game launches and resumes when you exit it
  • Graceful handling of the AudioRenderer session limit — the sysmodule enters a Disabled state and the overlay tells you, instead of crashing the running game
  • Marquee scrolling for long titles

Requirements

  • Nintendo Switch with Atmosphère CFW
  • Firmware 22.1.0 / Atmosphère 1.11.1 — this is the tested target; other versions are untested and Nintendo has broken ABI across major firmware releases before
  • nx-ovlloader (the ppkantorski fork) and the Ultrahand Overlay menu installed — this is what loads .ovl files
  • For building only: devkitPro with devkitA64, libnx, and switch-tools

Installation

  1. Download the release ZIP, or build it yourself (see Building).
  2. Copy the contents of the archive to the root of your SD card, merging with the folders already there. The result should look like this:
SD:/
  atmosphere/contents/4200000000000ADA/
    exefs.nsp
    toolbox.json
    flags/boot2.flag
  switch/.overlays/
    ovl-nada.ovl
  1. Create a /music folder on the SD card and organise it one folder per album:
SD:/music/
  Album Name/
    01 - Track One.flac
    02 - Track Two.mp3
    cover.jpg          (optional — used when a track has no embedded artwork)
  Another Album/
    ...
  1. Reboot the console.

flags/boot2.flag makes the sysmodule start automatically at boot, so music is available the moment the console is up. If the sysmodule ever gets terminated (for example by a game that fights it for audio resources), the overlay can relaunch it on demand via pmshell — you do not have to reboot.

Track order inside a folder is alphabetical by filename, not by track-number metadata. Prefixing filenames with 01 -, 02 - and so on is what gives you the album order you expect.

Usage

Open the Ultrahand overlay menu the way you normally do (default: L + D-pad Down + Right Stick Click) and pick nada. You land in the album browser; choose an album, then a track, and playback starts.

Controls

Input Action
D-pad Left / Right Move focus between transport controls (repeat, prev, play/pause, next, shuffle)
A Activate the focused control
D-pad Up Move focus to the seek bar
D-pad Left / Right (seek bar focused) Seek backward / forward 5 seconds
X / Y (seek bar focused) Volume up / down by 5%
X / Y (transport focused) Cycle repeat mode / toggle shuffle
D-pad Down Return focus from the seek bar to the transport row
Touch the seek bar Jump to that position
Touch a transport icon Trigger it directly, no focus needed
B Back / close the overlay

Note that X and Y do different things depending on where focus is — volume when the seek bar is focused, repeat/shuffle otherwise. This is deliberate: it gives you volume control without spending a separate button on it.

Configuration

Everything lives under /config/sys-nada/ on the SD card.

blacklist.ini — per-game blacklist

Some games claim both available AudioRenderer sessions, or otherwise misbehave when another audio consumer is present. Listing such a game here makes Nada pause automatically while that game is running, and resume when you leave it.

One title ID per line, hexadecimal. Blank lines are ignored, and # starts a comment:

# Games that don't get along with sys-nada
0100000000010000   # Super Mario Odyssey (example)
01008C0016544000

Limits: up to 32 entries, file must stay under 4 KB. The file is read when the running title changes, so you do not need to reboot after editing it — though returning to the HOME menu and relaunching the game is the reliable way to make the change take effect.

log.txt — debug log

Only written by builds compiled with DEBUG_LOG=1. Release builds do no logging at all, because SD card I/O from a sysmodule is not free.

Troubleshooting

No music, and the overlay says audio is disabled. The running game took both AudioRenderer sessions. This is a hard system limit, not a bug Nada can work around. Add that game's title ID to blacklist.ini so Nada stops competing with it.

The overlay doesn't appear in the Ultrahand menu. Check that ovl-nada.ovl is in /switch/.overlays/ (note the leading dot in .overlays) and that you are running the ppkantorski fork of nx-ovlloader, not the older WerWolv one.

The album list is empty. Nada only looks at /music at the SD card root, and only one level deep — albums must be direct subfolders of /music, with the audio files directly inside them. Only .flac and .mp3 are recognised.

A track plays but shows no cover art. The artwork is probably too large to decode within the sysmodule's tight memory budget (see Limitations). Drop a resized cover.jpg into the album folder, or run tools/fix_covers.py to extract and shrink the embedded artwork automatically:

cd tools
pip install -r requirements.txt
python3 fix_covers.py /path/to/music --dry-run   # preview what would change
python3 fix_covers.py /path/to/music             # do it

A game crashes or soft-locks when Nada is running. Blacklist it. Not every game is compatible, and this is a known limitation inherited from this whole class of audio sysmodules.

Building

source toolchain.env      # ensure DEVKITPRO and DEVKITA64 are set
make                      # builds sysmodule/sys-nada.nsp and overlay/ovl-nada.ovl
make dist                 # assembles the ready-to-copy SD layout in ./out/
make dist DEBUG_LOG=1     # same, but with debug logging to /config/sys-nada/log.txt
make clean

Everything the release ships is then in out/ — copy its contents to the SD card root.

Portlib dependencies, installed with dkp-pacman (not apt/pip/npm):

  • switch-libjpeg-turbo — JPEG cover decoding in the sysmodule
  • switch-curl, switch-zlib, switch-mbedtls — required by the Ultrahand overlay library

Vendored in third_party/:

  • dr_flac.h, dr_mp3.h — audio decoding (public domain / MIT-0)
  • stb_image.h — PNG cover decoding (public domain)

Clone with --recursive, or run git submodule update --init --recursive, so that overlay/lib/libultrahand is present.

CI builds both targets on every push and publishes a packaged ZIP on version tags.

Project structure

sysmodule/     Background service: playback engine, decoders, metadata, IPC server
overlay/       Ultrahand overlay: UI screens and IPC client
common/        Header-only shared code: IPC protocol and type definitions
third_party/   Vendored single-header libraries
tools/         fix_covers.py — detect and resize covers the sysmodule can't decode

common/ is header-only by design and has no Makefile: both sides include the same IPC definitions, so the protocol cannot drift between them.

Limitations

These are mostly consequences of one hard constraint: the sysmodule gets a 5 MB heap, which is the measured ceiling of the system memory pool on FW 22.1.0 / Atmosphère 1.11.1. Asking for 8 MB makes the console fail to boot. Any feature that needs a large buffer has to fit inside that budget.

  • Cover art size caps. Baseline JPEG has no size limit (it is decoded as a stream straight off the SD card). Progressive JPEG needs a coefficient buffer proportional to the image, so it is capped at roughly 1150×1150. PNG is decoded in full and capped at roughly 740×740. Anything above these limits is refused with a log message and falls back to cover.jpg/cover.png — it never crashes. tools/fix_covers.py fixes offending files in bulk.
  • AudioRenderer sessions. The system provides only two. A game using both puts Nada into the Disabled state until you exit that game.
  • Game compatibility. Some games are simply incompatible; the blacklist is the intended remedy.
  • No custom playlists. Queues are built from folders. .m3u and friends are not supported.
  • No streaming, no equaliser, no persistent library database. Out of scope by design.

License

MIT. Third-party libraries keep their own licenses — see third_party/.

About

Background music player for Nintendo Switch — play FLAC/MP3 from SD card while gaming

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages