Normalize volume (#53)

This commit is contained in:
vfsfitvnm
2022-06-21 19:37:01 +02:00
parent 0ab184cd85
commit 8a94c7e714
10 changed files with 399 additions and 28 deletions

View File

@@ -129,12 +129,13 @@ interface Database {
views = [
SortedSongInPlaylist::class
],
version = 4,
version = 5,
exportSchema = true,
autoMigrations = [
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3),
AutoMigration(from = 3, to = 4, spec = DatabaseInitializer.From3To4Migration::class),
AutoMigration(from = 4, to = 5),
],
)
abstract class DatabaseInitializer protected constructor() : RoomDatabase() {

View File

@@ -13,6 +13,8 @@ data class Song(
val lyrics: String? = null,
val likedAt: Long? = null,
val totalPlayTimeMs: Long = 0,
val loudnessDb: Float? = null,
val contentLength: Long? = null,
) {
val formattedTotalPlayTime: String
get() {

View File

@@ -44,6 +44,7 @@ import com.google.common.util.concurrent.ListenableFuture
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.MainActivity
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.Outcome
import kotlinx.coroutines.*
@@ -100,6 +101,8 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, MediaNotific
private val coroutineScope = CoroutineScope(Dispatchers.IO) + Job()
private val songPendingLoudnessDb = mutableMapOf<String, Float?>()
override fun onCreate() {
super.onCreate()
@@ -136,6 +139,18 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, MediaNotific
.build()
player.addListener(this)
coroutineScope.launch(Dispatchers.IO) {
while (true) {
delay(1000)
withContext(Dispatchers.Main) {
println("volume: ${player.volume}")
}
songPendingLoudnessDb.forEach { (key, value) ->
println(" $key = $value")
}
}
}
}
override fun onDestroy() {
@@ -269,13 +284,14 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, MediaNotific
val mediaItem =
eventTime.timeline.getWindow(eventTime.windowIndex, Timeline.Window()).mediaItem
coroutineScope.launch(Dispatchers.IO) {
Database.insert(mediaItem)
Database.internal.queryExecutor.execute {
Database.incrementTotalPlayTimeMs(mediaItem.mediaId, playbackStats.totalPlayTimeMs)
}
}
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
normalizeVolume()
radio?.let { radio ->
if (player.mediaItemCount - player.currentMediaItemIndex <= 3) {
coroutineScope.launch(Dispatchers.Main) {
@@ -285,6 +301,18 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, MediaNotific
}
}
private fun normalizeVolume() {
player.volume = player.currentMediaItem?.mediaId?.let { mediaId ->
songPendingLoudnessDb.getOrElse(mediaId) {
player.currentMediaItem?.mediaMetadata?.extras?.getFloat("loudnessDb")
}
?.takeIf { it > 0 }
?.let { loudnessDb ->
(1f - (0.01f + loudnessDb / 15)).coerceIn(0.1f, 1f)
}
} ?: 1f
}
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
@@ -454,10 +482,33 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, MediaNotific
val url = runBlocking(Dispatchers.IO) {
it.vfsfitvnm.youtubemusic.YouTube.player(videoId)
}.flatMap { body ->
val loudnessDb = body.playerConfig?.audioConfig?.loudnessDb?.toFloat()
songPendingLoudnessDb[videoId] = loudnessDb
runBlocking(Dispatchers.Main) {
normalizeVolume()
}
when (val status = body.playabilityStatus.status) {
"OK" -> body.streamingData?.adaptiveFormats?.findLast { format ->
format.itag == 251 || format.itag == 140
}?.url?.let { Outcome.Success(it) } ?: Outcome.Error.Unhandled(
}?.let { format ->
val mediaItem = runBlocking(Dispatchers.Main) {
player.currentMediaItem
}
if (mediaItem?.mediaId == videoId) {
Database.internal.queryExecutor.execute {
Database.update(Database.insert(mediaItem).copy(
loudnessDb = loudnessDb,
contentLength = format.contentLength
))
}
}
Outcome.Success(format.url)
} ?: Outcome.Error.Unhandled(
PlaybackException(
"Couldn't find a playable audio format",
null,

View File

@@ -236,9 +236,7 @@ fun BaseMediaItemMenu(
coroutineScope.launch(Dispatchers.IO) {
val playlistId = Database.playlist(playlist.id)?.id ?: Database.insert(playlist)
if (Database.song(mediaItem.mediaId) == null) {
Database.insert(mediaItem)
}
Database.insert(mediaItem)
Database.insert(
SongInPlaylist(

View File

@@ -111,9 +111,7 @@ fun IntentUriScreen(uri: Uri) {
items.valueOrNull
?.map(YouTube.Item.Song::asMediaItem)
?.forEachIndexed { index, mediaItem ->
if (Database.song(mediaItem.mediaId) == null) {
Database.insert(mediaItem)
}
Database.insert(mediaItem)
Database.insert(
SongInPlaylist(

View File

@@ -175,11 +175,7 @@ fun PlaylistOrAlbumScreen(
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
if (Database.song(mediaItem.mediaId) == null) {
Database.insert(
mediaItem
)
}
Database.insert(mediaItem)
Database.insert(
SongInPlaylist(

View File

@@ -17,7 +17,6 @@ import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
@@ -29,7 +28,6 @@ import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.models.Song
import it.vfsfitvnm.vimusic.ui.components.BottomSheet
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.screens.rememberLyricsRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
@@ -198,13 +196,15 @@ fun PlayerBottomSheet(
lyricsOutcome = nextOutcome.flatMap {
it.lyrics?.text().toNotNull()
}.map {
it ?: ""
}.map {
}.map { lyrics ->
lyrics ?: ""
}.map { lyrics ->
withContext(Dispatchers.IO) {
Database.update((song ?: Database.insert(player.mediaItem!!)).copy(lyrics = it))
(song ?: player.mediaItem?.let(Database::insert))?.let {
Database.update(it.copy(lyrics = lyrics))
}
}
it
lyrics
}
}
},
@@ -218,9 +218,11 @@ fun PlayerBottomSheet(
})
}
},
onLyricsUpdate = {
onLyricsUpdate = { lyrics ->
coroutineScope.launch(Dispatchers.IO) {
Database.update((song ?: Database.insert(player.mediaItem!!)).copy(lyrics = it))
(song ?: player.mediaItem?.let(Database::insert))?.let {
Database.update(it.copy(lyrics = lyrics))
}
}
}
)

View File

@@ -342,9 +342,9 @@ fun PlayerView(
modifier = Modifier
.clickable {
coroutineScope.launch(Dispatchers.IO) {
Database.update(
(song ?: Database.insert(player.mediaItem!!)).toggleLike()
)
(song ?: player.mediaItem?.let(Database::insert))?.let {
Database.update(it.toggleLike())
}
}
}
.padding(horizontal = 16.dp)

View File

@@ -137,7 +137,8 @@ val SongWithInfo.asMediaItem: MediaItem
"albumId" to album?.browseId,
"artistNames" to authors?.map { it.text },
"artistIds" to authors?.map { it.browseId },
"durationText" to song.durationText
"durationText" to song.durationText,
"loudnessDb" to song.loudnessDb
)
)
.build()