Skip to content

Commit a373d46

Browse files
committed
adding BPM based transition time syncing
and other UI changes
1 parent efc300c commit a373d46

21 files changed

+878
-476
lines changed

build.gradle.kts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
kotlin("plugin.compose")
99
kotlin("plugin.serialization")
1010
kotlin("plugin.power-assert")
11+
// kotlin("plugin.parcelize")
1112
id("dev.reformator.stacktracedecoroutinator")
1213
id("org.bytedeco.gradle-javacpp-platform") version "1.5.10"
1314
}
@@ -17,7 +18,6 @@ repositories {
1718
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
1819
google()
1920
}
20-
2121
kotlin {
2222
jvm("desktop")
2323
sourceSets {
@@ -27,6 +27,16 @@ kotlin {
2727
implementation(compose.runtime)
2828
implementation(compose.foundation)
2929
implementation(compose.material)
30+
implementation(compose.material3)
31+
//// implementation(compose.material3AdaptiveNavigationSuite)
32+
implementation(compose.materialIconsExtended)
33+
// implementation(compose.material3AdaptiveNavigationSuite)
34+
35+
implementation("org.jetbrains.compose.material3.adaptive:adaptive-layout-desktop:_")
36+
// implementation("androidx.compose.material3:material3-adaptive-navigation-suite-desktop:_") {
37+
//// exclude("", "")
38+
// }
39+
// implementation(compose.runtimeSaveable)
3040
implementation(compose.ui)
3141
implementation(compose.components.resources)
3242
implementation(compose.components.uiToolingPreview)
@@ -86,6 +96,8 @@ kotlin {
8696

8797
stacktraceDecoroutinator {
8898
enabled = false
99+
addAndroidRuntimeDependency = false
100+
addJvmRuntimeDependency = false
89101
}
90102

91103
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() {
@@ -119,6 +131,14 @@ powerAssert {
119131

120132
compose.desktop {
121133
application {
134+
buildTypes.release {
135+
proguard {
136+
isEnabled = false
137+
version = "7.4.0" // may break with compose-navigation
138+
// optimize = false
139+
// obfuscate = false
140+
}
141+
}
122142
mainClass = "Main"
123143

124144
mainJar.set(
@@ -159,7 +179,11 @@ project.afterEvaluate {
159179
jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
160180
}
161181
val createDistributable by getting(AbstractJPackageTask::class) {
162-
destinationDir.set(project.file("bin"))
182+
destinationDir.set(project.file("bin/debug"))
183+
// appImageRootDir.
184+
}
185+
val createReleaseDistributable by getting(AbstractJPackageTask::class) {
186+
destinationDir.set(project.file("bin/release"))
163187
// appImageRootDir.
164188
}
165189
val runDistributable by getting(AbstractRunDistributableTask::class) {
@@ -185,7 +209,7 @@ project.afterEvaluate {
185209
}
186210
val packageDistributable by creating(Zip::class) {
187211
group = "package"
188-
from(getByName("createDistributable"))
212+
from(createReleaseDistributable)
189213
from(project.file("README.md"))
190214
archiveBaseName.set("nestctrl")
191215
destinationDirectory.set(project.file("build"))
@@ -209,7 +233,7 @@ project.afterEvaluate {
209233
doFirst {
210234
File(System.getProperty("user.home")).resolve("VJ").resolve("nestctrl").deleteRecursively()
211235
}
212-
from(getByName("createDistributable"))
236+
from(createReleaseDistributable)
213237
from(project.file("README.md"))
214238
this.destinationDir = File(System.getProperty("user.home")).resolve("VJ")
215239
// doLast {

src/desktopMain/kotlin/Config.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ data class Config(
5555
data class DeckConfig(
5656
val triggerTime: Float = 1.0f,
5757
val transitionTime: Float = 5.0f,
58+
val transitionTimeBeatSync: Boolean = true,
59+
// val transitionTimeSyncFrameFraction: Float = 0.125f,
60+
val transitTimeBeats: Int = 8,
5861
// val presetQueues: PresetQueues = PresetQueues(),
5962
val search: SearchConfig = SearchConfig(),
6063
val presetQueue: PresetQueue = PresetQueue(),

src/desktopMain/kotlin/ScanPresets.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,13 @@ import kotlinx.coroutines.async
44
import kotlinx.coroutines.awaitAll
55
import kotlinx.coroutines.launch
66
import nestdrop.PresetLocation
7-
import tags.Tag
8-
import tags.nestdropCategoryTagsSet
97
import ui.screens.imgSpritesMap
108
import ui.screens.presetsMap
119
import java.io.File
1210
import kotlin.io.path.name
1311
import kotlin.time.measureTimedValue
1412

1513
suspend fun scanMilkdrop() {
16-
17-
val categoryTagsSet = mutableSetOf<Tag>()
18-
1914
var id: Int = 0
2015

2116
val milkPresets = run {
@@ -39,7 +34,7 @@ suspend fun scanMilkdrop() {
3934
}
4035

4136
val rootPresets =
42-
presetsFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "milk" }.map { file ->
37+
presetsFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "milk" }.sortedBy { it.name.lowercase() }.map { file ->
4338
milkLocation(
4439
file = file,
4540
id = id++
@@ -49,7 +44,7 @@ suspend fun scanMilkdrop() {
4944
val files = categoryFolder.walkTopDown().filter { it.isFile && it.extension == "milk" }.map {
5045
it.relativeToOrSelf(presetsFolder)
5146
}
52-
val sorted = files.sortedBy { it.path }
47+
val sorted = files.sortedBy { it.path.lowercase() }
5348
var previousParent: String? = null
5449
sorted.map {
5550
val parent = it.parent
@@ -196,7 +191,7 @@ suspend fun scanMilkdrop() {
196191
}
197192

198193
val rootPresets =
199-
spritesFolder.listFiles().orEmpty().filter { it.isFile
194+
spritesFolder.listFiles().orEmpty().sortedBy { it.name.lowercase() }.orEmpty().filter { it.isFile
200195
&& it.extension == "png" || it.extension == "jpg"
201196
}.map { file ->
202197
imgLocation(
@@ -210,7 +205,7 @@ suspend fun scanMilkdrop() {
210205
}.map {
211206
it.relativeToOrSelf(spritesFolder)
212207
}
213-
val sorted = files.sortedBy { it.path }
208+
val sorted = files.sortedBy { it.path.lowercase() }
214209
var previousParent: String? = null
215210
sorted.map {
216211
val parent = it.parent
@@ -226,7 +221,7 @@ suspend fun scanMilkdrop() {
226221
}
227222
rootPresets + categoryPresets
228223
}.associateBy { it.name }
229-
nestdropCategoryTagsSet.value = categoryTagsSet
224+
// nestdropCategoryTagsSet.value = categoryTagsSet
230225

231226
presetsMap.value = milkPresets
232227
imgSpritesMap.value = imgPresets

src/desktopMain/kotlin/beatCounter.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import kotlinx.coroutines.FlowPreview
33
import kotlinx.coroutines.delay
44
import kotlinx.coroutines.flow.MutableStateFlow
55
import kotlinx.coroutines.flow.combine
6+
import kotlinx.coroutines.flow.distinctUntilChanged
7+
import kotlinx.coroutines.flow.filterNotNull
68
import kotlinx.coroutines.flow.launchIn
79
import kotlinx.coroutines.flow.map
810
import kotlinx.coroutines.flow.onEach
@@ -29,6 +31,7 @@ val bpmSynced = OscSynced.ValueSingle<Float>(
2931
}
3032
val bpmRounded = MutableStateFlow(120f)
3133
val bpmInt = MutableStateFlow(120)
34+
val secondsPerBeat = MutableStateFlow(0.5f)
3235

3336
val beatCounter = MutableStateFlow(0.0)
3437

@@ -44,8 +47,40 @@ suspend fun startBeatCounter() {
4447
.onEach {
4548
bpmRounded.value = it
4649
bpmInt.value = it.roundToInt()
50+
secondsPerBeat.value = 60f / it
4751
}
4852
.launchIn(flowScope)
53+
54+
decks.forEach { deck ->
55+
56+
combine(
57+
secondsPerBeat,
58+
beatFrame,
59+
deck.presetSwitching.transitTimeSync,
60+
// deck.presetSwitching.transitTimeBeatframeFraction,
61+
deck.presetSwitching.transitTimeBeats,
62+
) { secondsPerBeat, beatFrame, enabled, transitTimeBeats ->
63+
if(enabled) {
64+
secondsPerBeat * transitTimeBeats
65+
} else {
66+
null
67+
}
68+
}.distinctUntilChanged()
69+
.filterNotNull()
70+
.map { value ->
71+
(value * 10).roundToInt() / 10.0f
72+
}
73+
.map { value ->
74+
value.coerceAtMost(30f)
75+
}
76+
.distinctUntilChanged()
77+
.onEach {
78+
logger.info { "updating ${deck.deckName} transit time to $it" }
79+
deck.ndTime.transitionTime.value = it
80+
}
81+
.launchIn(flowScope)
82+
}
83+
4984
beatProgress
5085
.onEach { beatProgress ->
5186
logger.trace { "beat progress: $beatProgress" }

src/desktopMain/kotlin/nestdrop/Queue.kt

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package nestdrop
22

3-
data class Queue<PRESET: Preset>(
3+
data class Queue<PRESET : Preset>(
44
val index: Int,
55
val name: String,
66
val type: QueueType,
@@ -15,7 +15,7 @@ data class Queue<PRESET: Preset>(
1515
// val xpath = "/NestDropSettings/QueueWindows/*[@Name='$name']"
1616
}
1717

18-
enum class PresetType(val type: Int){
18+
enum class PresetType(val type: Int) {
1919
Mildrop(1),
2020
ImgSprite(2),
2121
MIDI(4),
@@ -49,28 +49,32 @@ sealed interface Preset {
4949
override val id: Int,
5050
val effects: Int?,
5151
val overlay: Boolean?,
52-
): Preset {
53-
val label = name
52+
val comments: String?,
53+
) : Preset {
54+
val label = (comments ?: name
5455
.substringBeforeLast(".jpg")
55-
.substringBeforeLast(".png") +
56+
.substringBeforeLast(".png")) +
5657
if (effects != 0) {
57-
" FX: $effects"
58+
" | FX: $effects"
5859
} else {
5960
""
6061
}
6162
}
63+
6264
data class SpoutSprite(
6365
val index: Int,
6466
val name: String,
6567
override val id: Int,
6668
val effects: Int?,
6769
val overlay: Boolean?,
68-
): Preset {
69-
val label = name +
70+
val comments: String?,
71+
) : Preset {
72+
val shortLabel = (comments ?: name) +
7073
if (effects != 0) {
71-
" FX: $effects"
74+
" | FX: $effects"
7275
} else {
7376
""
7477
}
78+
val encoded get() = "$name fx=$effects overlay=$overlay comments=$comments"
7579
}
7680
}

src/desktopMain/kotlin/nestdrop/deck/Deck.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import kotlinx.serialization.Serializable
2929
import nestdrop.ImgMode
3030
import nestdrop.NestdropControl
3131
import nestdrop.NestdropSpriteQueue
32-
import nestdrop.Preset
3332
import nestdrop.PresetIdState
3433
import nestdrop.PresetLocation
3534
import nestdrop.Queue
@@ -81,6 +80,8 @@ class Deck(
8180

8281
val dimmedColor = color.copy(alpha = 0.5f).compositeOver(Color.Black)
8382
val disabledColor = color.copy(alpha = 0.25f).compositeOver(Color.Black)
83+
val dimmedColorTransparent = color.copy(alpha = 0.5f) //.compositeOver(Color.Black)
84+
val disabledColorTransparent = color.copy(alpha = 0.25f) //.compositeOver(Color.Black)
8485

8586
@Immutable
8687
inner class NdTime {
@@ -240,6 +241,9 @@ class Deck(
240241
val triggerTime = MutableStateFlow(0.75f)
241242
private val switchingLocked = MutableStateFlow(false)
242243
val isLocked = switchingLocked.asStateFlow()
244+
val transitTimeSync = MutableStateFlow(true)
245+
val transitTimeBeatframeFraction = MutableStateFlow(0.125f)
246+
val transitTimeBeats = MutableStateFlow(8)
243247

244248
private suspend fun doSwitch() {
245249
// change preset queue
@@ -374,8 +378,15 @@ class Deck(
374378
}
375379
}.toMap()
376380

377-
val id = pickItemToGenerate(filtered)
378-
nestdropSetPreset(id.id, deck = this@Deck.id)
381+
val selectedPreset = pickItemToGenerate(filtered)
382+
val pickedWeight = filtered[selectedPreset]
383+
logger.debug { "picked ($pickedWeight / ${filtered.values.sum()}) ${selectedPreset.name} out of ${filtered.size} options" }
384+
val selectedPresetTags = presetTags[selectedPreset.name]
385+
.orEmpty()
386+
.filter { it.namespace.first() != "nestdrop" }
387+
.filter { it.namespace.first() != "queue" }
388+
logger.debug { "tags: ${selectedPresetTags.joinToString { it.toString() }}" }
389+
nestdropSetPreset(selectedPreset.id, deck = this@Deck.id)
379390
}
380391
}
381392
// val search = MutableStateFlow<TagScoreEval?>(null)
@@ -831,11 +842,6 @@ class Deck(
831842
// @Deprecated("use spoutImgTarget")
832843
val index = MutableStateFlow(-1)
833844

834-
@Deprecated("use spoutImgTarget")
835-
val name = MutableStateFlow("uninitialized")
836-
837-
@Deprecated("use spoutImgTarget")
838-
val fx = MutableStateFlow(0)
839845
val spriteTargetKey = MutableStateFlow<SpriteKey?>(null)
840846

841847
private val nestdropSpout = NestdropSpriteQueue(
@@ -878,7 +884,7 @@ class Deck(
878884
// SpriteKey(id = it.id, name = it.name, mode = if(it.overlay == true) ImgMode.Overlay else ImgMode.Nested, fx = it.effects ?: 0)
879885
// }
880886
logger.info { "$deckName spout name: ${spoutPreset?.prettyPrint()}" }
881-
spriteTargetKey.value = SpriteKey(id=-1, name=spoutPreset?.label ?: "-", fx = spoutPreset?.effects ?: 0 )
887+
spriteTargetKey.value = SpriteKey(id=-1, name=spoutPreset?.shortLabel ?: "-", fx = spoutPreset?.effects ?: 0 )
882888
// fx.value = spoutPreset?.effects ?: 0
883889
// name.value = spoutPreset?.label ?: "-"
884890
}

src/desktopMain/kotlin/nestdrop/deck/DeckConfig.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ suspend fun Deck.applyConfig(deckConfig: DeckConfig) {
2222
this@applyConfig.ndStrobe.enabled.value = strobe.enabled
2323

2424
this@applyConfig.presetSwitching.triggerTime.value = triggerTime
25+
this@applyConfig.presetSwitching.transitTimeSync.value = transitionTimeBeatSync
26+
this@applyConfig.presetSwitching.transitTimeBeats.value = transitTimeBeats
2527

2628
run {
2729
// this@applyConfig.presetQueue.autoChange.value = presetQueue.autoChange
@@ -81,7 +83,7 @@ suspend fun Deck.applyConfig(deckConfig: DeckConfig) {
8183
logger.info { "loading spout sprites from queue for $deckName" }
8284
spoutQueueValue?.presets.orEmpty()
8385
}.orEmpty()
84-
this@applyConfig.spout.index.value = spoutSprites.indexOfFirst { it.label == spout.label }
86+
this@applyConfig.spout.index.value = spoutSprites.indexOfFirst { it.encoded == spout.label }
8587
.takeUnless { it == -1 } ?: spout.index
8688
}
8789
}
@@ -193,10 +195,14 @@ val Deck.configFlow: Flow<DeckConfig>
193195
configFlow,
194196
presetSwitching.triggerTime,
195197
presetSwitching.transitionTime,
196-
) { config, triggerTime, transitionTime ->
198+
presetSwitching.transitTimeSync,
199+
presetSwitching.transitTimeBeats
200+
) { config, triggerTime, transitionTime, transitTimeSync,transitTimeBeats ->
197201
config.copy(
198202
triggerTime = triggerTime,
199203
transitionTime = transitionTime,
204+
transitionTimeBeatSync = transitTimeSync,
205+
transitTimeBeats = transitTimeBeats,
200206
)
201207
}
202208
}
@@ -254,12 +260,12 @@ val Deck.configFlow: Flow<DeckConfig>
254260
}
255261
.combine(
256262
combine(
263+
spout,
257264
spout.index,
258-
spout.name,
259-
) { index, name ->
265+
) { spoutSprite, index ->
260266
DeckConfig.Spout(
261267
index = index,
262-
label = name,
268+
label = spoutSprite?.encoded,
263269
)
264270
}
265271
) { config, spout ->

0 commit comments

Comments
 (0)