Skip to content

Commit

Permalink
feat(audio): volume boosting (#57)
Browse files Browse the repository at this point in the history
* feat(audio): volume boosting

* feat: display volume as percentage setting
  • Loading branch information
abdallahmehiz authored Sep 8, 2024
1 parent 3bf81bb commit a800d14
Show file tree
Hide file tree
Showing 17 changed files with 556 additions and 329 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Build

on:
push:
branches:
- main

jobs:
build:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/nightlies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ permissions:
on:
workflow_dispatch:
push:
branches:
- main

jobs:
build:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class AudioPreferences(preferenceStore: PreferenceStore) {
val defaultAudioDelay = preferenceStore.getInt("audio_delay_default")
val audioPitchCorrection = preferenceStore.getBoolean("audio_pitch_correction", true)
val audioChannels = preferenceStore.getEnum("audio_channels", AudioChannels.AutoSafe)
val volumeBoostCap = preferenceStore.getInt("audio_volume_boost_cap", 30)
}

enum class AudioChannels(@StringRes val title: Int, val property: String, val value: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class PlayerPreferences(
"default_speed_presets",
setOf("0.25", "0.5", "0.75", "1.0", "1.25", "1.5", "1.75", "2.0", "2.5", "3.0", "3.5", "4.0")
)
val displayVolumeAsPercentage = preferenceStore.getBoolean("display_volume_as_percentage", true)
val savePositionOnQuit = preferenceStore.getBoolean("save_position", true)

val automaticallyEnterPip = preferenceStore.getBoolean("automatic_pip")
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/live/mehiz/mpvkt/ui/player/MPVView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ class MPVView(context: Context, attributes: AttributeSet) : BaseMPVView(context,
"time-pos" to MPVLib.mpvFormat.MPV_FORMAT_INT64,
"demuxer-cache-time" to MPVLib.mpvFormat.MPV_FORMAT_INT64,
"duration" to MPVLib.mpvFormat.MPV_FORMAT_INT64,
"volume" to MPVLib.mpvFormat.MPV_FORMAT_INT64,
"volume-max" to MPVLib.mpvFormat.MPV_FORMAT_INT64,

"sid" to MPVLib.mpvFormat.MPV_FORMAT_STRING,
"secondary-sid" to MPVLib.mpvFormat.MPV_FORMAT_STRING,
Expand All @@ -213,6 +215,7 @@ class MPVView(context: Context, attributes: AttributeSet) : BaseMPVView(context,
MPVLib.setOptionString("alang", audioPreferences.preferredLanguages.get())
MPVLib.setOptionString("audio-delay", (audioPreferences.defaultAudioDelay.get() / 1000.0).toString())
MPVLib.setOptionString("audio-pitch-correction", audioPreferences.audioPitchCorrection.get().toString())
MPVLib.setOptionString("volume-max", (audioPreferences.volumeBoostCap.get() + 100).toString())
}

// Setup
Expand Down
19 changes: 18 additions & 1 deletion app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import android.os.Build
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.provider.MediaStore
import android.provider.Settings
import android.util.Log
import android.util.Rational
import android.view.KeyEvent
Expand Down Expand Up @@ -282,6 +283,20 @@ class PlayerActivity : AppCompatActivity() {
}
}

override fun onResume() {
super.onResume()

viewModel.currentVolume.update {
audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).also {
if (it < viewModel.maxVolume) viewModel.changeMPVVolumeTo(100)
}
}
viewModel.currentBrightness.update {
Settings.System.getFloat(contentResolver, Settings.System.SCREEN_BRIGHTNESS)
.normalize(0f, 255f, 0f, 1f)
}
}

private fun setupIntents(intent: Intent) {
intent.getStringExtra("title")?.let {
viewModel.mediaTitle.update { _ -> it }
Expand Down Expand Up @@ -382,8 +397,10 @@ class PlayerActivity : AppCompatActivity() {
when (property) {
"time-pos" -> viewModel.updatePlayBackPos(value.toFloat())
"demuxer-cache-time" -> viewModel.updateReadAhead(value = value)
"duration" -> viewModel.duration.update { value.toFloat() }
"volume" -> viewModel.setMPVVolume(value.toInt())
"volume-max" -> viewModel.volumeBoostCap = value.toInt() - 100
"chapter" -> viewModel.updateChapter(value)
"duration" -> viewModel.duration.update { value.toFloat() }
}
}

Expand Down
26 changes: 23 additions & 3 deletions app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class PlayerViewModel(
.normalize(0f, 255f, 0f, 1f),
)
val currentVolume = MutableStateFlow(activity.audioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
val currentMPVVolume = MutableStateFlow(MPVLib.getPropertyInt("volume"))
var volumeBoostCap: Int = MPVLib.getPropertyInt("volume-max")

val sheetShown = MutableStateFlow(Sheets.None)
val panelShown = MutableStateFlow(Panels.None)
Expand Down Expand Up @@ -330,20 +332,29 @@ class PlayerViewModel(
fun changeBrightnessTo(
brightness: Float,
) {
isBrightnessSliderShown.update { sheetShown.value == Sheets.None }
isBrightnessSliderShown.update { true }
currentBrightness.update { brightness.coerceIn(0f, 1f) }
activity.window.attributes = activity.window.attributes.apply {
screenBrightness = brightness.coerceIn(0f, 1f)
}
}

val maxVolume = activity.audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
fun changeVolumeBy(change: Int) {
val mpvVolume = MPVLib.getPropertyInt("volume")
if (volumeBoostCap > 0 && currentVolume.value == maxVolume) {
if (mpvVolume == 100 && change < 0) changeVolumeTo(currentVolume.value + change)
val finalMPVVolume = (mpvVolume + change).coerceAtLeast(100)
if (finalMPVVolume in 100..volumeBoostCap + 100) {
changeMPVVolumeTo(finalMPVVolume)
return
}
}
changeVolumeTo(currentVolume.value + change)
}

val maxVolume = activity.audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
fun changeVolumeTo(volume: Int) {
isVolumeSliderShown.update { sheetShown.value == Sheets.None }
isVolumeSliderShown.update { true }
val newVolume = volume.coerceIn(0..maxVolume)
activity.audioManager.setStreamVolume(
AudioManager.STREAM_MUSIC,
Expand All @@ -353,6 +364,15 @@ class PlayerViewModel(
currentVolume.update { newVolume }
}

fun changeMPVVolumeTo(volume: Int) {
MPVLib.setPropertyInt("volume", volume)
}

fun setMPVVolume(volume: Int) {
if (volume != currentMPVVolume.value) isVolumeSliderShown.update { true }
currentMPVVolume.update { volume }
}

fun changeVideoAspect(aspect: VideoAspect) {
var ratio = -1.0
var pan = 1.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import `is`.xyz.mpv.MPVLib
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.update
import live.mehiz.mpvkt.preferences.AudioPreferences
import live.mehiz.mpvkt.preferences.PlayerPreferences
import live.mehiz.mpvkt.preferences.preference.collectAsState
import live.mehiz.mpvkt.presentation.components.LeftSideOvalShape
Expand All @@ -58,6 +59,7 @@ import org.koin.compose.koinInject
fun GestureHandler(modifier: Modifier = Modifier) {
val viewModel = koinInject<PlayerViewModel>()
val playerPreferences = koinInject<PlayerPreferences>()
val audioPreferences = koinInject<AudioPreferences>()
val panelShown by viewModel.panelShown.collectAsState()
val allowGesturesInPanels by playerPreferences.allowGesturesInPanels.collectAsState()
val duration by viewModel.duration.collectAsState()
Expand Down Expand Up @@ -105,7 +107,9 @@ fun GestureHandler(modifier: Modifier = Modifier) {
}
var isLongPressing by remember { mutableStateOf(false) }
val currentVolume by viewModel.currentVolume.collectAsState()
val currentMPVVolume by viewModel.currentMPVVolume.collectAsState()
val currentBrightness by viewModel.currentBrightness.collectAsState()
val volumeBoostingCap = audioPreferences.volumeBoostCap.get()
val haptics = LocalHapticFeedback.current
Box(
modifier = modifier.fillMaxSize(),
Expand Down Expand Up @@ -229,46 +233,78 @@ fun GestureHandler(modifier: Modifier = Modifier) {
.pointerInput(areControlsLocked) {
if ((!brightnessGesture && !volumeGesture) || areControlsLocked) return@pointerInput
var startingY = 0f
var mpvVolumeStartingY = 0f
var originalVolume = currentVolume
var originalMPVVolume = currentMPVVolume
var originalBrightness = currentBrightness
val brightnessGestureSens = 0.001f
val volumeGestureSens = 0.03f
val mpvVolumeGestureSens = 0.02f
val isIncreasingVolumeBoost: (Float) -> Boolean = {
volumeBoostingCap > 0 && currentVolume == viewModel.maxVolume &&
currentMPVVolume - 100 < volumeBoostingCap && it < 0
}
val isDecreasingVolumeBoost: (Float) -> Boolean = {
volumeBoostingCap > 0 && currentVolume == viewModel.maxVolume &&
currentMPVVolume - 100 in 1..volumeBoostingCap && it > 0
}
detectVerticalDragGestures(
onDragEnd = { startingY = 0f },
onDragStart = {
startingY = it.y
startingY = 0f
mpvVolumeStartingY = 0f
originalVolume = currentVolume
originalMPVVolume = currentMPVVolume
originalBrightness = currentBrightness
},
) { change, amount ->
when {
volumeGesture && brightnessGesture -> {
if (change.position.x < size.width / 2) {
viewModel.changeBrightnessTo(
originalBrightness + ((startingY - change.position.y) * brightnessGestureSens),
)
} else {
viewModel.changeVolumeTo(
originalVolume + ((startingY - change.position.y) * volumeGestureSens).toInt(),
)
val changeVolume: () -> Unit = {
if (isIncreasingVolumeBoost(amount) || isDecreasingVolumeBoost(amount)) {
if (mpvVolumeStartingY == 0f) {
startingY = 0f
originalVolume = currentVolume
mpvVolumeStartingY = change.position.y
}
}

brightnessGesture -> {
viewModel.changeBrightnessTo(
originalBrightness + ((startingY - change.position.y) * brightnessGestureSens),
viewModel.changeMPVVolumeTo(
calculateNewValue(originalMPVVolume, mpvVolumeStartingY, change.position.y, mpvVolumeGestureSens)
.coerceIn(100..volumeBoostingCap + 100),
)
}
// it's not always true, AS is drunk
volumeGesture -> {
} else {
if (startingY == 0f) {
mpvVolumeStartingY = 0f
originalMPVVolume = currentMPVVolume
startingY = change.position.y
}
viewModel.changeVolumeTo(
originalVolume + ((startingY - change.position.y) * volumeGestureSens).toInt(),
calculateNewValue(originalVolume, startingY, change.position.y, volumeGestureSens),
)
}
}
val changeBrightness: () -> Unit = {
if (startingY == 0f) startingY = change.position.y
viewModel.changeBrightnessTo(
calculateNewValue(originalBrightness, startingY, change.position.y, brightnessGestureSens)
)
}
when {
volumeGesture && brightnessGesture -> {
if (change.position.x < size.width / 2) changeBrightness() else changeVolume()
}

brightnessGesture -> changeBrightness()
// it's not always true, AS is drunk
volumeGesture -> changeVolume()
else -> {}
}
}
},
)
}

fun calculateNewValue(originalValue: Int, startingY: Float, newY: Float, sensitivity: Float): Int {
return originalValue + ((startingY - newY) * sensitivity).toInt()
}

fun calculateNewValue(originalValue: Float, startingY: Float, newY: Float, sensitivity: Float): Float {
return originalValue + ((startingY - newY) * sensitivity)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.update
import live.mehiz.mpvkt.R
import live.mehiz.mpvkt.preferences.AudioPreferences
import live.mehiz.mpvkt.preferences.PlayerPreferences
import live.mehiz.mpvkt.preferences.preference.collectAsState
import live.mehiz.mpvkt.ui.player.PlayerUpdates
Expand Down Expand Up @@ -83,6 +84,7 @@ fun PlayerControls(
val viewModel = koinInject<PlayerViewModel>()
val spacing = MaterialTheme.spacing
val playerPreferences = koinInject<PlayerPreferences>()
val audioPreferences = koinInject<AudioPreferences>()
val controlsShown by viewModel.controlsShown.collectAsState()
val areControlsLocked by viewModel.areControlsLocked.collectAsState()
val seekBarShown by viewModel.seekBarShown.collectAsState()
Expand Down Expand Up @@ -136,13 +138,14 @@ fun PlayerControls(
val isVolumeSliderShown by viewModel.isVolumeSliderShown.collectAsState()
val brightness by viewModel.currentBrightness.collectAsState()
val volume by viewModel.currentVolume.collectAsState()
val mpvVolume by viewModel.currentMPVVolume.collectAsState()

LaunchedEffect(volume, isVolumeSliderShown) {
delay(1000)
LaunchedEffect(volume, mpvVolume, isVolumeSliderShown) {
delay(2000)
if (isVolumeSliderShown) viewModel.isVolumeSliderShown.update { false }
}
LaunchedEffect(brightness, isBrightnessSliderShown) {
delay(1000)
delay(2000)
if (isBrightnessSliderShown) viewModel.isBrightnessSliderShown.update { false }
}
AnimatedVisibility(
Expand All @@ -166,9 +169,14 @@ fun PlayerControls(
bottom.linkTo(parent.bottom)
},
) {
val boostCap by audioPreferences.volumeBoostCap.collectAsState()
val displayVolumeAsPercentage by playerPreferences.displayVolumeAsPercentage.collectAsState()
VolumeSlider(
volume,
0..viewModel.maxVolume,
mpvVolume = mpvVolume,
range = 0..viewModel.maxVolume,
boostRange = if (boostCap > 0) 0..audioPreferences.volumeBoostCap.get() else null,
displayAsPercentage = displayVolumeAsPercentage
)
}

Expand Down
Loading

0 comments on commit a800d14

Please sign in to comment.