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
}
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

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 setTempo(factor: Float)
fun getCurrentBPM(): Float
fun updateVoiceVolume(voiceIndex: Int, newVolume: Float)
}
/*

View file

@ -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")

View file

@ -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")
@ -167,12 +179,62 @@ fun MidiControlPanel(
visible = showSATBTools,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
) {
Row {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
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(

View file

@ -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,22 +162,33 @@ 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 volume = voiceVolumes[i].toInt()
val isVoiceActive = voiceStates[i]
val volume = if (voiceStates[i]) 127 else 0
channels[i].controlChange(7, volume)
channels[i].controlChange(11, volume)
if(!isVoiceActive) {
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("SATB màj: $voiceStates, Volumes: ${voiceVolumes[i]}")
}
println("STAB màj: $voiceStates")
}
} catch (e: Exception) { e.printStackTrace() }
}