update ui tempo settings

This commit is contained in:
hasinarak3@gmail.com 2026-02-04 16:02:32 +03:00
parent 9fa116641b
commit 2f0be71dd8
8 changed files with 5157 additions and 316 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="256"
android:viewportHeight="256"
android:width="256dp"
android:height="256dp">
<path
android:pathData="M202.2 119.6c-2.1 0 -3.8 1.7 -3.8 3.8V171c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-47.7C206 121.3 204.3 119.6 202.2 119.6z"
android:fillColor="#000000" />
<path
android:pathData="M192.7 117h19c2.1 0 3.8 -1.7 3.8 -3.8V74.4c0 -2.1 -1.7 -3.8 -3.8 -3.8H206V54.9c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v15.7h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C188.9 115.3 190.6 117 192.7 117zM196.5 78.2h11.4v31.2h-11.4V78.2z"
android:fillColor="#000000" />
<path
android:pathData="M152.7 158.5c-2.1 0 -3.8 1.7 -3.8 3.8v9.3c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-9.3C156.6 160.2 154.9 158.5 152.7 158.5z"
android:fillColor="#000000" />
<path
android:pathData="M143.2 155.9h19c2.1 0 3.8 -1.7 3.8 -3.8v-38.8c0 -2.1 -1.7 -3.8 -3.8 -3.8h-5.7v-54c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v54h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C139.4 154.2 141.1 155.9 143.2 155.9zM147 117.1h11.4v31.2H147V117.1z"
android:fillColor="#000000" />
<path
android:pathData="M103.3 119.6c-2.1 0 -3.8 1.7 -3.8 3.8V171c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-47.7C107.1 121.3 105.4 119.6 103.3 119.6z"
android:fillColor="#000000" />
<path
android:pathData="M93.7 117h19c2.1 0 3.8 -1.7 3.8 -3.8V74.4c0 -2.1 -1.7 -3.8 -3.8 -3.8H107V54.9c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v15.7h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C89.9 115.3 91.6 117 93.7 117zM97.5 78.2H109v31.2H97.6L97.5 78.2L97.5 78.2z"
android:fillColor="#000000" />
<path
android:pathData="M53.8 158.5c-2.1 0 -3.8 1.7 -3.8 3.8v9.3c0 2.1 1.7 3.8 3.8 3.8s3.8 -1.7 3.8 -3.8v-9.3C57.6 160.2 55.9 158.5 53.8 158.5z"
android:fillColor="#000000" />
<path
android:pathData="M44.3 155.9h19c2.1 0 3.8 -1.7 3.8 -3.8v-38.8c0 -2.1 -1.7 -3.8 -3.8 -3.8h-5.7v-54c0 -2.1 -1.7 -3.8 -3.8 -3.8S50 53.4 50 55.5v54h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C40.4 154.2 42.1 155.9 44.3 155.9zM48.1 117.1h11.4v31.2H48.1V117.1z"
android:fillColor="#000000" />
<path
android:pathData="M242.2 31.1H13.8c-2.1 0 -3.8 1.7 -3.8 3.8v178.6c0 6.3 5.1 11.4 11.4 11.4h213.2c6.3 0 11.4 -5.1 11.4 -11.4V34.9C246 32.8 244.3 31.1 242.2 31.1zM238.4 38.7v149.1H17.6V38.7H238.4zM234.6 217.3H21.4c-2.1 0 -3.8 -1.7 -3.8 -3.8v-18h220.8v18C238.4 215.5 236.7 217.3 234.6 217.3z"
android:fillColor="#000000" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="512"
android:viewportHeight="512"
android:width="800dp"
android:height="800dp">
<path
android:pathData="M468.826 97.292v103.374h-55.943V48.647h-36.484v152.019h-55.943V0.001h-36.484v200.665h-55.943V0h-36.484v200.666h-55.943V48.647H99.117v152.019H43.174V97.293H6.689V512h498.621V97.292H468.826zM99.117 334.442H43.174v-36.484h55.943V334.442zM135.602 297.958h55.943v36.484h-55.943V297.958zM228.025 475.516H191.54V370.927h36.484V475.516zM228.03 297.958h55.943v36.484H228.03V297.958zM320.452 475.516h-36.484V370.927h36.484V475.516zM376.399 334.442h-55.943v-36.484h55.943V334.442zM468.826 334.442h-55.943v-36.484h55.943V334.442z"
android:fillColor="#000000" />
</vector>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="256"
android:viewportHeight="256"
android:width="256dp"
android:height="256dp">
<path
android:pathData="M202.2 119.6c-2.1 0 -3.8 1.7 -3.8 3.8V171c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-47.7C206 121.3 204.3 119.6 202.2 119.6z"
android:fillColor="#000000" />
<path
android:pathData="M192.7 117h19c2.1 0 3.8 -1.7 3.8 -3.8V74.4c0 -2.1 -1.7 -3.8 -3.8 -3.8H206V54.9c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v15.7h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C188.9 115.3 190.6 117 192.7 117zM196.5 78.2h11.4v31.2h-11.4V78.2z"
android:fillColor="#000000" />
<path
android:pathData="M152.7 158.5c-2.1 0 -3.8 1.7 -3.8 3.8v9.3c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-9.3C156.6 160.2 154.9 158.5 152.7 158.5z"
android:fillColor="#000000" />
<path
android:pathData="M143.2 155.9h19c2.1 0 3.8 -1.7 3.8 -3.8v-38.8c0 -2.1 -1.7 -3.8 -3.8 -3.8h-5.7v-54c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v54h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C139.4 154.2 141.1 155.9 143.2 155.9zM147 117.1h11.4v31.2H147V117.1z"
android:fillColor="#000000" />
<path
android:pathData="M103.3 119.6c-2.1 0 -3.8 1.7 -3.8 3.8V171c0 2.1 1.7 3.8 3.8 3.8c2.1 0 3.8 -1.7 3.8 -3.8v-47.7C107.1 121.3 105.4 119.6 103.3 119.6z"
android:fillColor="#000000" />
<path
android:pathData="M93.7 117h19c2.1 0 3.8 -1.7 3.8 -3.8V74.4c0 -2.1 -1.7 -3.8 -3.8 -3.8H107V54.9c0 -2.1 -1.7 -3.8 -3.8 -3.8c-2.1 0 -3.8 1.7 -3.8 3.8v15.7h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C89.9 115.3 91.6 117 93.7 117zM97.5 78.2H109v31.2H97.6L97.5 78.2L97.5 78.2z"
android:fillColor="#000000" />
<path
android:pathData="M53.8 158.5c-2.1 0 -3.8 1.7 -3.8 3.8v9.3c0 2.1 1.7 3.8 3.8 3.8s3.8 -1.7 3.8 -3.8v-9.3C57.6 160.2 55.9 158.5 53.8 158.5z"
android:fillColor="#000000" />
<path
android:pathData="M44.3 155.9h19c2.1 0 3.8 -1.7 3.8 -3.8v-38.8c0 -2.1 -1.7 -3.8 -3.8 -3.8h-5.7v-54c0 -2.1 -1.7 -3.8 -3.8 -3.8S50 53.4 50 55.5v54h-5.7c-2.1 0 -3.8 1.7 -3.8 3.8v38.8C40.4 154.2 42.1 155.9 44.3 155.9zM48.1 117.1h11.4v31.2H48.1V117.1z"
android:fillColor="#000000" />
<path
android:pathData="M242.2 31.1H13.8c-2.1 0 -3.8 1.7 -3.8 3.8v178.6c0 6.3 5.1 11.4 11.4 11.4h213.2c6.3 0 11.4 -5.1 11.4 -11.4V34.9C246 32.8 244.3 31.1 242.2 31.1zM238.4 38.7v149.1H17.6V38.7H238.4zM234.6 217.3H21.4c-2.1 0 -3.8 -1.7 -3.8 -3.8v-18h220.8v18C238.4 215.5 236.7 217.3 234.6 217.3z"
android:fillColor="#000000" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="512"
android:viewportHeight="512"
android:width="800dp"
android:height="800dp">
<path
android:pathData="M468.826 97.292v103.374h-55.943V48.647h-36.484v152.019h-55.943V0.001h-36.484v200.665h-55.943V0h-36.484v200.666h-55.943V48.647H99.117v152.019H43.174V97.293H6.689V512h498.621V97.292H468.826zM99.117 334.442H43.174v-36.484h55.943V334.442zM135.602 297.958h55.943v36.484h-55.943V297.958zM228.025 475.516H191.54V370.927h36.484V475.516zM228.03 297.958h55.943v36.484H228.03V297.958zM320.452 475.516h-36.484V370.927h36.484V475.516zM376.399 334.442h-55.943v-36.484h55.943V334.442zM468.826 334.442h-55.943v-36.484h55.943V334.442z"
android:fillColor="#000000" />
</vector>

