{"id":49493446,"url":"https://github.com/leohchen/harmoniq","last_synced_at":"2026-06-08T00:01:52.977Z","repository":{"id":354943586,"uuid":"1226108294","full_name":"LeoHChen/HarmonIQ","owner":"LeoHChen","description":"Music for your own collections, ported to your iPhone. A retro Winamp-flavored player for the MP3s and ripped CDs you already own — drive-portable library, real 10-band EQ, on-device AI playlists, fully offline. Open source.","archived":false,"fork":false,"pushed_at":"2026-05-10T07:34:35.000Z","size":16594,"stargazers_count":1,"open_issues_count":16,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-10T08:40:19.763Z","etag":null,"topics":["apple-intelligence","cd-ripping","data-sovereignty","equalizer","ios","mp3","music-player","offline","open-source","swiftui","visualizer","winamp"],"latest_commit_sha":null,"homepage":"https://www.leochen.net/HarmonIQ/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LeoHChen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T01:36:16.000Z","updated_at":"2026-05-10T07:34:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/LeoHChen/HarmonIQ","commit_stats":null,"previous_names":["leohchen/harmoniq"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/LeoHChen/HarmonIQ","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoHChen%2FHarmonIQ","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoHChen%2FHarmonIQ/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoHChen%2FHarmonIQ/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoHChen%2FHarmonIQ/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LeoHChen","download_url":"https://codeload.github.com/LeoHChen/HarmonIQ/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoHChen%2FHarmonIQ/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34042554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-intelligence","cd-ripping","data-sovereignty","equalizer","ios","mp3","music-player","offline","open-source","swiftui","visualizer","winamp"],"created_at":"2026-05-01T08:02:08.819Z","updated_at":"2026-06-08T00:01:52.971Z","avatar_url":"https://github.com/LeoHChen.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HarmonIQ\n\n**Website:** [www.leochen.net/HarmonIQ](https://www.leochen.net/HarmonIQ/) — landing site lives in [`docs/`](docs/).\n**TestFlight:** [Join the public beta](https://testflight.apple.com/join/y4Nmt7V1) (iOS 16+).\n\n\u003e **Music for your own collections, ported to your iPhone.**\n\nHarmonIQ is a SwiftUI music player for iPhone and iPad, built for the people who never threw out their MP3 folders — the CD ripper with three external drives full of FLACs, the Napster-era curator with a meticulous \"Best Of 2003\" directory, the audio-archivist who treats a hard drive like a library and not a cache.\n\nIt plays the music **you already own**, straight off your drive, with a player that looks like it was beamed in from 1999 and a 2026 brain bolted on top.\n\n## Why it exists\n\n**Data sovereignty for music.**\n\n- **You own your files.** Not a streaming service that can pull a license tomorrow. The MP3s, ALAC rips, and FLACs on your drive — the ones you ripped, paid for, downloaded, traded — those are yours, and HarmonIQ treats them that way. The on-drive index lives next to the music in a `HarmonIQ/` folder; move the drive, the library moves with it.\n- **You own your playlist algorithm.** Smart Play's rule-based modes are pure functions in [`SmartPlay.swift`](HarmonIQ/Player/SmartPlay.swift) — read them, fork them, replace them. The AI modes (Vibe Match, Storyteller, Sonic Contrast) prefer Apple Intelligence's on-device foundation model when available; *your library never leaves your phone.*\n- **You own your taste.** No engagement metrics, no recommendation graph, no listening history shipped off for ad targeting.\n- **Works offline. Always.** No network at index time, playback time, or AI curation time (with on-device AI on). Plug a USB-C SSD with a few thousand albums into your iPhone on the plane, in the subway, in the woods. It plays.\n\nThe vibe is \"bring your old MP3 collection forward\" — your CD rips and 2003-era downloads deserve a modern player that respects what they are: yours.\n\n## Screenshots\n\n| Sun-Bleached Grooves launch | Winamp-flavored Library |\n|---|---|\n| \u003cimg src=\"docs/screenshots/launch.png\" width=\"270\" alt=\"Launch screen: vinyl disc on sunset gradient\"\u003e | \u003cimg src=\"docs/screenshots/library.png\" width=\"270\" alt=\"Library tab: Smart Play, browse, add a music drive\"\u003e |\n\n| Smart Play curators | Skinned (Winamp) player |\n|---|---|\n| \u003cimg src=\"docs/screenshots/smartplay.png\" width=\"270\" alt=\"Smart Play screen: Pure Random, Artist Roulette, Genre Journey, Album Walk, Decade Shuffle, Freshly Added\"\u003e | \u003cimg src=\"docs/screenshots/skinned-player.png\" width=\"270\" alt=\"Skinned player: Base-2.91 skin with spectrum visualizer, equalizer, and playlist editor\"\u003e |\n\n## Features\n\n- **Drive-portable library.** Tracks, playlists, and artwork live in a `HarmonIQ/` folder on the drive itself. Plug the same drive into another iPhone and your library shows up unchanged — no reindex, no account.\n- **External-drive friendly.** Pick any folder Files can see (USB-C SSD, SMB share, iCloud Drive, on-device storage). Read-only locations are supported via a sandbox shadow store.\n- **Real 10-band EQ.** AVAudioEngine + AVAudioUnitEQ — the sliders move the actual signal. Built-in presets (Flat, Rock, Pop, Jazz, Classical, Bass Boost, Vocal Boost) plus persisted custom curves.\n- **Smart Play, 14 rule-based + 3 AI modes.** Pure Random, Artist Roulette, Genre Journey, Album Walk, Decade Shuffle, Freshly Added, Quick Hits, Long Player, Discovery Mix, Mood Arc, Deep Cut, One Per Artist, Genre Tunnel, Era Walk — plus **Vibe Match**, **Storyteller**, and **Sonic Contrast**. AI modes save as regular playlists.\n- **On-device AI by default.** AI Smart Play prefers Apple Intelligence's foundation model (iOS 26+, iPhone 15 Pro+) — no API key, no upload, no network. Falls back to Anthropic when an API key is configured.\n- **Background playback** with full lock-screen / Control Center / Live Activity / Dynamic Island controls.\n- **Winamp skins.** Drop classic `.wsz` files in via the importer; 9 bundled. Skinned main window, equalizer, and playlist all render from the original sprites.\n- **16 visualizer styles.** Spectrum, oscilloscope, plasma, mirror, radial pulse, particles, fire, starfield — plus 8 fancy oscilloscope variants (neon glow, harmonic layers, mirror wave, filled wave, radial wave, waterfall, Lissajous, beat flash). Tap the visualizer to cycle.\n- **Sleep timer.** Auto-stop after a fixed duration (15/30/45/60 min) or at the end of the current track, with a live LCD countdown.\n\n\u003e New to USB drives or CD ripping? See the [non-tech setup guide](https://www.leochen.net/HarmonIQ/getting-started.html) — picking a drive, finding a real data cable, ripping CDs, tagging, and plugging it all into your iPhone.\n\n## Requirements\n\n- iOS 16+\n- Xcode 15 / Swift 5.9\n- [XcodeGen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`) — `project.yml` is the source of truth, the `.pbxproj` is generated.\n\n## Build\n\n```bash\nxcodegen generate\nxcodebuild -project HarmonIQ.xcodeproj -scheme HarmonIQ \\\n  -destination 'platform=iOS Simulator,name=iPhone 17' build\n```\n\nIf `xcodebuild` complains about missing tools, prefix the command with `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer`.\n\nThere is no test target and no lint config — `xcodebuild` is the only check.\n\n## Architecture sketch\n\nFour `@MainActor` singletons, injected at app launch:\n\n| | |\n|---|---|\n| `LibraryStore` | Aggregates tracks/playlists across all mounted drives. |\n| `AudioPlayerManager` | `AVAudioPlayer` wrapper — queue, shuffle, repeat, audio levels. |\n| `MusicIndexer` | Detached file walker that writes the index to the drive's `HarmonIQ/` folder. |\n| `NowPlayingManager` | Bridges `MPRemoteCommandCenter` to the player. |\n\nEvery drive carries `HarmonIQ/library.json`, `HarmonIQ/playlists.json`, and `HarmonIQ/Artwork/` at its root. The app sandbox holds only `roots.json` (the device's bookmarks) and a local artwork mirror — see `CLAUDE.md` for the full persistence model.\n\n## Layout\n\n```\nHarmonIQ/\n  HarmonIQApp.swift          # Composition root\n  Models/                    # Track, Playlist\n  Persistence/               # LibraryStore, DriveLibraryStore, BookmarkStore\n  Indexer/                   # Music indexer + metadata extractor\n  Player/                    # AudioPlayerManager, NowPlayingManager, SmartPlay\n  Skins/                     # .wsz parsing + skin manager\n  Views/\n    Skin/                    # Skinned (Winamp) UI\n    *.swift                  # Native SwiftUI library, search, settings\n    WinampTheme.swift        # Shared design system\n  Resources/Skins/           # Bundled .wsz files\ndesign/                      # Icon design + render script\nproject.yml                  # XcodeGen source\n```\n\n## Releases\n\nSee [GitHub Releases](https://github.com/LeoHChen/HarmonIQ/releases) for the full notes and tag history. The latest build is on TestFlight at [testflight.apple.com/join/y4Nmt7V1](https://testflight.apple.com/join/y4Nmt7V1).\n\n### v1.1.1 — 2026-06-07\n\nThe \"take your playlists with you\" release. HarmonIQ now writes standard VLC-compatible playlists onto the drive, so the same library plays on the desktop without re-exporting anything.\n\n#### v1.1.1 build 7 — 2026-06-07\n\n- **VLC-compatible playlist export.** Every playlist is mirrored to the drive as a standard Extended M3U file at `\u003cDrive\u003e/HarmonIQ/Playlists/\u003cname\u003e.m3u8`, with relative paths that resolve wherever the drive is mounted (Mac/PC). Plug the drive into a computer and open them straight in VLC. (#134, #135)\n- **Whole-drive \"All Tracks\" playlist.** A `All Tracks (\u003cDriveName\u003e).m3u8` containing the entire drive library is exported alongside the per-playlist files, so the whole collection opens in one click. (#135)\n- **Automatic + self-healing.** Sidecars are (re)generated on every playlist edit, drive load, and reindex — so playlists made before this release, or on another device, appear without any manual step, and entries follow files that move between scans. (#135)\n\n### v1.1 — 2026-05-03\n\nThe \"make it look right, find it fast, keep it offline\" release. Polish across the palette, the browse modes, the maintenance flows, and the lock-screen — without giving up the offline-first stance.\n\n#### v1.1 build 6 — 2026-05-09\n\nA small App Store submission build over the same v1.1 release. No tag bump.\n\n- **About-screen easter egg.** Quietly tap the Version row five times. (#123)\n- **Export-compliance flag.** Sets `ITSAppUsesNonExemptEncryption = false` in Info.plist so App Store Connect can auto-answer the export-compliance question on every future upload. The app uses only standard OS-provided HTTPS for the opt-in album-art / artist-photo fetchers — no custom or non-exempt cryptography.\n\n**Charcoal Phosphor.** A more authentically Winamp-2.x palette: graphite chassis, deeper CRT-green LCD, sharper corners, amber + red chromatic accents in the spectrum visualizer. The whole player feels heavier and more \"machined\" without changing a single feature. (#76)\n\n**Browse by language.** New Library → **Language** hub partitions the library into Chinese / English / Others using a heuristic CJK + Latin classifier. Useful when your collection sprawls across scripts. (#91)\n\n**Artists, with photos.** New Artists browse renders as a visual grid with representative covers, then upgrades to *real* artist headshots when the opt-in network photo fetcher is enabled (MusicBrainz → Wikidata → Wikimedia → TheAudioDB → Wikipedia). Artist tiles are guaranteed to show a headshot or a placeholder — never an album cover masquerading as the artist. (#92, #94, #96)\n\n**Opt-in artwork on tap.** A new opt-in fetcher (off by default) backfills missing album art from MusicBrainz + Cover Art Archive. Albums view also picks up artwork files dropped manually into `\u003cDrive\u003e/HarmonIQ/Artwork/` — silent reconciliation pass on drive-load. (#74, #79)\n\n**Maintenance, consolidated.** All per-drive library actions live behind one **Refresh…** sheet in Settings: Quick refresh / Reindex tracks / Fetch from internet / Advanced → Rebuild. New `tools/library-doctor.swift` (`--report` / `--dedupe` / `--rebuild`) for offline cleanup; compilation albums fold to one \"Various Artists\" entry. (#90, #100)\n\n**Lock-screen lockstep.** The lock-screen `MPNowPlayingInfo` widget and the HarmonIQ Live Activity now share a single fan-out path — pause/skip/artwork updates land on both, in the same frame. (#104)\n\n**Visualizer + EQ polish.** Radial Pulse renders unambiguously radial at any energy level. Visualizer style picker commits on first tap. EQ preset picker is easier to tap and faster to commit. (#80, #85, #87)\n\n**AI Smart Play, gracefully absent.** On iPhones without Apple Intelligence and without an Anthropic key configured, the AI section now hides cleanly instead of teasing disabled rows. Cloud fallback continues to work on older devices when a key is configured. (#102)\n\n### v1.0 — 2026-05-02\n\nThe \"your collection deserves a real player\" release. Brings the offline-first, drive-portable foundation up to a 1.0 feature set built around **data sovereignty** for collectors with thousands of MP3s and ripped CDs.\n\n**Functional EQ.** AVAudioEngine + AVAudioUnitEQ replaces AVAudioPlayer; the 10-band sliders move the actual signal. Bundled presets (Flat, Rock, Pop, Jazz, Classical, Bass Boost, Vocal Boost) plus persisted custom curves. (#28)\n\n**On-device AI Smart Play.** Three new modes — **Vibe Match** (free-text vibe → curated queue), **Storyteller** (8–12 track narrative arc), **Sonic Contrast** (alternates by style). Default to Apple Intelligence's foundation model on iOS 26 + iPhone 15 Pro+; falls back to an Anthropic API key when configured. Save any AI-curated queue as a normal drive-resident playlist. (#25, #58)\n\n**Smarter rule-based modes.** Genre Tunnel (stay inside the playing track's genre), Era Walk (chronological tour), Mood Arc, Deep Cut, One Per Artist now ship alongside the original suite — 14 rule-based modes + 3 AI modes total.\n\n**Live Activity for background playback.** Lock-screen banner + Dynamic Island compact / expanded / minimal layouts; throttled tick updates respect ActivityKit budgets. (#18)\n\n**Favorites.** Heart toggle on both player skins; saves to a system \"Favorites\" playlist on the drive (Option A from the issue), so favorites travel between devices. (#33)\n\n**Fancy oscilloscope visualizers.** Eight new osc styles — Neon Glow, Harmonic Layers, Mirror Wave, Filled Wave, Radial Wave, Waterfall, Lissajous, Beat Flash — bringing the SwiftUI visualizer total to 16. The skinned (Winamp) player tap-cycles through all 16 too. (#26, #27, #36)\n\n**Drive UX overhaul.** Incremental reindex (skip work when the drive is unchanged; only re-extract metadata for files whose mtime changed). Auto-detect drive content via foreground refresh + manual **Reload** button per drive. Force-reindex flag so explicit Reindex never silently no-ops. (#55)\n\n**Settings polish.** Real Version / Build / Commit / Tag / Built-at in the About row (build-time `Info.plist` injection). New **Feedback** section with one-tap links to file feature requests, file bugs, browse open issues, star the repo, and copy build info for triage. AI section captures the user's optional Anthropic key. (#52, #59)\n\n**Public landing site.** [www.leochen.net/HarmonIQ](https://www.leochen.net/HarmonIQ/) — sun-bleached gradient hero, all 9 bundled skins as native previews, 16-style visualizer grid, \"Make it yours\" contribution invitations. Plain HTML/CSS, no analytics, no tracking. (#57)\n\n**Diagnostics.** `os.Logger` instrumentation in the playback path (subsystem `net.leochen.harmoniq`, category `playback`) — track-finish success flags, decode errors, audio-session interruptions, route changes, security-scope releases. Filter in Console.app to triage mid-track aborts.\n\n**UI consistency.** Skinned-player chrome bar redesigned — five buttons (skin / save-AI / favorite / sleep / close), all 36×36, all hierarchical-white SF Symbols. Search and Playlists tabs gain proper inline titles. Save-AI uses `bookmark.fill` instead of the share-arrow.\n\n### v0.4 — 2026-05-02\n- **Sleep timer**: stop after 15/30/45/60 min or at the end of the current track; live LCD countdown sits in the player transport.\n- **Visualizer overhaul**: 8 selectable styles (spectrum, oscilloscope, plasma, mirror, radial pulse, particles, fire, starfield); active style persists across launches; long-press or double-tap the visualizer to cycle. The skinned (Winamp) player honors the same choice within its 76×16 + palette constraints.\n- **SmartPlay**: three new rule-based modes — Mood Arc (high-energy → wind-down), Deep Cut (skip openers and \"Greatest Hits\" comps), One Per Artist (max library breadth).\n- **Branded launch screen**: Sun-Bleached Grooves splash matching the app icon (sunset gradient + black vinyl disc, no tonearm).\n- **Performance**: throttle `currentTime` publish to ~2 Hz, gate the visualizer + display link on view visibility, pause the display link when the app backgrounds. Substantially fewer SwiftUI invalidations during normal playback.\n- **Reliability**: fixed several Sendable / actor-isolation issues around background/foreground notification observers (no more compile-time isolation warnings; no spurious main-actor hops).\n- Repeat-one indicator: subtle \"1\" badge on the repeat button when single-track loop is active.\n\n### v0.3 — 2026-05-01\n- Albums and Artists drill-downs work again (taps push into detail instead of looping).\n- Album detail header redesigned: centered Winamp card with aligned PLAY / SHUFFLE.\n- \"PLAYER\" row in BROWSE returns to the now-playing sheet; icon pulses with a live audio-level glow + 3-bar mini-VU.\n- Skin picker overhauled: tap to cycle, long-press for a scrollable sheet, mirrored on the SwiftUI player.\n- Four more bundled skins: Bento Classified, Crystal Display, Glass Factory, Luna Steel.\n- Recent searches persisted (LRU, 10), shown when the search field is empty.\n- README added.\n\n### v0.2 — 2026-05-01\n- EQ and Playlist panels now skin alongside the main player; switching skins recolors the entire now-playing sheet.\n- Skin picker added to the SwiftUI (None) player.\n- Mini player removed (auto-present-on-tap handles reopening the player).\n- Fixed bogus read-only flag on every picked folder; on-drive index writes now work on writable locations.\n- Folder delete added to Library → Folders view.\n\n### v0.1\n- Initial release.\n\n## Feedback \u0026 feature requests\n\nHarmonIQ is built in the open. **Have an idea, hit a bug, or just miss a feature from your favorite player?** Open an issue — that's the most direct way to land it on the roadmap:\n\n- [🚀 Request a feature](https://github.com/LeoHChen/HarmonIQ/issues/new?labels=enhancement\u0026title=Feature%20request%3A%20)\n- [🐛 Report a bug](https://github.com/LeoHChen/HarmonIQ/issues/new?labels=bug\u0026title=Bug%3A%20)\n- [💬 Browse open issues / vote with 👍](https://github.com/LeoHChen/HarmonIQ/issues)\n\nPRs are welcome too. The project's small, the architecture is documented in [`CLAUDE.md`](CLAUDE.md), and there's a smoke checklist in [`TESTING.md`](TESTING.md) to verify changes before merging.\n\n## Acknowledgements\n\nSkin parsing leans on classic Winamp 2.x specs and the file format documented at [archive.org's Winamp Skin Museum](https://archive.org/details/winampskins).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleohchen%2Fharmoniq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleohchen%2Fharmoniq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleohchen%2Fharmoniq/lists"}