Skip to content

Repository files navigation

Rewamp

Rewamp - Retro Wave Media Player

Rewamp is the successor to Modizer, a demoscene and retrogaming oriented music player that was limited to iOS devices.

It is multiplatform, with a focus on MacOS/iOS/Android as primary targets and Linux/Windows as secondary ones.

Development of Rewamp involves AI usage, mostly as an accelerator to develop the UI part and some of the backend features. Several playback engines customization are reusing the work I've done previously for Modizer.

Main features

  • 1248 file extensions, 45 decoding engines — Amiga MOD, C64 SID, NSF, SPC, GBS, PSF, VGM, SNDH, ZX Spectrum AY, PC-98, X68000, game streams (Wwise, FSB…). Real chip emulation, not renderings: a 6502 runs the NSF driver, a 68000 runs the Amiga replayers.
  • Subsongs are first-class. A .nsf, .gbs, .sid or .sndh expands into its tracks, named with what the file itself carries — NSFe track labels, HVSC's STIL, the UADE song database — rather than numbers.
  • Seven visualizers: stereo oscilloscope, per-voice oscilloscope, spectrum, live notation, a piano keyboard, the tracker's own pattern grid, and Milkdrop presets through projectM. All GPU-rendered, and fed with what you are hearing.
  • Per-voice control: mute or solo a single chip channel while it plays, and watch each one's waveform and note.
  • Playback speed from 0.2× to 3×. Most engines change the tempo and keep the pitch — the speed-up happens in the song's own sequencer or in the emulated machine, not in the audio stream; the others resample.
  • Gapless playback, with an optional crossfade.
  • System media controls everywhere: Now Playing on iOS and macOS, the media notification on Android, MPRIS on Linux, the media overlay and media keys on Windows.
  • Queue with shuffle and repeat, playlists, favourites, listening statistics, and a library that survives a reinstall.
  • Optional online catalogue — browse and stream a large preservation archive with artwork, demoscene productions and per-subsong lengths. Local playback needs no server at all.
  • Plays archives (zip/7z/rar/lha) in place, follows an M3U found inside one, and takes files dropped on the window or opened from the file manager.
  • 18 languages.

Current coverage

Platform Minimum Status
iOS 15.0 Primary target — beta on TestFlight
Android 8.0 (API 26) Primary target — beta on the Play internal track
macOS 13.0 (Ventura) Primary target — beta as a notarized DMG
Linux glibc 2.35 (Ubuntu 22.04, Debian 12) Released as an AppImage, x86_64 and aarch64 — see Releases
Windows 10, x64 Released as a portable zip — see Releases

Android ships 64-bit only (arm64-v8a, x86_64). A 32-bit build links and runs but silently loses the formats whose codec goes through FFmpeg, and only 32-bit-only devices — pre-2018, low end — would ever have received it.

These floors come from what the engine needs, not from taste: API 26 is where AAudio arrives on Android, and the Apple versions are what the GLES visualizer stack and the Flutter toolchain require. The Linux floor is the glibc of the container the AppImage is built in (see .github/workflows/appimage.yml).

The AppImage needs fusermount3 and /dev/fuse, present on any modern desktop — not libfuse2, its runtime is statically linked. If mounting fails anyway: ./Rewamp-*.AppImage --appimage-extract && ./squashfs-root/AppRun (--appimage-extract-run does not exist in this runtime). Audio goes through PulseAudio/PipeWire: libpulse0 must be installed, or everything runs and nothing plays. On an X11 session the visualizers use a CPU read-back path, slower than the Wayland one but complete.

The Windows zip needs no installer: unpack it anywhere and run rewamp.exe (the Visual C++ runtime travels with it). It is not code-signed yet, so SmartScreen warns on the first launch. Every engine is built, and the visualizers and projectM run on a bundled ANGLE over OpenGL. Demoscene production videos are not available there yet: the embedded web view has no Windows implementation.

Tech architecture