View file

@ -1,84 +1,22 @@
package mg.dot.feufaro.ui package mg.dot.feufaro.ui
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.*
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
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.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AvTimer import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.Church import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.ClearAll import androidx.compose.material3.*
import androidx.compose.material.icons.filled.Keyboard import androidx.compose.runtime.*
import androidx.compose.material.icons.filled.Loop
import androidx.compose.material.icons.filled.MusicNote
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.Piano
import androidx.compose.material.icons.filled.PianoOff
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SettingsVoice
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.Tonality
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.automirrored.filled.VolumeOff
import androidx.compose.material.icons.filled.MoreHoriz
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.produceState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import feufaro.composeapp.generated.resources.Res import feufaro.composeapp.generated.resources.Res
@ -88,8 +26,6 @@ import kotlinx.coroutines.delay
import mg.dot.feufaro.getPlatform import mg.dot.feufaro.getPlatform
import mg.dot.feufaro.midi.MediaPlayer import mg.dot.feufaro.midi.MediaPlayer
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import org.koin.core.component.getScopeId
import javax.swing.Icon
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -107,240 +43,371 @@ fun MidiControlPanel(
val momo = duration.toInt() - currentPos.toInt() val momo = duration.toInt() - currentPos.toInt()
val loopState by produceState(Triple(-1L, -1L, false), mediaPlayer) { val loopState by produceState(Triple(-1L, -1L, false), mediaPlayer) {
while(true) { while (true) {
value = mediaPlayer.getLoopState() value = mediaPlayer.getLoopState()
delay(200) delay(200)
} }
} }
val voiceStates = mediaPlayer.getVoiceStates() val voiceStates = mediaPlayer.getVoiceStates()
val labels = listOf("S","A","T","B") val labels = listOf("S", "A", "T", "B")
val fullLabels = listOf("Soprano","Alto","Ténor","Basse") listOf("Soprano", "Alto", "Ténor", "Basse")
var tempo by remember { mutableStateOf(1.0f) } var tempo by remember { mutableStateOf(1.0f) }
var currentBpm by remember { mutableStateOf(mediaPlayer.getCurrentBPM()) } var currentBpm by remember { mutableStateOf(mediaPlayer.getCurrentBPM()) }
val basseBpm = 120f val basseBpm = 120f
var bpmInput by remember { mutableStateOf((basseBpm * tempo).toInt().toString()) }
var isPianoSelected by remember { mutableStateOf(true) } var isPianoSelected by remember { mutableStateOf(true) }
var showSATBTools by remember { mutableStateOf(false) } var showSATBTools by remember { mutableStateOf(false) }
var showVolumeTools by remember { mutableStateOf(false) }
var expandedCtl by remember { mutableStateOf(false) } var expandedCtl by remember { mutableStateOf(false) }
LaunchedEffect(tempo) { LaunchedEffect(tempo) {
currentBpm = mediaPlayer.getCurrentBPM() currentBpm = mediaPlayer.getCurrentBPM()
} }
Column ( fun updateTempoByBpmStep(step: Int) {
modifier = modifier val currentBpm = (basseBpm * tempo).toInt()
.fillMaxWidth() val newBpm = (currentBpm + step).coerceIn((basseBpm * 0.25f).toInt(), (basseBpm * 1.5f).toInt())
.padding(16.dp) val newFactor = newBpm.toFloat() / basseBpm
.background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)),
horizontalAlignment = Alignment.CenterHorizontally
) {
AnimatedVisibility(
visible = !expandedCtl
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("${currentPos.toInt() / 1000}s", color = Color.White)
Slider(
value = currentPos,
onValueChange = onSeek,
valueRange = 0f..(if (duration > 0) duration else 1f),
modifier = Modifier.weight(1f),
colors = SliderDefaults.colors(
thumbColor = Color.Red,
activeTrackColor = Color.Green,
inactiveTrackColor = Color.Gray,
inactiveTickColor = Color(0xffb0BEC5),
disabledThumbColor = Color(0xff78909C),
disabledActiveTickColor = Color(0xff757575),
disabledActiveTrackColor = Color(0xffBDBDBD),
disabledInactiveTickColor = Color(0xff616161),
disabledInactiveTrackColor = Color(0xffBCAAA4),
),
thumb = {
Box(
modifier = Modifier
.size(15.dp)
.background(Color.Gray, CircleShape)
)
},
track = { sliderState ->
SliderDefaults.Track(
sliderState = sliderState,
modifier = Modifier.height(5.dp)
)
}
)
Text("${momo / 1000}s", color = Color.White)
}
}
AnimatedVisibility( tempo = newFactor
visible = showSATBTools, mediaPlayer?.setTempo(newFactor)
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, }
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
fun updateTempoToBpm(targetBpm: Int) {
val clampedBpm = targetBpm.coerceIn((basseBpm * 0.25f).toInt(), (basseBpm * 1.5f).toInt())
val newFactor = clampedBpm.toFloat() / basseBpm
tempo = newFactor
mediaPlayer?.setTempo(newFactor)
println("tempo : $tempo")
}
Column(
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center
) { ) {
Row { Column(
Column ( modifier = modifier
horizontalAlignment = Alignment.CenterHorizontally .fillMaxWidth(if (getPlatform().name.startsWith("Android")) 0.9f else 0.6f)
.padding(16.dp)
.background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)),
horizontalAlignment = Alignment.CenterHorizontally
) {
AnimatedVisibility(
visible = !expandedCtl
) { ) {
Row( Row(
modifier = Modifier.padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement. spacedBy(4.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
labels.forEachIndexed { index, label -> Text("${currentPos.toInt() / 1000}s", color = Color.White)
FilterChip( Slider(
selected = voiceStates[index], value = currentPos,
onClick = { mediaPlayer.toggleVoice(index) }, onValueChange = onSeek,
label = { Text(label) }, valueRange = 0f..(if (duration > 0) duration else 1f),
colors = FilterChipDefaults.filterChipColors( modifier = Modifier.weight(1f),
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer, colors = SliderDefaults.colors(
selectedLabelColor = MaterialTheme.colorScheme.primary thumbColor = Color.Red,
activeTrackColor = Color.Green,
inactiveTrackColor = Color.Gray,
inactiveTickColor = Color(0xffb0BEC5),
disabledThumbColor = Color(0xff78909C),
disabledActiveTickColor = Color(0xff757575),
disabledActiveTrackColor = Color(0xffBDBDBD),
disabledInactiveTickColor = Color(0xff616161),
disabledInactiveTrackColor = Color(0xffBCAAA4),
),
thumb = {
Box(
modifier = Modifier
.size(15.dp)
.background(Color.Gray, CircleShape)
) )
},
track = { sliderState ->
SliderDefaults.Track(
sliderState = sliderState,
modifier = Modifier.height(5.dp)
)
}
)
Text("${momo / 1000}s", color = Color.White)
}
}
AnimatedVisibility(
visible = showSATBTools,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
) {
Row {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
labels.forEachIndexed { index, label ->
FilterChip(
selected = voiceStates[index],
onClick = { mediaPlayer.toggleVoice(index) },
label = { Text(label) },
colors = FilterChipDefaults.filterChipColors(
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
selectedLabelColor = MaterialTheme.colorScheme.primary
)
)
}
}
}
}
}
AnimatedVisibility(
visible = !expandedCtl,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
) {
Row {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier
.height(45.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0xFF2C3135))
.padding(vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (tempo >= 0.3) { // limite 40BPM
IconButton(
modifier = Modifier.background(Color(0XFF2C3130)),
onClick = { updateTempoByBpmStep(-10) }) {
Icon(
Icons.AutoMirrored.Filled.KeyboardArrowLeft,
"gauche",
tint = Color.White.copy(0.7f),
modifier = Modifier.size(22.dp)
)
}
}
val currentBpmInt = (basseBpm * tempo).toInt()
Row(
modifier = Modifier.padding(horizontal = 2.dp),
verticalAlignment = Alignment.CenterVertically
) {
val values = listOf(currentBpmInt - 1, currentBpmInt, currentBpmInt + 1)
values.forEach { bpmValue ->
AnimatedContent(
targetState = bpmValue,
transitionSpec = {
if (targetState > initialState) {
slideInHorizontally { it } + fadeIn() togetherWith slideOutHorizontally { -it } + fadeOut()
} else {
slideInHorizontally { -it } + fadeIn() togetherWith slideOutHorizontally { it } + fadeOut()
}
},
modifier = Modifier.padding(horizontal = 4.dp)
) { displayedBpm ->
val isCenter = displayedBpm == currentBpmInt
Text(
text = "$displayedBpm",
color = if (isCenter) Color.White else Color.Gray.copy(alpha = 0.4f),
fontSize = if (isCenter) 22.sp else 14.sp,
fontWeight = if (isCenter) FontWeight.Bold else FontWeight.Normal,
modifier = Modifier
.clip(CircleShape)
.clickable(enabled = !isCenter) { updateTempoToBpm(displayedBpm) }
.padding(horizontal = 4.dp)
)
}
}
}
if (tempo <= 1.3) { // limite 156BPM
IconButton(
modifier = Modifier.background(Color(0XFF2C3130)),
onClick = { updateTempoByBpmStep(10) }) {
Icon(
Icons.AutoMirrored.Filled.KeyboardArrowRight,
"droite",
tint = Color.White.copy(0.7f),
modifier = Modifier.size(22.dp)
)
}
}
Text(
text = "bpm",
color = Color.White.copy(0.6f),
fontSize = 12.sp,
modifier = Modifier.padding(end = 8.dp)
)
}
}
}
}
if (!expandedCtl) {
Spacer(modifier = Modifier.height(10.dp))
}
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column {
val isAtStart = loopState.first == -1L
val isWaitingForB = loopState.first != -1L && !loopState.third
val isLooping = loopState.third
OutlinedButton(
onClick = {
when {
isAtStart -> mediaPlayer.setPointA()
isWaitingForB -> mediaPlayer.setPointB()
isLooping -> {
mediaPlayer.clearLoop()
}
}
},
colors = ButtonDefaults.buttonColors(
containerColor = when {
(isWaitingForB || isLooping) -> Color.Red
else -> Color.LightGray
}
),
contentPadding = PaddingValues(horizontal = 2.dp, vertical = 0.dp),
shape = RoundedCornerShape(5.dp),
modifier = Modifier
.height(34.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "A",
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
fontSize = 16.sp
)
Icon(
imageVector = Icons.Default.Loop,
contentDescription = null,
tint = if (isLooping) Color.White else Color.Black,
modifier = Modifier.size(16.dp)
)
Text(
text = "B",
color = if (isLooping) Color.White else Color.Black,
fontSize = 16.sp
)
}
}
}
Spacer(modifier = Modifier.weight(1f))
Column {
IconButton(
onClick = {
showSATBTools = !showSATBTools
}, modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(if (showSATBTools) MaterialTheme.colorScheme.primary else Color.Transparent)
) {
Icon(
painter = painterResource(Res.drawable.ic_mixer_satb),
contentDescription = "SATB",
tint = if (showSATBTools) Color.White else Color.Black,
modifier = Modifier.size(35.dp)
)
}
}
Spacer(modifier = Modifier.weight(1f))
Column {
IconButton(
onClick = {
isPianoSelected = !isPianoSelected
if (isPianoSelected) {
mediaPlayer?.changeInstru(1)
} else {
mediaPlayer?.changeInstru(20)
}
}
) {
if (isPianoSelected) {
Icon(
Icons.Default.Piano,
contentDescription = "Piano",
tint = Color.Black
)
} else {
Icon(
painter = painterResource(Res.drawable.ic_organ),
contentDescription = "Orgue",
tint = Color.Black,
modifier = Modifier.size(25.dp)
)
}
}
}
Spacer(modifier = Modifier.weight(1f))
Column(
modifier = Modifier.wrapContentWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
IconButton(
onClick = onPlayPauseClick,
modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
) {
Icon(
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
contentDescription = "Pla",
tint = Color.White
)
}
}
Spacer(modifier = Modifier.weight(1f))
val platform = getPlatform()
if (platform.name.startsWith("Java")) {
ModernVolumeSlider(
volume, onVolumeChange = onVolumeChange
)
}
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.wrapContentWidth(),
horizontalArrangement = Arrangement.End,
) {
IconButton(
onClick = { expandedCtl = !expandedCtl },
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert,
contentDescription = "More",
tint = Color.White
) )
} }
} }
} }
} }
} }
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround
){
Box( contentAlignment = Alignment.CenterStart) {
val isAtStart = loopState.first == -1L
val isWaitingForB = loopState.first != -1L && !loopState.third
val isLooping = loopState.third
OutlinedButton(
onClick = {
when {
isAtStart -> mediaPlayer.setPointA()
isWaitingForB -> mediaPlayer.setPointB()
isLooping -> {
mediaPlayer.clearLoop()
}
}
},
colors = ButtonDefaults.buttonColors(
containerColor = when {
(isWaitingForB || isLooping) -> Color.Red
else -> Color.LightGray
}
),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
shape = RoundedCornerShape(5.dp) ,
modifier = Modifier
.height(34.dp)
.padding(horizontal = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "A",
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
fontSize = 18.sp
)
Icon(
imageVector = Icons.Default.Loop,
contentDescription = null,
tint = if (isLooping) Color.White else Color.Black,
modifier = Modifier.size(18.dp)
)
Text(
text = "B",
color = if (isLooping) Color.White else Color.Black,
fontSize = 18.sp
)
}
}
}
Box(contentAlignment = Alignment.Center) {
IconButton(
onClick = {
showSATBTools = !showSATBTools
}
,modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(if (showSATBTools) MaterialTheme.colorScheme.primary else Color.Transparent)) {
Icon(
painter = painterResource(Res.drawable.ic_mixer_satb),
contentDescription = "SATB",
tint = if (showSATBTools) Color.White else Color.Black,
modifier = Modifier.size(35.dp)
)
}
}
Box(contentAlignment = Alignment.Center) {
IconButton(
onClick = onPlayPauseClick,
modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
) {
Icon(
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
contentDescription = "Pla",
tint = Color.White
)
}
}
Box(contentAlignment = Alignment.CenterEnd) {
IconButton(
onClick = {
isPianoSelected = !isPianoSelected
if (isPianoSelected) {
mediaPlayer?.changeInstru(1)
} else {
mediaPlayer?.changeInstru(20)
}
}
) {
if (isPianoSelected) {
Icon(
Icons.Default.Piano,
contentDescription = "Piano",
tint = Color.Black
)
} else {
Icon(
painter = painterResource(Res.drawable.ic_organ),
contentDescription = "Orgue",
tint = Color.Black,
modifier = Modifier.size(25.dp)
)
}
}
}
val platform = getPlatform()
if (platform.name.startsWith("Java")) {
ModernVolumeSlider(
volume, onVolumeChange = onVolumeChange
)
}
Row(
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.width(8.dp))
IconButton(onClick = { expandedCtl = !expandedCtl }) {
Icon(
imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert,
contentDescription = "More",
tint = Color.White
)
}
}
}
} }
} }

View file

@ -29,45 +29,47 @@ fun ModernVolumeSlider(
volume: Float, volume: Float,
onVolumeChange: (Float) -> Unit onVolumeChange: (Float) -> Unit
) { ) {
Row( Column {
verticalAlignment = Alignment.CenterVertically, Row(
modifier = Modifier verticalAlignment = Alignment.CenterVertically,
.height(42.dp) modifier = Modifier
.background(Color.White.copy(alpha = 0.1f), CircleShape) .height(42.dp)
.padding(horizontal = 12.dp, vertical = 4.dp) .background(Color.White.copy(alpha = 0.1f), CircleShape)
) { .padding(horizontal = 12.dp, vertical = 4.dp)
Icon( ) {
imageVector = when { Icon(
volume == 0f -> Icons.AutoMirrored.Filled.VolumeOff imageVector = when {
volume < 0.5f -> Icons.AutoMirrored.Filled.VolumeDown volume == 0f -> Icons.AutoMirrored.Filled.VolumeOff
else -> Icons.AutoMirrored.Filled.VolumeUp volume < 0.5f -> Icons.AutoMirrored.Filled.VolumeDown
}, else -> Icons.AutoMirrored.Filled.VolumeUp
contentDescription = null, },
tint = if (volume > 0) Color.Black else Color.Gray, contentDescription = null,
modifier = Modifier.size(20.dp) tint = if (volume > 0) Color.Black else Color.Gray,
) modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Slider( Slider(
value = volume, value = volume,
onValueChange = onVolumeChange, onValueChange = onVolumeChange,
modifier = Modifier.fillMaxWidth(0.30f), modifier = Modifier.fillMaxWidth(0.30f),
colors = SliderDefaults.colors( colors = SliderDefaults.colors(
activeTrackColor = Color(0xFFF59E0B), activeTrackColor = Color(0xFFF59E0B),
inactiveTrackColor = Color.White.copy(alpha = 0.2f), inactiveTrackColor = Color.White.copy(alpha = 0.2f),
thumbColor = Color.Transparent thumbColor = Color.Transparent
), ),
track = { sliderState -> track = { sliderState ->
SliderDefaults.Track( SliderDefaults.Track(
sliderState = sliderState, sliderState = sliderState,
modifier = Modifier.height(15.dp), modifier = Modifier.height(15.dp),
thumbTrackGapSize = 0.dp thumbTrackGapSize = 0.dp
) )
}, },
thumb = { thumb = {
Box(Modifier.size(0.dp)) Box(Modifier.size(0.dp))
} }
) )
}
} }
} }