Split voices by sliding volumes per voices SATB, mixer desktop

This commit is contained in:
hasinarak3@gmail.com 2026-02-17 16:23:53 +03:00
parent e02afb3580
commit 86d6de7796
9 changed files with 106 additions and 46 deletions

View file

@ -150,6 +150,12 @@ actual fun setVolume(level: Float) {
return 120f return 120f
} }
actual fun updateVoiceVolume(voiceIndex: Int, newVolume: Float) {
if (voiceIndex in 0..3) {
//TODO: implements split voices & change volume per voices
}
}
fun release() { fun release() {
mediaPlayer?.release() mediaPlayer?.release()
mediaPlayer = null mediaPlayer = null

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -19,6 +19,7 @@ expect class FMediaPlayer(filename: String, onFinished: () -> Unit) {
fun clearLoop() fun clearLoop()
fun setTempo(factor: Float) fun setTempo(factor: Float)
fun getCurrentBPM(): Float fun getCurrentBPM(): Float
fun updateVoiceVolume(voiceIndex: Int, newVolume: Float)
} }
/* /*

View file

@ -1,20 +1,11 @@
package mg.dot.feufaro.ui package mg.dot.feufaro.ui
import SharedScreenModel import SharedScreenModel
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.*
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.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
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.material.icons.Icons 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.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
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 androidx.compose.ui.platform.* import kotlinx.coroutines.delay
import kotlinx.coroutines.* import kotlinx.coroutines.launch
import mg.dot.feufaro.data.DrawerItem
import mg.dot.feufaro.data.getDrawerItems 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.solfa.Solfa
import mg.dot.feufaro.viewmodel.SolfaScreenModel import mg.dot.feufaro.viewmodel.SolfaScreenModel
import java.io.File
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -266,6 +252,9 @@ LaunchedEffect(isPlay, isPos) {
sharedScreenModel.setVolume(newVolume) sharedScreenModel.setVolume(newVolume)
println("Changement volume $newVolume -l $volumelevel") println("Changement volume $newVolume -l $volumelevel")
}, },
onVoiceVolumeChange = { index, volume ->
player?.updateVoiceVolume(index, volume)
}
) )
} else { } else {
Text("Sélectionner un morceau") Text("Sélectionner un morceau")

View file

@ -1,13 +1,14 @@
package mg.dot.feufaro.ui package mg.dot.feufaro.ui
import androidx.compose.animation.* 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.LinearEasing
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@ -20,13 +21,20 @@ import androidx.compose.runtime.*
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.draw.paint
import androidx.compose.ui.graphics.Color 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.text.font.FontWeight
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
import feufaro.composeapp.generated.resources.ic_mixer_satb import feufaro.composeapp.generated.resources.ic_mixer_satb
import feufaro.composeapp.generated.resources.ic_organ 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 kotlinx.coroutines.delay
import mg.dot.feufaro.getPlatform import mg.dot.feufaro.getPlatform
import mg.dot.feufaro.midi.FMediaPlayer import mg.dot.feufaro.midi.FMediaPlayer
@ -43,6 +51,7 @@ fun MidiControlPanel(
onPlayPauseClick: () -> Unit, onPlayPauseClick: () -> Unit,
onSeek: (Float) -> Unit, onSeek: (Float) -> Unit,
onVolumeChange: (Float) -> Unit, onVolumeChange: (Float) -> Unit,
onVoiceVolumeChange: (voiceIndex: Int, newVolume: Float) -> Unit,
mediaPlayer: FMediaPlayer, mediaPlayer: FMediaPlayer,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@ -56,6 +65,9 @@ fun MidiControlPanel(
} }
val voiceStates = mediaPlayer.getVoiceStates() val voiceStates = mediaPlayer.getVoiceStates()
val sliderVolumes = remember {
mutableStateListOf(127f, 127f, 127f, 127f)
}
val labels = listOf("S", "A", "T", "B") val labels = listOf("S", "A", "T", "B")
listOf("Soprano", "Alto", "Ténor", "Basse") listOf("Soprano", "Alto", "Ténor", "Basse")
@ -168,11 +180,61 @@ fun MidiControlPanel(
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
) { ) {
Row { Row(
Column( modifier = Modifier
horizontalAlignment = Alignment.CenterHorizontally .height(100.dp)
) { .widthIn(max = 250.dp)
Row( .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), modifier = Modifier.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp) horizontalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
@ -187,8 +249,7 @@ fun MidiControlPanel(
) )
) )
} }
} }*/
}
} }
} }
AnimatedVisibility( AnimatedVisibility(

View file

@ -1,14 +1,6 @@
package mg.dot.feufaro.midi package mg.dot.feufaro.midi
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
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 mg.dot.feufaro.getConfigDirectoryPath import mg.dot.feufaro.getConfigDirectoryPath
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.File import java.io.File
@ -40,7 +32,7 @@ actual class FMediaPlayer actual constructor(
private var isLoopingAB: Boolean = false private var isLoopingAB: Boolean = false
private val voiceStates = mutableListOf(true, true, true, true) private val voiceStates = mutableListOf(true, true, true, true)
private val voiceVolumes = FloatArray(4) { 127f }
private var currentGlobalVolume: Float = 0.8f private var currentGlobalVolume: Float = 0.8f
private var currentTempo: Float = 1.0f private var currentTempo: Float = 1.0f
@ -170,23 +162,34 @@ actual class FMediaPlayer actual constructor(
saveVoiceStates() saveVoiceStates()
applyVoiceStates() applyVoiceStates()
} }
actual fun updateVoiceVolume(voiceIndex: Int, newVolume: Float) {
if (voiceIndex in 0..3) {
voiceVolumes[voiceIndex] = newVolume
applyVoiceStates()
}
}
private fun applyVoiceStates() { private fun applyVoiceStates() {
try { try {
synthetizer?.channels?.let { channels -> synthetizer?.channels?.let { channels ->
for (i in 0 until 4) { for (i in 0 until 4) {
if (i < channels.size) { if (i < channels.size) {
val isVoiceActive = voiceStates[i] val volume = voiceVolumes[i].toInt()
val volume = if (voiceStates[i]) 127 else 0 val isVoiceActive = voiceStates[i]
channels[i].controlChange(7, volume) val currentVolume = voiceVolumes[i].toInt()
channels[i].controlChange(11, volume)
if(!isVoiceActive) { if (volume == 0) {
channels[i].controlChange(123, 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() } } catch (e: Exception) { e.printStackTrace() }
} }