Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

feat: support for setting a custom playback speed and pitch #57

Merged
merged 1 commit into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackParameters
import androidx.media3.session.MediaController
import app.suhasdissa.vibeyou.MellowMusicApplication
import app.suhasdissa.vibeyou.backend.data.Song
Expand Down Expand Up @@ -102,6 +103,12 @@ class PlayerViewModel(
controller!!.playGracefully(song.asMediaItem)
}

fun setPlaybackParams(speed: Float, pitch: Float) {
controller!!.playbackParameters = PlaybackParameters(speed, pitch)
}

fun getPlaybackParams() = controller!!.playbackParameters

fun toggleFavourite(id: String) {
viewModelScope.launch {
val song = songDatabaseRepository.getSongById(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.material.icons.filled.RepeatOneOn
import androidx.compose.material.icons.filled.SkipNext
import androidx.compose.material.icons.filled.SkipPrevious
import androidx.compose.material.icons.rounded.ExpandMore
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.CircularProgressIndicator
Expand Down Expand Up @@ -77,6 +78,8 @@ fun FullScreenPlayer(
playerViewModel: PlayerViewModel = viewModel(factory = PlayerViewModel.Factory)
) {
var showQueueSheet by remember { mutableStateOf(false) }
var showSongOptions by remember { mutableStateOf(false) }

val view = LocalView.current
CenterAlignedTopAppBar(navigationIcon = {
IconButton({
Expand All @@ -89,9 +92,9 @@ fun FullScreenPlayer(
)
}
}, title = { Text(stringResource(R.string.now_playing)) }, actions = {
// IconButton(onClick = { }) {
// Icon(Icons.Rounded.MoreVert, contentDescription = stringResource(R.string.song_options))
// }
IconButton(onClick = { showSongOptions = true }) {
Icon(Icons.Rounded.MoreVert, contentDescription = stringResource(R.string.song_options))
}
})
Divider(Modifier.fillMaxWidth())
Column(
Expand Down Expand Up @@ -167,6 +170,7 @@ fun FullScreenPlayer(
}
}
if (showQueueSheet) QueueSheet(onDismissRequest = { showQueueSheet = false })
if (showSongOptions) SongOptionsSheet(onDismissRequest = { showSongOptions = false })
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package app.suhasdissa.vibeyou.ui.screens.player

import android.view.SoundEffectConstants
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ExpandMore
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.Divider
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import app.suhasdissa.vibeyou.R
import app.suhasdissa.vibeyou.backend.viewmodel.PlayerViewModel
import kotlinx.coroutines.launch

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SongOptionsSheet(
onDismissRequest: () -> Unit,
playerViewModel: PlayerViewModel = viewModel(factory = PlayerViewModel.Factory)
) {
val playerSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
val scope = rememberCoroutineScope()
val view = LocalView.current
ModalBottomSheet(
onDismissRequest = onDismissRequest,
sheetState = playerSheetState,
shape = RoundedCornerShape(8.dp),
tonalElevation = 0.dp,
dragHandle = null
) {
CenterAlignedTopAppBar(navigationIcon = {
IconButton({
view.playSoundEffect(SoundEffectConstants.CLICK)
scope.launch {
playerSheetState.hide()
}.invokeOnCompletion {
onDismissRequest()
}
}) {
Icon(
Icons.Rounded.ExpandMore,
contentDescription = null
)
}
}, title = { Text(stringResource(R.string.song_options)) })
Divider(Modifier.fillMaxWidth())

val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(scrollState)
.padding(horizontal = 12.dp, vertical = 10.dp)
) {
var speed by remember {
mutableFloatStateOf(playerViewModel.getPlaybackParams().speed)
}
var pitch by remember {
mutableFloatStateOf(playerViewModel.getPlaybackParams().pitch)
}
fun updatePlaybackParams() = playerViewModel.setPlaybackParams(speed, pitch)

Text(text = stringResource(R.string.playback_speed), fontSize = 16.sp)
Slider(value = speed, onValueChange = { speed = it; updatePlaybackParams() }, valueRange = 0.25f..4f, steps = 14)
ElevatedCard(modifier = Modifier.align(Alignment.CenterHorizontally)) {
Text(
modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp),
text = "%.2f".format(speed)
)
}
Text(text = stringResource(R.string.pitch), fontSize = 16.sp)
Slider(value = pitch, onValueChange = { pitch = it; updatePlaybackParams() }, valueRange = 0.5f..2f, steps = 5)
ElevatedCard(modifier = Modifier.align(Alignment.CenterHorizontally)) {
Text(
modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp),
text = "%.2f".format(pitch)
)
}
}
Spacer(modifier = Modifier.height(20.dp))
}
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,6 @@
<string name="change_music_cache_size">Change music cache size</string>
<string name="music_cache_limit">Music cache limit</string>
<string name="unlimited">Unlimited</string>
<string name="playback_speed">Playback speed</string>
<string name="pitch">Pitch</string>
</resources>