Putting the audio in AudioRun
Posted 2026-08-30
The name AudioRun was chosen to highlight the most distinct feature of the app: the spoken announcements and solid audio handling. This blog post will give some background for the choices that have been made, and how they work.
You pick the stats
Every stat is its own toggle: distance, pace, elapsed time, heart rate, split times, the last interval’s time, distance and pace, interval count, elevation change. Cadence is separate — every 100 m, 400 m, every kilometer, on a timer, or both. Kilometer (or mile) splits are independent of the interval, so quiet 400 m repeats can still get split callouts.
The sentence is assembled clause by clause, in the order you set. Anything that would be meaningless is dropped rather than spoken. No GPS on a treadmill means the distance clause would be “0 meters” every interval, so it stays quiet. A flat interval does not announce “climbed 0 meters”; elevation only exists once there is a whole meter to report. What is left comes out as one sentence:
“4 point 5 kilometers. In 25 minutes 10 seconds. Average pace 5 minutes 35 seconds per kilometer. Climbed 12 meters.”
Digits, colons and slashes never reach the speech engine. “5:35/km” comes out of TTS as “five colon thirty-five slash kay em”, which is useless, so every value is formatted for speech first. Related small thing: “in 25 minutes” only works as a follow-on to a distance clause. If distance is off, or GPS dropped it, the time clause switches to “elapsed time 25 minutes” and carries its own subject. The demo button on the config screen runs the same text builder as a live run. What you preview on the couch is the same string you will hear on the trail.
Three things want the speaker
State changes (“Run paused”), interval stats, and the AI coach all go through one priority queue.
State changes are HIGH. They interrupt whatever is playing and clear everything pending. If you have stopped at a crossing you want “Run paused” now, not after a twenty-second stats readout that no longer applies. Stats are MEDIUM and wait their turn. Coaching is LOW: it queues behind stats and gets a 500 ms gap before speaking, so a coach reply does not slam into the end of a stats sentence.
The queue also sends context back upstream. The coach is told what the last interval announcement said, so it does not repeat the pace you heard ten seconds earlier. The queue is capped. If the speech engine dies mid-run, the app will not save up a half-hour of stale callouts and dump them when it recovers.
Multiple audio sources
This is the bit most running apps get wrong, and one of the main features of AudioRun is about getting this right. AudioRun is designed to be used on long runs, and during that time, you might want to listen to music, an audiobook, or a podcast. AudioRun will handle this gracefully, and make sure that the spoken announcements are heard and not drowned out by other audio sources, or cause the audio streams to trip each other up.
Each announcement is tagged with Android’s
USAGE_ASSISTANCE_NAVIGATION_GUIDANCE audio attributes and a
speech content type — the same class as turn-by-turn directions, which
is roughly what a stats callout is. Right before speaking, the app requests
transient audio focus with MAY_DUCK: a few seconds,
and whatever is already playing may keep going quieter underneath. Any music playing is
attenuated to make room for the announcements, and then fades back in when the announcements are done.
At least, that's the intention. What the other app does with that attenuation signal is up to it. Spotify "ducks": the music dips, the announcement lands, the music comes back. Audible pauses instead, which is also correct (for audiobook playback, where you really don't want to miss a sentence).
Transient also matters because AudioRun is not the only thing talking on a run. My Garmin watch delivers its own spoken alerts through the phone. AudioRun only holds focus for the seconds it is actually speaking, never for the length of the run, so the watch gets a turn at the same ducking machinery. If AudioRun were to grab the audio focus for a whole session it would break this feature.
Focus is best-effort. If the request is refused, the announcement speaks anyway. Worst case, two things overlap for a moment. The alternative is a swallowed callout, and for an app whose interface is audio, silence is the one failure I want to avoid.
The reliability stuff
Most of this is invisible when it works.
“Run started” is often requested before the speech engine has finished initializing. It stays queued and plays when the engine is up, instead of disappearing. TTS engines can die quietly after a long stretch with the screen off. A failed speak call releases the queue’s speaking claim so the pipeline cannot wedge, and the next announcement retries. When you stop a run, the background service stays alive until the end-of-run summary has actually finished playing. Android is otherwise happy to kill it mid-sentence. And since the phone is in a pocket, GPS signal loss is announced out loud. That is the only way you would know why the distance callouts went quiet.
You can pick which installed TTS voice to use, and how fast it speaks. The audio features of the app have been tested thoroughly, and are expected to work reliably, but due to the nature of the Android ecosystem, there will inevitably be combinations of hardware, software and settings that will cause issues. If you encounter issues, please report them to me using the in-app feedback form, and include the following information:
- The version of AudioRun you are using
- The version of Android you are using
- The TTS voice you are using
- The speed you are using
- The device you are using