Pitch-shift audio files from A440 to A432 Hz (432/440 = 0.9818... ratio, ~31.77 cents down). Preserves tempo, metadata, and original format. Android port of retune432.py.
- Pitch shift via ffmpeg rubberband filter (primary) or asetrate+aresample (fallback)
- Batch processing — select multiple files at once
- Format support — mp3, flac, wav, ogg, aac, m4a
- Metadata preservation — ID3v2, Vorbis comments, FLAC tags carried through
- Skip duplicates — won't re-convert files already in output folder
- Material Design 3 UI with dynamic color (Android 12+)
- Foreground service with progress notification
- Output to
Music/Retune432/on device storage
- Android Studio Ladybug (2024.2.1) or newer
- JDK 17
- Android SDK 35
- Kotlin 2.1.0
# Clone
cd ~/retune432-android
# Open in Android Studio
studio .
# Or build from command line (requires Android SDK + ANDROID_HOME set)
./gradlew assembleDebug
# Install on connected device
./gradlew installDebugThe first build will download ffmpeg-kit-full (~80MB) — this bundles the full ffmpeg binary with all codecs including rubberband.
If gradlew is missing, generate it:
gradle wrapper --gradle-version 8.9retune432-android/
├── app/
│ ├── build.gradle.kts # App module build config
│ ├── proguard-rules.pro # Keep ffmpeg-kit classes
│ └── src/main/
│ ├── AndroidManifest.xml # Permissions, service, activity
│ ├── java/com/retune432/app/
│ │ ├── Retune432App.kt # Application class, notification channel
│ │ ├── MainActivity.kt # Entry point, Compose host
│ │ ├── service/
│ │ │ └── ConversionService.kt # Foreground service for batch conversion
│ │ ├── ui/
│ │ │ ├── theme/Theme.kt # MD3 theme with dynamic color
│ │ │ ├── screens/MainScreen.kt # Main UI: file picker + progress + log
│ │ │ └── components/
│ │ │ ├── FileChip.kt # Selected file chip with remove
│ │ │ └── ConversionLogItem.kt # Result row in log
│ │ └── util/
│ │ └── AudioConverter.kt # Core conversion logic (rubberband + fallback)
│ └── res/
│ ├── drawable/ic_music_note.xml
│ ├── values/strings.xml
│ ├── values/themes.xml
│ └── xml/file_paths.xml
├── build.gradle.kts # Root build file
├── settings.gradle.kts # Project settings
├── gradle.properties # JVM args, AndroidX
├── gradle/
│ ├── libs.versions.toml # Version catalog
│ └── wrapper/gradle-wrapper.properties
├── .gitignore
└── README.md
- AudioConverter — stateless converter object. Two-tier strategy:
rubberband=pitch=0.9818(high quality, preserves formants)asetrate + aresamplefallback if rubberband unavailable
- ConversionService — foreground service running conversions off the main thread.
Exposes a
StateFlow<ConversionState>consumed by the UI. - MainScreen — single-screen Compose UI. File picker → convert button → progress bar → results log.
| Permission | Why | When |
|---|---|---|
READ_MEDIA_AUDIO (API 33+) |
Read audio files | File selection |
READ_EXTERNAL_STORAGE (API <33) |
Same, legacy | File selection |
FOREGROUND_SERVICE |
Run conversion in background | During conversion |
POST_NOTIFICATIONS (API 33+) |
Show progress notification | During conversion |
Same as retune432.py — MIT.