Skip to main content

Music and Books

Two apps that are not emulators: an MP3 player (media-go) and an e-book reader held upright (ebook-go). Both are SD apps: their images live in /retro-go/apps/ on the card and take turns with OutRun in the shared sdapp partition (640 KB). The flash layout does not change. Starting one when another SD app is in the partition copies it first. This takes 4-6 s, on a black screen with the hourglass and a percentage.

MusicBooks
Launcher tabMusic (/roms/music)Books (/roms/books)
Files.mp3.epub (no DRM), .txt
App, sizemedia-go, 444 KBebook-go, 568 KB
Started byA on the file (no game menu)A on the file (no game menu)
Sourceretro-go/media-go/main/main.cretro-go/ebook-go/main/main.c

Test media stay out of git: test-media/{audio,book,video} in the main repository is ignored.

Music (media-go)​

KeyAction
Aplay / pause
X / Y, LEFT / RIGHTprevious / next track (LEFT restarts the track after 3 s)
L / Rback / forward 10 s
UP / DOWNvolume ±5 %
Bback to the launcher
MENUretro-go menu
  • Playlist: the other .mp3 files of the same folder, sorted by name. At the end of the playlist the player stops on the first track.
  • Decoding: minimp3 (CC0, unmodified) on a task on core 1. rg_audio_submit() blocks on the I2S queue and so paces the decoder.
    • minimp3 keeps a ~16 KB scratch on its stack, so the task has 32 KB.
  • Output: 32 kHz like every app on this board. Other rates are resampled linearly, and mono is doubled to stereo.
  • Screen: title, format, progress, volume, and a 48-band spectrum. The spectrum is a 512-point FFT of the latest decoded samples: Hann window, bands log-spaced from 40 Hz to 16 kHz, scale -66..0 dB, with a slow fall. The UI is drawn at 20 Hz. An earlier peak meter sat at full scale on any modern master.
  • Duration and seeking: both come from the first frame's bitrate.
    • Duration: file size divided by the bitrate, with the ID3v2 tag skipped.
    • Seeking: L/R jump in the file by bytes, and minimp3 resyncs on the next frame.

Board check (2026-10-16):

  • Test tones at 440 Hz (44.1 kHz stereo) and 1 kHz (22.05 kHz mono) came out at 440 and 1000.0 Hz in the submitted samples (board_ctl.py acap).
  • Next/previous, pause (no samples), seek and B all work.
  • A 128 kbps song plays without dropouts.

Books (ebook-go)​

The console is held a quarter turn, D-pad below the screen. "Rotation" in the menu flips it to D-pad above. The keys follow the picture.

KeyAction
A, R, D-pad toward screen right or bottomnext page (into the next chapter)
L, D-pad toward screen left or topprevious page
X / Ysmaller / larger text (3 sizes)
B or STARTreader menu; B closes it
MENUback to the launcher (the position is saved)

The menu holds: continue, chapters (list of the book's chapters), text size, light/dark theme, rotation, back to the launcher. The reading position is saved per book in /retro-go/saves/ebook-go/<file>.pos every 5 pages and on exit.

How a book is read​

  • EPUB: the app has its own ZIP reader. It reads the central directory and inflates with the ROM's tinfl. The built-in rg_storage_unzip_file only reads the first entry, which is not enough for an EPUB.
    • META-INF/container.xml gives the OPF file, and the OPF spine gives the chapters.
    • The XHTML of each chapter becomes words, paragraphs, headings, italic and bold, and entities are decoded. CSS is ignored.
    • Inline tags (<i>, <a>, <span>) glue the pieces of a word, so no stray spaces appear before punctuation.
    • A chapter's title is its first heading.
    • The cover comes from the OPF (cover-image, or <meta name="cover">). When no page shows it, it becomes the first page.
  • Pictures: JPEG goes through the ROM's TJpgDec (baseline only, descaled by 1/2-1/8 before the final fit), PNG through lodepng. An SVG page uses its PNG or JPG twin when the book ships one.
  • TXT: the file is read as UTF-8 when it is valid UTF-8, else as Windows-1252.
    • Hard-wrapped lines are joined when the text separates its paragraphs with blank lines.
    • The text is cut into parts of about 24 KB, so that a part is laid out quickly.
  • Layout: justified text, with a first-line indent and long words split.
    • A line that would stretch by more than six spaces per gap is left ragged.
    • Font: Noto Serif (SIL Open Font License 1.1), regular, italic and bold at 15, 18 and 22 px, 4-bit anti-aliased.
    • Character set: Latin-1 plus the Italian typographic marks (’ “ ” « » – — …) and €.
    • The fonts are generated by retro-go/tools/ebook_fonts.py into ebook-go/main/fonts.h.
  • Portrait: the page is drawn into a 320×480 buffer and rotated into the panel's 480×320 frame on the way out. The display driver is unchanged.
  • Fade-in: the app fade-in in rg_display.c advances only when a frame is sent. A page is sent once, so the first frame is re-sent for 400 ms, until the fade has finished. Without this the screen sometimes stayed dark until the first key.

Not supported:

  • PDF: fixed A4 pages are unreadable at 320 px, and a PDF engine is too heavy.
  • MOBI/AZW: Amazon formats, nearly always with DRM. Calibre converts DRM-free books to EPUB.
  • Progressive JPEG: shown as [picture].

Where to find books:

  • Standard Ebooks (English, public domain).
  • Liber Liber (Italian).
  • Project Gutenberg cannot be reached from Italian ISPs (blocked at DNS).

Board check (2026-10-16): framebuffer shots (console shot) and the webcam.

  • Standard Ebooks' The Adventures of Pinocchio: cover JPEG 1400×2100, title page and logo PNG, 41 chapters.
  • An Italian test TXT: accents, typographic quotes, dashes, €, joined lines and a split long word.
  • Page turns, X/Y sizes, the chapter list, the dark theme and the rotation all work.
  • The position resumes after MENU and reopening.
  • Three entries out of three were fully lit 3 s after the start.

Testing on the PC​

retro-go/ebook-go/test/run.sh <book> <keys> <sheet.png> builds the reader's C file on Linux and runs it.

  • Build: against stub headers (ebook-go/test/inc: rg_* calls replaced, zlib in place of the ROM tinfl, no JPEG), with AddressSanitizer.
  • Run: a key script drives it, one character per press (a next, b menu, x/y size, u/d/l/r D-pad).
  • Output: a contact sheet of the pages it sent.

This bench found the chapter-menu overflow before it reached the board.