Skip to content

iPixelGalaxy/Spicy-Player

 
 

Repository files navigation

Spicy Player

Spicy Player is an offline music player for Android with a port/recreation of Spicy Lyrics - A Spicetify Extension, designed to achieve visual parity with Spicy Lyrics' rendering. Built using Jetpack Compose (Canvas API) and ExoPlayer.

This repo also includes an iOS SwiftUI app target under ios/project.yml. The iOS version uses an app-local library workflow: import songs or whole folders, copy audio and .ttml files into the app's storage, auto-pair by basename, and render synchronized lyrics during playback.

Warning

This is a work in progress. The app is not yet complete and may have bugs.


Key Features

High-Fidelity Lyrics Rendering

  • Sub-Pixel Text Positioning: Uses Compose's TextMeasurer for exact glyph calculation.
  • Duet-Aware Layout: Identifies primary (v1) and guest (v2) artists from TTML metadata.
  • Intelligent Alignment: Primary artists are justified to the left, and guest artists to the right, with multi-line blocks correctly right-aligned for balance.

Physics-Driven Motion

  • Analytic Spring Engine: Replaces traditional Euler/Verlet integration with a mathematically exact (closed-form) solution for damped harmonic oscillators. This ensures that a target set 500ms in the future is reached exactly, with zero energy drift.
  • Critical Damping (ζ = 1.0): Used for all auto-scroll centering to provide the fastest non-oscillatory return possible.
  • Under-damped Springs: Used for word-bouncing and interlude-dot expansions to provide a lively, bouncy character.

Synchronized Animations

  • Held Word Bounce: Syllables with a duration >= 1000ms automatically receive a bouncy scale and Y-offset animation, highlighting them letter-by-letter.
  • Instrumental Break Dots: Injected automatically for gaps >= 3000ms, featuring a "breathe" pulse effect synced with the song's timing.
  • Seamless Seek: Clicking any lyric line instantly re-bases the physics spring to the current visual position, ensuring no "snap-back" jumps when transitioning from manual scrolling back to auto-focus.

Project Architecture & Core Logic

1. The Rendering Pipeline (LyricsRenderer.kt, SpicyLyricsView.kt)

The app uses a single-pass rendering loop that updates at the device's native refresh rate (60Hz/90Hz/120Hz).

  • Coordinate Systems:
    • Canvas Space: (0, 0) is the top-left of the view.
    • Scroll Space: A virtual Y-coordinate where the lyrics live.
    • Screen Center: Used as the anchor point for the active line focus.
  • Calculations: targetY = -clusterCenterY (where the cluster is the average Y-position of all currently active lines).

2. The Physics Solver (SpringSimulation.kt)

Solving $x'' + 2\zeta\omega x' + \omega^2 x = \omega^2 \cdot \text{goal}$ for $x(t)$:

  • Critically Damped ($\zeta = 1$): $x(t) = (x_0 + (v_0 + \omega x_0)t)e^{-\omega t}$
  • Under-damped ($\zeta < 1$): $x(t) = e^{-\zeta\omega t}(A \cos(\omega_d t) + B \sin(\omega_d t))$ This ensures perfect smoothness regardless of fluctuating frame times.

3. TTML Parser (TtmlLyricsParser.kt)

A stateful XML parser that:

  1. Scans for <ttm:agent> metadata to identify primary artists.
  2. Tokenizes <p> tags into high-precision Word objects.
  3. Injects virtual Line objects for instrumental breaks.

Roadmap

Find the full feature and bug roadmap here.


Tech Stack

  • Jetpack Compose: For the entire UI declaration and Canvas manipulation.
  • Media3 (ExoPlayer): Industrial-grade media decoding and playback.
  • Kotlin Coroutines: For non-blocking IO during TTML and audio file scanning.
  • Custom XML Pull Parser: For lightweight, low-memory performance on large lyric files.
  • SwiftUI + AVFoundation: For the iOS player, local file importing, and synchronized lyric rendering.

Manual Builds

GitHub Actions includes a manual workflow at .github/workflows/build-mobile.yml.

  • platform: build android, ios, or both
  • android_variant: build Debug or Release
  • ios_export: export an unsigned device ipa for sideload tools or an unsigned simulator-app

The workflow generates the Xcode project from ios/project.yml using XcodeGen on the macOS runner, then builds either:

  • an unsigned device .ipa suitable for local resigning/sideloading tools such as Sideloadly
  • an unsigned iOS Simulator .app zip

The iOS app icons are generated during the Xcode build and in CI from the existing Android source logo at app/src/main/res/drawable/logo.png, so the repo does not need committed per-size iOS icon PNGs.

iOS Import Behavior

  • Import Song: copies one audio file into the app-local library and attempts to auto-import a sibling .ttml
  • Import Folder: recursively scans a selected folder for supported audio files and .ttml files
  • Attach Lyrics: manually pairs a selected .ttml file with the currently loaded track
  • Imported tracks persist across launches because they are stored in the app's Application Support directory

License

This project is licensed under the AGPL-3.0 License, inherited from the Spicy Lyrics project. See the LICENSE file for the full text.


Made by TX24 with the help of Antigravity's available models. Based on Spicy Lyrics - A Spicetify Extension

About

Spicy Player is an offline music player for Android with a port/recreation of Spicy Lyrics- A Spicetify Extension, designed to achieve visual parity with Spicy Lyrics' rendering.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Kotlin 80.5%
  • Swift 19.5%