Flutter for the UI, C/C++ for everything that makes sound, joined by FFI. miniaudio drives the output device.

  • A decoder is a plugin. Each engine exposes one vtable — probe / open / read / seek / length / close — and the registry picks a winner per file by scoring extension and header, so a format recognised by its magic beats one merely claimed by a generic suffix. The core itself knows no formats.
  • Decoding never runs in the audio callback. A dedicated producer thread, in the realtime scheduling band, keeps a ring buffer filled; the callback only pops and copies. Audio is immune to UI load by construction rather than by politeness.
  • Oscilloscope data is timestamped, not sampled. Chip cores write per-voice samples as a side effect of decoding, keyed by absolute frame; the visualizers read at the consumer position. That is what keeps the picture matching the sound under a two-second look-ahead.
  • Visualizers are native GL (GLES 3.0) shared across platforms: ANGLE over Metal on macOS/iOS behind a Flutter texture, a SurfaceView on Android, an EGLImage shared with GTK on Linux (a CPU read-back path under X11), and our own ANGLE build on Windows.
  • The engine is built twice from one source set — CocoaPods podspecs on Apple, CMake for Android, Linux and Windows. Keeping the two in step is a standing constraint of the project.
  • Library, playlists and history live in a local SQLite database. The online catalogue is a set of HTTPS RPCs, and the app is fully usable without it.

Building from source

Flutter 3.41 or newer (Dart 3; the Linux CI builds with 3.47.5, the Windows CI with 3.47.6). Then, per platform: Xcode for macOS/iOS, the Android SDK + NDK for Android, Visual Studio 2022 or newer with the Desktop development with C++ workload for Windows (CMake and Ninja come with it), and on Linux:

sudo apt install clang cmake ninja-build pkg-config \
     libgtk-3-dev libegl1-mesa-dev libgles2-mesa-dev \
     libasound2-dev libsecret-1-dev libbz2-dev \
     libavcodec-dev libavformat-dev libavutil-dev libswresample-dev
sudo apt install libpulse0        # runtime, see below

Two of these hide their absence. The FFmpeg -dev packages: without them the build succeeds and vgmstream silently loses every format whose codec goes through FFmpeg (Vorbis, Opus, AAC, ATRAC3, WMA, XMA…) — CMake picks the system FFmpeg up through pkg-config, or a prebuilt one through REWAMP_FFMPEG_DIR. And libpulse0 at runtime: no -dev package pulls it in and miniaudio only dlopens it, so without it everything compiles, the app starts, and nothing plays. On Ubuntu 22.04 the stock clang is 14 and is not enough: install clang-15 (in the distribution's repositories) and select it with update-alternatives, as the CI does.

1. Clone, submodules included

git clone --recurse-submodules https://github.com/yoyofr/rewamp-app.git
cd rewamp-app

On Windows, switch Git to LF first — git config --global core.autocrlf false, before cloning. With Git for Windows' default (true) the patch files of step 2 are checked out with CRLF line endings and sync_vgmstream.sh fails with a conflict, which looks like a stale patch and is not.

Three decoders are git submodules pinned to genuine upstream commits (libopenmpt, vgmstream, libvgm). If you already cloned without them: git submodule update --init --recursive.

2. Apply the rewamp patches — do not skip this

The submodules point at unmodified upstream. Rewamp's changes to them live in packages/rewamp_audio/patches/ and are applied as working-tree changes:

cd packages/rewamp_audio
./scripts/sync_libopenmpt.sh
./scripts/sync_vgmstream.sh
./scripts/sync_libvgm.sh

(On Windows, run them from Git Bash.) A later git submodule update resets the submodules to upstream and erases the patches: run the three scripts again after it.

Skipping this compiles fine and misbehaves quietly — no Wwise Vorbis in vgmstream, XM modules with a broken 16-bit tempo, missing per-voice capture in libvgm. The other vendored engines are committed already patched; only these three need the step.

3. Bootstrap the platform scaffolding

./setup.sh          # regenerates the Runner glue, leaves hand-written code alone

4. Prebuilt libraries

Apple and Windows. None of these are committed (they are large binaries). On Apple each release script checks for them, because the failure otherwise surfaces as an unrelated link error:

cd packages/rewamp_audio
./scripts/build_angle_macos.sh      # or build_angle_ios.sh — offscreen GLES for the visualizers
./scripts/build_libopenmpt_macos.sh # or _ios.sh
./scripts/build_vgmstream_macos.sh  # or _ios.sh

Rebuild the libopenmpt/vgmstream ones after any patch change: a plain flutter build links the previous .a and will not see it.

On Windows, once per checkout, from a PowerShell prompt (both go through the vcpkg that ships with Visual Studio — nothing to install, network required):

powershell -File packages/rewamp_audio/scripts/build_angle_windows.ps1   # GLES for the visualizers
powershell -File packages/rewamp_audio/scripts/build_ffmpeg_windows.ps1  # minimal FFmpeg for vgmstream

Without them the CMake configuration stops on purpose; set REWAMP_WINDOWS_VIZ=0 or REWAMP_ALLOW_NO_FFMPEG=1 to build without the visualizers or without FFmpeg. One more Windows trap: a plugin fetches CppWinRT through NuGet, and a %APPDATA%\NuGet\NuGet.Config that declares no package source fails with primarySources while blaming the package — add https://api.nuget.org/v3/index.json as a source.

Android and Linux build every decoder from source through CMake — nothing to prebuild.

5. Build and run

cd app
flutter pub get
cd macos && pod install && cd ..   # Apple only, and AFTER pub get
flutter run -d macos               # or: -d ios, -d android, -d linux, -d windows

The manual pod install is required on Apple: the Podfile reads the ephemeral xcconfig that flutter pub get generates. flutter clean wipes it, so run flutter pub get again before the next pod install.

Pointing at a backend

The catalogue, artwork and account features talk to a server. A build made here defaults to the project's own instance, https://api.rewamp.app, which is what the published binaries are built against. Point a build at your own server instead:

flutter run --dart-define=REWAMP_API_URL=https://your-instance

That server is run for the published app, not as a public API: treat it as something that may rate-limit, change shape or require an authenticated client without notice. A fork meant for other people should host its own.

Local playback of your own files needs no server at all — the catalogue is entirely optional.

Turning engines off

Every decoder is on by default and gated by a flag; set one to 0 to leave it out:

REWAMP_WITH_OPENMPT=0 flutter build macos

The environment variable works the same way for the CMake builds (Android, Linux, Windows); a -D on the CMake command line takes precedence.

Known gap

The prebuilt ffmpeg-kit slices for Android are not redistributed here. Without them vgmstream builds without FFmpeg and loses the formats whose codec goes through it; everything else is unaffected. Drop your own under packages/rewamp_audio/third_party/ffmpeg-kit/android-{arm64,x86_64}/ffmpeg/, or point CMake at a build with REWAMP_FFMPEG_DIR (environment or -D). On Linux the system FFmpeg is found through pkg-config instead, and the AppImage carries its own minimal build (scripts/appimage/build_ffmpeg_minimal.sh). On Windows, build_ffmpeg_windows.ps1 (step 4) builds it.

Statistics

Lines of code (cloc, comments and blank lines excluded), Rewamp's own code apart from the engines and libraries it bundles. Recomputed at every snapshot.

[ LANGUAGES BREAKDOWN — lines of code ]

                    Rewamp      Bundled        Total
C                   10,715    1,820,271    1,830,986
C/C++ Header         2,100    1,347,667    1,349,767
C++                 22,221      994,836    1,017,057
Dart                87,440            0       87,440
Assembly                 0       36,627       36,627
Objective-C++          767        3,044        3,811
GLSL                   362          989        1,351
Swift                  777            0          777
Kotlin                 672            0          672
Objective-C             26            0           26
Others               8,911      262,355      271,266

TOTAL              133,991    4,465,789    4,599,780

Licence

Rewamp is GPL-3.0-or-later. The app's own code could be more permissive, but the binary statically links GPL-3 decoders (ZXTune, sc68, vio2sf), so the whole is GPL-3.

Every bundled engine and library keeps its own licence and authors — see THIRD-PARTY-NOTICES.md, generated from the same list the app shows under Settings → About.

Some licences ask for a visible credit; those appear in the app itself: SunVox (© Alexander Zolotov · WarmPlace.ru), the UADE song database (CC BY-NC-SA), Milkwave's transition patterns (BSD-3), and Jan Mróz's spectrum shader (CC BY 3.0).

UnRAR is included for solid RAR/RSN archives under its own licence: its source "may be used in any software to handle RAR archives without limitations free of charge, but cannot be used to develop RAR (WinRAR) compatible archiver and to re-create RAR compression algorithm, which is proprietary" — full text in packages/rewamp_audio/third_party/unrar/license.txt.

Releases

Packages

Contributors

Languages