Cygnus Player is a minimalist, high-performance local audio player for Android, specifically engineered to handle massive, duplicate-heavy M3U/M3U8 playlists of MP3 files with sub-millisecond responsiveness.
Designed for collectors with large libraries, Cygnus Player prioritizes a low memory footprint (
Cygnus Player is a personal, open-source hobby project provided strictly as-is without official support or formal issue tracking. Feature scope is intentionally tight and focused on a minimalist MP3 playback engine. However, pull requests targeting main (or develop) that align with project goals and technical standards may be accepted. Please review CONTRIBUTING.md before submitting code changes. You are also welcome to fork the repository and adapt the code to suit your own needs under the terms of the MIT License.
The name is a direct tribute to the legendary progressive rock band Rush and their epic multi-album masterpiece, Cygnus X-1.
Specifically, it honors what is "the ultimate transition" in rock history: the frantic, heavy sci-fi cliffhanger ending of "Cygnus X-1 Book I: The Voyage" (the finale of A Farewell to Kings) bridging seamlessly into the triumphant, complex multi-movement opening of "Book II: Hemispheres" (on the subsequent Hemispheres album).
Modern commercial streaming apps and feature-bloated players treat music like a data-harvesting commodity. To borrow a line from Rush in Superconductor: "That's entertainment!" But it isn't an optimal listening experience for a serious archive. Traditional metadata-tag or basic folder-shuffling players completely destroy complex narrative arcsβrandomly inserting a compressed compilation track or a raw bootleg right in the middle of a gapless, multi-album experience.
Cygnus Player is engineered to anchor precise M3U sequences down to the millisecond, preserving historical sonic continuity while still providing the mathematical flexibility to drop the entire library into total chaos mode when desired.
- Sequence-Critical Playback: Unlike traditional players, Cygnus treats every entry in an M3U playlist as a unique node. Duplicate tracks in a sequence retain independent selection probabilities and metadata context.
- Massive Scale Support: Optimized Room database schema and primitive-array shuffle mappings handle 45,000+ item queues and 78,000+ track libraries with zero lag.
- Advanced Shuffle Modes:
SEQUENTIAL: Standard linear playback.TRACK_RANDOM: True shuffle across the entire sequence.RANDOM_FOLDER_SEQUENTIAL: Shuffles directory groups, playing all tracks within a folder sequentially before moving to the next random folder (with a 24-folder history buffer).
- Minted Identity: Playlists are assigned a shuffle strategy at the moment of creation, ensuring deterministic behavior for specialized collections (e.g., a dedicated "Chaos" vs. "Album" playlist).
- No-Skip Philosophy: Designed for the "full experience" listenerβno forward/back controls. Playback is an immutable journey once started.
- Dynamic ReplayGain: "Smart" real-time volume normalization. Automatically applies
ALBUM_GAINfor sequential flows andTRACK_GAINfor randomized tracks. - Persistent State Across Updates: Per-playlist state (last played
sequence_id, exact track position, activeShuffleMode, and exact shuffled order mapping) is stored in the Room database, ensuring all playback positions and custom shuffle orders are retained across application restarts and updates. - Modern Android Core: Built for Android 16 (API 36) using Jetpack Compose, Media3 (ExoPlayer), and Jetpack Glance.
| App Launch | Select Music Root | SAF Root Permission | Media Permission |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Empty Playlist History | Mint Shuffle Mode | Active Playlist History |
|---|---|---|
![]() |
![]() |
![]() |
| Minimalist Now Playing | Home Screen Widget | Settings & Diagnostics |
|---|---|---|
![]() |
![]() |
![]() |
Cygnus Player is fully compatible with modern Android Scoped Storage requirements. To ensure high-performance relative path resolution for massive libraries, please follow these steps:
- Select Music Root: On first launch, use the prompt to select your main music directory (e.g.,
Internal Storage > Music). This grants Cygnus persistent, recursive access to your entire library. - Relative Path Resolution: The app uses the MediaStore API to map M3U relative paths (e.g.,
Rush/2112/01 - 2112.mp3) to system-registered content URIs. This avoids restricted direct filesystem access and ensures absolute sequence integrity. - Permissions: Ensure the
READ_MEDIA_AUDIOpermission is granted to allow the system to index your music files for the MediaStore.
This project is developed and managed using Google AI models. The architecture, implementation, and repository maintenance are guided by specialized AI agents to ensure high-performance, minimalist engineering standards.
- Target Platform: Android 16 (API Level 36)
- UI Framework: Jetpack Compose
- Playback Engine:
androidx.media3:media3-exoplayer&MediaSessionService - Database:
androidx.roomwith SQLite indexing onsequence_idandfile_path - Widgets:
androidx.glance - Language: Kotlin with Coroutines and Flow
Traditional media queues struggle with duplicate file paths. Cygnus Player maps every playlist entry to a unique sequence_id. This allows the engine to distinguish between multiple occurrences of the same file, preserving the exact intent of the M3U creator.
To ensure "Resuming" never triggers a new shuffle, Cygnus stores the generated playback sequence (LongArray) in the database. When you switch between your specialized playlists, you return to the exact track and sequence state you left.
To support enormous playlists, the ShuffleEngine operates on primitive LongArray mappings. Heavy domain models and metadata are never loaded for the entire queue at once. Instead, Cygnus utilizes a sliding cursor window of a few tracks at a time to lazily populate UI and media session context.
The database tracks folder relationships via a dedicated FolderEntity. This enables native support for folder-based automation and randomized folder sequencing without expensive file system traversals during playback.
Cygnus Player utilizes a custom-designed Adaptive Icon that reflects the cosmic and musical themes of the project:
- Design Principle: A minimalist geometric representation of Cygnus X-1, the first black hole discovered in our galaxy.
- Visual Elements:
- The Singularity: A central circle (Monokai Purple) representing the core of the music and the point of no return for the listener's focus.
- The Event Horizon: Four symmetrical circular arcs (Monokai Blue/Cyan) implying rotation, gravitational pull, and the dynamic energy of the audio sequence.
- Rationale: The design uses common geometric property to ensure absolute Copyright Safety. It avoids literal depictions or franchise-specific imagery while maintaining a high-performance, scientific aesthetic.
- Accessibility: Optimized for the Monokai Pro (Filter Spectrum) palette, ensuring high visibility for red-green color-blind users.
- Core Database: Entities for massive library management.
-
TrackEntity: Metadata and ReplayGain storage (with non-nullable defaults). -
FolderEntity: Directory-aware grouping. -
QueueEntity: Unique sequence mapping (duplicates support). -
PlaylistState: Per-M3U persistence with strongly-typedShuffleMode.
-
- Data Logic: High-performance M3U parser and file picker integration.
- M3U/M3U8 Parser: Efficient line-by-line relative path resolution.
- Metadata Extractor: Background extraction of ReplayGain and media tags (with
"<not found>"fallback). - File Picker: System integration for playlist selection.
- Playlist History: Manageable UI for recently opened M3Us (supports removal).
- Core Logic: High-performance sequence and shuffle management.
-
ShuffleEngine:$O(1)$ memory-efficient primitive array mappings with Forward-Only logic. - Folder-Sequential Logic: History-aware directory shuffling (24-folder buffer).
- ReplayGain Controller: "Smart" gain switching logic (Album vs. Track).
-
- Playback: Media3 Service integration with ReplayGain and Audio Focus.
-
MediaSessionService: Foreground service with Android 16 security bounds. - ExoPlayer Core: Gapless transition and volume normalization.
- Lazy Queue Controller: Sliding window logic for
$O(1)$ memory playback. - System Integration: Audio Focus and
BECOMING_NOISYhandling. - Playback Control Integration: Hooking UI Play/Pause actions to the service.
- Scoped Storage Compliance: Folder-based access and MediaStore URI resolution.
-
- UI Baseline: Minimalist playback screen (Index/Total display) and Home Screen Widget. No navigation controls.
- Theme: Monokai Pro palette with high-contrast accessibility (Red-Green).
- App Icon: "Singularity" adaptive icon (Cygnus X-1 theme).
- Main Screen: Branding updated to "Cygnus Player" and Marquee text implemented.
- Glance Widget: Minimalist 4x1 high-contrast remote views with dynamic artwork.
- Advanced Features: Relative path sanitization improvements, Scrobbler integration testing, and Minimalist Android Auto support (via
MediaLibraryServicefor safe, voice-controlled library access).- Android Auto & AAOS Support: Minimalist browsing and dashboard control.
- Position Persistence: Per-playlist millisecond-accurate resumption.
- Smart Bluetooth: Automated playback resumption upon device reconnection.
To maintain "Zero-Manual-Discovery" of bugs while bypassing framework-level environmental issues and supporting multi-device environments (Phone vs. Emulator), use the following PowerShell aliases.
To obtain your target device serial numbers, run adb devices in your terminal:
adb devices
# Output example:
# List of devices attached
# 44201JEKB09382 device
# emulator-5554 deviceReplace "44201JEKB09382" with your physical device's serial number, or pass "emu" to target the local emulator (emulator-5554).
Add these helper functions to your PowerShell $PROFILE:
function ctest {
param([string]$target = "44201JEKB09382")
if ($target -eq "emu") { $target = "emulator-5554" }
adb -s $target shell input keyevent 224
adb -s $target shell wm dismiss-keyguard
adb -s $target uninstall com.festerhead.cygnusplayer
try {
$env:ANDROID_SERIAL = $target
./gradlew test connectedDebugAndroidTest
} finally {
$env:ANDROID_SERIAL = $null
}
}
function crun {
param([string]$target = "44201JEKB09382")
if ($target -eq "emu") { $target = "emulator-5554" }
adb -s $target shell input keyevent 224
adb -s $target shell wm dismiss-keyguard
adb -s $target uninstall com.festerhead.cygnusplayer
try {
$env:ANDROID_SERIAL = $target
./gradlew installRelease
} finally {
$env:ANDROID_SERIAL = $null
}
adb -s $target install app/build/outputs/apk/release/app-release.apk
adb -s $target shell am start -n com.festerhead.cygnusplayer/.MainActivity
}
function cdebug {
param([string]$target = "44201JEKB09382")
if ($target -eq "emu") { $target = "emulator-5554" }
adb -s $target shell input keyevent 224
adb -s $target shell wm dismiss-keyguard
adb -s $target uninstall com.festerhead.cygnusplayer
try {
$env:ANDROID_SERIAL = $target
./gradlew installDebug
} finally {
$env:ANDROID_SERIAL = $null
}
adb -s $target shell am start -n com.festerhead.cygnusplayer/.MainActivity
}Note
The $env:ANDROID_SERIAL environment variable ensures Gradle tasks (like connectedDebugAndroidTest or installDebug) target only the specified serial when multiple Android devices are connected.
If you prefer the standard Gradle tasks, ensure the emulator is awake and unlocked first:
# Wake up and unlock
adb shell input keyevent 224; adb shell wm dismiss-keyguard
# Run full suite
./gradlew test connectedDebugAndroidTestThe fastest way to deploy is using the crun or cdebug aliases defined above. Alternatively, use the manual commands:
./gradlew :app:assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n com.festerhead.cygnusplayer/.MainActivityNote
If adb is not recognized, you may need to use the full path to the executable (e.g., C:\Users\<User>\AppData\Local\Android\Sdk\platform-tools\adb.exe) or add the platform-tools directory to your system's PATH.
Cygnus Player utilizes GitHub Actions for continuous integration and delivery:
- Debug Builds (
develop): You can manually trigger a Debug build from the Actions tab usingworkflow_dispatchon thedevelopbranch (or any other branch). The resulting APK is available to download as a temporary Artifact. - Release Verification (PRs to
main): Opening a Pull Request against themainbranch automatically triggers theAndroid Release Buildworkflow. It securely signs and builds a Production-ready APK, attached as a temporary Artifact for verification. You can also manually trigger this workflow viaworkflow_dispatchon thedevelopbranch for pre-release testing. - Automated GitHub Releases (Pushes to
main): When a Pull Request is merged intomain, the workflow automatically creates a public GitHub Release and attaches the signed Production APK to it. The release tag (e.g.,v1.0.0) is automatically determined by reading theversion.propertiesfile.
The app's version is maintained in two locations for build stability:
version.properties: The primary source of truth used by Gradle and CI/CD.VersionInfo.kt: A static object in the source code used by the UI to avoidBuildConfigrace conditions in experimental environments.
Before merging to main to trigger a release, ensure both files are updated:
# version.properties
VERSION_NAME=1.0.3
VERSION_CODE=4// VersionInfo.kt
object VersionInfo {
const val VERSION_NAME = "1.0.3"
const val VERSION_CODE = 4
}Gradle will automatically inject these values into the APK, and the GitHub Action will parse them to name your automated Release!
To enable automated signed releases, the GitHub repository must be configured with the following Repository Secrets (Settings > Secrets and variables > Actions):
KEY_ALIAS: The alias for the signing key.KEY_PASSWORD: The strong UUID password for the key.KEYSTORE_PASSWORD: The strong UUID password for the keystore.KEYSTORE_BASE64: The full base64-encoded string of thecygnus-release.keystorebinary file.
- "Impact Moderato" by Kevin MacLeod (incompetech.com). Licensed under Creative Commons: By Attribution 4.0 License. Used for automated metadata extraction testing.
Cygnus Player adheres to Keep a Changelog and Semantic Versioning. Check CHANGELOG.md for the latest updates.









