Skip to content

Commit

Permalink
refactor: extract player volume fade out into a new class
Browse files Browse the repository at this point in the history
  • Loading branch information
mebarbosa committed Dec 17, 2024
1 parent fc4e802 commit f4f7e20
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2320,22 +2320,9 @@ open class PlaybackManager @Inject constructor(
}
}

fun setupFadeOutWhenFinishingSleepTimer() {
if (isSleepAfterEpisodeEnabled() || isSleepAfterChapterEnabled()) return

// it needs to run in the main thread because of player getVolume
applicationScope.launch(Dispatchers.Main) {
val timeLeft = sleepTimer.getState().timeLeft.inWholeSeconds
val fadeDuration = 5
val startVolume = (player as? SimplePlayer)?.getVolume() ?: 1.0f

if (timeLeft <= fadeDuration) {
val fraction = timeLeft.toFloat() / fadeDuration
val newVolume = startVolume * fraction
player?.setVolume(newVolume)
} else {
player?.setVolume(startVolume)
}
fun performVolumeFadeOut(duration: Double) {
player?.let {
PlayerVolumeFadeOut(it, applicationScope).performFadeOut(duration, onStopPlaying = { restorePlayerVolume() })
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ open class PlaybackService : MediaBrowserServiceCompat(), CoroutineScope {

private val disposables = CompositeDisposable()

private var timerDisposable: Disposable? = null
private var sleepTimerDisposable: Disposable? = null
private var currentTimeLeft: Duration = ZERO

override val coroutineContext: CoroutineContext
Expand Down Expand Up @@ -191,6 +191,7 @@ open class PlaybackService : MediaBrowserServiceCompat(), CoroutineScope {
super.onDestroy()

disposables.clear()
sleepTimerDisposable?.dispose()

LogBuffer.i(LogBuffer.TAG_PLAYBACK, "Playback service destroyed")
}
Expand Down Expand Up @@ -702,16 +703,19 @@ open class PlaybackService : MediaBrowserServiceCompat(), CoroutineScope {
return
}

if (timerDisposable == null || timerDisposable!!.isDisposed) {
if (sleepTimerDisposable == null || sleepTimerDisposable!!.isDisposed) {
currentTimeLeft = newTimeLeft

timerDisposable = Observable.interval(1, TimeUnit.SECONDS, Schedulers.computation())
sleepTimerDisposable = Observable.interval(1, TimeUnit.SECONDS, Schedulers.computation())
.takeWhile { currentTimeLeft > ZERO }
.doOnNext {
playbackManager.setupFadeOutWhenFinishingSleepTimer()
currentTimeLeft = currentTimeLeft.minus(1.seconds)
sleepTimer.updateSleepTimerStatus(sleepTimeRunning = currentTimeLeft != ZERO, timeLeft = currentTimeLeft)

if (currentTimeLeft == 5.seconds) {
playbackManager.performVolumeFadeOut(5.0)
}

if (currentTimeLeft <= ZERO) {
LogBuffer.i(LogBuffer.TAG_PLAYBACK, "Paused from sleep timer.")
CoroutineScope(Dispatchers.Main).launch {
Expand All @@ -730,8 +734,8 @@ open class PlaybackService : MediaBrowserServiceCompat(), CoroutineScope {
}

private fun cancelSleepTimer() {
timerDisposable?.dispose()
timerDisposable = null
sleepTimerDisposable?.dispose()
sleepTimerDisposable = null
currentTimeLeft = ZERO
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package au.com.shiftyjelly.pocketcasts.repositories.playback

import kotlin.math.exp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class PlayerVolumeFadeOut(
private var player: Player,
private val scope: CoroutineScope,
) {
companion object {
private const val VOLUME_CHANGES_PER_SECOND = 30.0
private const val FADE_VELOCITY = 2.0
private const val FROM_VOLUME = 1.0
private const val TO_VOLUME = 0.0
}

fun performFadeOut(duration: Double, onStopPlaying: () -> Unit) {
val totalSteps = (duration * VOLUME_CHANGES_PER_SECOND).toInt()
val delayBetweenSteps = (1000 / VOLUME_CHANGES_PER_SECOND).toLong()

var currentStep = 0

scope.launch(Dispatchers.Main) {
while (currentStep < totalSteps) {
val normalizedTime = (currentStep.toDouble() / totalSteps).coerceIn(0.0, 1.0)
val volumeMultiplier = exp(-FADE_VELOCITY * normalizedTime) * (1 - normalizedTime)
val newVolume = TO_VOLUME + (FROM_VOLUME - TO_VOLUME) * volumeMultiplier

if (player.isPlaying()) {
player.setVolume(newVolume.toFloat())
} else {
onStopPlaying()
break
}

currentStep++
delay(delayBetweenSteps)
}

player.setVolume(TO_VOLUME.toFloat())
}
}
}

0 comments on commit f4f7e20

Please sign in to comment.