QR Share
This commit is contained in:
parent
27aabc139d
commit
581bf42b32
20 changed files with 1544 additions and 192 deletions
|
|
@ -13,6 +13,13 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
linuxX64 {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
androidTarget {
|
androidTarget {
|
||||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
|
|
@ -21,7 +28,20 @@ kotlin {
|
||||||
}
|
}
|
||||||
|
|
||||||
jvm("desktop")
|
jvm("desktop")
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
afterEvaluate {
|
||||||
|
tasks.configureEach {
|
||||||
|
if (
|
||||||
|
name.startsWith("compile")
|
||||||
|
&& name.endsWith("KotlinMetadata")
|
||||||
|
) {
|
||||||
|
println("disabling $name")
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val desktopMain by getting
|
val desktopMain by getting
|
||||||
|
|
||||||
|
|
@ -30,7 +50,11 @@ kotlin {
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.koin.android) // Koin Android-specific extensions
|
implementation(libs.koin.android) // Koin Android-specific extensions
|
||||||
implementation(libs.koin.androidx.compose)
|
implementation(libs.koin.androidx.compose)
|
||||||
|
implementation("com.google.zxing:core:3.5.4")
|
||||||
|
implementation("androidx.camera:camera-view:1.5.2")
|
||||||
|
implementation("androidx.camera:camera-core:1.5.2")
|
||||||
|
implementation("androidx.camera:camera-camera2:1.5.2")
|
||||||
|
implementation("androidx.camera:camera-lifecycle:1.5.2")
|
||||||
}
|
}
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(compose.components.resources)
|
implementation(compose.components.resources)
|
||||||
|
|
@ -57,6 +81,9 @@ kotlin {
|
||||||
implementation(libs.cafe.voyager.koin)
|
implementation(libs.cafe.voyager.koin)
|
||||||
implementation(libs.androidx.material.icons.extended)
|
implementation(libs.androidx.material.icons.extended)
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
|
implementation(compose.desktop.currentOs)
|
||||||
|
//implementation("org.jetbrains.compose.foundation:foundation-desktop")
|
||||||
|
|
||||||
//implementation(libs.ktmidi)
|
//implementation(libs.ktmidi)
|
||||||
}
|
}
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
|
|
@ -65,6 +92,8 @@ kotlin {
|
||||||
desktopMain.dependencies {
|
desktopMain.dependencies {
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
implementation(libs.kotlinx.coroutinesSwing)
|
implementation(libs.kotlinx.coroutinesSwing)
|
||||||
|
implementation("com.google.zxing:core:3.5.4")
|
||||||
|
implementation("com.google.zxing:javase:3.5.4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
package mg.dot.feufaro.data
|
||||||
|
|
||||||
|
private val LISTE_FICHIERS_SOLFA = listOf(
|
||||||
|
"480.txt",
|
||||||
|
"ews-10.txt",
|
||||||
|
"ews-11.txt",
|
||||||
|
"ews-12.txt",
|
||||||
|
"ews-13.txt",
|
||||||
|
"ews-14.txt",
|
||||||
|
"ews-15.txt",
|
||||||
|
"ews-16.txt",
|
||||||
|
"ews-179.txt",
|
||||||
|
"ews-17.txt",
|
||||||
|
"ews-18.txt",
|
||||||
|
"ews-190.txt",
|
||||||
|
"ews-19.txt",
|
||||||
|
"ews-1.txt",
|
||||||
|
"ews-20.txt",
|
||||||
|
"ews-21.txt",
|
||||||
|
"ews-22.txt",
|
||||||
|
"ews-23.txt",
|
||||||
|
"ews-24.txt",
|
||||||
|
"ews-25-1.txt",
|
||||||
|
"ews-25-2.txt",
|
||||||
|
"ews-26-1.txt",
|
||||||
|
"ews-26-2.txt",
|
||||||
|
"ews-27.txt",
|
||||||
|
"ews-28.txt",
|
||||||
|
"ews-29.txt",
|
||||||
|
"ews-2.txt",
|
||||||
|
"ews-30-1.txt",
|
||||||
|
"ews-30-2.txt",
|
||||||
|
"ews-31.txt",
|
||||||
|
"ews-32.txt",
|
||||||
|
"ews-33.txt",
|
||||||
|
"ews-34.txt",
|
||||||
|
"ews-351-1.txt",
|
||||||
|
"ews-35.txt",
|
||||||
|
"ews-36.txt",
|
||||||
|
"ews-37.txt",
|
||||||
|
"ews-38.txt",
|
||||||
|
"ews-39.txt",
|
||||||
|
"ews-3.txt",
|
||||||
|
"ews-40.txt",
|
||||||
|
"ews-41.txt",
|
||||||
|
"ews-42.txt",
|
||||||
|
"ews-43.txt",
|
||||||
|
"ews-443.txt",
|
||||||
|
"ews-44.txt",
|
||||||
|
"ews-456.txt",
|
||||||
|
"ews-45.txt",
|
||||||
|
"ews-46.txt",
|
||||||
|
"ews-47.txt",
|
||||||
|
"ews-4.txt",
|
||||||
|
"ews-5.txt",
|
||||||
|
"ews-66.txt",
|
||||||
|
"ews-6.txt",
|
||||||
|
"ews-7.txt",
|
||||||
|
"ews-89.txt",
|
||||||
|
"ews-8.txt",
|
||||||
|
"ews-999.txt",
|
||||||
|
"ews-9.txt",
|
||||||
|
"ff-33.txt",
|
||||||
|
"ff-43.txt",
|
||||||
|
"ff-7.txt",
|
||||||
|
"ff-8.txt",
|
||||||
|
"ff-9.txt",
|
||||||
|
"ffmp-16.txt",
|
||||||
|
"ffpm-100.txt",
|
||||||
|
"ffpm-101.txt",
|
||||||
|
"ffpm-103.txt",
|
||||||
|
"ffpm-105.txt",
|
||||||
|
"ffpm-106.txt",
|
||||||
|
"ffpm-110.txt",
|
||||||
|
"ffpm-111.txt",
|
||||||
|
"ffpm-118.txt",
|
||||||
|
"ffpm-12-1.txt",
|
||||||
|
"ffpm-12-2.txt",
|
||||||
|
"ffpm-123.txt",
|
||||||
|
"ffpm-126.txt",
|
||||||
|
"ffpm-127.txt",
|
||||||
|
"ffpm-129.txt",
|
||||||
|
"ffpm-1-2.txt",
|
||||||
|
"ffpm-131.txt",
|
||||||
|
"ffpm-132.txt",
|
||||||
|
"ffpm-133.txt",
|
||||||
|
"ffpm-134.txt",
|
||||||
|
"ffpm-136.txt",
|
||||||
|
"ffpm-137.txt",
|
||||||
|
"ffpm-140-1.txt",
|
||||||
|
"ffpm-140-2.txt",
|
||||||
|
"ffpm-141.txt",
|
||||||
|
"ffpm-14.txt",
|
||||||
|
"ffpm-153.txt",
|
||||||
|
"ffpm-154.txt",
|
||||||
|
"ffpm-155.txt",
|
||||||
|
"ffpm-157.txt",
|
||||||
|
"ffpm-160.txt",
|
||||||
|
"ffpm-161.txt",
|
||||||
|
"ffpm-163.txt",
|
||||||
|
"ffpm-164.txt",
|
||||||
|
"ffpm-165.txt",
|
||||||
|
"ffpm-166-new.txt",
|
||||||
|
"ffpm-166.txt",
|
||||||
|
"ffpm-16.txt",
|
||||||
|
"ffpm-170.txt",
|
||||||
|
"ffpm-172-1.txt",
|
||||||
|
"ffpm-172-2.txt",
|
||||||
|
"ffpm-175.txt",
|
||||||
|
"ffpm-178.txt",
|
||||||
|
"ffpm-179.txt",
|
||||||
|
"ffpm-17.txt",
|
||||||
|
"ffpm-180.txt",
|
||||||
|
"ffpm-189.txt",
|
||||||
|
"ffpm-190.txt",
|
||||||
|
"ffpm-191.txt",
|
||||||
|
"ffpm-192.txt",
|
||||||
|
"ffpm-194.txt",
|
||||||
|
"ffpm-195-1.txt",
|
||||||
|
"ffpm-195-2.txt",
|
||||||
|
"ffpm-198.txt",
|
||||||
|
"ffpm-199.txt",
|
||||||
|
"ffpm-19.txt",
|
||||||
|
"ffpm-202.txt",
|
||||||
|
"ffpm-203.txt",
|
||||||
|
"ffpm-204.txt",
|
||||||
|
"ffpm-209.txt",
|
||||||
|
"ffpm-20.txt",
|
||||||
|
"ffpm-210.txt",
|
||||||
|
"ffpm-211.txt",
|
||||||
|
"ffpm-212.txt",
|
||||||
|
"ffpm-213.txt",
|
||||||
|
"ffpm-214.txt",
|
||||||
|
"ffpm-21.txt",
|
||||||
|
"ffpm-220.txt",
|
||||||
|
"ffpm-221.txt",
|
||||||
|
"ffpm-222.txt",
|
||||||
|
"ffpm-223.txt",
|
||||||
|
"ffpm-224.txt",
|
||||||
|
"ffpm-225.txt",
|
||||||
|
"ffpm-229.txt",
|
||||||
|
"ffpm-232.txt",
|
||||||
|
"ffpm-233.txt",
|
||||||
|
"ffpm-236.txt",
|
||||||
|
"ffpm-237.txt",
|
||||||
|
"ffpm-238-1.txt",
|
||||||
|
"ffpm-238-2.txt",
|
||||||
|
"ffpm-239-1.txt",
|
||||||
|
"ffpm-239-2.txt",
|
||||||
|
"ffpm-249.txt",
|
||||||
|
"ffpm-250-1.txt",
|
||||||
|
"ffpm-250-2.txt",
|
||||||
|
"ffpm-251.txt",
|
||||||
|
"ffpm-253.txt",
|
||||||
|
"ffpm-254.txt",
|
||||||
|
"ffpm-255.txt",
|
||||||
|
"ffpm-256.txt",
|
||||||
|
"ffpm-257-1.txt",
|
||||||
|
"ffpm-257-2.txt",
|
||||||
|
"ffpm-259.txt",
|
||||||
|
"ffpm-260.txt",
|
||||||
|
"ffpm-263.txt",
|
||||||
|
"ffpm-269.txt",
|
||||||
|
"ffpm-270.txt",
|
||||||
|
"ffpm-271.txt",
|
||||||
|
"ffpm-272.txt",
|
||||||
|
"ffpm-273.txt",
|
||||||
|
"ffpm-274.txt",
|
||||||
|
"ffpm-276.txt",
|
||||||
|
"ffpm-279.txt",
|
||||||
|
"ffpm-281.txt",
|
||||||
|
"ffpm-297.txt",
|
||||||
|
"ffpm-307.txt",
|
||||||
|
"ffpm-310.txt",
|
||||||
|
"ffpm-311.txt",
|
||||||
|
"ffpm-315.txt",
|
||||||
|
"ffpm-321.txt",
|
||||||
|
"ffpm-32.txt",
|
||||||
|
"ffpm-332.txt",
|
||||||
|
"ffpm-33.txt",
|
||||||
|
"ffpm-352.txt",
|
||||||
|
"ffpm-353-1.txt",
|
||||||
|
"ffpm-353-2.txt",
|
||||||
|
"ffpm-357.txt",
|
||||||
|
"ffpm-358.txt",
|
||||||
|
"ffpm-35.txt",
|
||||||
|
"ffpm-367.txt",
|
||||||
|
"ffpm-381-1.txt",
|
||||||
|
"ffpm-381-2.txt",
|
||||||
|
"ffpm-38.txt",
|
||||||
|
"ffpm-392.txt",
|
||||||
|
"ffpm-408-1.txt",
|
||||||
|
"ffpm-408-2.txt",
|
||||||
|
"ffpm-413.txt",
|
||||||
|
"ffpm-4-1.txt",
|
||||||
|
"ffpm-42-1.txt",
|
||||||
|
"ffpm-42-2.txt",
|
||||||
|
"ffpm-428.txt",
|
||||||
|
"ffpm-4-2.txt",
|
||||||
|
"ffpm-43.txt",
|
||||||
|
"ffpm-440-1.txt",
|
||||||
|
"ffpm-444-1.txt",
|
||||||
|
"ffpm-444-2.txt",
|
||||||
|
"ffpm-449.txt",
|
||||||
|
"ffpm-44.txt",
|
||||||
|
"ffpm-46.txt",
|
||||||
|
"ffpm-47.txt",
|
||||||
|
"ffpm-483.txt",
|
||||||
|
"ffpm-489.txt",
|
||||||
|
"ffpm-490.txt",
|
||||||
|
"ffpm-49-1.txt",
|
||||||
|
"ffpm-49-2.txt",
|
||||||
|
"ffpm-501.txt",
|
||||||
|
"ffpm-503.txt",
|
||||||
|
"ffpm-5-1.txt",
|
||||||
|
"ffpm-51.txt",
|
||||||
|
"ffpm-5-2.txt",
|
||||||
|
"ffpm-52.txt",
|
||||||
|
"ffpm-539.txt",
|
||||||
|
"ffpm-546.txt",
|
||||||
|
"ffpm-549.txt",
|
||||||
|
"ffpm-54.txt",
|
||||||
|
"ffpm-558.txt",
|
||||||
|
"ffpm-55.txt",
|
||||||
|
"ffpm-563.txt",
|
||||||
|
"ffpm-564.txt",
|
||||||
|
"ffpm-56-new.txt",
|
||||||
|
"ffpm-56.txt",
|
||||||
|
"ffpm-57-new.txt",
|
||||||
|
"ffpm-57.txt",
|
||||||
|
"ffpm-58.txt",
|
||||||
|
"ffpm-603-1.txt",
|
||||||
|
"ffpm-603-2.txt",
|
||||||
|
"ffpm-610.txt",
|
||||||
|
"ffpm-611-1.txt",
|
||||||
|
"ffpm-611-2.txt",
|
||||||
|
"ffpm-616.txt",
|
||||||
|
"ffpm-61.txt",
|
||||||
|
"ffpm-626.txt",
|
||||||
|
"ffpm-636.txt",
|
||||||
|
"ffpm-640.txt",
|
||||||
|
"ffpm-642-1.txt",
|
||||||
|
"ffpm-642-2.txt",
|
||||||
|
"ffpm-653.txt",
|
||||||
|
"ffpm-674.txt",
|
||||||
|
"ffpm-69.txt",
|
||||||
|
"ffpm-6.txt",
|
||||||
|
"ffpm-705-2.txt",
|
||||||
|
"ffpm-71.txt",
|
||||||
|
"ffpm-725.txt",
|
||||||
|
"ffpm-726-1.txt",
|
||||||
|
"ffpm-726-2.txt",
|
||||||
|
"ffpm-729.txt",
|
||||||
|
"ffpm-72.txt",
|
||||||
|
"ffpm-733.txt",
|
||||||
|
"ffpm-734.txt",
|
||||||
|
"ffpm-735.txt",
|
||||||
|
"ffpm-737.txt",
|
||||||
|
"ffpm-750.txt",
|
||||||
|
"ffpm-75.txt",
|
||||||
|
"ffpm-760.txt",
|
||||||
|
"ffpm-79-1.txt",
|
||||||
|
"ffpm-79-2.txt",
|
||||||
|
"ffpm-796.txt",
|
||||||
|
"ffpm-799.txt",
|
||||||
|
"ffpm-7.txt",
|
||||||
|
"ffpm-82-1.txt",
|
||||||
|
"ffpm-82-2.txt",
|
||||||
|
"ffpm-825.txt",
|
||||||
|
"ffpm-87.txt",
|
||||||
|
"ffpm-89.txt",
|
||||||
|
"ffpm-8.txt",
|
||||||
|
"ffpm-91.txt",
|
||||||
|
"ffpm-93.txt",
|
||||||
|
"ffpm-94.txt",
|
||||||
|
"ffpm-97-1.txt",
|
||||||
|
"ffpm-97-2.txt",
|
||||||
|
"ffpm-9.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val ASSET_PREFIX = "assets://"
|
||||||
|
|
||||||
|
actual fun getDrawerItems(): List<DrawerItem> {
|
||||||
|
return LISTE_FICHIERS_SOLFA.mapIndexed { index, fileName ->
|
||||||
|
val titleWithoutExtension = fileName.removeSuffix(".txt")
|
||||||
|
|
||||||
|
DrawerItem(
|
||||||
|
id = index + 1,
|
||||||
|
title = titleWithoutExtension,
|
||||||
|
path = ASSET_PREFIX + fileName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package mg.dot.feufaro.midi
|
||||||
|
|
||||||
|
import android.media.midi.*
|
||||||
|
import mg.dot.feufaro.FileRepository
|
||||||
|
|
||||||
|
actual class MidiWriterKotlin actual constructor(private val fileRepository: FileRepository) {
|
||||||
|
private val midiEvents = mutableListOf<String>()
|
||||||
|
actual fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
||||||
|
// TODO: Implémentation Android pour ajouter des notes
|
||||||
|
midiEvents.add("Note: $note on tick $tick")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun save(filePath: String) {
|
||||||
|
// TODO: Implémentation Android pour écrire le fichier MIDI
|
||||||
|
println("Sauvegarde MIDI sur Android (Non implémenté complètement)")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) {
|
||||||
|
// TODO: Implémentation Android pour ajouter des Meta Messages
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun process(pitches: List<MidiPitch>) {
|
||||||
|
// TODO: Implémentation Android pour traiter les pitches
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import com.google.zxing.BarcodeFormat
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Color
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
|
actual fun generateQRCode(content: String, size: Int): ImageBitmap? {
|
||||||
|
if (content.isNullOrBlank()) return null
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, size, size)
|
||||||
|
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565)
|
||||||
|
|
||||||
|
for (x in 0 until size) {
|
||||||
|
for (y in 0 until size) {
|
||||||
|
bitmap.setPixel(x, y, if (bitMatrix[x, y]) Color.BLACK else Color.WHITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bitmap.asImageBitmap()
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Erreur de génération du QR Code Android: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun ScrollableDrawerContent(
|
||||||
|
modifier: Modifier,
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,7 @@ import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import mg.dot.feufaro.solfa.LazyVerticalGridTUO
|
import mg.dot.feufaro.solfa.LazyVerticalGridTUO
|
||||||
|
import mg.dot.feufaro.ui.MainScreenWithDrawer
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.koin.koinScreenModel
|
import cafe.adriel.voyager.koin.koinScreenModel
|
||||||
|
|
@ -61,82 +62,105 @@ object ScreenSolfa : Screen {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
Box(
|
var isScanning by remember { mutableStateOf(false) }
|
||||||
Modifier.fillMaxSize()
|
var qrCodeResult by remember { mutableStateOf("Aucun code scanné") }
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
Modifier
|
|
||||||
.verticalScroll(scrollState)
|
|
||||||
.pointerInput(Unit) {
|
|
||||||
detectTapGestures(
|
|
||||||
onDoubleTap = { offset ->
|
|
||||||
menuPosition = offset
|
|
||||||
showContextualMenu = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
|
||||||
if (showContextualMenu) {
|
|
||||||
Popup(
|
|
||||||
alignment = Alignment.TopStart,
|
|
||||||
offset = IntOffset(
|
|
||||||
menuPosition.x.roundToInt(),
|
|
||||||
menuPosition.y.roundToInt()
|
|
||||||
),
|
|
||||||
onDismissRequest = {
|
|
||||||
showContextualMenu = false
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
ContextualMenu(onMenuItemClick = { item ->
|
|
||||||
println("Clicked: $item")
|
|
||||||
showContextualMenu = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
FlowRow(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
|
||||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
||||||
) {
|
|
||||||
val measureString: String by sharedScreenModel.measure.collectAsState()
|
|
||||||
val songTitle: String by sharedScreenModel.songTitle.collectAsState()
|
|
||||||
val songKey: String by sharedScreenModel.songKey.collectAsState()
|
|
||||||
Text(text = songTitle, fontWeight = FontWeight.Bold)
|
|
||||||
Text(
|
|
||||||
text = songKey,
|
|
||||||
modifier = Modifier.background(Color(0xff, 0xea, 0xe7))
|
|
||||||
.padding(horizontal = 4.dp)
|
|
||||||
)
|
|
||||||
Text(text = measureString)
|
|
||||||
Text(text = "Stanza: $stanza")
|
|
||||||
ScreenTranspose.Content()
|
|
||||||
}
|
|
||||||
LazyVerticalGridTUO(
|
|
||||||
gridTUOData,
|
|
||||||
gridWidthPx = gridWidthPx,
|
|
||||||
onGridWidthMeasured = { width -> gridWidthPx = width }
|
|
||||||
)
|
|
||||||
FlowRow(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
||||||
) {
|
|
||||||
MGButton(onClick = {
|
|
||||||
//showContent = !showContent
|
|
||||||
|
|
||||||
solfaScreenModel.loadNextInPlaylist()
|
/* if (isScanning) {
|
||||||
}, modifier = Modifier.height(40.dp)) {
|
ScannerScreen(
|
||||||
val nextLabel: String by sharedScreenModel.nextLabel.collectAsState()
|
onScanComplete = { result ->
|
||||||
Text(nextLabel)
|
qrCodeResult = result
|
||||||
|
isScanning = false
|
||||||
|
},
|
||||||
|
onClose = {
|
||||||
|
isScanning = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {*/
|
||||||
|
|
||||||
|
MainScreenWithDrawer(
|
||||||
|
solfaScreenModel,
|
||||||
|
sharedScreenModel,
|
||||||
|
qrCodeResult = qrCodeResult,
|
||||||
|
onScannerButtonClick = {
|
||||||
|
isScanning = true
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
Box(
|
||||||
|
Modifier.fillMaxSize().padding(paddingValues)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onDoubleTap = { offset ->
|
||||||
|
menuPosition = offset
|
||||||
|
showContextualMenu = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
if (showContextualMenu) {
|
||||||
|
Popup(
|
||||||
|
alignment = Alignment.TopStart,
|
||||||
|
offset = IntOffset(
|
||||||
|
menuPosition.x.roundToInt(),
|
||||||
|
menuPosition.y.roundToInt()
|
||||||
|
),
|
||||||
|
onDismissRequest = {
|
||||||
|
showContextualMenu = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
ContextualMenu(onMenuItemClick = { item ->
|
||||||
|
println("Clicked: $item")
|
||||||
|
showContextualMenu = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
MGButton(onClick = {
|
}
|
||||||
/*println("Load btn clicked")
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
FlowRow(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||||
|
.padding(start = 8.dp, end = 8.dp, top = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
val measureString: String by sharedScreenModel.measure.collectAsState()
|
||||||
|
val songTitle: String by sharedScreenModel.songTitle.collectAsState()
|
||||||
|
val songKey: String by sharedScreenModel.songKey.collectAsState()
|
||||||
|
Text(text = songTitle, fontWeight = FontWeight.Bold)
|
||||||
|
Text(
|
||||||
|
text = songKey,
|
||||||
|
modifier = Modifier.background(Color(0xff, 0xea, 0xe7))
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
)
|
||||||
|
Text(text = measureString)
|
||||||
|
Text(text = "Stanza: $stanza")
|
||||||
|
ScreenTranspose.Content()
|
||||||
|
}
|
||||||
|
LazyVerticalGridTUO(
|
||||||
|
gridTUOData,
|
||||||
|
gridWidthPx = gridWidthPx,
|
||||||
|
onGridWidthMeasured = { width -> gridWidthPx = width }
|
||||||
|
)
|
||||||
|
FlowRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
MGButton(onClick = {
|
||||||
|
//showContent = !showContent
|
||||||
|
|
||||||
|
solfaScreenModel.loadNextInPlaylist()
|
||||||
|
}, modifier = Modifier.height(40.dp)) {
|
||||||
|
val nextLabel: String by sharedScreenModel.nextLabel.collectAsState()
|
||||||
|
Text(nextLabel)
|
||||||
|
}
|
||||||
|
MGButton(onClick = {
|
||||||
|
/*println("Load btn clicked")
|
||||||
launchFilePicker(
|
launchFilePicker(
|
||||||
mimeTypes = arrayOf("text/plain"),
|
mimeTypes = arrayOf("text/plain"),
|
||||||
onFileSelected = { path ->
|
onFileSelected = { path ->
|
||||||
|
|
@ -147,48 +171,49 @@ object ScreenSolfa : Screen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*/
|
)*/
|
||||||
solfaScreenModel.loadCustomFile()
|
solfaScreenModel.loadCustomFile()
|
||||||
}) {
|
}) {
|
||||||
val loadFile: String by sharedScreenModel.loadFile.collectAsState()
|
val loadFile: String by sharedScreenModel.loadFile.collectAsState()
|
||||||
Text(loadFile)
|
Text(loadFile)
|
||||||
}
|
|
||||||
}
|
|
||||||
FlowRow(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
|
||||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
||||||
) {
|
|
||||||
val nbStanzas: Int by sharedScreenModel.nbStanzas.collectAsState()
|
|
||||||
for (i in 1..nbStanzas) {
|
|
||||||
MGButton(
|
|
||||||
enabled = (i != stanza.toInt()),
|
|
||||||
onClick = {
|
|
||||||
sharedScreenModel.setStanza(i)
|
|
||||||
coroutineScope.launch {
|
|
||||||
scrollState.animateScrollTo(0)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(horizontal = 4.dp)
|
|
||||||
) {
|
|
||||||
Text("$i")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val songAuthor: String by sharedScreenModel.songAuthor.collectAsState()
|
FlowRow(
|
||||||
val songComposer: String by sharedScreenModel.songComposer.collectAsState()
|
modifier = Modifier.fillMaxWidth()
|
||||||
val songRhythm: String by sharedScreenModel.songRhythm.collectAsState()
|
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||||
Text(text = songAuthor, fontStyle = FontStyle.Italic)
|
.padding(start = 8.dp, end = 8.dp, top = 8.dp),
|
||||||
Text(text = songRhythm)
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
Text(text = songComposer)
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
val nbStanzas: Int by sharedScreenModel.nbStanzas.collectAsState()
|
||||||
|
for (i in 1..nbStanzas) {
|
||||||
|
MGButton(
|
||||||
|
enabled = (i != stanza.toInt()),
|
||||||
|
onClick = {
|
||||||
|
sharedScreenModel.setStanza(i)
|
||||||
|
coroutineScope.launch {
|
||||||
|
scrollState.animateScrollTo(0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(horizontal = 4.dp)
|
||||||
|
) {
|
||||||
|
Text("$i")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val songAuthor: String by sharedScreenModel.songAuthor.collectAsState()
|
||||||
|
val songComposer: String by sharedScreenModel.songComposer.collectAsState()
|
||||||
|
val songRhythm: String by sharedScreenModel.songRhythm.collectAsState()
|
||||||
|
Text(text = songAuthor, fontStyle = FontStyle.Italic)
|
||||||
|
Text(text = songRhythm)
|
||||||
|
Text(text = songComposer)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MyVerticalScrollbar(
|
||||||
|
scrollState,
|
||||||
|
modifier = Modifier.align(Alignment.CenterEnd)
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
MyVerticalScrollbar(
|
|
||||||
scrollState,
|
|
||||||
modifier = Modifier.align(Alignment.CenterEnd)
|
|
||||||
) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package mg.dot.feufaro.data
|
||||||
|
|
||||||
|
data class DrawerItem(
|
||||||
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val path: String
|
||||||
|
)
|
||||||
|
|
||||||
|
expect fun getDrawerItems(): List<DrawerItem>
|
||||||
|
|
@ -1,71 +1,10 @@
|
||||||
package mg.dot.feufaro.midi
|
package mg.dot.feufaro.midi
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
import javax.sound.midi.*
|
|
||||||
|
|
||||||
class MidiWriterKotlin (private val fileRepository: FileRepository) {
|
expect class MidiWriterKotlin(fileRepository: FileRepository) {
|
||||||
private val sequence = Sequence(Sequence.PPQ, 60)
|
fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long)
|
||||||
private val track = sequence.createTrack()
|
fun save(filePath: String)
|
||||||
private var tick: Long = 0
|
fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String)
|
||||||
private var nextTick: MutableList<Int> = mutableListOf()
|
fun process(pitches: List<MidiPitch>)
|
||||||
private val noteOn = ShortMessage()
|
|
||||||
private val noteOff = ShortMessage()
|
|
||||||
private val lastPitch : MutableList<Int> = mutableListOf()
|
|
||||||
private val useChord : Boolean = true
|
|
||||||
fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
|
||||||
var channel: Int = voiceNumber - 1
|
|
||||||
if (useChord) {
|
|
||||||
channel = channel / 2
|
|
||||||
}
|
|
||||||
var note = note
|
|
||||||
if (voiceNumber == 3 || voiceNumber == 4) {
|
|
||||||
note -= 12
|
|
||||||
}
|
|
||||||
if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) {
|
|
||||||
noteOff.setMessage(ShortMessage.NOTE_OFF, channel, lastPitch[voiceNumber], 0)
|
|
||||||
val n2 = noteOff.clone() as MidiMessage
|
|
||||||
track.add(MidiEvent(n2, tick))
|
|
||||||
}
|
|
||||||
var velocity = velocity
|
|
||||||
if (note <= 0) {
|
|
||||||
note = 40
|
|
||||||
velocity = 0
|
|
||||||
}
|
|
||||||
noteOn.setMessage(ShortMessage.NOTE_ON, channel, note, velocity)
|
|
||||||
val n1: MidiMessage = noteOn.clone() as MidiMessage
|
|
||||||
track.add(MidiEvent(n1, tick))
|
|
||||||
while(lastPitch.size <= voiceNumber) {
|
|
||||||
lastPitch.add(0)
|
|
||||||
}
|
|
||||||
lastPitch[voiceNumber] = note
|
|
||||||
}
|
|
||||||
fun save(filePath: String) {
|
|
||||||
val parseScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
parseScope.launch {
|
|
||||||
val out = fileRepository.getOutputStream(filePath)
|
|
||||||
MidiSystem.write(sequence, 1, out)
|
|
||||||
out.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) {
|
|
||||||
val byteArray = metaByteString.toByteArray()
|
|
||||||
val metaMessage = MetaMessage(type, byteArray, nbData)
|
|
||||||
track.add(MidiEvent(metaMessage, tick.toLong()))
|
|
||||||
}
|
|
||||||
fun process(pitches: List<MidiPitch>) {
|
|
||||||
val lastTick = 0
|
|
||||||
nextTick.clear()
|
|
||||||
// addMetaMessage(0x59, 4, 2, 2,0)
|
|
||||||
tick = 0
|
|
||||||
pitches.forEach {
|
|
||||||
if (it.metaType > 0) {
|
|
||||||
addMetaMessage(it.metaType, it.tick, it.metaByteSize, it.metaBytes)
|
|
||||||
} else if (it.pitch != "") {
|
|
||||||
addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,10 +2,12 @@ package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
import SharedScreenModel
|
import SharedScreenModel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import mg.dot.feufaro.getConfigDirectoryPath
|
||||||
import mg.dot.feufaro.launchFilePicker
|
import mg.dot.feufaro.launchFilePicker
|
||||||
import mg.dot.feufaro.midi.MidiPitch
|
import mg.dot.feufaro.midi.MidiPitch
|
||||||
import mg.dot.feufaro.midi.MidiWriterKotlin
|
import mg.dot.feufaro.midi.MidiWriterKotlin
|
||||||
|
|
@ -144,20 +146,28 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFile() {
|
fun loadFile() {
|
||||||
println("Load btn clicked")
|
val screenModelScope= CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||||
launchFilePicker(
|
screenModelScope.launch {
|
||||||
mimeTypes = arrayOf("text/plain"),
|
val initialPath = fileRepository.readLastDirectoryPath()
|
||||||
onFileSelected = { path ->
|
val homedir = getConfigDirectoryPath();
|
||||||
if (path != null) {
|
|
||||||
println("fichier $path");
|
launchFilePicker(
|
||||||
sharedScreenModel.reset()
|
mimeTypes = arrayOf("text/plain"),
|
||||||
parse(path)
|
onFileSelected = { path ->
|
||||||
//loadSolfa()
|
if (path != null) {
|
||||||
} else {
|
println("fichier $path");
|
||||||
println("Pas de dichier")
|
sharedScreenModel.reset()
|
||||||
|
parse(path)
|
||||||
|
screenModelScope.launch {
|
||||||
|
fileRepository.saveLastDirectoryPath(path)
|
||||||
|
}
|
||||||
|
//loadSolfa()
|
||||||
|
} else {
|
||||||
|
println("Pas de dichier")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parse(sourceFile: String) {
|
fun parse(sourceFile: String) {
|
||||||
|
|
@ -170,6 +180,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
println("Opening $sourceFile raised exception {${e.message}")
|
println("Opening $sourceFile raised exception {${e.message}")
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
val contentString = fileRepository.readFileLines(sourceFile).joinToString("\n")
|
||||||
|
sharedScreenModel.setFileContent(contentString, sourceFile)
|
||||||
T.clear()
|
T.clear()
|
||||||
N.clear()
|
N.clear()
|
||||||
L.clear()
|
L.clear()
|
||||||
|
|
|
||||||
373
composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt
Normal file
373
composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import SharedScreenModel
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
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.material.icons.Icons
|
||||||
|
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.alpha
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
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 kotlinx.coroutines.*
|
||||||
|
import mg.dot.feufaro.data.DrawerItem
|
||||||
|
import mg.dot.feufaro.data.getDrawerItems
|
||||||
|
import mg.dot.feufaro.getPlatform
|
||||||
|
import mg.dot.feufaro.solfa.Solfa
|
||||||
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun MainScreenWithDrawer(
|
||||||
|
solfaScreenModel: SolfaScreenModel,
|
||||||
|
sharedScreenModel: SharedScreenModel,
|
||||||
|
qrCodeResult: String,
|
||||||
|
onScannerButtonClick: () -> Unit,
|
||||||
|
content: @Composable (PaddingValues) -> Unit,
|
||||||
|
) {
|
||||||
|
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val items = getDrawerItems()
|
||||||
|
|
||||||
|
val songTitle = sharedScreenModel.songTitle.collectAsState().value
|
||||||
|
val filteredSongs by sharedScreenModel.filteredSongs.collectAsState()
|
||||||
|
val songKey = sharedScreenModel.songKey.collectAsState().value
|
||||||
|
val measure = sharedScreenModel.measure.collectAsState().value
|
||||||
|
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
val currentActivePath = Solfa.currentFile
|
||||||
|
var textInput = sharedScreenModel.searchTitle.collectAsState().value
|
||||||
|
|
||||||
|
var isSearchActive by remember { mutableStateOf(false) }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
LaunchedEffect(isSearchActive) {
|
||||||
|
if (isSearchActive) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModalNavigationDrawer(
|
||||||
|
drawerState = drawerState,
|
||||||
|
drawerContent = {
|
||||||
|
SimpleDrawerContent(
|
||||||
|
items, solfaScreenModel, sharedScreenModel, currentActivePath, drawerState, scope,
|
||||||
|
onScannerButtonClick = {
|
||||||
|
scope.launch { drawerState.close() }
|
||||||
|
onScannerButtonClick()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
Scaffold(
|
||||||
|
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(55.dp)
|
||||||
|
.windowInsetsPadding(WindowInsets.statusBars),
|
||||||
|
title = {
|
||||||
|
Column (
|
||||||
|
modifier= Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
) {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = isSearchActive,
|
||||||
|
label = "Search Transition"
|
||||||
|
) {
|
||||||
|
targetIsActive ->
|
||||||
|
if(targetIsActive) {
|
||||||
|
TextField(
|
||||||
|
value = textInput,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
textInput = newValue
|
||||||
|
sharedScreenModel.updateSearchTxt(newValue)
|
||||||
|
},
|
||||||
|
placeholder = {
|
||||||
|
Text("Rechercher...")
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = "Icône de recherche",
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
},
|
||||||
|
textStyle = MaterialTheme.typography.titleLarge.copy(
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.focusRequester(focusRequester)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(songTitle,
|
||||||
|
modifier = Modifier.weight(1f, fill = true),
|
||||||
|
maxLines = 1,
|
||||||
|
softWrap = false,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
scope.launch { drawerState.open() }
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
if (isSearchActive) {
|
||||||
|
IconButton(onClick = {
|
||||||
|
textInput = ""
|
||||||
|
isSearchActive = false
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Default.Close, contentDescription = "Annuler la recherche")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = measure,
|
||||||
|
modifier = Modifier.weight(1f, fill = false),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
maxLines = 2,
|
||||||
|
softWrap = false,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = songKey,
|
||||||
|
fontSize = 25.sp,
|
||||||
|
fontWeight = FontWeight.Black,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
IconButton(onClick = {
|
||||||
|
isSearchActive = true
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Default.Search, contentDescription = "Rechercher")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
colors = TopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton (
|
||||||
|
onClick = {
|
||||||
|
sharedScreenModel.toggleQRCodeVisibility()
|
||||||
|
},
|
||||||
|
modifier = Modifier.alpha(0.5f)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Share,
|
||||||
|
contentDescription = "Partager",
|
||||||
|
tint = Color.Blue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
paddingValues ->
|
||||||
|
|
||||||
|
Box(modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues)
|
||||||
|
.windowInsetsPadding(WindowInsets.ime)
|
||||||
|
){
|
||||||
|
content(PaddingValues(0.dp))
|
||||||
|
if (sharedScreenModel.isQRCodeVisible.value) {
|
||||||
|
QRDisplay(sharedScreenModel = sharedScreenModel)
|
||||||
|
} else {
|
||||||
|
if (filteredSongs.isNotEmpty()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(0.65f)
|
||||||
|
.heightIn(max = 350.dp)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, shape = MaterialTheme.shapes.medium)
|
||||||
|
){
|
||||||
|
if (filteredSongs.isNotEmpty()) {
|
||||||
|
LazyColumn(Modifier.fillMaxSize()) {
|
||||||
|
itemsIndexed(filteredSongs){ index, item ->
|
||||||
|
ListItem(
|
||||||
|
headlineContent = { Text(item.title) },
|
||||||
|
supportingContent = { Text(item.path, maxLines = 1) },
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
sharedScreenModel.updateSearchTxt("")
|
||||||
|
sharedScreenModel.reset()
|
||||||
|
solfaScreenModel.loadFromFile(item.path)
|
||||||
|
isSearchActive = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (songTitle.trim().isNotEmpty() && songTitle != sharedScreenModel.songTitle.value) {
|
||||||
|
Text(
|
||||||
|
text = "Aucune chanson trouvée pour \"$songTitle\"",
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
content(paddingValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SimpleDrawerContent(
|
||||||
|
items: List<DrawerItem>,
|
||||||
|
solfaScreenModel: SolfaScreenModel,
|
||||||
|
sharedScreenModel: SharedScreenModel,
|
||||||
|
activePath: String = Solfa.currentFile,
|
||||||
|
drawerState: DrawerState,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
onScannerButtonClick: () -> Unit
|
||||||
|
) {
|
||||||
|
var searchTxt by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
val context = getPlatform()
|
||||||
|
|
||||||
|
|
||||||
|
ModalDrawerSheet(
|
||||||
|
modifier = Modifier.width(300.dp)
|
||||||
|
) {
|
||||||
|
Box (
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
ScrollableDrawerContent(
|
||||||
|
lazyListState = lazyListState,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
|
){
|
||||||
|
stickyHeader {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
modifier = Modifier.fillParentMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Liste des solfa disponibles",
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemsIndexed(items){ index, item ->
|
||||||
|
val isSelected = item.path == activePath
|
||||||
|
val title = item.title
|
||||||
|
var isFfpm = false
|
||||||
|
var isEws = false
|
||||||
|
var isFF = false
|
||||||
|
|
||||||
|
if (title.startsWith("ffpm")) {
|
||||||
|
isFfpm = true
|
||||||
|
} else if(title.startsWith("ews")) {
|
||||||
|
isEws = true
|
||||||
|
} else {
|
||||||
|
isFF = true
|
||||||
|
}
|
||||||
|
NavigationDrawerItem(
|
||||||
|
label = {
|
||||||
|
Text(item.title)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
val isIcon = when {
|
||||||
|
isFfpm -> Icons.Filled.MenuBook
|
||||||
|
isEws -> Icons.Filled.MusicNote
|
||||||
|
isFF -> Icons.Filled.Book
|
||||||
|
else -> Icons.Filled.Menu
|
||||||
|
}
|
||||||
|
Icon(
|
||||||
|
isIcon,
|
||||||
|
contentDescription = "",
|
||||||
|
tint = Color.Blue
|
||||||
|
)
|
||||||
|
},
|
||||||
|
badge = {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Menu,
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selected = if(isSelected){ true } else { false },
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
drawerState.close()
|
||||||
|
}
|
||||||
|
sharedScreenModel.reset()
|
||||||
|
solfaScreenModel.loadFromFile(item.path)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.padding(16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onScannerButtonClick()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Scanner")
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
drawerState.close()
|
||||||
|
}
|
||||||
|
solfaScreenModel.loadCustomFile()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Importer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import SharedScreenModel
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun QRDisplay(sharedScreenModel: SharedScreenModel) {
|
||||||
|
|
||||||
|
val content by sharedScreenModel.fileContent
|
||||||
|
|
||||||
|
val qrCodeImage = remember(content) {
|
||||||
|
if (content != null) {
|
||||||
|
generateQRCode(content!!, size = 800)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.5f)) // Fond semi-transparent
|
||||||
|
.clickable { sharedScreenModel.toggleQRCodeVisibility() } // Fermer au clic
|
||||||
|
.imePadding()
|
||||||
|
.systemBarsPadding(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.wrapContentSize().shadow(16.dp, shape = MaterialTheme.shapes.large)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(24.dp)
|
||||||
|
) {
|
||||||
|
Text("Scanner ceci: ")
|
||||||
|
if (qrCodeImage != null) {
|
||||||
|
Image(
|
||||||
|
bitmap = qrCodeImage,
|
||||||
|
contentDescription = "Code QR du fichier actif",
|
||||||
|
modifier = Modifier.size(400.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text("Chargement du Code QR ou contenu vide...", modifier = Modifier.padding(16.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
|
||||||
|
expect fun generateQRCode(content: String, size: Int): ImageBitmap?
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
expect fun ScrollableDrawerContent(
|
||||||
|
modifier: Modifier,
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
)
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
// commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt
|
// commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import mg.dot.feufaro.data.DrawerItem
|
||||||
import mg.dot.feufaro.solfa.TimeUnitObject
|
import mg.dot.feufaro.solfa.TimeUnitObject
|
||||||
|
import mg.dot.feufaro.data.getDrawerItems
|
||||||
|
|
||||||
class SharedScreenModel() : ScreenModel {
|
class SharedScreenModel() : ScreenModel {
|
||||||
private val _nextLabel = MutableStateFlow<String>("Next ...")
|
private val _nextLabel = MutableStateFlow<String>("Next ...")
|
||||||
|
|
@ -44,7 +53,44 @@ class SharedScreenModel() : ScreenModel {
|
||||||
var _hasMarker = MutableStateFlow<Boolean>(false)
|
var _hasMarker = MutableStateFlow<Boolean>(false)
|
||||||
val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()
|
val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()
|
||||||
|
|
||||||
fun appendData(otherData: String) {
|
private val _searchTitle = MutableStateFlow<String>("")
|
||||||
|
val searchTitle: StateFlow<String> = _searchTitle.asStateFlow()
|
||||||
|
|
||||||
|
@OptIn
|
||||||
|
val filteredSongs: StateFlow<List<DrawerItem>> = searchTitle
|
||||||
|
.mapLatest { currentTitle ->
|
||||||
|
val searchTxt = if (currentTitle == "") "" else currentTitle.trim()
|
||||||
|
return@mapLatest if(searchTxt.isEmpty()) {
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
getDrawerItems().filter { item ->
|
||||||
|
item.title.contains(searchTxt, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.stateIn (
|
||||||
|
scope = CoroutineScope(Dispatchers.Default),
|
||||||
|
started = SharingStarted.WhileSubscribed(5000),
|
||||||
|
initialValue = emptyList()
|
||||||
|
)
|
||||||
|
private val _fileContent = mutableStateOf<String?>(null)
|
||||||
|
val fileContent: State<String?> = _fileContent
|
||||||
|
|
||||||
|
private val _isQRCodeVisible = mutableStateOf(false)
|
||||||
|
val isQRCodeVisible: State<Boolean> = _isQRCodeVisible
|
||||||
|
|
||||||
|
private val _activeFilePath = mutableStateOf("")
|
||||||
|
val activeFilePath: State<String> = _activeFilePath
|
||||||
|
|
||||||
|
fun setFileContent(content: String?, path: String) {
|
||||||
|
_fileContent.value = content
|
||||||
|
_activeFilePath.value = path
|
||||||
|
}
|
||||||
|
fun toggleQRCodeVisibility() {
|
||||||
|
_isQRCodeVisible.value = !_isQRCodeVisible.value
|
||||||
|
}
|
||||||
|
fun updateSearchTxt(searchValue: String) {
|
||||||
|
_searchTitle.value = searchValue
|
||||||
|
} fun appendData(otherData: String) {
|
||||||
_nextLabel.value += otherData
|
_nextLabel.value += otherData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,7 @@ class SolfaScreenModel(
|
||||||
fun loadCustomFile() {
|
fun loadCustomFile() {
|
||||||
solfa.loadFile()
|
solfa.loadFile()
|
||||||
}
|
}
|
||||||
|
fun loadFromFile(path: String) {
|
||||||
|
solfa.parse(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
package mg.dot.feufaro.data
|
||||||
|
|
||||||
|
actual fun getDrawerItems(): List<DrawerItem> {
|
||||||
|
return listOf(
|
||||||
|
DrawerItem(1, "480", "assets://480.txt"),
|
||||||
|
DrawerItem(2, "ews-10", "assets://ews-10.txt"),
|
||||||
|
DrawerItem(3, "ews-11", "assets://ews-11.txt"),
|
||||||
|
DrawerItem(4, "ews-12", "assets://ews-12.txt"),
|
||||||
|
DrawerItem(5, "ews-13", "assets://ews-13.txt"),
|
||||||
|
DrawerItem(6, "ews-14", "assets://ews-14.txt"),
|
||||||
|
DrawerItem(7, "ews-15", "assets://ews-15.txt"),
|
||||||
|
DrawerItem(8, "ews-16", "assets://ews-16.txt"),
|
||||||
|
DrawerItem(9, "ews-179", "assets://ews-179.txt"),
|
||||||
|
DrawerItem(10, "ews-17", "assets://ews-17.txt"),
|
||||||
|
DrawerItem(11, "ews-18", "assets://ews-18.txt"),
|
||||||
|
DrawerItem(12, "ews-190", "assets://ews-190.txt"),
|
||||||
|
DrawerItem(13, "ews-19", "assets://ews-19.txt"),
|
||||||
|
DrawerItem(14, "ews-1", "assets://ews-1.txt"),
|
||||||
|
DrawerItem(15, "ews-20", "assets://ews-20.txt"),
|
||||||
|
DrawerItem(16, "ews-21", "assets://ews-21.txt"),
|
||||||
|
DrawerItem(17, "ews-22", "assets://ews-22.txt"),
|
||||||
|
DrawerItem(18, "ews-23", "assets://ews-23.txt"),
|
||||||
|
DrawerItem(19, "ews-24", "assets://ews-24.txt"),
|
||||||
|
DrawerItem(20, "ews-25-1", "assets://ews-25-1.txt"),
|
||||||
|
DrawerItem(21, "ews-25-2", "assets://ews-25-2.txt"),
|
||||||
|
DrawerItem(22, "ews-26-1", "assets://ews-26-1.txt"),
|
||||||
|
DrawerItem(23, "ews-26-2", "assets://ews-26-2.txt"),
|
||||||
|
DrawerItem(24, "ews-27", "assets://ews-27.txt"),
|
||||||
|
DrawerItem(25, "ews-28", "assets://ews-28.txt"),
|
||||||
|
DrawerItem(26, "ews-29", "assets://ews-29.txt"),
|
||||||
|
DrawerItem(27, "ews-2", "assets://ews-2.txt"),
|
||||||
|
DrawerItem(28, "ews-30-1", "assets://ews-30-1.txt"),
|
||||||
|
DrawerItem(29, "ews-30-2", "assets://ews-30-2.txt"),
|
||||||
|
DrawerItem(30, "ews-31", "assets://ews-31.txt"),
|
||||||
|
DrawerItem(31, "ews-32", "assets://ews-32.txt"),
|
||||||
|
DrawerItem(32, "ews-33", "assets://ews-33.txt"),
|
||||||
|
DrawerItem(33, "ews-34", "assets://ews-34.txt"),
|
||||||
|
DrawerItem(34, "ews-351-1", "assets://ews-351-1.txt"),
|
||||||
|
DrawerItem(35, "ews-35", "assets://ews-35.txt"),
|
||||||
|
DrawerItem(36, "ews-36", "assets://ews-36.txt"),
|
||||||
|
DrawerItem(37, "ews-37", "assets://ews-37.txt"),
|
||||||
|
DrawerItem(38, "ews-38", "assets://ews-38.txt"),
|
||||||
|
DrawerItem(39, "ews-39", "assets://ews-39.txt"),
|
||||||
|
DrawerItem(40, "ews-3", "assets://ews-3.txt"),
|
||||||
|
DrawerItem(41, "ews-40", "assets://ews-40.txt"),
|
||||||
|
DrawerItem(42, "ews-41", "assets://ews-41.txt"),
|
||||||
|
DrawerItem(43, "ews-42", "assets://ews-42.txt"),
|
||||||
|
DrawerItem(44, "ews-43", "assets://ews-43.txt"),
|
||||||
|
DrawerItem(45, "ews-443", "assets://ews-443.txt"),
|
||||||
|
DrawerItem(46, "ews-44", "assets://ews-44.txt"),
|
||||||
|
DrawerItem(47, "ews-456", "assets://ews-456.txt"),
|
||||||
|
DrawerItem(48, "ews-45", "assets://ews-45.txt"),
|
||||||
|
DrawerItem(49, "ews-46", "assets://ews-46.txt"),
|
||||||
|
DrawerItem(50, "ews-47", "assets://ews-47.txt"),
|
||||||
|
DrawerItem(51, "ews-4", "assets://ews-4.txt"),
|
||||||
|
DrawerItem(52, "ews-5", "assets://ews-5.txt"),
|
||||||
|
DrawerItem(53, "ews-66", "assets://ews-66.txt"),
|
||||||
|
DrawerItem(54, "ews-6", "assets://ews-6.txt"),
|
||||||
|
DrawerItem(55, "ews-7", "assets://ews-7.txt"),
|
||||||
|
DrawerItem(56, "ews-89", "assets://ews-89.txt"),
|
||||||
|
DrawerItem(57, "ews-8", "assets://ews-8.txt"),
|
||||||
|
DrawerItem(58, "ews-999", "assets://ews-999.txt"),
|
||||||
|
DrawerItem(59, "ews-9", "assets://ews-9.txt"),
|
||||||
|
DrawerItem(60, "ff-33", "assets://ff-33.txt"),
|
||||||
|
DrawerItem(61, "ff-43", "assets://ff-43.txt"),
|
||||||
|
DrawerItem(62, "ff-7", "assets://ff-7.txt"),
|
||||||
|
DrawerItem(63, "ff-8", "assets://ff-8.txt"),
|
||||||
|
DrawerItem(64, "ff-9", "assets://ff-9.txt"),
|
||||||
|
DrawerItem(65, "ffmp-16", "assets://ffmp-16.txt"),
|
||||||
|
DrawerItem(66, "ffpm-100", "assets://ffpm-100.txt"),
|
||||||
|
DrawerItem(67, "ffpm-101", "assets://ffpm-101.txt"),
|
||||||
|
DrawerItem(68, "ffpm-103", "assets://ffpm-103.txt"),
|
||||||
|
DrawerItem(69, "ffpm-105", "assets://ffpm-105.txt"),
|
||||||
|
DrawerItem(70, "ffpm-106", "assets://ffpm-106.txt"),
|
||||||
|
DrawerItem(71, "ffpm-110", "assets://ffpm-110.txt"),
|
||||||
|
DrawerItem(72, "ffpm-111", "assets://ffpm-111.txt"),
|
||||||
|
DrawerItem(73, "ffpm-118", "assets://ffpm-118.txt"),
|
||||||
|
DrawerItem(74, "ffpm-12-1", "assets://ffpm-12-1.txt"),
|
||||||
|
DrawerItem(75, "ffpm-12-2", "assets://ffpm-12-2.txt"),
|
||||||
|
DrawerItem(76, "ffpm-123", "assets://ffpm-123.txt"),
|
||||||
|
DrawerItem(77, "ffpm-126", "assets://ffpm-126.txt"),
|
||||||
|
DrawerItem(78, "ffpm-127", "assets://ffpm-127.txt"),
|
||||||
|
DrawerItem(79, "ffpm-129", "assets://ffpm-129.txt"),
|
||||||
|
DrawerItem(80, "ffpm-1-2", "assets://ffpm-1-2.txt"),
|
||||||
|
DrawerItem(81, "ffpm-131", "assets://ffpm-131.txt"),
|
||||||
|
DrawerItem(82, "ffpm-132", "assets://ffpm-132.txt"),
|
||||||
|
DrawerItem(83, "ffpm-133", "assets://ffpm-133.txt"),
|
||||||
|
DrawerItem(84, "ffpm-134", "assets://ffpm-134.txt"),
|
||||||
|
DrawerItem(85, "ffpm-136", "assets://ffpm-136.txt"),
|
||||||
|
DrawerItem(86, "ffpm-137", "assets://ffpm-137.txt"),
|
||||||
|
DrawerItem(87, "ffpm-140-1", "assets://ffpm-140-1.txt"),
|
||||||
|
DrawerItem(88, "ffpm-140-2", "assets://ffpm-140-2.txt"),
|
||||||
|
DrawerItem(89, "ffpm-141", "assets://ffpm-141.txt"),
|
||||||
|
DrawerItem(90, "ffpm-14", "assets://ffpm-14.txt"),
|
||||||
|
DrawerItem(91, "ffpm-153", "assets://ffpm-153.txt"),
|
||||||
|
DrawerItem(92, "ffpm-154", "assets://ffpm-154.txt"),
|
||||||
|
DrawerItem(93, "ffpm-155", "assets://ffpm-155.txt"),
|
||||||
|
DrawerItem(94, "ffpm-157", "assets://ffpm-157.txt"),
|
||||||
|
DrawerItem(95, "ffpm-160", "assets://ffpm-160.txt"),
|
||||||
|
DrawerItem(96, "ffpm-161", "assets://ffpm-161.txt"),
|
||||||
|
DrawerItem(97, "ffpm-163", "assets://ffpm-163.txt"),
|
||||||
|
DrawerItem(98, "ffpm-164", "assets://ffpm-164.txt"),
|
||||||
|
DrawerItem(99, "ffpm-165", "assets://ffpm-165.txt"),
|
||||||
|
DrawerItem(100, "ffpm-166-new", "assets://ffpm-166-new.txt"),
|
||||||
|
DrawerItem(101, "ffpm-166", "assets://ffpm-166.txt"),
|
||||||
|
DrawerItem(102, "ffpm-16", "assets://ffpm-16.txt"),
|
||||||
|
DrawerItem(103, "ffpm-170", "assets://ffpm-170.txt"),
|
||||||
|
DrawerItem(104, "ffpm-172-1", "assets://ffpm-172-1.txt"),
|
||||||
|
DrawerItem(105, "ffpm-172-2", "assets://ffpm-172-2.txt"),
|
||||||
|
DrawerItem(106, "ffpm-175", "assets://ffpm-175.txt"),
|
||||||
|
DrawerItem(107, "ffpm-178", "assets://ffpm-178.txt"),
|
||||||
|
DrawerItem(108, "ffpm-179", "assets://ffpm-179.txt"),
|
||||||
|
DrawerItem(109, "ffpm-17", "assets://ffpm-17.txt"),
|
||||||
|
DrawerItem(110, "ffpm-180", "assets://ffpm-180.txt"),
|
||||||
|
DrawerItem(111, "ffpm-189", "assets://ffpm-189.txt"),
|
||||||
|
DrawerItem(112, "ffpm-190", "assets://ffpm-190.txt"),
|
||||||
|
DrawerItem(113, "ffpm-191", "assets://ffpm-191.txt"),
|
||||||
|
DrawerItem(114, "ffpm-192", "assets://ffpm-192.txt"),
|
||||||
|
DrawerItem(115, "ffpm-194", "assets://ffpm-194.txt"),
|
||||||
|
DrawerItem(116, "ffpm-195-1", "assets://ffpm-195-1.txt"),
|
||||||
|
DrawerItem(117, "ffpm-195-2", "assets://ffpm-195-2.txt"),
|
||||||
|
DrawerItem(118, "ffpm-198", "assets://ffpm-198.txt"),
|
||||||
|
DrawerItem(119, "ffpm-199", "assets://ffpm-199.txt"),
|
||||||
|
DrawerItem(120, "ffpm-19", "assets://ffpm-19.txt"),
|
||||||
|
DrawerItem(121, "ffpm-202", "assets://ffpm-202.txt"),
|
||||||
|
DrawerItem(122, "ffpm-203", "assets://ffpm-203.txt"),
|
||||||
|
DrawerItem(123, "ffpm-204", "assets://ffpm-204.txt"),
|
||||||
|
DrawerItem(124, "ffpm-209", "assets://ffpm-209.txt"),
|
||||||
|
DrawerItem(125, "ffpm-20", "assets://ffpm-20.txt"),
|
||||||
|
DrawerItem(126, "ffpm-210", "assets://ffpm-210.txt"),
|
||||||
|
DrawerItem(127, "ffpm-211", "assets://ffpm-211.txt"),
|
||||||
|
DrawerItem(128, "ffpm-212", "assets://ffpm-212.txt"),
|
||||||
|
DrawerItem(129, "ffpm-213", "assets://ffpm-213.txt"),
|
||||||
|
DrawerItem(130, "ffpm-214", "assets://ffpm-214.txt"),
|
||||||
|
DrawerItem(131, "ffpm-21", "assets://ffpm-21.txt"),
|
||||||
|
DrawerItem(132, "ffpm-220", "assets://ffpm-220.txt"),
|
||||||
|
DrawerItem(133, "ffpm-221", "assets://ffpm-221.txt"),
|
||||||
|
DrawerItem(134, "ffpm-222", "assets://ffpm-222.txt"),
|
||||||
|
DrawerItem(135, "ffpm-223", "assets://ffpm-223.txt"),
|
||||||
|
DrawerItem(136, "ffpm-224", "assets://ffpm-224.txt"),
|
||||||
|
DrawerItem(137, "ffpm-225", "assets://ffpm-225.txt"),
|
||||||
|
DrawerItem(138, "ffpm-229", "assets://ffpm-229.txt"),
|
||||||
|
DrawerItem(139, "ffpm-232", "assets://ffpm-232.txt"),
|
||||||
|
DrawerItem(140, "ffpm-233", "assets://ffpm-233.txt"),
|
||||||
|
DrawerItem(141, "ffpm-236", "assets://ffpm-236.txt"),
|
||||||
|
DrawerItem(142, "ffpm-237", "assets://ffpm-237.txt"),
|
||||||
|
DrawerItem(143, "ffpm-238-1", "assets://ffpm-238-1.txt"),
|
||||||
|
DrawerItem(144, "ffpm-238-2", "assets://ffpm-238-2.txt"),
|
||||||
|
DrawerItem(145, "ffpm-239-1", "assets://ffpm-239-1.txt"),
|
||||||
|
DrawerItem(146, "ffpm-239-2", "assets://ffpm-239-2.txt"),
|
||||||
|
DrawerItem(147, "ffpm-249", "assets://ffpm-249.txt"),
|
||||||
|
DrawerItem(148, "ffpm-250-1", "assets://ffpm-250-1.txt"),
|
||||||
|
DrawerItem(149, "ffpm-250-2", "assets://ffpm-250-2.txt"),
|
||||||
|
DrawerItem(150, "ffpm-251", "assets://ffpm-251.txt"),
|
||||||
|
DrawerItem(151, "ffpm-253", "assets://ffpm-253.txt"),
|
||||||
|
DrawerItem(152, "ffpm-254", "assets://ffpm-254.txt"),
|
||||||
|
DrawerItem(153, "ffpm-255", "assets://ffpm-255.txt"),
|
||||||
|
DrawerItem(154, "ffpm-256", "assets://ffpm-256.txt"),
|
||||||
|
DrawerItem(155, "ffpm-257-1", "assets://ffpm-257-1.txt"),
|
||||||
|
DrawerItem(156, "ffpm-257-2", "assets://ffpm-257-2.txt"),
|
||||||
|
DrawerItem(157, "ffpm-259", "assets://ffpm-259.txt"),
|
||||||
|
DrawerItem(158, "ffpm-260", "assets://ffpm-260.txt"),
|
||||||
|
DrawerItem(159, "ffpm-263", "assets://ffpm-263.txt"),
|
||||||
|
DrawerItem(160, "ffpm-269", "assets://ffpm-269.txt"),
|
||||||
|
DrawerItem(161, "ffpm-270", "assets://ffpm-270.txt"),
|
||||||
|
DrawerItem(162, "ffpm-271", "assets://ffpm-271.txt"),
|
||||||
|
DrawerItem(163, "ffpm-272", "assets://ffpm-272.txt"),
|
||||||
|
DrawerItem(164, "ffpm-273", "assets://ffpm-273.txt"),
|
||||||
|
DrawerItem(165, "ffpm-274", "assets://ffpm-274.txt"),
|
||||||
|
DrawerItem(166, "ffpm-276", "assets://ffpm-276.txt"),
|
||||||
|
DrawerItem(167, "ffpm-279", "assets://ffpm-279.txt"),
|
||||||
|
DrawerItem(168, "ffpm-281", "assets://ffpm-281.txt"),
|
||||||
|
DrawerItem(169, "ffpm-297", "assets://ffpm-297.txt"),
|
||||||
|
DrawerItem(170, "ffpm-307", "assets://ffpm-307.txt"),
|
||||||
|
DrawerItem(171, "ffpm-310", "assets://ffpm-310.txt"),
|
||||||
|
DrawerItem(172, "ffpm-311", "assets://ffpm-311.txt"),
|
||||||
|
DrawerItem(173, "ffpm-315", "assets://ffpm-315.txt"),
|
||||||
|
DrawerItem(174, "ffpm-321", "assets://ffpm-321.txt"),
|
||||||
|
DrawerItem(175, "ffpm-32", "assets://ffpm-32.txt"),
|
||||||
|
DrawerItem(176, "ffpm-332", "assets://ffpm-332.txt"),
|
||||||
|
DrawerItem(177, "ffpm-33", "assets://ffpm-33.txt"),
|
||||||
|
DrawerItem(178, "ffpm-352", "assets://ffpm-352.txt"),
|
||||||
|
DrawerItem(179, "ffpm-353-1", "assets://ffpm-353-1.txt"),
|
||||||
|
DrawerItem(180, "ffpm-353-2", "assets://ffpm-353-2.txt"),
|
||||||
|
DrawerItem(181, "ffpm-357", "assets://ffpm-357.txt"),
|
||||||
|
DrawerItem(182, "ffpm-358", "assets://ffpm-358.txt"),
|
||||||
|
DrawerItem(183, "ffpm-35", "assets://ffpm-35.txt"),
|
||||||
|
DrawerItem(184, "ffpm-367", "assets://ffpm-367.txt"),
|
||||||
|
DrawerItem(185, "ffpm-381-1", "assets://ffpm-381-1.txt"),
|
||||||
|
DrawerItem(186, "ffpm-381-2", "assets://ffpm-381-2.txt"),
|
||||||
|
DrawerItem(187, "ffpm-38", "assets://ffpm-38.txt"),
|
||||||
|
DrawerItem(188, "ffpm-392", "assets://ffpm-392.txt"),
|
||||||
|
DrawerItem(189, "ffpm-408-1", "assets://ffpm-408-1.txt"),
|
||||||
|
DrawerItem(190, "ffpm-408-2", "assets://ffpm-408-2.txt"),
|
||||||
|
DrawerItem(191, "ffpm-413", "assets://ffpm-413.txt"),
|
||||||
|
DrawerItem(192, "ffpm-4-1", "assets://ffpm-4-1.txt"),
|
||||||
|
DrawerItem(193, "ffpm-42-1", "assets://ffpm-42-1.txt"),
|
||||||
|
DrawerItem(194, "ffpm-42-2", "assets://ffpm-42-2.txt"),
|
||||||
|
DrawerItem(195, "ffpm-428", "assets://ffpm-428.txt"),
|
||||||
|
DrawerItem(196, "ffpm-4-2", "assets://ffpm-4-2.txt"),
|
||||||
|
DrawerItem(197, "ffpm-43", "assets://ffpm-43.txt"),
|
||||||
|
DrawerItem(198, "ffpm-440-1", "assets://ffpm-440-1.txt"),
|
||||||
|
DrawerItem(199, "ffpm-444-1", "assets://ffpm-444-1.txt"),
|
||||||
|
DrawerItem(200, "ffpm-444-2", "assets://ffpm-444-2.txt"),
|
||||||
|
DrawerItem(201, "ffpm-449", "assets://ffpm-449.txt"),
|
||||||
|
DrawerItem(202, "ffpm-44", "assets://ffpm-44.txt"),
|
||||||
|
DrawerItem(203, "ffpm-46", "assets://ffpm-46.txt"),
|
||||||
|
DrawerItem(204, "ffpm-47", "assets://ffpm-47.txt"),
|
||||||
|
DrawerItem(205, "ffpm-483", "assets://ffpm-483.txt"),
|
||||||
|
DrawerItem(206, "ffpm-489", "assets://ffpm-489.txt"),
|
||||||
|
DrawerItem(207, "ffpm-490", "assets://ffpm-490.txt"),
|
||||||
|
DrawerItem(208, "ffpm-49-1", "assets://ffpm-49-1.txt"),
|
||||||
|
DrawerItem(209, "ffpm-49-2", "assets://ffpm-49-2.txt"),
|
||||||
|
DrawerItem(210, "ffpm-501", "assets://ffpm-501.txt"),
|
||||||
|
DrawerItem(211, "ffpm-503", "assets://ffpm-503.txt"),
|
||||||
|
DrawerItem(212, "ffpm-5-1", "assets://ffpm-5-1.txt"),
|
||||||
|
DrawerItem(213, "ffpm-51", "assets://ffpm-51.txt"),
|
||||||
|
DrawerItem(214, "ffpm-5-2", "assets://ffpm-5-2.txt"),
|
||||||
|
DrawerItem(215, "ffpm-52", "assets://ffpm-52.txt"),
|
||||||
|
DrawerItem(216, "ffpm-539", "assets://ffpm-539.txt"),
|
||||||
|
DrawerItem(217, "ffpm-546", "assets://ffpm-546.txt"),
|
||||||
|
DrawerItem(218, "ffpm-549", "assets://ffpm-549.txt"),
|
||||||
|
DrawerItem(219, "ffpm-54", "assets://ffpm-54.txt"),
|
||||||
|
DrawerItem(220, "ffpm-558", "assets://ffpm-558.txt"),
|
||||||
|
DrawerItem(221, "ffpm-55", "assets://ffpm-55.txt"),
|
||||||
|
DrawerItem(222, "ffpm-563", "assets://ffpm-563.txt"),
|
||||||
|
DrawerItem(223, "ffpm-564", "assets://ffpm-564.txt"),
|
||||||
|
DrawerItem(224, "ffpm-56-new", "assets://ffpm-56-new.txt"),
|
||||||
|
DrawerItem(225, "ffpm-56", "assets://ffpm-56.txt"),
|
||||||
|
DrawerItem(226, "ffpm-57-new", "assets://ffpm-57-new.txt"),
|
||||||
|
DrawerItem(227, "ffpm-57", "assets://ffpm-57.txt"),
|
||||||
|
DrawerItem(228, "ffpm-58", "assets://ffpm-58.txt"),
|
||||||
|
DrawerItem(229, "ffpm-603-1", "assets://ffpm-603-1.txt"),
|
||||||
|
DrawerItem(230, "ffpm-603-2", "assets://ffpm-603-2.txt"),
|
||||||
|
DrawerItem(231, "ffpm-610", "assets://ffpm-610.txt"),
|
||||||
|
DrawerItem(232, "ffpm-611-1", "assets://ffpm-611-1.txt"),
|
||||||
|
DrawerItem(233, "ffpm-611-2", "assets://ffpm-611-2.txt"),
|
||||||
|
DrawerItem(234, "ffpm-616", "assets://ffpm-616.txt"),
|
||||||
|
DrawerItem(235, "ffpm-61", "assets://ffpm-61.txt"),
|
||||||
|
DrawerItem(236, "ffpm-626", "assets://ffpm-626.txt"),
|
||||||
|
DrawerItem(237, "ffpm-636", "assets://ffpm-636.txt"),
|
||||||
|
DrawerItem(238, "ffpm-640", "assets://ffpm-640.txt"),
|
||||||
|
DrawerItem(239, "ffpm-642-1", "assets://ffpm-642-1.txt"),
|
||||||
|
DrawerItem(240, "ffpm-642-2", "assets://ffpm-642-2.txt"),
|
||||||
|
DrawerItem(241, "ffpm-653", "assets://ffpm-653.txt"),
|
||||||
|
DrawerItem(242, "ffpm-674", "assets://ffpm-674.txt"),
|
||||||
|
DrawerItem(243, "ffpm-69", "assets://ffpm-69.txt"),
|
||||||
|
DrawerItem(244, "ffpm-6", "assets://ffpm-6.txt"),
|
||||||
|
DrawerItem(245, "ffpm-705-2", "assets://ffpm-705-2.txt"),
|
||||||
|
DrawerItem(246, "ffpm-71", "assets://ffpm-71.txt"),
|
||||||
|
DrawerItem(247, "ffpm-725", "assets://ffpm-725.txt"),
|
||||||
|
DrawerItem(248, "ffpm-726-1", "assets://ffpm-726-1.txt"),
|
||||||
|
DrawerItem(249, "ffpm-726-2", "assets://ffpm-726-2.txt"),
|
||||||
|
DrawerItem(250, "ffpm-729", "assets://ffpm-729.txt"),
|
||||||
|
DrawerItem(251, "ffpm-72", "assets://ffpm-72.txt"),
|
||||||
|
DrawerItem(252, "ffpm-733", "assets://ffpm-733.txt"),
|
||||||
|
DrawerItem(253, "ffpm-734", "assets://ffpm-734.txt"),
|
||||||
|
DrawerItem(254, "ffpm-735", "assets://ffpm-735.txt"),
|
||||||
|
DrawerItem(255, "ffpm-737", "assets://ffpm-737.txt"),
|
||||||
|
DrawerItem(256, "ffpm-750", "assets://ffpm-750.txt"),
|
||||||
|
DrawerItem(257, "ffpm-75", "assets://ffpm-75.txt"),
|
||||||
|
DrawerItem(258, "ffpm-760", "assets://ffpm-760.txt"),
|
||||||
|
DrawerItem(259, "ffpm-79-1", "assets://ffpm-79-1.txt"),
|
||||||
|
DrawerItem(260, "ffpm-79-2", "assets://ffpm-79-2.txt"),
|
||||||
|
DrawerItem(261, "ffpm-796", "assets://ffpm-796.txt"),
|
||||||
|
DrawerItem(262, "ffpm-799", "assets://ffpm-799.txt"),
|
||||||
|
DrawerItem(263, "ffpm-7", "assets://ffpm-7.txt"),
|
||||||
|
DrawerItem(264, "ffpm-82-1", "assets://ffpm-82-1.txt"),
|
||||||
|
DrawerItem(265, "ffpm-82-2", "assets://ffpm-82-2.txt"),
|
||||||
|
DrawerItem(266, "ffpm-825", "assets://ffpm-825.txt"),
|
||||||
|
DrawerItem(267, "ffpm-87", "assets://ffpm-87.txt"),
|
||||||
|
DrawerItem(268, "ffpm-89", "assets://ffpm-89.txt"),
|
||||||
|
DrawerItem(269, "ffpm-8", "assets://ffpm-8.txt"),
|
||||||
|
DrawerItem(270, "ffpm-91", "assets://ffpm-91.txt"),
|
||||||
|
DrawerItem(271, "ffpm-93", "assets://ffpm-93.txt"),
|
||||||
|
DrawerItem(272, "ffpm-94", "assets://ffpm-94.txt"),
|
||||||
|
DrawerItem(273, "ffpm-97-1", "assets://ffpm-97-1.txt"),
|
||||||
|
DrawerItem(274, "ffpm-97-2", "assets://ffpm-97-2.txt"),
|
||||||
|
DrawerItem(275, "ffpm-9", "assets://ffpm-9.txt")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package mg.dot.feufaro.midi
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import javax.sound.midi.*
|
||||||
|
|
||||||
|
class `MidiWriterKotlin-1` (private val fileRepository: FileRepository) {
|
||||||
|
private val sequence = Sequence(Sequence.PPQ, 60)
|
||||||
|
private val track = sequence.createTrack()
|
||||||
|
private var tick: Long = 0
|
||||||
|
private var nextTick: MutableList<Int> = mutableListOf()
|
||||||
|
private val noteOn = ShortMessage()
|
||||||
|
private val noteOff = ShortMessage()
|
||||||
|
private val lastPitch : MutableList<Int> = mutableListOf()
|
||||||
|
private val useChord : Boolean = true
|
||||||
|
fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
||||||
|
var channel: Int = voiceNumber - 1
|
||||||
|
if (useChord) {
|
||||||
|
channel = channel / 2
|
||||||
|
}
|
||||||
|
var note = note
|
||||||
|
if (voiceNumber == 3 || voiceNumber == 4) {
|
||||||
|
note -= 12
|
||||||
|
}
|
||||||
|
if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) {
|
||||||
|
noteOff.setMessage(ShortMessage.NOTE_OFF, channel, lastPitch[voiceNumber], 0)
|
||||||
|
val n2 = noteOff.clone() as MidiMessage
|
||||||
|
track.add(MidiEvent(n2, tick))
|
||||||
|
}
|
||||||
|
var velocity = velocity
|
||||||
|
if (note <= 0) {
|
||||||
|
note = 40
|
||||||
|
velocity = 0
|
||||||
|
}
|
||||||
|
noteOn.setMessage(ShortMessage.NOTE_ON, channel, note, velocity)
|
||||||
|
val n1: MidiMessage = noteOn.clone() as MidiMessage
|
||||||
|
track.add(MidiEvent(n1, tick))
|
||||||
|
while(lastPitch.size <= voiceNumber) {
|
||||||
|
lastPitch.add(0)
|
||||||
|
}
|
||||||
|
lastPitch[voiceNumber] = note
|
||||||
|
}
|
||||||
|
fun save(filePath: String) {
|
||||||
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
parseScope.launch {
|
||||||
|
val out = fileRepository.getOutputStream(filePath)
|
||||||
|
MidiSystem.write(sequence, 1, out)
|
||||||
|
out.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) {
|
||||||
|
val byteArray = metaByteString.toByteArray()
|
||||||
|
val metaMessage = MetaMessage(type, byteArray, nbData)
|
||||||
|
track.add(MidiEvent(metaMessage, tick.toLong()))
|
||||||
|
}
|
||||||
|
fun process(pitches: List<MidiPitch>) {
|
||||||
|
val lastTick = 0
|
||||||
|
nextTick.clear()
|
||||||
|
// addMetaMessage(0x59, 4, 2, 2,0)
|
||||||
|
tick = 0
|
||||||
|
pitches.forEach {
|
||||||
|
if (it.metaType > 0) {
|
||||||
|
addMetaMessage(it.metaType, it.tick, it.metaByteSize, it.metaBytes)
|
||||||
|
} else if (it.pitch != "") {
|
||||||
|
addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package mg.dot.feufaro.midi
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import javax.sound.midi.*
|
||||||
|
|
||||||
|
actual class MidiWriterKotlin actual constructor(private val fileRepository: FileRepository) {
|
||||||
|
private val sequence = Sequence(Sequence.PPQ, 60)
|
||||||
|
private val track = sequence.createTrack()
|
||||||
|
private var tick: Long = 0
|
||||||
|
//private var nextTick: MidiPitch<Int> = mutableListOf()
|
||||||
|
private var nextTick: MutableList<MidiPitch> = mutableListOf()
|
||||||
|
private val noteOn = ShortMessage()
|
||||||
|
private val noteOff = ShortMessage()
|
||||||
|
private val lastPitch : MutableList<Int> = mutableListOf()
|
||||||
|
private val useChord : Boolean = true
|
||||||
|
actual fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
||||||
|
var channel: Int = voiceNumber - 1
|
||||||
|
if (useChord) {
|
||||||
|
channel = channel / 2
|
||||||
|
}
|
||||||
|
var note = note
|
||||||
|
if (voiceNumber == 3 || voiceNumber == 4) {
|
||||||
|
note -= 12
|
||||||
|
}
|
||||||
|
if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) {
|
||||||
|
noteOff.setMessage(ShortMessage.NOTE_OFF, channel, lastPitch[voiceNumber], 0)
|
||||||
|
val n2 = noteOff.clone() as MidiMessage
|
||||||
|
track.add(MidiEvent(n2, tick))
|
||||||
|
}
|
||||||
|
var velocity = velocity
|
||||||
|
if (note <= 0) {
|
||||||
|
note = 40
|
||||||
|
velocity = 0
|
||||||
|
}
|
||||||
|
noteOn.setMessage(ShortMessage.NOTE_ON, channel, note, velocity)
|
||||||
|
val n1: MidiMessage = noteOn.clone() as MidiMessage
|
||||||
|
track.add(MidiEvent(n1, tick))
|
||||||
|
while(lastPitch.size <= voiceNumber) {
|
||||||
|
lastPitch.add(0)
|
||||||
|
}
|
||||||
|
lastPitch[voiceNumber] = note
|
||||||
|
}
|
||||||
|
actual fun save(filePath: String) {
|
||||||
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
parseScope.launch {
|
||||||
|
val out = fileRepository.getOutputStream(filePath)
|
||||||
|
MidiSystem.write(sequence, 1, out)
|
||||||
|
out.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) {
|
||||||
|
val byteArray = metaByteString.toByteArray()
|
||||||
|
val metaMessage = MetaMessage(type, byteArray, nbData)
|
||||||
|
track.add(MidiEvent(metaMessage, tick.toLong()))
|
||||||
|
}
|
||||||
|
actual fun process(pitches: List<MidiPitch>) {
|
||||||
|
val lastTick = 0
|
||||||
|
nextTick.clear()
|
||||||
|
// addMetaMessage(0x59, 4, 2, 2,0)
|
||||||
|
tick = 0
|
||||||
|
pitches.forEach {
|
||||||
|
if (it.metaType > 0) {
|
||||||
|
addMetaMessage(it.metaType, it.tick, it.metaByteSize, it.metaBytes)
|
||||||
|
} else if (it.pitch != "") {
|
||||||
|
addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||||
|
import com.google.zxing.BarcodeFormat
|
||||||
|
import com.google.zxing.client.j2se.MatrixToImageWriter
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter
|
||||||
|
|
||||||
|
actual fun generateQRCode(content: String, size: Int): ImageBitmap? {
|
||||||
|
return try {
|
||||||
|
val writer = QRCodeWriter()
|
||||||
|
val bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, size, size)
|
||||||
|
|
||||||
|
val image = MatrixToImageWriter.toBufferedImage(bitMatrix)
|
||||||
|
|
||||||
|
image.toComposeImageBitmap()
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Erreur QR Code Desktop: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun ScrollableDrawerContent(
|
||||||
|
modifier: Modifier,
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
content()
|
||||||
|
|
||||||
|
VerticalScrollbar(
|
||||||
|
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight().width(8.dp),
|
||||||
|
adapter = rememberScrollbarAdapter(lazyListState)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue