Normalize volume (#53)
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user