Hifee: my own music player for the Mac

A native macOS music player I had my AI agent build to replace Audirvana: bit-perfect local playback, ReplayGain on every track, Apple Music for discovery, and 5x less memory.

Hifee is the music player I use all day at my desk. It plays my local lossless library bit-perfect to my audio interface, adds Apple Music for discovery, and does nothing else. It replaces Audirvana, which I used since 2021.

I didn't write the code. I described what I wanted and my AI agent built it, tested it and shipped it. The why is in The era of personal software is here. This note covers what Hifee does and how it works.

Hifee's main window in dark mode: a sidebar with library sections and playlists, an album grid, a lyrics panel on the right, and a player bar with the cover in the bottom-left corner

Why I built it

I had 2 apps and wanted neither of them:

  • Audirvana plays local files well, but it's heavy, its playlists are clunky, and I pay a subscription for features I don't use.
  • Apple Music is good for finding new music, but it doesn't do bit-perfect playback with sample-rate switching for a local library of FLAC files.

I wanted 1 app with only my features, in my own design, controlled from my keyboard and from Alfred. The name follows the -ee family of my tools, next to Dictee: my own local dictation app and Stickee: sticky notes that go full screen on every monitor.

What it does

Feature How it works
Bit-perfect desk The output switches to each track's sample rate (44.1, 96, 192 kHz) and back when Hifee quits
Gapless Live albums and DJ mixes play without a gap. A focus track loops with no click
ReplayGain Every track plays at the same loudness. New tracks are measured on import
Apple Music Search and charts from the Apple Music catalogue, next to the local results
Home output 1 switch sends the music to my AirPlay speakers, with 1 master volume for all of them
Focus mode 1 hotkey starts a binaural focus loop, from anywhere on the Mac
Playlists Manual and smart playlists (recently added, most played, never played, hi-res, favourites)
Lyrics Embedded lyrics first, then LRCLIB, synced when available
Tag editing Edits FLAC tags in the files. Hifee never moves or deletes a file
Command Centre ⌘K lists every command and its keys. Type to filter, Return to run
Pauses itself Music pauses when I start dictating, and when a call starts
Prompter layout A layout for the 1024x600 screen of my Elgato Prompter, more columns on bigger windows

The Prompter layout uses tabs instead of a sidebar, so it stays readable at 1024x600:

Hifee at 1024 by 600 pixels: a row of tabs at the top (Now, Library, Playlists, Search, Queue, Focus), an album grid, and a compact player bar

Playlists use a real table, with a column for each field and a click on a header to sort:

A Hifee smart playlist called Most played, shown as a table with columns for position, title, artist, album, time, format, rating, favourite, plays, last played and added date

And ⌘K opens the Command Centre, so I never have to remember a shortcut:

The Hifee Command Centre over the album grid: a search field, then commands grouped under Playback, Output and Go to, each with its keyboard shortcut and Alfred keyword on the right

Memory

I measured the 3 apps on the same day, with the same library of about 29,000 tracks:

App Memory
Hifee 94 MB
Apple Music 174 MB
Audirvana Studio 508 MB

Apple Music had almost no local library at the time, and it still used 2x more than Hifee. Memory matters more to me than it used to, because my local AI models need it.

For business people

Hifee is a personal tool for 1 user. It shows what a non-developer can now get from an AI agent: a native app that replaces a paid subscription, built around 1 person's habits instead of a product team's roadmap.

  • Cost: no licence and no subscription for local playback. The Apple Music subscription stays, for discovery.
  • Fit: only the features I use. The keywords I typed in Alfred for years (oa open and play, pa pause, ns next song) now drive Hifee.
  • Risk: I own the maintenance. When macOS changes, the agent and I fix it. Nobody else will.
  • Limit: Mac only, no phone app, no sync. My phone keeps Apple Music.

For technical people

Stack

Native Swift. SwiftUI for the views, AppKit where SwiftUI is too slow or too heavy. The library is a SQLite database, with FTS5 for search, so "royksopp" finds "Röyksopp". A 1-time script imported my Audirvana history: ratings, play counts, playlists and ReplayGain values.

Playback

  • 1 AVAudioPlayerNode in an AVAudioEngine, with each track scheduled as a segment. The next track is scheduled 20 seconds before the end, which gives gapless playback and a seamless loop.
  • Sample-rate switching through Core Audio (kAudioDevicePropertyNominalSampleRate). The gotcha: the output unit reports the new rate a moment after the device changes. Starting the engine before that left it silent. Hifee now waits until the output format matches.
  • ReplayGain 2.0 at -18 LUFS, with a BS.1770 loudness meter for new tracks. At a gapless boundary, the gain change is a scheduled audio-unit parameter event, so the next track starts at its own level.
  • FLAC tags are written in place when the padding allows, else to a copy that replaces the file atomically. Only the comment block changes; the audio is checked before the swap.

Apple Music

MusicKit needs 2 things for an app signed outside the App Store: the MusicKit service on the App ID, and an embedded provisioning profile that carries the application identifier. With the service alone, every request fails with developerTokenRequestFailed.

AirPlay

⚠️ WARNING: a third-party Mac app can't select AirPlay speakers by itself.

The AirPlay picker runs in a system service. Hifee can read the speakers I tick and set their group volume, through the private class behind the picker. It can't search for speakers or select them. When Hifee quits, macOS drops the selection. So after a restart, Hifee opens the AirPlay picker and lists the speakers to tick.

Testing

The agent wrote a test harness that drives the real app from JSON scripts, on a copy of the database and a muted virtual audio device. It checks gapless transitions, rate switches, the focus loop, call and dictation pauses, memory, and renders every layout to images. The screenshots in this note come from those renders.

1 small fix worth stealing: Alfred kept listing the build copies of the app. A folder whose name ends in .noindex is invisible to Spotlight, so the build now goes to build.noindex/.

Further reading

NicAI
Written by NicAI, Nic's AI assistant, for his personal knowledge base. Researched and drafted by the model, not hand-written by Nic. Verify anything you plan to act on.