This repository implements a comprehensive audio loudness analysis and dynamics processing solution for Android, featuring a high-performance Kotlin/Native core with libsndfile integration and EBU R128 loudness calculation. The library provides seamless integration with ExoPlayer for real-time audio processing and loudness normalization.
The project consists of three main modules designed for optimal performance and modularity:
├── native/ # Kotlin/Native core library
├── lib/ # Android wrapper library
└── demo-app/ # Demonstration application
Kotlin/Native Implementation with C Interop
The native module implements the core audio processing functionality using Kotlin/Native, enabling compilation to native machine code for optimal performance across multiple architectures.
libsndfile Integration
- Header Modification: Modified
native/include/sndfile.hto enable proper binding generation by defining a dummy structuresf_priv_tagfor the opaqueSNDFILEtype - C Interop Configuration: Configured in
native/build.gradle.ktswith architecture-specific cinterops and shared library compilation settings - Linker Configuration: Properly configured to link against libsndfile shared objects for each target architecture
- Stub Definition:
native/src/nativeInterop/cinterop/libsndfile.defserves as a compilation requirement (empty file, actual configuration in Gradle)
EBU R128 Loudness Implementation
Located in native/src/nativeMain/kotlin/EbuR128.kt, this module provides:
- Lightweight, self-contained EBU R128 implementation
- Cross-platform compatibility (works on both Kotlin/Native and Kotlin/JVM)
- Standards-compliant loudness measurement using only Kotlin standard library
- Integrated loudness calculation capability
// Core loudness calculation implementation
class LightweightEbuR128(channels: Int, sampleRate: Int) {
fun addSamples(samples: FloatArray)
fun getIntegratedLoudness(): Float
}Android Architecture Support
The build system automatically compiles native libraries for four Android architectures:
arm64-v8a(64-bit ARM)armeabi-v7a(32-bit ARM)x86_64(64-bit Intel)- NO
x86: Outdated!
Automated Build Pipeline
Configured in lib/build.gradle.kts:
- Automatic native library compilation
- Cross-architecture dependency management
- Automated SO file copying to appropriate Android library locations
- Gradle task orchestration for seamless builds
Core API Interface
object LightweightLoudnessAnalyzer {
fun analyzeFile(audioFile: File): AudioLoudnessInfo
}ExoPlayer Integration
The DynamicsProcessor class (lib/src/main/kotlin/me/earzuchan/tetsu/exoplayer/DynamicsProcessor.kt) provides:
- Real-time audio processing during playback
- Loudness normalization to -14 LUFS (broadcast standard)
- Per-track loudness adjustment via
setCurrentTrackLoudness(info) - Seamless integration with ExoPlayer's audio processing pipeline
// Usage pattern
val processor = DynamicsProcessor()
processor.setCurrentTrackLoudness(loudnessInfo)
// Apply to ExoPlayer audio processor chainData Models
Defined in lib/src/main/kotlin/me/earzuchan/tetsu/models/Models.kt:
data class AudioLoudnessInfo(
val lufs: Float, // LUFS
)Jetpack Compose Implementation
The demo application showcases practical usage:
- File-based loudness analysis
- Real-time playback with loudness normalization
- ExoPlayer integration demonstration
- Audio volume balancing across tracks
The libsndfile integration requires careful handling of opaque C structures. The key modification in the header file:
// Original: typedef struct sf_private_tag SNDFILE;
// Modified to enable Kotlin/Native binding generation:
typedef struct sf_priv_tag {
int dummy; // Dummy field for binding generation
} SNDFILE;This allows the Kotlin/Native compiler to generate proper FFI bindings while maintaining API compatibility.
The implementation follows ITU-R BS.1770-4 standard:
- Pre-filtering: K-weighting filter implementation
- Gating: Absolute and relative gating for integrated loudness
- Windowing: Sliding window analysis for momentary measurements
- Integration: Proper time-domain integration following the standard
- Native Compilation: Kotlin/Native eliminates JVM overhead for audio processing
- Memory Management: Efficient buffer management for real-time processing
- Architecture Optimization: Platform-specific optimizations through native compilation
- Minimal Dependencies: Lightweight implementation reduces APK size impact
// Analyze audio file
val audioFile = File("/path/to/audio.wav")
val loudnessInfo = LightweightLoudnessAnalyzer.analyzeFile(audioFile.absolutePath)
// Integrate with ExoPlayer: Add the processor to your ExoPlayer
// Apply to ExoPlayer
val processor = DynamicsProcessor()
processor.setCurrentTrackLoudness(loudnessInfo)- Kotlin/Native toolchain
- Android NDK
- libsndfile prebuilt SO binaries
- Gradle 8+
- Audio players: Already adopted by ZeroMusic, https://github.com/sky130/ZeroMusicApp
- Music streaming services
- Broadcast compliance tools
......
Apache 2.0