Two voices, one audio route
On its second day Pathfinder took on a second voice. Dad asked for Pippa inside the navigator:
피파까지 심어진 차/도보 내비 앱이어야 해. 내비 도중 피파 보이스모드 가능해야 하고. 운전/도보 중 피파랑 자연스러운 대화가 가능한.
A car and walking navigator with Pippa planted in it: her voice mode during guidance, a natural conversation while driving or walking. The day before, navigation had been ruled out of the Pippa app so her voice mode would stay stable. So the talk came to the road, rather than the road to the Pippa app, and the architecture doc gives the reason in one line: one app on the audio route is the only way the guidance and her voice can share it without talking over each other, or the microphone hearing the guidance as Dad.
Two apps would each hold an audio session and know nothing about the other. The navigator could not ask "is she in the middle of a sentence?", and the talk could not know that the voice in the cabin is a turn instruction rather than Dad. Inside one process, both questions have an answer.
Why the talk has to be told
Her talk plays on the microphone's own audio engine: CwkKitVoice switches on Apple's voice processing for that engine, and the engine's own output is the reference its echo canceller subtracts, so her voice comes out of what the mic hears. Guidance plays on a separate player, VoicePlayer, outside that engine. Pathfinder's architecture doc puts it flatly: the mic's echo cancellation cannot hear that player. Read that as the premise this configuration is designed on, not as a law of echo cancellation; how much of a second player Apple's processing might catch on a given device is something the app neither measures nor leans on. If the talk trusted the canceller and it missed, its ears would transcribe "300미터 앞에서 좌회전해" as something Dad said, and its barge-in detector would cut her off for it.
So the talk is the guidance voice's gate. Before every line, VoicePlayer asks GuidanceVoiceGate.guidanceLineArrives whether the line may play now, and it reports guidanceLine(playing:) when a line starts and ends. PippaTalk implements both. The rules it follows are not in the app at all: they are TalkInterplay, a pure function in PathfinderKit, under test on a Mac with no audio hardware anywhere near it.
The table
| Talk phase | Line priority | Action |
|---|---|---|
| off, paused, stopped | any | speak: the line plays as it always did |
| connecting, listening, thinking | any | speakOverListening: play now, and shut the talk's ears until it ends |
| speaking | info | drop: a status line is not worth interrupting her for |
| speaking | urgent | cutHerThenSpeak: she stops where she is |
| speaking | guidance | waitForHer if her current stretch ends within 3 s, otherwise cutHerThenSpeak |
The three seconds are a bound, and the code's comment argues for it: at 100 km/h three seconds is about 80 metres, "inside the rounding of 1 km or 300 m". Check that arithmetic before you repeat it. A held line keeps the words it was composed with. A "300미터" line that waits the full three seconds at 100 km/h (about 83 m) plays with roughly 217 m left, which SpokenDistance would now say as 200 m; a "1킬로미터" line composed at 1,000 m plays at about 917 m, a "900미터" reading. So the wait can cost one rounding step, always in the same direction: the line names a little more road than is left. What keeps that acceptable is the line that never waits. An urgent line ("잠시 후", "속도 줄여") cuts straight in, because its whole point is now, and on the highway plan the 300 m stage is exactly that urgent "잠시 후", so the line that says the turn is here never waits. And "within 3 s" is only known once the player has decoded her whole stretch. While it is still arriving, herRemaining is nil, and a guidance line cuts.
What the app does with each answer
- Ears shut. While a guidance line sounds, and for half a second after it (
earsAfterGuidance: the cabin still rings), mic chunks are not passed to the listener at all. Barge-in is never judged against the road either. - Waiting. A held line replaces any older held line, because the newest one is the one that matters. When her stretch ends, the held line plays before her next stretch. A deadline of three and a half seconds backs the promise: if her stretch ran longer than it said, she is cut off and the line plays.
- Cutting. She stops where she is, cwkPippa is told how far Dad heard (voice mode's
heard_until, so her next answer picks up from there), and the talk listens again at once with its ears still shut for the line. A cut-in check in progress is dropped: the road is not Dad talking. - Her next stretch. If a guidance line is playing when her next stretch is ready, the stretch waits for the line to finish.
- The session. While the talk holds the audio session in
.playAndRecord, the guidance player leaves the category alone and plays inside it.
The same gate covers a provider's own voice. When Google speaks the turns, its voice is muted while the talk holds the microphone, so a spoken road name never lands in her conversation as Dad's words.