Implement velocity {ppp,pp,p,mp,mf,f,ff,fff}
This commit is contained in:
parent
0b0f0e48a6
commit
44495aa5cd
2 changed files with 127 additions and 35 deletions
|
|
@ -0,0 +1,17 @@
|
|||
package mg.dot.feufaro.midi
|
||||
|
||||
enum class Dynamic(val velocity: Int, val label: String, val factor: Float) {
|
||||
PPP(16, "ppp", 16f / 80f),
|
||||
PP (33, "pp", 33f / 80f),
|
||||
P (49, "p", 49f / 80f),
|
||||
MP (64, "mp", 64f / 80f),
|
||||
MF (80, "mf", 1.00f),
|
||||
F (96, "f", 96f / 80f),
|
||||
FF (112, "ff", 112f / 80f),
|
||||
FFF(126, "fff", 126f / 80f);
|
||||
|
||||
companion object {
|
||||
fun fromVelocity(v: Int): Dynamic =
|
||||
entries.minByOrNull { kotlin.math.abs(it.velocity - v) } ?: MF
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ actual class FMediaPlayer actual constructor(
|
|||
|
||||
private val voiceVolumes = FloatArray(4) { 127f }
|
||||
private var currentGlobalVolume: Float = 0.8f
|
||||
private var currentDynamicVelocity: Int = Dynamic.MF.velocity
|
||||
private var currentDynamicFactor: Float = Dynamic.MF.factor
|
||||
|
||||
private var currentTempo: Float = 1.0f
|
||||
private var targetBpm: Float = 120f
|
||||
|
|
@ -51,6 +53,7 @@ actual class FMediaPlayer actual constructor(
|
|||
val isHold: Boolean = false, // point d'orgue
|
||||
var alreadyDone: Boolean = false, // done
|
||||
var beatInDC: Int = 1, // temps note sur le marker
|
||||
val dynamic: Dynamic?= null, // velocité
|
||||
)
|
||||
private val navigationSteps = mutableListOf<NavigationStep>()
|
||||
|
||||
|
|
@ -119,6 +122,36 @@ actual class FMediaPlayer actual constructor(
|
|||
private fun resetNavigationFlags() {
|
||||
navigationSteps.forEach { it.alreadyDone = false }
|
||||
}
|
||||
|
||||
private var dynamicJob: Job? = null
|
||||
|
||||
private fun applyDynamicSmooth(targetFactor: Float, durationMs: Long = 300L) {
|
||||
dynamicJob?.cancel()
|
||||
dynamicJob = playerScope.launch {
|
||||
val startFactor = currentDynamicFactor
|
||||
val steps = 30
|
||||
val stepMs = durationMs / steps
|
||||
|
||||
for (i in 1..steps) {
|
||||
val progress = i.toFloat() / steps
|
||||
val smooth = progress * progress * (3f - 2f * progress)
|
||||
|
||||
currentDynamicFactor = startFactor + (targetFactor - startFactor) * smooth
|
||||
applyVoiceStates()
|
||||
delay(stepMs)
|
||||
}
|
||||
currentDynamicFactor = targetFactor
|
||||
applyVoiceStates()
|
||||
}
|
||||
}
|
||||
private fun applyDynamic(dynamic: Dynamic) {
|
||||
val targetFactor = when (dynamic) {
|
||||
Dynamic.MF -> 1.0f
|
||||
else -> dynamic.factor
|
||||
}
|
||||
println("applyDynamic → ${dynamic.label} | ${currentDynamicFactor} → $targetFactor")
|
||||
applyDynamicSmooth(targetFactor, durationMs = 300L)
|
||||
}
|
||||
private fun prepareNavigation(sharedScreenModel: SharedScreenModel) {
|
||||
val metadataList = sharedScreenModel.getFullMarkers()
|
||||
navigationSteps.clear()
|
||||
|
|
@ -134,10 +167,12 @@ actual class FMediaPlayer actual constructor(
|
|||
|
||||
val last_grid = sharedScreenModel.getTotalGridCount()
|
||||
when {
|
||||
// segno
|
||||
marker.contains("$") -> {
|
||||
lastSegno = currentIndex
|
||||
println("Cible ($) mémorisée au $lastSegno")
|
||||
}
|
||||
// Point d'orgue
|
||||
marker.contains("\uD834\uDD10") -> {
|
||||
val beat = if(note.contains('•')) 2 else 1 // demi-ton sur .) ou non
|
||||
navigationSteps.add(
|
||||
|
|
@ -205,6 +240,18 @@ actual class FMediaPlayer actual constructor(
|
|||
)
|
||||
println("Lien DC créé : $marker à $indx vers le début")
|
||||
}
|
||||
// velocité
|
||||
Dynamic.entries.find { it.label == marker.trim() } != null -> {
|
||||
val dyn = Dynamic.entries.first { it.label == marker.trim() }
|
||||
navigationSteps.add(
|
||||
NavigationStep(
|
||||
marker = marker,
|
||||
gridIndex = currentIndex,
|
||||
dynamic = dyn
|
||||
)
|
||||
)
|
||||
println("Dynamique '${dyn.label}' (vel=${dyn.velocity}) mémorisée à la grille $currentIndex")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -233,7 +280,8 @@ actual class FMediaPlayer actual constructor(
|
|||
|
||||
if (step != null) {
|
||||
// Point d'orgue
|
||||
if (step.isHold) {
|
||||
when {
|
||||
step.isHold -> {
|
||||
val currentBpm = sequencer?.tempoInBPM ?: targetBpm
|
||||
val beatMs = (60_000 / currentBpm).toLong()
|
||||
val holdDuration = if (step.beatInDC == 2) beatMs / 2 else beatMs * 2
|
||||
|
|
@ -248,7 +296,13 @@ actual class FMediaPlayer actual constructor(
|
|||
|
||||
sequencer?.tempoFactor = previousFactor
|
||||
synthetizer?.channels?.forEach { it?.controlChange(64, 0) }
|
||||
} else {
|
||||
}
|
||||
|
||||
//velocity
|
||||
step.dynamic != null -> {
|
||||
applyDynamic(step.dynamic)
|
||||
}
|
||||
else -> {
|
||||
// $ dc ds ...
|
||||
step.alreadyDone = true
|
||||
|
||||
|
|
@ -275,6 +329,7 @@ actual class FMediaPlayer actual constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private var syncJob: Job? = null
|
||||
|
||||
private fun startSyncLoop(sharedScreenModel: SharedScreenModel) {
|
||||
|
|
@ -296,6 +351,13 @@ actual class FMediaPlayer actual constructor(
|
|||
sequencer?.tickPosition = tick
|
||||
|
||||
applyBpm()
|
||||
// Voir les velocity avant
|
||||
val lastDynamic = navigationSteps
|
||||
.filter { it.dynamic != null && it.gridIndex <= gridIndex }
|
||||
.maxByOrNull { it.gridIndex }
|
||||
?.dynamic ?: Dynamic.MF
|
||||
currentDynamicFactor = lastDynamic.factor
|
||||
applyVoiceStates()
|
||||
}
|
||||
|
||||
actual fun play(){
|
||||
|
|
@ -367,6 +429,7 @@ actual class FMediaPlayer actual constructor(
|
|||
resetNavigationFlags()
|
||||
navigationSteps.clear()
|
||||
clearLoop()
|
||||
currentDynamicFactor = Dynamic.MF.factor
|
||||
// release()
|
||||
}
|
||||
actual fun getDuration(): Long {
|
||||
|
|
@ -387,7 +450,8 @@ actual class FMediaPlayer actual constructor(
|
|||
actual fun setVolume(level: Float) {
|
||||
try {
|
||||
this.currentGlobalVolume = level
|
||||
val volumeInt = (level * 127).toInt().coerceIn(0, 127)
|
||||
applyVoiceStates()
|
||||
/*val volumeInt = (level * 127).toInt().coerceIn(0, 127)
|
||||
|
||||
synthetizer?.channels?.forEachIndexed { index, channel ->
|
||||
if (index < 4) {
|
||||
|
|
@ -396,7 +460,7 @@ actual class FMediaPlayer actual constructor(
|
|||
} else {
|
||||
channel?.controlChange(7, volumeInt)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
val mixer = AudioSystem.getMixer(null)
|
||||
val lines = mixer.sourceLines
|
||||
for (line in lines) {
|
||||
|
|
@ -462,14 +526,25 @@ actual class FMediaPlayer actual constructor(
|
|||
synthetizer?.channels?.let { channels ->
|
||||
for (i in 0 until 4) {
|
||||
if (i < channels.size) {
|
||||
val targetVolume = voiceVolumes[i].toInt()
|
||||
val userVol = voiceVolumes[i] / 127f
|
||||
val globalVol = currentGlobalVolume
|
||||
|
||||
val finalVol = (127f * userVol * globalVol * currentDynamicFactor).toInt().coerceIn(0, 127)
|
||||
|
||||
println("Voice[$i] user=${voiceVolumes[i]} global=$currentGlobalVolume dynFactor=$currentDynamicFactor → finalVol=$finalVol")
|
||||
|
||||
channels[i].controlChange(7, finalVol)
|
||||
channels[i].controlChange(11, finalVol)
|
||||
|
||||
if (finalVol == 0) channels[i].controlChange(123, 0)
|
||||
/*val targetVolume = voiceVolumes[i].toInt()
|
||||
|
||||
channels[i].controlChange(7, targetVolume)
|
||||
channels[i].controlChange(11, targetVolume)
|
||||
|
||||
if (targetVolume == 0) {
|
||||
channels[i].controlChange(123, 0)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
// println("SATB $i Volumes: ${voiceVolumes[i]}")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue