Compare commits
3 commits
bb64cf1530
...
2f0be71dd8
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f0be71dd8 | |||
| 9fa116641b | |||
| 5dfe4ffc58 |
8 changed files with 5161 additions and 386 deletions
2344
composeApp/src/androidMain/res/drawable/ic_metronome.xml
Normal file
2344
composeApp/src/androidMain/res/drawable/ic_metronome.xml
Normal file
File diff suppressed because it is too large
Load diff
33
composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml
Normal file
33
composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml
Normal 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>
|
||||
9
composeApp/src/androidMain/res/drawable/ic_organ.xml
Normal file
9
composeApp/src/androidMain/res/drawable/ic_organ.xml
Normal 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>
|
||||
2344
composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml
Normal file
2344
composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,91 +1,31 @@
|
|||
package mg.dot.feufaro.ui
|
||||
package mg.dot.feufaro.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
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.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
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.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
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.filled.AvTimer
|
||||
import androidx.compose.material.icons.filled.Church
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.Keyboard
|
||||
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.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.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.sp
|
||||
import feufaro.composeapp.generated.resources.Res
|
||||
import feufaro.composeapp.generated.resources.ic_mixer_satb
|
||||
import feufaro.composeapp.generated.resources.ic_organ
|
||||
import kotlinx.coroutines.delay
|
||||
import mg.dot.feufaro.getPlatform
|
||||
import mg.dot.feufaro.midi.MediaPlayer
|
||||
import org.koin.core.component.getScopeId
|
||||
import javax.swing.Icon
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -103,34 +43,55 @@ fun MidiControlPanel(
|
|||
val momo = duration.toInt() - currentPos.toInt()
|
||||
|
||||
val loopState by produceState(Triple(-1L, -1L, false), mediaPlayer) {
|
||||
while(true) {
|
||||
while (true) {
|
||||
value = mediaPlayer.getLoopState()
|
||||
delay(200)
|
||||
}
|
||||
}
|
||||
|
||||
val voiceStates = mediaPlayer.getVoiceStates()
|
||||
val labels = listOf("S","A","T","B")
|
||||
val fullLabels = listOf("Soprano","Alto","Ténor","Basse")
|
||||
val labels = listOf("S", "A", "T", "B")
|
||||
listOf("Soprano", "Alto", "Ténor", "Basse")
|
||||
|
||||
var tempo by remember { mutableStateOf(1.0f) }
|
||||
var currentBpm by remember { mutableStateOf(mediaPlayer.getCurrentBPM()) }
|
||||
val basseBpm = 120f
|
||||
var bpmInput by remember { mutableStateOf((basseBpm * tempo).toInt().toString()) }
|
||||
|
||||
var isPianoSelected by remember { mutableStateOf(true) }
|
||||
|
||||
var showBPMTools by remember { mutableStateOf(false) }
|
||||
var showInstruTools by remember { mutableStateOf(false) }
|
||||
var showSATBTools by remember { mutableStateOf(false) }
|
||||
var showVolumeTools by remember { mutableStateOf(false) }
|
||||
var expandedCtl by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(tempo) {
|
||||
currentBpm = mediaPlayer.getCurrentBPM()
|
||||
}
|
||||
Column (
|
||||
fun updateTempoByBpmStep(step: Int) {
|
||||
val currentBpm = (basseBpm * tempo).toInt()
|
||||
val newBpm = (currentBpm + step).coerceIn((basseBpm * 0.25f).toInt(), (basseBpm * 1.5f).toInt())
|
||||
val newFactor = newBpm.toFloat() / basseBpm
|
||||
|
||||
tempo = newFactor
|
||||
mediaPlayer?.setTempo(newFactor)
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.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
|
||||
|
|
@ -183,12 +144,12 @@ fun MidiControlPanel(
|
|||
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
|
||||
) {
|
||||
Row {
|
||||
Column (
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement. spacedBy(4.dp)
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
labels.forEachIndexed { index, label ->
|
||||
FilterChip(
|
||||
|
|
@ -206,111 +167,106 @@ fun MidiControlPanel(
|
|||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = showInstruTools,
|
||||
visible = !expandedCtl,
|
||||
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
|
||||
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
|
||||
) {
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
FilledIconToggleButton(
|
||||
checked = isPianoSelected,
|
||||
onCheckedChange = {
|
||||
isPianoSelected = true;
|
||||
mediaPlayer.changeInstru(1)
|
||||
}
|
||||
){
|
||||
Icon(imageVector = Icons.Filled.Piano, contentDescription = "Piano")
|
||||
}
|
||||
FilledIconToggleButton(
|
||||
checked = !isPianoSelected,
|
||||
onCheckedChange = {
|
||||
isPianoSelected = false;
|
||||
mediaPlayer.changeInstru(20)
|
||||
}){
|
||||
Icon(imageVector = Icons.Filled.PianoOff, contentDescription = "Church Organ")
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = showBPMTools,
|
||||
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
|
||||
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
){
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = bpmInput, onValueChange = { newValue ->
|
||||
if (newValue.all { it.isDigit() } && newValue.length <= 3) {
|
||||
bpmInput = newValue
|
||||
}
|
||||
}, label = { Text("BPM") }, singleLine = true, keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Number, imeAction = ImeAction.Done
|
||||
), keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
val newBpm = bpmInput.toFloatOrNull() ?: 0f
|
||||
val newFactor = newBpm / basseBpm
|
||||
tempo = newFactor
|
||||
mediaPlayer.setTempo(newFactor)
|
||||
bpmInput = (basseBpm * newFactor).toInt().toString()
|
||||
}), modifier = Modifier.width(65.dp))
|
||||
}
|
||||
Row {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Slider(value = tempo, onValueChange = {
|
||||
tempo = it
|
||||
mediaPlayer?.setTempo(it)
|
||||
currentBpm = mediaPlayer.getCurrentBPM()
|
||||
bpmInput = (basseBpm * it).toInt().toString()
|
||||
}, valueRange = 0.25f..1.5f, modifier = Modifier.width(200.dp), thumb = {
|
||||
Box(
|
||||
modifier = Modifier.size(15.dp).background(Color.Magenta, CircleShape)
|
||||
)
|
||||
}, track = { sliderState ->
|
||||
SliderDefaults.Track(
|
||||
sliderState = sliderState, modifier = Modifier.height(5.dp)
|
||||
)
|
||||
})
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(45.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color(0xFF2C3135))
|
||||
.padding(vertical = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = {
|
||||
tempo = 1.0f
|
||||
mediaPlayer?.setTempo(1.0f)
|
||||
currentBpm = mediaPlayer.getCurrentBPM()
|
||||
}) {
|
||||
Icon(Icons.Default.Refresh, contentDescription = "Reset")
|
||||
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(),
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceAround
|
||||
){
|
||||
Box(contentAlignment = Alignment.CenterStart) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
showBPMTools = !showBPMTools
|
||||
}
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.AvTimer, contentDescription = "Tempo")
|
||||
}
|
||||
}
|
||||
Box( contentAlignment = Alignment.CenterStart) {
|
||||
Column {
|
||||
val isAtStart = loopState.first == -1L
|
||||
val isWaitingForB = loopState.first != -1L && !loopState.third
|
||||
val isLooping = loopState.third
|
||||
|
||||
Button(
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
when {
|
||||
isAtStart -> mediaPlayer.setPointA()
|
||||
|
|
@ -326,11 +282,10 @@ fun MidiControlPanel(
|
|||
else -> Color.LightGray
|
||||
}
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
|
||||
shape = RoundedCornerShape(5.dp) ,
|
||||
contentPadding = PaddingValues(horizontal = 2.dp, vertical = 0.dp),
|
||||
shape = RoundedCornerShape(5.dp),
|
||||
modifier = Modifier
|
||||
.height(34.dp)
|
||||
.padding(horizontal = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -339,67 +294,111 @@ fun MidiControlPanel(
|
|||
Text(
|
||||
text = "A",
|
||||
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 18.sp
|
||||
fontSize = 16.sp
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Loop,
|
||||
contentDescription = null,
|
||||
tint = if (isLooping) Color.White else Color.Black,
|
||||
modifier = Modifier.size(18.dp)
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "B",
|
||||
color = if (isLooping) Color.White else Color.Black,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 18.sp
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Column {
|
||||
IconButton(
|
||||
onClick = {
|
||||
showSATBTools = !showSATBTools
|
||||
}) {
|
||||
Icon(imageVector = Icons.Default.SettingsVoice, contentDescription = "SATB")
|
||||
}, 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) {
|
||||
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)
|
||||
modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
|
||||
contentDescription = "Pla",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(contentAlignment = Alignment.CenterEnd) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
showInstruTools = !showInstruTools
|
||||
}
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Tune, contentDescription = "Instru")
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
IconButton(onClick = { expandedCtl = !expandedCtl }) {
|
||||
IconButton(
|
||||
onClick = { expandedCtl = !expandedCtl },
|
||||
modifier = Modifier.size(48.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert,
|
||||
contentDescription = "More",
|
||||
|
|
@ -409,4 +408,6 @@ fun MidiControlPanel(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ fun ModernVolumeSlider(
|
|||
volume: Float,
|
||||
onVolumeChange: (Float) -> Unit
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
|
|
@ -70,4 +71,5 @@ fun ModernVolumeSlider(
|
|||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue