Skip to content

pepperonas/bpbel

Repository files navigation

🎵 bpbel

Real-time BPM & decibel detection for Android — wrapped in a Material 3 Expressive interface.

Release Download APK Release workflow Platform Min SDK Target SDK Kotlin Jetpack Compose Material 3 Expressive AGP Tests License: MIT Package PRs Welcome


bpbel — live BPM + dB view    bpbel — microphone permission

✨ Was bpbel macht

bpbel hört über das Mikrofon die Musik in deiner Umgebung und zeigt live:

  • 🥁 BPM (Beats per Minute) — das Tempo des laufenden Tracks, erkannt über Kick-Onset-Detection.
  • 🔊 Dezibel (dBFS) — die aktuelle Lautstärke, als animiertes Segment-Meter mit Peak-Hold.

Beides wird in einer Material 3 Expressive Oberfläche visualisiert: ein Orb mit dunklem Kern (damit die Zahl lesbar bleibt), der im Takt federt, bei jedem Beat Sonar-Ringe aussendet, von einem Glow-Arc umkreist wird und in lebendigen Verlaufsfarben schimmert.


📥 Download

Die fertige, signierte App gibt es in der Releases-Sektion:

👉 Neueste APK herunterladenbpbel-vX.Y.Z.apk

APK auf dem Android-Gerät öffnen und installieren (ggf. „Installation aus unbekannten Quellen" für deinen Browser/Dateimanager erlauben). Alle Releases sind mit demselben Schlüssel signiert, sodass Updates direkt über eine bestehende Installation laufen.

Für Maintainer — neues Release veröffentlichen: versionCode/versionName in app/build.gradle.kts erhöhen, committen, dann taggen:

git tag v1.0.1 && git push origin v1.0.1

Der Release-Workflow baut die signierte APK und hängt sie automatisch an einen neuen GitHub Release.


🧠 Wie die BPM-Erkennung funktioniert

Der Detektor ist ein energie-basierter Onset-Detector mit IOI-Clustering (Inter-Onset-Interval) — der klassische, praxiserprobte Ansatz für 4/4-Musik mit klarem Kick (typische Genauigkeit 85–95 %). Die Kernlogik (BpmAnalyzer.kt) ist ein 1:1-Port eines kampferprobten TypeScript-Detektors und wird durch eine portierte Test-Suite plus End-to-End-DSP-Tests (23 Tests) gegen das Original abgesichert.

Mikrofon (AudioRecord, 44.1 kHz mono PCM-16)
   │
   ├──▶ Full-band RMS ─────────▶ dBFS            ──▶ Lautstärke-Meter
   │
   └──▶ Kick-Bandpass (Highpass 30 Hz → Lowpass 100 Hz, kick-only)
            │
            ├─▶ RMS-Energie pro 1024-Sample-Frame
            ├─▶ gleitender Mittelwert (Baseline-Gate)
            ├─▶ Onset, wenn  Energie > Schwelle · Mittelwert
            │        UND  steigende Flanke (SuperFlux: Energie schlägt ihren
            │             eigenen jüngsten Peak → echter neuer Anschlag, kein
            │             erneutes Triggern auf gehaltenem Bass)
            │        UND  Lautstärke-Gate offen (echte Musik, keine Stille)
            │        UND  Refraktärzeit verstrichen
            ├─▶ Onsets → IOIs → Median → 60000 / Median = BPM
            ├─▶ Oktav-Korrektur (60–200 BPM) + Oktav-Snap (kein 120↔240-Flackern)
            └─▶ 4-s-Fenster-Mittel = stabiler Anzeigewert  (+ Stale-Reset bei Stille)

Die SuperFlux-Flankenerkennung (Böck & Widmer 2013) und das Lautstärke-Gate sind direkt aus dem produktiven Detektor des disco-controllers übernommen, der auf demselben BpmAnalyzer aufsetzt: Ersteres unterdrückt den Geister-Beat direkt nach einem echten Bass auf langsamen, gehaltenen Tracks; Letzteres verhindert, dass sich die BPM in einem leisen Raum auf Mikrofon-Rauschen einrastet.

Konfidenz entsteht aus der IOI-Konsistenz (1 − stddev/median): ein gleichmäßiger Viertel-Puls liefert ~0,9, Sprache/Rauschen ~0,1.

Warum kein FFT/Autokorrelation? Für „welcher Track läuft gerade — 120, 140 oder 170?" ist Onset-Detection deutlich günstiger (kein FFT) und völlig ausreichend.


🎨 Material 3 Expressive

Element Umsetzung
Theme MaterialExpressiveTheme — alle M3-Komponenten erben das feder-basierte expressive MotionScheme.
Motion Beat-Puls über Feder-Physik (spring, Overshoot), Sonar-Ringe pro Beat, umlaufender Glow-Arc, Liquid-Sweep-Gradient, BPM-Count-up.
Shapes Morphing Circle ↔ Star via androidx.graphics:graphics-shapes (Morph, RoundedPolygon).
Color Durchgängig dunkles, kuratiertes Theme (Violett → Magenta → Cyan) — club-style, garantiert lesbare Hell-auf-Dunkel-Readouts.
Typografie Emphasized Type-Scale (Black/Bold) für die Hero-Numerik.

🚀 Build & Run

Voraussetzungen: JDK 17, Android SDK (compileSdk 36), ein Gerät/Emulator mit Mikrofon.

# Debug-APK bauen
./gradlew :app:assembleDebug

# Auf verbundenem Gerät/Emulator installieren
./gradlew :app:installDebug
# oder:
adb install -r app/build/outputs/apk/debug/app-debug.apk

# Release (minified, R8)
./gradlew :app:assembleRelease

Beim ersten Start fragt die App die RECORD_AUDIO-Berechtigung an. Danach genügt es, das Gerät in die Nähe einer Musikquelle zu halten — der BPM-Wert pendelt sich nach ~5 Sekunden ein.


🧪 Testing

./gradlew :app:testDebugUnitTest

BpmAnalyzerTest synthetisiert Onset-Trains bei bekannten Tempi (90/120/175 BPM, Half-/Double-Time) und prüft Lock-on, Oktav-Korrektur, Refraktärzeit, Stale-Reset, Oktav-Snap und Konfidenz — als Beweis, dass der Kotlin-Port das Verhalten des Referenz-Algorithmus exakt reproduziert.

AudioChainTest treibt die echte KickBandpassBpmAnalyzer-Kette mit realistischem Frame-Timing und prüft u. a., dass ein Backbeat-Snare das Tempo nicht verdoppelt und ein langsamer, gehaltener Bass weiterhin sauber lockt (die SuperFlux-Flanke unterdrückt Geister-Beats, ohne echte langsame Beats zu schlucken). Auf Analyzer-Ebene sichern zwei Tests die neuen Gates ab: ein gehaltener Ton feuert genau einen Onset (kein Re-Trigger) und ein geschlossenes Lautstärke-Gate unterdrückt selbst laute, periodische Energie komplett.


🏗️ Projektstruktur

app/src/main/java/io/celox/bpbel/
├── MainActivity.kt              # Permission-Flow + Engine-Lifecycle
├── audio/
│   ├── BpmAnalyzer.kt           # Onset/IOI-Tempo-Detektor (portiert)
│   ├── Biquad.kt                # RBJ-Biquad + Kick-Bandpass (30→100 Hz, exakt wie inspector-rust)
│   └── AudioEngine.kt           # AudioRecord → dBFS + BPM → StateFlow
└── ui/
    ├── BpmScreen.kt             # Screen-Layout + Permission-States
    ├── BeatPulse.kt             # Morphende Spring-Puls-Visualisierung
    ├── DecibelMeter.kt          # Segment-Meter mit Attack/Release + Peak-Hold
    └── theme/                   # Material 3 Expressive Theme/Color/Type

⚠️ Bekannte Grenzen

  • dBFS ≠ kalibrierte SPL — relativer digitaler Pegel (abhängig von Mikrofon-Gain/AGC), keine echte akustische Dezibel-Messung.
  • Genauigkeit folgt dem Onset-Ansatz: stark auf 4/4-Musik mit klarem Kick, schwächer bei synkopiertem/ambientem Material.
  • Hört nur im Vordergrund (kein Background-Service).

🤝 Contributing

PRs willkommen. Bitte vor dem Push ./gradlew testDebugUnitTest laufen lassen.

📄 License

MIT © pepperonas

About

Real-time BPM & decibel detection for Android — Material 3 Expressive UI

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages