diff --git a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt index 9bedd3f..c59453b 100644 --- a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -150,6 +150,12 @@ actual fun setVolume(level: Float) { return 120f } + actual fun updateVoiceVolume(voiceIndex: Int, newVolume: Float) { + if (voiceIndex in 0..3) { + //TODO: implements split voices & change volume per voices + } + } + fun release() { mediaPlayer?.release() mediaPlayer = null diff --git a/composeApp/src/androidMain/res/drawable/mixer_bg.png b/composeApp/src/androidMain/res/drawable/mixer_bg.png new file mode 100644 index 0000000..878acc5 Binary files /dev/null and b/composeApp/src/androidMain/res/drawable/mixer_bg.png differ diff --git a/composeApp/src/androidMain/res/drawable/mixer_fader.png b/composeApp/src/androidMain/res/drawable/mixer_fader.png new file mode 100644 index 0000000..dacf68d Binary files /dev/null and b/composeApp/src/androidMain/res/drawable/mixer_fader.png differ diff --git a/composeApp/src/commonMain/composeResources/drawable/mixer_bg.png b/composeApp/src/commonMain/composeResources/drawable/mixer_bg.png new file mode 100644 index 0000000..878acc5 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/mixer_bg.png differ diff --git a/composeApp/src/commonMain/composeResources/drawable/mixer_fader.png b/composeApp/src/commonMain/composeResources/drawable/mixer_fader.png new file mode 100644 index 0000000..dacf68d Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/mixer_fader.png differ diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt index 12a1e12..2496818 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -19,6 +19,7 @@ expect class FMediaPlayer(filename: String, onFinished: () -> Unit) { fun clearLoop() fun setTempo(factor: Float) fun getCurrentBPM(): Float + fun updateVoiceVolume(voiceIndex: Int, newVolume: Float) } /* diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt index 2c05533..1637bd1 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt @@ -1,20 +1,11 @@ package mg.dot.feufaro.ui import SharedScreenModel -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.ExperimentalSharedTransitionApi -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.animation.* import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -27,20 +18,15 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.platform.* -import kotlinx.coroutines.* -import mg.dot.feufaro.data.DrawerItem +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import mg.dot.feufaro.data.getDrawerItems -import mg.dot.feufaro.getConfigDirectoryPath -import mg.dot.feufaro.getPlatform import mg.dot.feufaro.solfa.Solfa import mg.dot.feufaro.viewmodel.SolfaScreenModel -import java.io.File @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -266,6 +252,9 @@ LaunchedEffect(isPlay, isPos) { sharedScreenModel.setVolume(newVolume) println("Changement volume $newVolume -l $volumelevel") }, + onVoiceVolumeChange = { index, volume -> + player?.updateVoiceVolume(index, volume) + } ) } else { Text("Sélectionner un morceau") diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt index 38d9a8d..0ded1d3 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt @@ -1,13 +1,14 @@ package mg.dot.feufaro.ui import androidx.compose.animation.* -import androidx.compose.animation.core.AnimationSpec -import androidx.compose.animation.core.ExperimentalAnimationSpecApi import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image import androidx.compose.foundation.ScrollState 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.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -20,13 +21,20 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.paint import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import feufaro.composeapp.generated.resources.Res import feufaro.composeapp.generated.resources.ic_mixer_satb import feufaro.composeapp.generated.resources.ic_organ +import feufaro.composeapp.generated.resources.mixer_bg +import feufaro.composeapp.generated.resources.mixer_fader import kotlinx.coroutines.delay import mg.dot.feufaro.getPlatform import mg.dot.feufaro.midi.FMediaPlayer @@ -43,6 +51,7 @@ fun MidiControlPanel( onPlayPauseClick: () -> Unit, onSeek: (Float) -> Unit, onVolumeChange: (Float) -> Unit, + onVoiceVolumeChange: (voiceIndex: Int, newVolume: Float) -> Unit, mediaPlayer: FMediaPlayer, modifier: Modifier = Modifier ) { @@ -56,6 +65,9 @@ fun MidiControlPanel( } val voiceStates = mediaPlayer.getVoiceStates() + val sliderVolumes = remember { + mutableStateListOf(127f, 127f, 127f, 127f) + } val labels = listOf("S", "A", "T", "B") listOf("Soprano", "Alto", "Ténor", "Basse") @@ -168,11 +180,61 @@ fun MidiControlPanel( enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } ) { - Row { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Row( + Row( + modifier = Modifier + .height(100.dp) + .widthIn(max = 250.dp) + .paint( + painter = painterResource(Res.drawable.mixer_bg), + contentScale = ContentScale.FillBounds + ) + .padding(start = 0.dp, end = 0.dp) + , + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ) { + for (i in 0 until 4) { + Box( + modifier = Modifier + .fillMaxHeight() + .weight(1f), + contentAlignment = Alignment.Center + ) { + Slider( + value = sliderVolumes[i], + onValueChange = { newValue -> + sliderVolumes[i] = newValue + mediaPlayer.updateVoiceVolume(i, newValue) + }, + valueRange = 0f..126f, + modifier = Modifier + .fillMaxHeight() + .graphicsLayer { + rotationZ = -90f + transformOrigin = TransformOrigin.Center + } + .fillMaxWidth(), + thumb = { + Image( + painter = painterResource(Res.drawable.mixer_fader), + contentDescription = "Fader Thumb", + modifier = Modifier + .size(width = 20.dp, height = 20.dp) + .graphicsLayer { + rotationZ = 90f + }, + contentScale = ContentScale.Fit + ) + }, + colors = SliderDefaults.colors( + thumbColor = Color.Transparent, + activeTrackColor = Color.Transparent, + inactiveTrackColor = Color.Transparent + ), + ) + } + } + /*Row( modifier = Modifier.padding(vertical = 8.dp), horizontalArrangement = Arrangement.spacedBy(4.dp) ) { @@ -187,8 +249,7 @@ fun MidiControlPanel( ) ) } - } - } + }*/ } } AnimatedVisibility( diff --git a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt index 0b60395..4172535 100644 --- a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -1,14 +1,6 @@ package mg.dot.feufaro.midi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import mg.dot.feufaro.FileRepository +import kotlinx.coroutines.* import mg.dot.feufaro.getConfigDirectoryPath import java.io.ByteArrayInputStream import java.io.File @@ -40,7 +32,7 @@ actual class FMediaPlayer actual constructor( private var isLoopingAB: Boolean = false private val voiceStates = mutableListOf(true, true, true, true) - + private val voiceVolumes = FloatArray(4) { 127f } private var currentGlobalVolume: Float = 0.8f private var currentTempo: Float = 1.0f @@ -170,23 +162,34 @@ actual class FMediaPlayer actual constructor( saveVoiceStates() applyVoiceStates() } + actual fun updateVoiceVolume(voiceIndex: Int, newVolume: Float) { + if (voiceIndex in 0..3) { + voiceVolumes[voiceIndex] = newVolume + applyVoiceStates() + } + } private fun applyVoiceStates() { try { synthetizer?.channels?.let { channels -> - for (i in 0 until 4) { - if (i < channels.size) { - val isVoiceActive = voiceStates[i] - val volume = if (voiceStates[i]) 127 else 0 - channels[i].controlChange(7, volume) - channels[i].controlChange(11, volume) - if(!isVoiceActive) { - channels[i].controlChange(123, 0) - } + for (i in 0 until 4) { + if (i < channels.size) { + val volume = voiceVolumes[i].toInt() + val isVoiceActive = voiceStates[i] + val currentVolume = voiceVolumes[i].toInt() + + if (volume == 0) { + channels[i].controlChange(123, 0) + voiceStates[i] = false + } else { + channels[i].controlChange(7, currentVolume) + channels[i].controlChange(11, currentVolume) + voiceStates[i] = true } } - println("STAB màj: $voiceStates") + println("SATB màj: $voiceStates, Volumes: ${voiceVolumes[i]}") } + } } catch (e: Exception) { e.printStackTrace() } }