a4c0fa9a6e20a6879477dc7393564e4f19e5b5de
AudioEngine.stop() only called Thread.interrupt(), which doesn't interrupt blocking InputStream.read() on OkHttp streams. This caused audio to continue after stop and blocked subsequent play attempts (old job never completed). Now closes timedStream to force the blocking read to fail. Removed LowLatencySocketFactory (16KB receive buffer) which triggered Icecast slow-client disconnection on burst-on-connect. Force HTTP/1.1 to avoid HTTP/2 negotiation issues with Icecast servers. Also fixed: awaitEngine() SharedFlow collector coroutine leak, and added MAX_CATCHUP_FRAMES safety cap to prevent infinite frame skipping. Made-with: Cursor
24/7 Radio
Personal-use Android app for 24/7 internet radio streaming.
Features
- Custom Raw Audio Pipeline — OkHttp → IcyParser → Mp3FrameSync → MediaCodec → AudioTrack for absolute minimum latency (~26ms per MP3 frame)
- Stay Connected Mode — Aggressive reconnection with exponential backoff, never gives up
- Dual Timers — Session elapsed time and connection elapsed time
- Latency Indicator — Estimated stream-to-speaker latency
- Icecast/Shoutcast Metadata — Track title, artist extraction from ICY protocol
- Album Art — MusicBrainz/Cover Art Archive lookup with fallback chain
- Playlist Management — PLS/M3U import/export with #EXTIMG support
- Station Organization — Playlists, starring/favoriting, manual reorder
Requirements
- Android 9.0+ (API 28)
- Internet connection
Build
./gradlew assembleDebug
Import Stations
- Open the app
- Tap the import icon in the top bar
- Select a .m3u or .pls file
- Stations are added to your list
Architecture
See Design Document and Implementation Plan.
Description
Android app for 24/7 internet radio streaming with minimum latency, aggressive reconnection, and full Icecast/Shoutcast metadata support.
Languages
Kotlin
97.3%
Shell
2.7%