Stop sudden loud audio from blasting your ears.
VolumeSentinel watches your Android device’s media audio in real time. When a spike exceeds your threshold, it instantly cuts the media volume, then gradually restores it when things quiet down.
Inspired by the Windows version, this Android edition brings the same hearing protection to mobile — especially for headphone and IEM users.
中文说明见 README.zh-CN.md · 架构见 ARCHITECTURE.md · 变更记录见 CHANGELOG.md
- Dual capture engine — MediaProjection (true look-ahead) or Visualizer API (no screen share)
- Real-time peak detection — analyzes audio every ~10 ms with configurable danger confirmation
- Look-ahead buffer — 20 ms pre-emptive analysis (MediaProjection mode)
- Smart recovery — gradual volume restoration with hold delay and step control
- Per-device profiles — independent settings for speaker, wired headphones, Bluetooth, USB DAC
- Per-engine settings — switching engines loads the matching parameter set (
mp_/viz_) - Audition preview — play a reference tone at the threshold level to calibrate by ear
- 0–100 loudness mapping — Visualizer mode maps dBFS to an intuitive 0–100 scale
- Volume compensation — prevents oscillation during sustained loud audio (Visualizer mode)
- Foreground service — persistent background protection with notification-bar status
- Android 10 (API 29) or later
- No root required
Download the latest APK from Releases.
Release builds may be unsigned. Android will ask you to allow installs from unknown sources. Prefer building and signing yourself for production use.
# Prerequisites: Android Studio Hedgehog+ (JDK 17+), Android SDK 34
git clone git@github.com:yaki1210/VolumeSentinel-Android.git
cd VolumeSentinel-Android
./gradlew assembleDebug # debug APK
./gradlew assembleRelease # release APK (unsigned unless you configure signing)
# Outputs:
# app/build/outputs/apk/debug/app-debug.apk
# app/build/outputs/apk/release/app-release-unsigned.apkWindows:
gradlew.bat assembleDebug- Install the APK and launch VolumeSentinel
- Grant microphone and notifications permissions
- Choose a capture engine (see comparison below)
- Adjust Trigger Threshold — tap ▶ to audition the threshold loudness
- Set Safe Volume Level — volume used while protecting
- Toggle the main switch to start protection
Status in the persistent notification:
| Status | Meaning |
|---|---|
| SAFE | Monitoring, no danger |
| PROTECTING | Volume reduced due to a spike |
| RECOVERING | Gradually restoring volume |
VolumeSentinel offers two independent capture engines. They measure audio at different points in the playback path, so their readings, defaults, and trade-offs differ a lot. Do not copy thresholds between engines.
| MediaProjection | Visualizer | |
|---|---|---|
| Capture position | Pre-volume (content loudness, before system volume) | Post-volume (after STREAM_MUSIC volume) |
| Signal | Full PCM 16-bit / 48 kHz / stereo | Peak & RMS in millibels (mB → dBFS) |
| Look-ahead | Yes (~20 ms ring buffer) | No (real-time poll only) |
| Screen-share dialog | Required every session / after token expiry | Not needed |
| Typical threshold | around −12 dBFS | around −50 dBFS (or ~loudness 50/100) |
| Stability | MediaProjection token may expire | Watchdog re-attaches after stuck silence |
| Foreground service type | MEDIA_PROJECTION |
MICROPHONE |
| Best for | Highest accuracy, true look-ahead, calibration close to Windows | Everyday use without screen-share UI, fewer interruptions |
- Captures the original media signal before system volume is applied — same idea as Windows WASAPI loopback.
- Look-ahead lets the app react slightly before the loud part is fully heard.
- Requires the system screen / media projection consent dialog. On many devices this can affect notifications or show a persistent “casting / sharing” indicator.
- If the projection token expires, protection pauses until you re-authorize (the app can prompt via the notification / UI).
- Attaches to the global output mix (session
0) via the Visualizer API. - Only needs mic + audio-settings permissions — no screen share.
- Because capture is after volume, lowering volume also lowers the measured peak. The controller applies volume compensation so sustained loud content does not oscillate between protect and recover.
- Readings are typically much lower than MediaProjection for the same content — always calibrate on this engine’s own meter / audition tone.
- A watchdog briefly restarts Visualizer after ~50 consecutive silence frames (~500 ms at 10 ms poll) when the API gets stuck after app/device switches.
Media apps ──► [pre-volume] ──► system volume ──► [post-volume] ──► speaker / headphones
▲ ▲
MediaProjection Visualizer
Settings are stored per engine and per output device. Switching engine or plugging headphones loads the matching set.
| Parameter | Default (MP) | Default (Viz) | What it does | Tuning tips |
|---|---|---|---|---|
| Trigger Threshold | −12 dBFS | −50 dBFS | Peak level that starts protection | Start near default. Use ▶ audition. Lower (more negative) = more sensitive / triggers earlier. Raise if it fires on normal peaks. |
| Safe Volume Level | 5% | 5% | Volume while protecting | Headphones/IEM: 3–8%. Speaker: 10–20% is often fine. Too low can feel like mute on devices with few volume steps. |
| Recovery Delay (hold) | 500 ms | 800 ms | Quiet time before recovery starts | Increase if content has frequent short gaps (ads, game SFX). Viz defaults higher because there is no look-ahead. |
| Recovery Step | 10% | 8% | Volume increase per recovery step | Smaller = smoother / less “pumping”. Larger = snappier return to full volume. |
| Parameter | Default | What it does | Tuning tips |
|---|---|---|---|
| Poll Interval | 10 ms | How often peaks are sampled | 10 ms is a good balance. Higher saves a bit of CPU but increases reaction latency. |
| Danger Frames | 1 (MP) / 2 (Viz) | Consecutive dangerous frames required | Raise to 2–3 if single-frame spikes false-trigger (especially Visualizer). Lower for fastest reaction. |
| Lookback Frames | 2 | Historical peak window size | Slightly larger window smooths flicker; too large delays “all clear”. |
| Look-Ahead Buffer | 20 ms | Pre-emptive window (MP only) | 10–30 ms is typical. Higher uses more memory/latency budget; Visualizer ignores this. |
These are type defaults used when a device has no custom profile yet (MediaProjection-oriented). On Visualizer, still re-calibrate with the on-screen meter.
| Output | Threshold (ref.) | Safe vol. | Hold | Notes |
|---|---|---|---|---|
| Built-in speaker | ~−8 dBFS | ~15% | ~400 ms | More forgiving; room noise / lower risk |
| Wired / USB headset | ~−15 dBFS | ~5% | ~600 ms | Most critical for hearing protection |
| Bluetooth A2DP | ~−14 dBFS | ~5% | ~500 ms | Codec / latency vary by device |
- Pick the engine you will actually use daily.
- Play representative content (the apps that surprised you before).
- Watch the live peak meter during normal loud parts vs. spikes.
- Set threshold slightly above normal peaks, below the spikes you hate — or use ▶ audition.
- Trigger protection once on purpose; confirm Safe Volume is still listenable, not silent.
- If it chatters (protect ↔ recover rapidly): raise threshold a little, increase hold, or raise danger frames.
- If it misses spikes: lower threshold, lower danger frames (MP), ensure poll stays ~10 ms.
- Plug headphones and re-check — profiles are per device; don’t assume speaker settings transfer.
- Threshold UI shows dBFS (loudness/100). Use loudness as a human-friendly scale; the engine still decides in dBFS.
- After a volume duck, compensation reconstructs “content loudness” so recovery waits for real quiet, not for the duck itself.
- If the meter freezes at silence while audio plays, wait for the watchdog (~0.5 s) or toggle protection once.
- RECORD_AUDIO is required for both engines (Android’s capture APIs), even though this app is not a traditional “microphone recorder” for your voice.
- POST_NOTIFICATIONS (Android 13+) is required for the status notification; without it, background status is hard to see.
- MediaProjection may show a system “screen sharing / casting” indicator and can interact poorly with some OEMs’ notification policies.
- Some OEMs (Xiaomi, Huawei, Oppo, Vivo, Samsung, etc.) aggressively kill background apps — whitelist VolumeSentinel / allow “autostart” / disable battery restrictions for reliable protection.
Typically works with: YouTube, Bilibili, Netflix, games, browser video, music players, local video.
Not supported / not intended: phone calls, VoLTE, voice messages, video-conference call audio (separate Android audio paths; ordinary apps should not duck call audio).
- This tool reduces system media volume after detection. It is not a medical device, not a substitute for safe listening levels, and not a guarantee against all sudden loudness (especially Visualizer without look-ahead, or after projection expiry).
- Always use reasonable base volume. Protection is a safety net, not permission to max the volume slider.
- On devices with few volume steps, very low Safe Volume % may round to near-mute — raise it slightly if recovery seems stuck silent (fixed path uses
roundToInt, but hardware steps still limit resolution).
- Prefer leaving the notification visible so you can see SAFE / PROTECTING / RECOVERING.
- After long standby or audio-route changes, confirm the service is still running.
- Switching engines does not copy thresholds — each engine keeps its own calibration.
- Changing device (BT ↔ wired) switches profiles automatically; customize each if needed.
- Debug builds are larger and slower; use release APK for daily use once you trust a build.
- Audio is analyzed on device for peaks. The app does not upload audio content.
- MediaProjection is used for playback capture, not for recording your screen to disk.
| Scope | Keys / mechanism |
|---|---|
| Global | enabled, capture_mode |
| Per engine | mp_* / viz_* prefixed SharedPreferences |
| Per device | JSON profiles in a separate prefs file |
Legacy unprefixed keys are migrated once to the mp_ prefix on first load.
app/src/main/java/com/volumesentinel/android/
├── MainActivity.kt # Entry, permissions
├── audio/
│ ├── CaptureMode.kt # Engine selection
│ ├── AudioCaptureEngine.kt # MediaProjection capture
│ ├── VisualizerCaptureEngine.kt # Visualizer API capture
│ ├── AudioFrameBuffer.kt # Look-ahead ring buffer
│ ├── PeakDetector.kt # PCM peak / RMS analysis
│ ├── VolumeController.kt # STREAM_MUSIC volume
│ └── AuditionPlayer.kt # Threshold preview tone
├── config/
│ └── ProtectionConfig.kt # Per-mode persisted settings
├── device/
│ ├── AudioDeviceMonitor.kt # Output device detection
│ └── DeviceProfileManager.kt # Per-device profiles
├── protection/
│ ├── ProtectionController.kt # State machine + compensation
│ └── ProtectionState.kt
├── service/
│ └── ProtectionService.kt # Foreground orchestrator
└── ui/
├── MainScreen.kt
└── theme/
./gradlew test # unit tests
./gradlew assembleDebug # debug APK
./gradlew assembleRelease # release APKStack: Kotlin, Jetpack Compose, Foreground Service, MediaProjection / Visualizer, minSdk 29, targetSdk 34.
Current version: 1.1.0 (versionCode 2). See CHANGELOG.md.
Color OS (based on Android 14) employs additional background management policies. Even with "Allow background operation" enabled, the system may:
- Suspend the service's volume control capability after prolonged inactivity
- Freeze the app process in the background, preventing it from responding to audio changes
- Throttle frequent notification updates, indirectly affecting service liveness
Symptoms: The protection notification remains visible, but volume changes are no longer applied. Reopening the app restores normal behavior.
Workarounds:
- Add VolumeSentinel to the "Unrestricted battery optimization" whitelist
- Lock the app in the recent tasks menu
- Disable "Smart power saving" or "Auto freeze" for VolumeSentinel in system settings
- If all else fails, check Developer Options for background process limits
Pure Android 16 provides reliable foreground service scheduling. After disabling battery optimization, VolumeSentinel runs perfectly in the background with consistent volume protection behavior.
- Always add VolumeSentinel to the battery optimization whitelist on all platforms
- If protection stops responding, simply reopen the app to restore functionality — no need to restart the service
- The Visualizer engine (no screen-share consent) may be more stable on certain ROMs
MIT — free to use, share, and modify.
- VolumeSentinel (Windows) — original project inspiration