Compare commits
4 commits
8b171fceaf
...
78876d7ec0
| Author | SHA1 | Date | |
|---|---|---|---|
| 78876d7ec0 | |||
| 88b734cdc2 | |||
| b8b9eda836 | |||
| ec9194ce14 |
13 changed files with 232 additions and 72 deletions
|
|
@ -181,6 +181,8 @@ actual class FMediaPlayer actual constructor(
|
||||||
private val navigationSteps = mutableListOf<NavigationStep>()
|
private val navigationSteps = mutableListOf<NavigationStep>()
|
||||||
private val prefs: Settings = provideSettings()
|
private val prefs: Settings = provideSettings()
|
||||||
|
|
||||||
|
|
||||||
|
@Volatile private var userCustomTempo: Boolean = false
|
||||||
init {
|
init {
|
||||||
midiDriver.start()
|
midiDriver.start()
|
||||||
loadVoiceVolumes()
|
loadVoiceVolumes()
|
||||||
|
|
@ -196,7 +198,9 @@ actual class FMediaPlayer actual constructor(
|
||||||
val usPerQuarter = ((md[0].toInt() and 0xFF) shl 16) or
|
val usPerQuarter = ((md[0].toInt() and 0xFF) shl 16) or
|
||||||
((md[1].toInt() and 0xFF) shl 8) or
|
((md[1].toInt() and 0xFF) shl 8) or
|
||||||
(md[2].toInt() and 0xFF)
|
(md[2].toInt() and 0xFF)
|
||||||
targetBpm = 60_000_000f / usPerQuarter
|
val parsedBpm = 60_000_000f / usPerQuarter
|
||||||
|
targetBpm = parsedBpm
|
||||||
|
initialBpm = parsedBpm
|
||||||
}
|
}
|
||||||
|
|
||||||
usPerTick = (60_000_000.0 / targetBpm) / resolution
|
usPerTick = (60_000_000.0 / targetBpm) / resolution
|
||||||
|
|
@ -292,8 +296,10 @@ actual class FMediaPlayer actual constructor(
|
||||||
private fun resetTempoToInitial() {
|
private fun resetTempoToInitial() {
|
||||||
tempoChangeJob?.cancel()
|
tempoChangeJob?.cancel()
|
||||||
isInTempoChange = false
|
isInTempoChange = false
|
||||||
targetBpm = initialBpm
|
//targetBpm = initialBpm
|
||||||
boundModel?.setBpmFlow(initialBpm)
|
userCustomTempo = false
|
||||||
|
usPerTick = (60_000_000.0 / targetBpm.toDouble()) / resolution.toDouble()
|
||||||
|
boundModel?.setBpmFlow(targetBpm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractDynamic(marker: String) = Dynamic.entries.firstOrNull {
|
private fun extractDynamic(marker: String) = Dynamic.entries.firstOrNull {
|
||||||
|
|
@ -382,6 +388,7 @@ actual class FMediaPlayer actual constructor(
|
||||||
} else {
|
} else {
|
||||||
yield()
|
yield()
|
||||||
}
|
}
|
||||||
|
if (!isRunning || !isActive) break
|
||||||
|
|
||||||
currentTickPos = ev.tickAbsolute
|
currentTickPos = ev.tickAbsolute
|
||||||
lastEventNano = System.nanoTime()
|
lastEventNano = System.nanoTime()
|
||||||
|
|
@ -438,9 +445,13 @@ actual class FMediaPlayer actual constructor(
|
||||||
currentTickPos = gridIndex.toLong() * resolution
|
currentTickPos = gridIndex.toLong() * resolution
|
||||||
lastEventNano = System.nanoTime()
|
lastEventNano = System.nanoTime()
|
||||||
|
|
||||||
val initialOrPreviousBpm = navigationSteps
|
val initialOrPreviousBpm = if (userCustomTempo) {
|
||||||
.filter { it.newBpm != null && it.gridIndex <= gridIndex }
|
targetBpm
|
||||||
.maxByOrNull { it.gridIndex }?.newBpm ?: initialBpm
|
} else {
|
||||||
|
navigationSteps
|
||||||
|
.filter { it.newBpm != null && it.gridIndex <= gridIndex }
|
||||||
|
.maxByOrNull { it.gridIndex }?.newBpm ?: initialBpm
|
||||||
|
}
|
||||||
|
|
||||||
targetBpm = initialOrPreviousBpm
|
targetBpm = initialOrPreviousBpm
|
||||||
sharedScreenModel.setBpmFlow(initialOrPreviousBpm)
|
sharedScreenModel.setBpmFlow(initialOrPreviousBpm)
|
||||||
|
|
@ -451,6 +462,9 @@ actual class FMediaPlayer actual constructor(
|
||||||
navigationSteps.forEach { step ->
|
navigationSteps.forEach { step ->
|
||||||
if (step.gridIndex > gridIndex) {
|
if (step.gridIndex > gridIndex) {
|
||||||
step.alreadyDone = false
|
step.alreadyDone = false
|
||||||
|
if (step.isFarany) {
|
||||||
|
step.faranyActive = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyVoiceStates()
|
applyVoiceStates()
|
||||||
|
|
@ -470,7 +484,10 @@ actual class FMediaPlayer actual constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetNavigationFlags() {
|
private fun resetNavigationFlags() {
|
||||||
navigationSteps.forEach { it.alreadyDone = false }
|
navigationSteps.forEach {
|
||||||
|
it.alreadyDone = false
|
||||||
|
it.faranyActive = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareNavigation(sharedScreenModel: SharedScreenModel) {
|
private fun prepareNavigation(sharedScreenModel: SharedScreenModel) {
|
||||||
|
|
@ -595,10 +612,12 @@ actual class FMediaPlayer actual constructor(
|
||||||
|
|
||||||
// ── Farany ────────────────────────────────
|
// ── Farany ────────────────────────────────
|
||||||
finReg.containsMatchIn(mTrim) -> {
|
finReg.containsMatchIn(mTrim) -> {
|
||||||
val hasDcAfter = metadataList.any { m ->
|
val hasDcOrDsAfter = metadataList.any { m ->
|
||||||
(m.gridIndex ?: 0) > ci && m.marker.any { mk -> dcR.containsMatchIn(mk) }
|
(m.gridIndex ?: 0) > ci && m.marker.any { mk ->
|
||||||
|
dcR.containsMatchIn(mk) || dsR.containsMatchIn(mk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (hasDcAfter) {
|
if (hasDcOrDsAfter) {
|
||||||
navigationSteps.add(
|
navigationSteps.add(
|
||||||
NavigationStep(
|
NavigationStep(
|
||||||
marker,
|
marker,
|
||||||
|
|
@ -691,9 +710,14 @@ actual class FMediaPlayer actual constructor(
|
||||||
//♩
|
//♩
|
||||||
step.newBpm != null -> {
|
step.newBpm != null -> {
|
||||||
step.alreadyDone = true
|
step.alreadyDone = true
|
||||||
println("Exécution changement de tempo : ${step.newBpm} BPM à la grille $currentIndex")
|
if (step.gridIndex == 0 && userCustomTempo) {
|
||||||
targetBpm = step.newBpm
|
|
||||||
sharedScreenModel.setBpmFlow(step.newBpm)
|
}
|
||||||
|
else {
|
||||||
|
println("Exécution changement de tempo : ${step.newBpm} BPM à la grille $currentIndex")
|
||||||
|
targetBpm = step.newBpm
|
||||||
|
sharedScreenModel.setBpmFlow(step.newBpm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modulation
|
// Modulation
|
||||||
|
|
@ -755,9 +779,13 @@ actual class FMediaPlayer actual constructor(
|
||||||
step.isFarany -> {
|
step.isFarany -> {
|
||||||
println("C Farany ok $step")
|
println("C Farany ok $step")
|
||||||
if (pendingFin || step.faranyActive) {
|
if (pendingFin || step.faranyActive) {
|
||||||
println("Farany activé → STOP à grille $currentIndex")
|
step.alreadyDone = true
|
||||||
isRunning = false
|
isRunning = false
|
||||||
allNotesOff()
|
allNotesOff()
|
||||||
|
playJob?.cancel()
|
||||||
|
seekToGrid(0)
|
||||||
|
boundModel?.updateActiveIndexByIndex(0)
|
||||||
|
println("Farany activé → STOP à grille $currentIndex")
|
||||||
onFinished()
|
onFinished()
|
||||||
return@collect
|
return@collect
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -810,7 +838,7 @@ actual class FMediaPlayer actual constructor(
|
||||||
val interpolated = (currentTickPos + extraTicks) / resolution
|
val interpolated = (currentTickPos + extraTicks) / resolution
|
||||||
val rawGrid = interpolated.toInt().coerceAtLeast(0)
|
val rawGrid = interpolated.toInt().coerceAtLeast(0)
|
||||||
//sharedScreenModel.updateActiveIndexByIndex(rawGrid)
|
//sharedScreenModel.updateActiveIndexByIndex(rawGrid)
|
||||||
if (interpolated.toInt() >= lastSentGrid) {
|
if (interpolated.toInt() != lastSentGrid) {
|
||||||
lastSentGrid = interpolated.toInt()
|
lastSentGrid = interpolated.toInt()
|
||||||
sharedScreenModel.updateActiveIndexByIndex(interpolated.toInt())
|
sharedScreenModel.updateActiveIndexByIndex(interpolated.toInt())
|
||||||
}
|
}
|
||||||
|
|
@ -922,7 +950,9 @@ actual class FMediaPlayer actual constructor(
|
||||||
allNotesOff(); currentTickPos = 0L
|
allNotesOff(); currentTickPos = 0L
|
||||||
resetNavigationFlags(); navigationSteps.clear(); clearLoop()
|
resetNavigationFlags(); navigationSteps.clear(); clearLoop()
|
||||||
currentDynamicFactor = Dynamic.MF.factor
|
currentDynamicFactor = Dynamic.MF.factor
|
||||||
resetTempoToNormal()
|
//resetTempoToNormal()
|
||||||
|
currentPlaybackBpm = targetBpm
|
||||||
|
usPerTick = (60_000_000.0 / targetBpm.toDouble()) / resolution.toDouble()
|
||||||
resetTempoToInitial()
|
resetTempoToInitial()
|
||||||
}
|
}
|
||||||
actual fun release() {
|
actual fun release() {
|
||||||
|
|
@ -943,7 +973,9 @@ actual class FMediaPlayer actual constructor(
|
||||||
actual fun setTempo(bpm: Float) {
|
actual fun setTempo(bpm: Float) {
|
||||||
targetBpm = bpm
|
targetBpm = bpm
|
||||||
currentPlaybackBpm = bpm
|
currentPlaybackBpm = bpm
|
||||||
|
userCustomTempo = true
|
||||||
usPerTick = (60_000_000.0 / bpm.toDouble()) / resolution.toDouble()
|
usPerTick = (60_000_000.0 / bpm.toDouble()) / resolution.toDouble()
|
||||||
|
boundModel?.setBpmFlow(bpm)
|
||||||
boundModel?.let { prepareNavigation(it) }
|
boundModel?.let { prepareNavigation(it) }
|
||||||
needsClockSync = true
|
needsClockSync = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
M0:|c:G|m:6/4|r:P.M|t:FF 7 Tsy hainay ny hangina|a:Ramarolahy Aina Nirina Jaona|h:
|
M0:|c:G|m:6/4|r:P.M|t:FF 7 Tsy hainay ny hangina|a:Ramarolahy Aina Nirina Jaona|h:
|
||||||
U0:zK:4 C444 C84 444444 C84 C444 C84 444444 C8${$}4 ${x7} 444444${x0} K4 444444 (444)84 444444 C84 444444 (444)84 444444 84${D.S}8
|
U0:zK:4C444C84444444C84C444C84444444C8${$}${Fiverenana}4444444444444444444444444444444444444444444${Farany}K4444444(444)84444444C84444444(444)8444444484${D.S.Fin}8
|
||||||
N1:s,mmfsls smsmfmrrd mrFmrsr #s,tmrdrtlss sm-3f#t,ls-4 ssrmfsls-4 s,m-3fls-ssfrdsmfmrd d4msmf--d d4mrdsf r4Fmrs--r trrrd#s,tls--
|
N1:s,mmfsls smsmfmrrd mrFmrsr #s,tmrdrtlss sm-3f#t,ls-4 ssrmfsls-4 s,m-3fls-ssfrdsmfmrd d4msmf--d d4mrdsf r4Fmrs--r trrrd#s,tls--
|
||||||
N2:#f,sddrmRm sdddttsss sFdddtt sF6r,f sdstdrRmdrmddt--t-- dmdtdrdsdt-- dmrrdlsddttss T7llTl T7ll d7ttdt sl3F3r,m,f
|
N2:#f,sddrmRm sdddttsss sFdddtt sF6r,f sdstdrRmdrmddt--t-- dmdtdrdsdt-- dmrrdlsddttss T7llTl T7ll d7ttdt sl3F3r,m,f
|
||||||
N3:#l,s5Fs ssmdrsffm dr7d4rdtt ssmfs--smfsmmr--f-- msmrmfsmmf-- msT'T'l'fmmsrsfm m4smsddrd m4sfmdd F4l'Fl'rrmr rF3rrdtdr
|
N3:#l,s5Fs ssmdrsffm dr7d4rdtt ssmfs--smfsmmr--f-- msmrmfsmmf-- msT'T'l'fmmsrsfm m4smsddrd m4sfmdd F4l'Fl'rrmr rF3rrdtdr
|
||||||
N4:#r,sd6 s7dd ddllFss sr6ss sdmsd-- dmsddls--s-- ddmslt d3s--dmmfrs7d, d7f4 d7ff r'7s4 r'4r3slt
|
N4:#r,sd6 s7dd ddllFss sr6ss sdmsd-- dmsddls--s-- ddmslt d3s--dmmfrs7d, d7f4 d7ff r'7s4 r'4r3slt
|
||||||
Y1:Tsy hainay ny hangina/Ka tsy hankalaza ny Avo/Nameno fitahiana/Ka tsy mba namela ho lavo.${R=}Ka de_4ra ry Ray!_5/Sy saotra no\ atolotra\ Anao_5/ny fo_4tena fa_2ly loatra no entinay midera\ Anao./Ny sainay tsy ampy\ hahalala/Ny fatran’ny fitiavana_o/Ka raiso ry Tompo Malala/Ny fankasitrahana\ Anao (ry Ray).
|
Y1:Tsy hainay ny hangina/Ka tsy hankalaza ny Avo/Nameno fitahiana/Ka tsy mba namela ho lavo.${R=}Ka de_4ra ry Ray!_4/Sy saotra no\ atolotra\ Anao_4/ny fo_4tena fa_2ly loatra no entinay midera\ Anao./Ny sainay tsy ampy\ hahalala/Ny fatran’ny fitiavana_o/Ka raiso ry Tompo Malala/Ny fankasitrahana\ Anao (ry Ray).
|
||||||
Y2:Tsy hainay hadinoina/‘Lay fara-fitiavanao, Ray o!/Fa maty novonoina/Ny Zanakao hamonjy anay./
|
Y2:Tsy hainay hadinoina/‘Lay fara-fitiavanao, Ray o!/Fa maty novonoina/Ny Zanakao hamonjy anay./
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ object ScreenSolfa : Screen {
|
||||||
var showContextualMenu = false
|
var showContextualMenu = false
|
||||||
var viewportHeight by remember { mutableStateOf(0) }
|
var viewportHeight by remember { mutableStateOf(0) }
|
||||||
|
|
||||||
var isScanning by remember { mutableStateOf(false) }
|
|
||||||
var qrCodeResult by remember { mutableStateOf("Aucun code scanné") }
|
var qrCodeResult by remember { mutableStateOf("Aucun code scanné") }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
DeepLinkHandler.onSongReceived = { content, filePath ->
|
DeepLinkHandler.onSongReceived = { content, filePath ->
|
||||||
|
|
@ -88,9 +87,6 @@ object ScreenSolfa : Screen {
|
||||||
solfaScreenModel,
|
solfaScreenModel,
|
||||||
sharedScreenModel,
|
sharedScreenModel,
|
||||||
qrCodeResult = qrCodeResult,
|
qrCodeResult = qrCodeResult,
|
||||||
onScannerButtonClick = {
|
|
||||||
isScanning = true
|
|
||||||
},
|
|
||||||
solfaScrollState = scrollState
|
solfaScrollState = scrollState
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ data class MidiPitch (
|
||||||
metaType = type
|
metaType = type
|
||||||
}
|
}
|
||||||
fun setNote(note: String, theDuration: Int) {
|
fun setNote(note: String, theDuration: Int) {
|
||||||
val midiPitch = Transpose.transposeToMidi(note, key, "C")
|
val midiPitch = Transpose.transposeToMidi(note, "C", key)
|
||||||
velocity = if (midiPitch < 0) {
|
velocity = if (midiPitch < 0) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -102,17 +102,19 @@ object HarmonicAnalyzer {
|
||||||
return degreeLine
|
return degreeLine
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyzeChordName(notes: Map<Int, String>, songKey: String = "C"): String {
|
fun analyzeChordName(notes: Map<Int, String>, songKey: String = "C", transposeInt: Int = 0): String {
|
||||||
val keyOffset = keyOffsets[songKey] ?: 0
|
val keyOffset = keyOffsets[songKey] ?: 0
|
||||||
|
val totalOffset = ((keyOffset + transposeInt) % 12 + 12) % 12
|
||||||
val slices = splitSlices(notes)
|
val slices = splitSlices(notes)
|
||||||
|
|
||||||
val names = slices.map { slice ->
|
val names = slices.map { slice ->
|
||||||
val cleaned = cleanNotes(slice)
|
val cleaned = cleanNotes(slice)
|
||||||
val match = findChordMatch(cleaned) ?: return@map ""
|
val match = findChordMatch(cleaned) ?: return@map ""
|
||||||
val absoluteRoot = (keyOffset + match.root) % 12
|
|
||||||
|
val absoluteRoot = ((totalOffset + match.root) % 12 + 12) % 12
|
||||||
val chordName = noteNames[absoluteRoot] + match.quality
|
val chordName = noteNames[absoluteRoot] + match.quality
|
||||||
if (match.bass != -1 && match.bass != match.root) {
|
if (match.bass != -1 && match.bass != match.root) {
|
||||||
val absoluteBass = (keyOffset + match.bass) % 12
|
val absoluteBass = ((totalOffset + match.bass) % 12 + 12) % 12
|
||||||
"$chordName/${noteNames[absoluteBass]}"
|
"$chordName/${noteNames[absoluteBass]}"
|
||||||
} else {
|
} else {
|
||||||
chordName
|
chordName
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
|
|
||||||
private val prefs: Settings = provideSettings()
|
private val prefs: Settings = provideSettings()
|
||||||
|
|
||||||
fun generateMidiFile(bpm: Float, outputPath: String = "whawyd3.mid") {
|
fun generateMidiFile(bpm: Float, outputPath: String = "whawyd3.mid", transpI: Int = 0) {
|
||||||
val parseScope = CoroutineScope(Dispatchers.Default)
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
parseScope.launch {
|
parseScope.launch {
|
||||||
val pitchesSorted = pitches.sortedWith(compareBy({ it.tick }, { it.voiceNumber }))
|
val pitchesSorted = pitches.sortedWith(compareBy({ it.tick }, { it.voiceNumber }))
|
||||||
|
|
@ -276,12 +276,14 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
3 to prefs.getInt("voice_instrument", 0),
|
3 to prefs.getInt("voice_instrument", 0),
|
||||||
4 to prefs.getInt("voice_instrument", 0),
|
4 to prefs.getInt("voice_instrument", 0),
|
||||||
)
|
)
|
||||||
|
val originalKey = sharedScreenModel.songKey.value
|
||||||
|
val transposedKey = Transpose.transposeKey(baseKey = originalKey, interval = transpI)
|
||||||
midiWriter.process(
|
midiWriter.process(
|
||||||
pitchesSorted,
|
pitchesSorted,
|
||||||
sharedScreenModel.getFullMarkers(),
|
sharedScreenModel.getFullMarkers(),
|
||||||
initialBpm = bpm,
|
initialBpm = bpm,
|
||||||
instruments,
|
instruments,
|
||||||
initialKey = sharedScreenModel.songKey.value
|
initialKey = transposedKey
|
||||||
)
|
)
|
||||||
midiWriter.save(outputPath)
|
midiWriter.save(outputPath)
|
||||||
}
|
}
|
||||||
|
|
@ -2086,7 +2088,12 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
|
|
||||||
midiPitch.currentVoiceNumber(voiceNumber)
|
midiPitch.currentVoiceNumber(voiceNumber)
|
||||||
if (voiceNumber == 1) {
|
if (voiceNumber == 1) {
|
||||||
midiPitch.initKey(meta["C"] ?: "C", meta["C"]?.endsWith("m") ?: false)
|
val rawKey = sharedScreenModel.songKey.value.ifEmpty { meta["C"] ?: "C" }
|
||||||
|
val interval = sharedScreenModel.transpositionInterval.value
|
||||||
|
val isMinor = rawKey.endsWith("m", ignoreCase = true)
|
||||||
|
|
||||||
|
val transposedKey = Transpose.transposeKey(rawKey, interval)
|
||||||
|
midiPitch.initKey(transposedKey, isMinor)
|
||||||
pushMidi("meta")
|
pushMidi("meta")
|
||||||
if (midiPitch.initMeasure(meta["m"] ?: "4/4")) {
|
if (midiPitch.initMeasure(meta["m"] ?: "4/4")) {
|
||||||
pushMidi("measure")
|
pushMidi("measure")
|
||||||
|
|
|
||||||
|
|
@ -959,8 +959,10 @@ fun LazyVerticalGridTUO(
|
||||||
val degreeName = remember(chordMap) {
|
val degreeName = remember(chordMap) {
|
||||||
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeDegree(chordMap) else null
|
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeDegree(chordMap) else null
|
||||||
}
|
}
|
||||||
val chordName = remember(chordMap) {
|
val appliedInterval by sharedScreenModel.transpositionInterval.collectAsState()
|
||||||
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeChordName(chordMap, sharedScreenModel.songKey.value) else null
|
val songKey = sharedScreenModel.songKey.value
|
||||||
|
val chordName = remember(chordMap, songKey, appliedInterval) {
|
||||||
|
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeChordName(chordMap, songKey, appliedInterval) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -1104,7 +1106,7 @@ fun LazyVerticalGridTUO(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val currentInterval by sharedScreenModel.transpositionInterval.collectAsState()
|
val currentInterval by sharedScreenModel.transpToCInt.collectAsState()
|
||||||
TimeUnitComposable(
|
TimeUnitComposable(
|
||||||
tuo = oneTUO,
|
tuo = oneTUO,
|
||||||
stanzaNumber = currentStanza,
|
stanzaNumber = currentStanza,
|
||||||
|
|
|
||||||
|
|
@ -200,22 +200,81 @@ class Transpose {
|
||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
val noteNaked = regexFound.groupValues[1]
|
val noteNaked = regexFound.groupValues[1]
|
||||||
|
val originalOctaveStr = regexFound.groupValues[2]
|
||||||
|
val rest = regexFound.groupValues[3]
|
||||||
val noteNum = noteToNumber.indexOf(noteNaked)
|
val noteNum = noteToNumber.indexOf(noteNaked)
|
||||||
val oldKey = keyToNumber.indexOf(fromKey)
|
val oldKey = keyToNumber.indexOf(fromKey)
|
||||||
val newKey = keyToNumber.indexOf(toKey)
|
val newKey = keyToNumber.indexOf(toKey)
|
||||||
var newNoteNum = noteNum - newKey + oldKey + alter
|
if (oldKey == -1 || newKey == -1) return note
|
||||||
var suffix = ""
|
|
||||||
|
var keyShift = newKey - oldKey
|
||||||
|
if (keyShift > 6) {
|
||||||
|
keyShift -= 12
|
||||||
|
} else if (keyShift < -6) {
|
||||||
|
keyShift += 12
|
||||||
|
}
|
||||||
|
|
||||||
|
var newNoteNum = noteNum + keyShift + alter
|
||||||
|
var octaveShift = 0
|
||||||
val noteSize = noteToNumber.size
|
val noteSize = noteToNumber.size
|
||||||
|
|
||||||
while (newNoteNum < 0) {
|
while (newNoteNum < 0) {
|
||||||
newNoteNum += noteSize
|
newNoteNum += noteSize
|
||||||
suffix += ","
|
octaveShift -= 1
|
||||||
}
|
}
|
||||||
while (newNoteNum >= noteSize) {
|
while (newNoteNum >= noteSize) {
|
||||||
newNoteNum -= noteSize
|
newNoteNum -= noteSize
|
||||||
suffix += "'"
|
octaveShift += 1
|
||||||
}
|
}
|
||||||
val newNote = noteToNumber[newNoteNum] + regexFound.groupValues[2] + suffix
|
|
||||||
return simplifyNote(newNote) + regexFound.groupValues[3]
|
val originalOctaveVal = octaveToInt(originalOctaveStr)
|
||||||
|
val finalOctaveVal = originalOctaveVal + octaveShift
|
||||||
|
val finalOctaveStr = intToOctave(finalOctaveVal)
|
||||||
|
|
||||||
|
val transposedNote = noteToNumber[newNoteNum]
|
||||||
|
return transposedNote + finalOctaveStr + rest
|
||||||
|
}
|
||||||
|
fun octaveToInt(octaveStr: String): Int {
|
||||||
|
var valOctave = 0
|
||||||
|
for (char in octaveStr) {
|
||||||
|
when (char) {
|
||||||
|
'\'', '¹' -> valOctave += 1
|
||||||
|
',', '₁' -> valOctave -= 1
|
||||||
|
'²' -> valOctave += 2
|
||||||
|
'₂' -> valOctave -= 2
|
||||||
|
'³' -> valOctave += 3
|
||||||
|
'₃' -> valOctave -= 3
|
||||||
|
'⁴' -> valOctave += 4
|
||||||
|
'₄' -> valOctave -= 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valOctave
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intToOctave(octaveVal: Int): String {
|
||||||
|
val index = octaveVal + 4
|
||||||
|
return if (index in octaveSigns.indices) {
|
||||||
|
octaveSigns[index]
|
||||||
|
} else if (octaveVal > 0) {
|
||||||
|
"'".repeat(octaveVal)
|
||||||
|
} else if (octaveVal < 0) {
|
||||||
|
",".repeat(-octaveVal)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transposeKey(baseKey: String, interval: Int): String {
|
||||||
|
if (baseKey.isBlank()) return "C"
|
||||||
|
|
||||||
|
val isMinor = baseKey.endsWith("m", ignoreCase = true)
|
||||||
|
val rootKey = if (isMinor) baseKey.dropLast(1) else baseKey
|
||||||
|
val baseIndex = keyToNumber.indexOfFirst { it.equals(rootKey, ignoreCase = true) }
|
||||||
|
if (baseIndex == -1) return baseKey
|
||||||
|
val newIndex = (baseIndex + interval).mod(12)
|
||||||
|
val newRootKey = keyToNumber[newIndex]
|
||||||
|
|
||||||
|
return if (isMinor) "${newRootKey}m" else newRootKey
|
||||||
}
|
}
|
||||||
@OptIn(ExperimentalMaterial3Api::class) // Ajoutez cette annotation pour utiliser ExposedDropdownMenuBox
|
@OptIn(ExperimentalMaterial3Api::class) // Ajoutez cette annotation pour utiliser ExposedDropdownMenuBox
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
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 cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
||||||
|
import cafe.adriel.voyager.navigator.internal.BackHandler
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -53,13 +55,13 @@ import mg.dot.feufaro.solfa.Transpose
|
||||||
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, InternalVoyagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreenWithDrawer(
|
fun MainScreenWithDrawer(
|
||||||
solfaScreenModel: SolfaScreenModel,
|
solfaScreenModel: SolfaScreenModel,
|
||||||
sharedScreenModel: SharedScreenModel,
|
sharedScreenModel: SharedScreenModel,
|
||||||
qrCodeResult: String,
|
qrCodeResult: String,
|
||||||
onScannerButtonClick: () -> Unit,
|
|
||||||
solfaScrollState: ScrollState,
|
solfaScrollState: ScrollState,
|
||||||
content: @Composable (PaddingValues) -> Unit,
|
content: @Composable (PaddingValues) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
@ -199,6 +201,22 @@ fun MainScreenWithDrawer(
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
var keyboardHeightDp by remember { mutableStateOf(0.dp) }
|
var keyboardHeightDp by remember { mutableStateOf(0.dp) }
|
||||||
|
|
||||||
|
BackHandler(enabled = isSearchActive) {
|
||||||
|
sharedScreenModel.showSearchMenu(false)
|
||||||
|
}
|
||||||
|
BackHandler(enabled = showMidiCtrl) {
|
||||||
|
sharedScreenModel.setMidiCtrl(false)
|
||||||
|
}
|
||||||
|
BackHandler(enabled = isQrVisible) {
|
||||||
|
sharedScreenModel.toggleQRCodeVisibility()
|
||||||
|
}
|
||||||
|
BackHandler(enabled = (!showMidiCtrl && isExpanded)) {
|
||||||
|
sharedScreenModel.setExpandedFAB(!isExpanded)
|
||||||
|
}
|
||||||
|
BackHandler(enabled = drawerState.isOpen) {
|
||||||
|
scope.launch { drawerState.close() }
|
||||||
|
}
|
||||||
|
|
||||||
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
||||||
SimpleDrawerContent(
|
SimpleDrawerContent(
|
||||||
items,
|
items,
|
||||||
|
|
@ -207,10 +225,6 @@ fun MainScreenWithDrawer(
|
||||||
currentActivePath,
|
currentActivePath,
|
||||||
drawerState,
|
drawerState,
|
||||||
scope,
|
scope,
|
||||||
onScannerButtonClick = {
|
|
||||||
scope.launch { drawerState.close() }
|
|
||||||
onScannerButtonClick()
|
|
||||||
},
|
|
||||||
onSongSelected = { newSong ->
|
onSongSelected = { newSong ->
|
||||||
sharedScreenModel.loadNewSong("$midiFile")
|
sharedScreenModel.loadNewSong("$midiFile")
|
||||||
},
|
},
|
||||||
|
|
@ -454,7 +468,8 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, actions = {
|
}, actions = {
|
||||||
var tempInterval by remember(fileContent) { mutableStateOf(0) }
|
val appliedInterval by sharedScreenModel.transpositionInterval.collectAsState()
|
||||||
|
var tempInterval by remember(fileContent, appliedInterval) { mutableStateOf(appliedInterval) }
|
||||||
var isEyeVisible by remember { mutableStateOf(false) }
|
var isEyeVisible by remember { mutableStateOf(false) }
|
||||||
val isMinor = songKey.endsWith("m", ignoreCase = true)
|
val isMinor = songKey.endsWith("m", ignoreCase = true)
|
||||||
val cleanSongKey = if (isMinor) songKey.dropLast(1) else songKey
|
val cleanSongKey = if (isMinor) songKey.dropLast(1) else songKey
|
||||||
|
|
@ -462,10 +477,9 @@ fun MainScreenWithDrawer(
|
||||||
val songKeyIndex = keysOrder.indexOf(cleanSongKey).takeIf { it != -1 } ?: 0
|
val songKeyIndex = keysOrder.indexOf(cleanSongKey).takeIf { it != -1 } ?: 0
|
||||||
val rawKeyIndex = (songKeyIndex + tempInterval) % 12
|
val rawKeyIndex = (songKeyIndex + tempInterval) % 12
|
||||||
val tempUiKey = keysOrder[if (rawKeyIndex < 0) rawKeyIndex + 12 else rawKeyIndex]
|
val tempUiKey = keysOrder[if (rawKeyIndex < 0) rawKeyIndex + 12 else rawKeyIndex]
|
||||||
val appliedInterval by sharedScreenModel.transpositionInterval.collectAsState()
|
|
||||||
val isPendingChange = tempInterval != appliedInterval
|
val isPendingChange = tempInterval != appliedInterval
|
||||||
val isCurrentlyTransposed = appliedInterval != 0
|
val isCurrentlyTransposed = appliedInterval != 0
|
||||||
|
val formattedKey = if (isMinor) "${tempUiKey}m" else tempUiKey
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(end = 16.dp),
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
contentAlignment = Alignment.TopCenter
|
contentAlignment = Alignment.TopCenter
|
||||||
|
|
@ -475,11 +489,11 @@ fun MainScreenWithDrawer(
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if(isMinor) "${tempUiKey}m" else tempUiKey,
|
text = formattedKey,
|
||||||
style = MaterialTheme.typography.displaySmall,
|
style = MaterialTheme.typography.displaySmall,
|
||||||
fontWeight = FontWeight.Black,
|
fontWeight = FontWeight.Black,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = if (isCurrentlyTransposed && !isPendingChange) Color(0xFFFFD700) else Color.White,
|
color = if (isCurrentlyTransposed || isPendingChange) MaterialTheme.colorScheme.tertiary else Color.White,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = sharedInteractionSource,
|
interactionSource = sharedInteractionSource,
|
||||||
|
|
@ -493,7 +507,10 @@ fun MainScreenWithDrawer(
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = isEyeVisible,
|
expanded = isEyeVisible,
|
||||||
onDismissRequest = { isEyeVisible = false },
|
onDismissRequest = {
|
||||||
|
tempInterval = appliedInterval
|
||||||
|
isEyeVisible = false
|
||||||
|
},
|
||||||
containerColor = Color(0xFF2C3135).copy(alpha = 0.75f),
|
containerColor = Color(0xFF2C3135).copy(alpha = 0.75f),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
modifier = Modifier.padding(12.dp)
|
modifier = Modifier.padding(12.dp)
|
||||||
|
|
@ -503,6 +520,8 @@ fun MainScreenWithDrawer(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
/* < = > */
|
/* < = > */
|
||||||
|
val isOriginalKeyC = cleanSongKey.equals("C", ignoreCase = true)
|
||||||
|
val isTransposedToC = tempUiKey.equals("C", ignoreCase = true) && !isOriginalKeyC
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
|
|
@ -535,11 +554,11 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
|
|
||||||
val centralIcon =
|
val centralIcon =
|
||||||
if (isPendingChange) Icons.Filled.Check else Icons.Filled.SwapHoriz
|
if (isPendingChange && !isTransposedToC) Icons.Filled.Check else Icons.Filled.SwapHoriz
|
||||||
val centralTint =
|
val centralTint =
|
||||||
if (isPendingChange) MaterialTheme.colorScheme.tertiary else Color.White
|
if (isPendingChange) MaterialTheme.colorScheme.tertiary else Color.White
|
||||||
val tooltipText =
|
val tooltipText =
|
||||||
if (isPendingChange) "Transposer en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else "Transposer"
|
if (isPendingChange) "Écoutez en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else ""
|
||||||
|
|
||||||
TooltipBox(
|
TooltipBox(
|
||||||
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
|
@ -554,13 +573,18 @@ fun MainScreenWithDrawer(
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(36.dp),
|
modifier = Modifier.size(36.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!isPendingChange && isCurrentlyTransposed) {
|
if (isPendingChange) {
|
||||||
|
sharedScreenModel.setTranspositionInterval(tempInterval)
|
||||||
|
sharedScreenModel.stopMidi()
|
||||||
|
sharedScreenModel.seekTo(0f)
|
||||||
|
solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value)
|
||||||
|
} else if (isCurrentlyTransposed) {
|
||||||
tempInterval = 0
|
tempInterval = 0
|
||||||
sharedScreenModel.setTranspositionInterval(0)
|
sharedScreenModel.setTranspositionInterval(0)
|
||||||
} else {
|
sharedScreenModel.stopMidi()
|
||||||
sharedScreenModel.setTranspositionInterval(tempInterval)
|
sharedScreenModel.seekTo(0f)
|
||||||
|
solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value)
|
||||||
}
|
}
|
||||||
solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value)
|
|
||||||
isEyeVisible = false
|
isEyeVisible = false
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
@ -599,22 +623,35 @@ fun MainScreenWithDrawer(
|
||||||
|
|
||||||
val cIndex = keysOrder.indexOf("C")
|
val cIndex = keysOrder.indexOf("C")
|
||||||
val intervalToC = if (cIndex != -1) cIndex - songKeyIndex else 0
|
val intervalToC = if (cIndex != -1) cIndex - songKeyIndex else 0
|
||||||
|
val buttonText = if (isTransposedToC) "Revenir en $songKey" else "Transposer en C"
|
||||||
|
val tooltipTextC = if (isTransposedToC) "Revenir en clé $songKey" else "Écrire les notes en Do (C)"
|
||||||
TooltipBox(
|
TooltipBox(
|
||||||
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
tooltip = {
|
tooltip = {
|
||||||
PlainTooltip(
|
PlainTooltip(
|
||||||
containerColor = Color.DarkGray,
|
containerColor = Color.DarkGray,
|
||||||
contentColor = Color.White
|
contentColor = Color.White
|
||||||
) { Text(text = "Transposer en Do (C)", fontSize = 12.sp) }
|
) { Text(text = tooltipTextC, fontSize = 12.sp) }
|
||||||
},
|
},
|
||||||
state = rememberTooltipState()
|
state = rememberTooltipState()
|
||||||
) {
|
) {
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
tempInterval = intervalToC
|
if (isTransposedToC) {
|
||||||
sharedScreenModel.setTranspositionInterval(intervalToC)
|
tempInterval = 0
|
||||||
solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value)
|
sharedScreenModel.setTranspositionInterval(0)
|
||||||
|
sharedScreenModel.setTranspToC(0)
|
||||||
|
sharedScreenModel.stopMidi()
|
||||||
|
sharedScreenModel.seekTo(0f)
|
||||||
|
} else {
|
||||||
|
val currentKey = sharedScreenModel.songKey.value
|
||||||
|
val intervalC = getIntervalToC(currentKey)
|
||||||
|
|
||||||
|
tempInterval = -intervalC
|
||||||
|
sharedScreenModel.setTranspToC(intervalC)
|
||||||
|
sharedScreenModel.stopMidi()
|
||||||
|
sharedScreenModel.seekTo(0f)
|
||||||
|
}
|
||||||
isEyeVisible = false
|
isEyeVisible = false
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().height(36.dp),
|
modifier = Modifier.fillMaxWidth().height(36.dp),
|
||||||
|
|
@ -628,7 +665,7 @@ fun MainScreenWithDrawer(
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Transposer en C",
|
text = buttonText,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|
@ -785,9 +822,10 @@ fun MainScreenWithDrawer(
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!savedPath.isNullOrEmpty()) {
|
if (!savedPath.isNullOrEmpty()) {
|
||||||
|
val transpositionInterval = sharedScreenModel.transpositionInterval.value
|
||||||
val bpm = sharedScreenModel.getBpmFlow().value ?: 120f
|
val bpm = sharedScreenModel.getBpmFlow().value ?: 120f
|
||||||
|
|
||||||
solfaScreenModel.generateMidiFile(bpm, savedPath)
|
solfaScreenModel.generateMidiFile(bpm, savedPath, transpositionInterval)
|
||||||
println("Fichier MIDI enregistré avec succès sous : $savedPath")
|
println("Fichier MIDI enregistré avec succès sous : $savedPath")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -1124,6 +1162,24 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
private fun getIntervalToC(key: String): Int {
|
||||||
|
val cleanKey = key.removeSuffix("m").trim().uppercase()
|
||||||
|
return when (cleanKey) {
|
||||||
|
"C" -> 0
|
||||||
|
"DB" -> 1
|
||||||
|
"D" -> 2
|
||||||
|
"EB" -> 3
|
||||||
|
"E" -> 4
|
||||||
|
"F" -> 5
|
||||||
|
"GB" -> 6
|
||||||
|
"G" -> 7
|
||||||
|
"AB" -> 8
|
||||||
|
"A" -> 9
|
||||||
|
"BB" -> 10
|
||||||
|
"B" -> 11
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MyFAB(
|
private fun MyFAB(
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ fun SimpleDrawerContent(
|
||||||
activePath: String = Solfa.currentFile,
|
activePath: String = Solfa.currentFile,
|
||||||
drawerState: DrawerState,
|
drawerState: DrawerState,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
onScannerButtonClick: () -> Unit,
|
|
||||||
onSongSelected: (String) -> Unit,
|
onSongSelected: (String) -> Unit,
|
||||||
onSettingsCheck: () -> Unit,
|
onSettingsCheck: () -> Unit,
|
||||||
onAboutCheck: () -> Unit,
|
onAboutCheck: () -> Unit,
|
||||||
|
|
@ -195,7 +194,7 @@ fun SimpleDrawerContent(
|
||||||
|
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
DrawerHeaderSticky(
|
DrawerHeaderSticky(
|
||||||
title = "Personnel",
|
title = "Fichiers locaux",
|
||||||
icon = Icons.Default.LibraryMusic,
|
icon = Icons.Default.LibraryMusic,
|
||||||
isExpanded = externalExpanded,
|
isExpanded = externalExpanded,
|
||||||
onToggle = { externalExpanded = !externalExpanded },
|
onToggle = { externalExpanded = !externalExpanded },
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,11 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
transpositionInterval.value = interval
|
transpositionInterval.value = interval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val transpToCInt = MutableStateFlow(0)
|
||||||
|
fun setTranspToC(count: Int) {
|
||||||
|
transpToCInt.value = count
|
||||||
|
}
|
||||||
|
|
||||||
private val _synchronizedSYllables = MutableStateFlow<List<String>>(emptyList())
|
private val _synchronizedSYllables = MutableStateFlow<List<String>>(emptyList())
|
||||||
val synchronizedSyllables: StateFlow<List<String>> = _synchronizedSYllables.asStateFlow()
|
val synchronizedSyllables: StateFlow<List<String>> = _synchronizedSYllables.asStateFlow()
|
||||||
|
|
||||||
|
|
@ -728,6 +733,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
_sourceModeState.value = false
|
_sourceModeState.value = false
|
||||||
_harmonyView.value = false
|
_harmonyView.value = false
|
||||||
setTranspositionInterval(0)
|
setTranspositionInterval(0)
|
||||||
|
setTranspToC(0)
|
||||||
setSilentDurBefr(0)
|
setSilentDurBefr(0)
|
||||||
_midiMarkersList.value = emptyList()
|
_midiMarkersList.value = emptyList()
|
||||||
_tuoTimestamps.value = emptyList()
|
_tuoTimestamps.value = emptyList()
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class SolfaScreenModel(
|
||||||
solfa.createNewSolfa(metadata)
|
solfa.createNewSolfa(metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun generateMidiFile(bpm: Float, path: String) {
|
fun generateMidiFile(bpm: Float, path: String, transpI: Int) {
|
||||||
solfa.generateMidiFile(bpm, path)
|
solfa.generateMidiFile(bpm, path, transpI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -405,7 +405,7 @@ actual class FMediaPlayer actual constructor(
|
||||||
}
|
}
|
||||||
// Farany
|
// Farany
|
||||||
finReg.containsMatchIn(mTrim) -> {
|
finReg.containsMatchIn(mTrim) -> {
|
||||||
val hasDcAfter = metadataList.any { m ->
|
val hasDcOrDsAfter = metadataList.any { m ->
|
||||||
val after = (m.gridIndex ?: 0) > currentIndex
|
val after = (m.gridIndex ?: 0) > currentIndex
|
||||||
|
|
||||||
println(
|
println(
|
||||||
|
|
@ -413,14 +413,15 @@ actual class FMediaPlayer actual constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
val hasMarker = m.marker.any { mk ->
|
val hasMarker = m.marker.any { mk ->
|
||||||
dcRegex.matches(mk.trim())
|
dcRegex.containsMatchIn(mk) || dsRegex.containsMatchIn(mk)
|
||||||
}
|
}
|
||||||
|
|
||||||
println("after=$after, hasMarker=$hasMarker, result=${after && hasMarker}")
|
println("after=$after, hasMarker=$hasMarker, result=${after && hasMarker}")
|
||||||
|
|
||||||
after && hasMarker
|
after && hasMarker
|
||||||
}
|
}
|
||||||
if (hasDcAfter) {
|
|
||||||
|
if (hasDcOrDsAfter) {
|
||||||
navigationSteps.add(
|
navigationSteps.add(
|
||||||
NavigationStep(
|
NavigationStep(
|
||||||
marker = marker,
|
marker = marker,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue