PDVoice Buddy
BETA
Version numbers are informal — this is a prototype, not a shipped product. Where a decision was reversed, the reason is recorded rather than quietly erased, because the reversals are the most useful part of a document like this.
On Windows the gear button did nothing at all. The settings list grew a fourth field per sensitivity step in 0.4 (the dB target), but two lines in the settings window still expected three, so building the window raised a ValueError before anything appeared.
Both lines now index rather than unpack, so adding a sixth field later cannot break them the same way.
Errors opening the settings window are now reported — a tray notification with the error, and a traceback to stderr for build-debug.bat. This is the more important half of the fix: a windowed .exe has no console, so the original failure was completely silent. A button that does nothing, with no explanation anywhere, is the worst possible failure mode, and it survived a release because nothing could have surfaced it.
Caught by using the app, not by a test. Worth noting that the measurement has good test coverage and the interface has none.
The project became a GitHub repository, largely so that beta testers can be updated without being emailed files.
AddedAndroid cannot install silently outside the Play Store, so it hands the file to the browser and the user taps to install. The banner says so plainly rather than implying otherwise.
Nothing downloads without a button press. A tool someone relies on should not change under them unannounced.
A critical update is an insistent banner that loses its "Later" button — it is NOT a lock. The dot keeps running on an unsupported version. Switching off someone's voice cue to force an upgrade would punish them for a developer mistake.
The Windows download is verified against a checksum before being run. Fetching an executable over the network and running it unverified would be careless.
Update failures are silent. No network, no server, bad JSON — the app carries on and tries tomorrow. An update check is not worth an error message in front of a patient.
The repository must be public, because a private one would require every copy of the app to carry a secret key.
AlsoThe room estimate rose too fast while someone was talking. Nine seconds of continuous speech dragged it up 14 dB — the whole range the dot works in — so during a long anecdote the bar climbed to meet the voice and the dot went dark. Exactly backwards: the cue failed at the moment it was most needed.
Rise rate cut from 0.006 to 0.0004 per frame, in both apps.
It cannot simply be zero. A dishwasher or air conditioner starting up must still raise the bar, or the app reads the appliance as a raised voice. The two are told apart by duration: speech barely moves the estimate over ten seconds, steady noise still catches up over a minute. Both behaviours have tests.
Worth recording that this was caught by "talking does not raise the room estimate much", a test written months before the bug existed. It failed on the first run of the release build.
The dot needed a projected voice, not a conversational one, on the "Normal" setting. The cause was the calibration constant: 95 dB assumed a microphone more sensitive than a real tablet's, so 63 dB SPL was being asked for at -32 dBFS. Tablet mics at arm's length read ordinary speech nearer -40 dBFS.
Default offset 95 → 105, on both platforms. Anyone who had already nudged theirs keeps their own value; only the default moved. Still a guess, and the reason the calibration screen exists.
The +/− buttons looked deadThey were not — the offset changed and the microphone loop reloaded correctly. But the only number on screen was the live reading, which drifts several dB on its own, so a 1 dB nudge was invisible. The offset is now shown as its own line, and marked when it is the default. A control that works but appears not to is worse than one that is missing.
No way to stop itThe pill had an X; the full screen had none, and the notification only reopened the app. Added a Stop action to the notification, and an X to the full screen. Both release the microphone properly rather than hiding.
On battery: the app holds a foreground microphone service, so it does use power continuously — modest, but real. Stop now genuinely stops.
Note for the next release: three places carry the version number and they must always agree: android/app/build.gradle.kts, python/updates.py, and latest.json. The Android versionCode is a plain counter that must only ever rise.
Prompted by a Parkinson's physician's assistant reviewing the app. Her correction was the most substantive external feedback the project has had.
The problem she identifiedUp to 0.3 the dot measured one thing: is the voice above the room? That works in a restaurant and fails badly in a quiet living room, where a whisper is comfortably above the background and lights the dot. The app was rewarding the exact habit it exists to correct.
Being audible and projecting at a normal conversational volume are not the same goal, and the app was only pursuing the first.
What changedThe dot now requires the voice to clear TWO bars at once:
Whichever is higher governs. The room decides in a restaurant; the absolute level decides in a silent room.
The five sensitivity steps now carry a dB target across the normal conversational band: Gentle ~57 dB, Easy ~60 dB, Normal ~63 dB (the default), Firm ~66 dB, Strong ~69 dB.
AddedHonest limitation, recorded deliberately: a phone or tablet microphone has no absolute reference, its gain varies by model, and its reading falls off with distance. The dB numbers are approximations, not measurements, and will not match a real meter. This is stated in the app's manual as well as here. It must never be presented as measurement.
One existing test — "the absolute scale is irrelevant" — asserted exactly the assumption this release overturns. Rewritten rather than deleted, so the change of mind stays legible: the relative half alone ignores absolute scale (kept, narrowed); a whisper in a silent room does not light the dot (new); in a noisy room the room sets the bar (new); the bar is whichever threshold is higher (new).
Documentation fixPart 4 of the manual opened with "the last two you should leave alone", which by position pointed at Open at login and the car setting — telling the reader to ignore the driving-safety control. Now names Microphone calibration explicitly. Counting by position was the mistake; sections move, names don't.
One build, not two. The tablet follows the device language by default, so there is nothing to choose and no second download.
"English" and "Español" are never translated — each appears in its own language always. Someone hunting for Spanish on an English-configured tablet has to recognise "Español" on sight, whatever the app is currently showing. Only "Automatic" translates.
The setting exists because a device set up in English by a family member must not decide what language the person using it reads.
Translation choices flagged for proofreadingA proofreading sheet accompanies this, written for a fluent speaker with no technical knowledge. All strings still awaiting review.
Not translatedThe user guide, feedback survey, install notes and website copy. A few thousand words; worth doing only if Spanish-speaking testers materialise.
AlsoThe Windows app was rewritten so both platforms are genuinely the same app: same measurement, same five sensitivity steps, same colours, same two shapes, same wording. Previously they had drifted.
Added (desktop)The measurement model is now a deliberate line-for-line port between Kotlin and Python, so the two cannot quietly diverge again.
Originally logged as "works unchanged — a cradle is a dock." That was wrong, and reversing it was the most important call in the project.
A dot pulsing in time with speech, mounted in a driver's eyeline, is unsafe and illegal in many jurisdictions. Split into passenger (dot stays on, nobody is steering) and driving (screen dark, short vibration only).
A device cannot tell the two apart, so it asks, and the answer resets on every launch — a stale "passenger" must never survive into the next journey. Detecting the car automatically, via Bluetooth or activity recognition, remains the right answer and is not built.
The original concept. A plug-in device on a desk. Worked as an idea, but tied to one room and required building hardware.
A Windows-only desktop appReal, used, and the version that proved the idea. Still supported, but a computer is not where most conversations happen.
An Apple versionAttempted, abandoned. A volunteer with a Mac hit permission problems and there was no step-by-step guide for macOS. Revisit only if someone with a Mac and patience volunteers.
The screensaver-only approachBriefly the recommended way to run it, until the obvious objection: it requires the device to be plugged in, so it does not travel. Kept as an option for a docked tablet, dropped as the primary route.
What was deliberately removedA daily percentage — "you were audible 64% of today". Cut. It reset whenever the service restarted, so it could not answer the only question worth asking ("how did the week go?"), and a score risks turning a reminder into a judgement. If it returns, it needs to survive restarts and be genuinely encouraging.
Nothing is recorded, ever. Audio becomes one loudness number and is discarded. There is no recording to leak because none is made.
The dot is for the corner of the eye. Anything that makes someone look directly at it — text, numbers, animation for its own sake — is a regression.
Subtract before adding. No scores, no streaks, no notifications about the voice. The tool most likely to still be in use next month is the one that asks for the least.
Do not overclaim. "Prototype", "free" and "not a medical device" stay on every page. Overselling a homemade tool to people managing a degenerative illness would be a genuine wrong, not merely bad marketing.
The person is not a patient. They are someone who wants to be heard at dinner.