Resonova · Engineering note · September 2026
One authority decides what plays next
Resonova turns a playlist into a hosted radio show: two AI hosts talk between the real songs. On Android the hard part isn't generating the show. It is playing it in the right order — song, then commentary, then the next song — while Spotify, the network and the phone each have their own idea of what is happening.
The problem
A cast mixes two kinds of audio that live in different places. The commentary is Resonova's own audio. The songs are played by Spotify, a separate app that Resonova can only command and observe — through Spotify's App Remote SDK on the phone, or its Web API over the network.
Four parties can disagree at any moment:
- Spotify reports its own state, sometimes late, sometimes not at all.
- Android backgrounds the app, locks the screen and puts the phone into Doze, where timers starve.
- The network drops, or claims to be online when it isn't.
- The listener taps pause, skip or stop in the middle of all of it.
Early versions split the decision between two runtimes: a web view that ran the show while visible, and native code that took over in the background. Each could advance the show. The symptoms were what you'd expect from two writers on one cursor: the two runtimes racing to move the show on, commentary and music colliding, and timers that starved when the phone went into Doze.
The decision
One authority: a single native conductor, running in an Android foreground service, owns the show's timeline. The UI sends commands and shows state. It never advances the show by itself.
The service keeps running across foreground, background, lock screen and Doze, so the show never depends on the screen being on. Android's media session is the system's view of that one timeline — lock screen, notification and headset buttons all talk to the same conductor.
The rules that keep it honest
- Spotify reports facts; it never decides Spotify's state tells the conductor what happened to a song. Only the conductor decides what happens next.
- Every result says how it is known Instead of a bare paused flag, results are PauseAccepted, ObservedIdle, Unknown or NotEnded, each tagged with where it came from (App Remote or Web API). Unknown is never treated as silence.
- Uncertainty never blocks the show A result the app can't determine changes how Spotify is cleaned up, not whether the cast moves on. The one exception: if every way to see Spotify is gone, the show stops and the listener decides — because moving on might start commentary over music.
- No second clock A song ends when Spotify says it moved off, cleared or restarted it — not when a timer thinks it should have. The app steps in only when Spotify goes quiet, with one coarse watchdog whose job is to ask, not to guess.
- Commentary never plays over music If music is audible and the pause isn't confirmed, that commentary is skipped and the show moves on. Skip, never block.
- Own the show, report the rest Where Spotify plays, whether the network is real, and the listener's sign-in belong to someone else. When one of them breaks the show, the player says so in one plain sentence — what happened, what the app is still doing, what the listener can do — instead of a silent retry.
What I rejected
- Letting the UI take the show back when the app returns to the foreground. It recreated the two-writer bugs. (Since the UI became fully native, there is nothing left to take it back.)
- Handing a failed song start to another layer. It hid the failure and split authority again.
- Replacing Spotify with my own player. Not banned, but deferred: owning the decoder would remove some latency, at the cost of a much bigger product.
What broke, and what it taught me
The rules came from bugs, not from a whiteboard. Two stand out:
- A "safety check" that stalled casts. To be sure a song had ended, a release asked Spotify several times before letting the show advance. That quietly turned an observation back into an authority, and it caused a release regression. Removing the check, and writing rule 01 down, fixed it.
- A timer that ended songs early. Predicting the end from the song's length cut songs short and raced Spotify's own report. Replacing the deadline with the observed end (rule 04) made endings exact.
Bugs like these live in the gaps between Spotify, Android's power management and the network, so unit tests alone don't catch them. Playback changes are verified on a physical phone before they ship.
Result
One place decides, so there is one place to reason about. Casts keep playing in the background, on the lock screen and through Doze; commentary and music don't collide; and when something outside the app breaks, the listener is told the truth instead of watching a spinner.