Skip to content

Commit ece3298

Browse files
committed
adding QueueControl UI
1 parent a373d46 commit ece3298

26 files changed

+1637
-703
lines changed

src/desktopMain/kotlin/ScanPresets.kt

Lines changed: 143 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import io.github.oshai.kotlinlogging.KotlinLogging
22
import kotlinx.coroutines.Dispatchers
33
import kotlinx.coroutines.async
44
import kotlinx.coroutines.awaitAll
5+
import kotlinx.coroutines.flow.MutableStateFlow
56
import kotlinx.coroutines.launch
7+
import nestdrop.Preset
68
import nestdrop.PresetLocation
7-
import ui.screens.imgSpritesMap
8-
import ui.screens.presetsMap
99
import java.io.File
1010
import kotlin.io.path.name
1111
import kotlin.time.measureTimedValue
1212

13+
val presetsMap = MutableStateFlow<Map<String, PresetLocation.Milk>>(emptyMap())
14+
val imgSpritesMap = MutableStateFlow<Map<String, PresetLocation.Img>>(emptyMap())
15+
1316
suspend fun scanMilkdrop() {
1417
var id: Int = 0
1518

@@ -19,13 +22,13 @@ suspend fun scanMilkdrop() {
1922
id: Int,
2023
): PresetLocation.Milk {
2124
val relative = file.relativeToOrSelf(presetsFolder)
22-
val name = relative.nameWithoutExtension
2325
val path = relative.path
2426
val previewPath = relative.resolveSibling(relative.nameWithoutExtension + ".jpg").path
2527

2628
// categoryTagsSet += Tag(categoryFolder.name, listOf("nestdrop"))
2729
return PresetLocation.Milk(
28-
name = name,
30+
name = relative.nameWithoutExtension,
31+
nameWithExtension = relative.name,
2932
id = id,
3033
path = path,
3134
previewPath = previewPath,
@@ -34,7 +37,8 @@ suspend fun scanMilkdrop() {
3437
}
3538

3639
val rootPresets =
37-
presetsFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "milk" }.sortedBy { it.name.lowercase() }.map { file ->
40+
presetsFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "milk" }
41+
.sortedBy { it.name.lowercase() }.map { file ->
3842
milkLocation(
3943
file = file,
4044
id = id++
@@ -59,149 +63,39 @@ suspend fun scanMilkdrop() {
5963
}
6064
}
6165
rootPresets + categoryPresets
62-
}.associateBy { it.name }
63-
64-
65-
// val milkPresets = presetsFolder.listFiles().orEmpty().filter { it.isDirectory }.flatMap { categoryFolder ->
66-
// val categoryFiles = categoryFolder.listFiles().orEmpty().filter { it.isFile }.filter { it.extension == "milk" }
67-
// val categoryPresets = categoryFiles.filterNotNull().map { file ->
68-
// val name = file.nameWithoutExtension
69-
// val path = file.toRelativeString(presetsFolder)
70-
// val previewPath = file.resolveSibling(file.nameWithoutExtension + ".jpg").toRelativeString(presetsFolder)
71-
//
72-
// categoryTagsSet += Tag(categoryFolder.name, listOf("nestdrop"))
73-
// PresetLocation.Milk(
74-
// name = name,
75-
// id = id++,
76-
// path = path,
77-
// previewPath = previewPath,
78-
// category = categoryFolder.name,
79-
// )
80-
// }
81-
// val subCategories = categoryFolder.listFiles().orEmpty().filter { it.isDirectory }
82-
//// .also {
83-
//// logger.debug { "pre-sort: ${it.map { it.name }}" }
84-
//// }
85-
// .sortFileNames()
86-
//// .also {
87-
//// logger.debug { "post-sort: ${it.map { it.name }}" }
88-
//// }
89-
//
90-
// val subCategoryEntries = subCategories.flatMapIndexed() { index, subCategoryFolder ->
91-
// val subCategoryFiles =
92-
// subCategoryFolder.listFiles().orEmpty().filter { it.isFile }.filter { it.extension == "milk" }
93-
// if (subCategoryFiles.isNotEmpty()) {
94-
// if (index == 0) {
95-
// if (categoryPresets.isNotEmpty()) {
96-
// id++
97-
// }
98-
// } else {
99-
// id++
100-
// }
101-
// }
102-
// subCategoryFiles.filterNotNull().map { file ->
103-
// val name = file.nameWithoutExtension
104-
// val path = file.toRelativeString(presetsFolder)
105-
// val previewPath =
106-
// file.resolveSibling(file.nameWithoutExtension + ".jpg").toRelativeString(presetsFolder)
107-
//
108-
// categoryTagsSet += Tag(categoryFolder.name, listOf("nestdrop"))
109-
// categoryTagsSet += Tag(subCategoryFolder.name, listOf("nestdrop", categoryFolder.name))
110-
//
111-
// PresetLocation.Milk(
112-
// name = name,
113-
// id = id++,
114-
// path = path,
115-
// previewPath = previewPath,
116-
// category = categoryFolder.name,
117-
// subCategory = subCategoryFolder.name,
118-
// )
119-
// }
120-
// }
121-
//
122-
// categoryPresets + subCategoryEntries
123-
// }.associateBy { it.name }
124-
125-
126-
// val imgPresets =
127-
// spritesFolder.listFiles().orEmpty().filter { it.isDirectory }.sortFileNames().flatMap { categoryFolder ->
128-
// val categoryFiles = categoryFolder.listFiles().orEmpty().filter { it.isFile }
129-
// .filter { it.extension == "png" || it.extension == "jpg" }.sortFileNames()
130-
// val categoryPresets = categoryFiles.filterNotNull().map { file ->
131-
// val name = file.name
132-
// val path = file.toRelativeString(presetsFolder)
133-
//
134-
// PresetLocation.Img(
135-
// name = name,
136-
// id = id++,
137-
// path = path,
138-
// category = categoryFolder.name,
139-
// )
140-
// }
141-
// val subCategories = categoryFolder.listFiles().orEmpty().filter { it.isDirectory }.sortFileNames()
142-
//
143-
// val subCategoryEntries = subCategories.flatMapIndexed() { index, subCategoryFolder ->
144-
// val subCategoryFiles =
145-
// subCategoryFolder.listFiles().orEmpty().filter { it.extension == "png" || it.extension == "jpg" }
146-
// .sortFileNames()
147-
// if (subCategoryFiles.isNotEmpty()) {
148-
// if (index == 0) {
149-
// if (categoryPresets.isNotEmpty()) {
150-
// id++
151-
// }
152-
// } else {
153-
// id++
154-
// }
155-
// }
156-
// subCategoryFiles.filterNotNull().map { file ->
157-
// val name = file.name
158-
// val path = file.toRelativeString(presetsFolder)
159-
//
160-
// PresetLocation.Img(
161-
// name = name,
162-
// id = id++,
163-
// path = path,
164-
// category = categoryFolder.name,
165-
// subCategory = subCategoryFolder.name,
166-
// )
167-
// }
168-
// }
169-
//
170-
// categoryPresets + subCategoryEntries
171-
// }.associateBy { it.name }
172-
173-
66+
}.associateBy { it.nameWithExtension }
17467

17568
val imgPresets = run {
17669
fun imgLocation(
17770
file: File,
17871
id: Int,
17972
): PresetLocation.Img {
18073
val relative = file.relativeToOrSelf(spritesFolder)
181-
val name = relative.nameWithoutExtension
182-
val path = relative.path
18374

18475
// categoryTagsSet += Tag(categoryFolder.name, listOf("nestdrop"))
18576
return PresetLocation.Img(
186-
name = name,
77+
name = relative.nameWithoutExtension,
78+
nameWithExtension = relative.name,
18779
id = id,
188-
path = path,
80+
path = relative.path,
18981
categoryPath = relative.parentFile?.toPath()?.map { it.name }.orEmpty(),
19082
)
19183
}
19284

19385
val rootPresets =
194-
spritesFolder.listFiles().orEmpty().sortedBy { it.name.lowercase() }.orEmpty().filter { it.isFile
195-
&& it.extension == "png" || it.extension == "jpg"
86+
spritesFolder.listFiles().orEmpty().sortedBy { it.name.lowercase() }.orEmpty().filter {
87+
it.isFile
88+
&& it.extension == "png" || it.extension == "jpg"
19689
}.map { file ->
19790
imgLocation(
19891
file = file,
19992
id = id++
20093
)
20194
}
20295
val categoryPresets = spritesFolder.listFiles().orEmpty().filter { it.isDirectory }.flatMap { categoryFolder ->
203-
val files = categoryFolder.walkTopDown().filter { it.isFile
204-
&& it.extension == "png" || it.extension == "jpg"
96+
val files = categoryFolder.walkTopDown().filter {
97+
it.isFile
98+
&& it.extension == "png" || it.extension == "jpg"
20599
}.map {
206100
it.relativeToOrSelf(spritesFolder)
207101
}
@@ -220,7 +114,7 @@ suspend fun scanMilkdrop() {
220114
}
221115
}
222116
rootPresets + categoryPresets
223-
}.associateBy { it.name }
117+
}.associateBy { it.nameWithExtension }
224118
// nestdropCategoryTagsSet.value = categoryTagsSet
225119

226120
presetsMap.value = milkPresets
@@ -246,4 +140,127 @@ private val logger = KotlinLogging.logger {}
246140

247141
private fun List<File>.sortFileNames() = sortedBy { file ->
248142
file.nameWithoutExtension.lowercase().replace("_", "|") + "_"
143+
}
144+
145+
fun scanFileSystemQueueForMilk(path: String): List<Preset.Milkdrop> {
146+
val presetsFolder = File(path)
147+
// id is sequential, starting at 1
148+
var id = 1
149+
150+
logger.info { "scanning $presetsFolder" }
151+
152+
fun milkLocation(
153+
file: File,
154+
id: Int,
155+
): PresetLocation.Milk {
156+
val relative = file.relativeToOrSelf(presetsFolder)
157+
val path = relative.path
158+
val previewPath = relative.resolveSibling(relative.nameWithoutExtension + ".jpg").path
159+
160+
// categoryTagsSet += Tag(categoryFolder.name, listOf("nestdrop"))
161+
return PresetLocation.Milk(
162+
name = relative.nameWithoutExtension,
163+
nameWithExtension = relative.name,
164+
id = id,
165+
path = path,
166+
previewPath = previewPath,
167+
categoryPath = relative.parentFile?.toPath()?.map { it.name }.orEmpty(),
168+
)
169+
}
170+
171+
val rootPresets =
172+
presetsFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "milk" }
173+
.sortedBy { it.name.lowercase() }.map { file ->
174+
milkLocation(
175+
file = file,
176+
id = id++
177+
)
178+
}
179+
180+
val categoryPresets = presetsFolder.listFiles().orEmpty().filter { it.isDirectory }.flatMap { categoryFolder ->
181+
val files = categoryFolder.walkTopDown().filter { it.isFile && it.extension == "milk" }.map {
182+
it.relativeToOrSelf(presetsFolder)
183+
}
184+
val sorted = files.sortedBy { it.path.lowercase() }
185+
sorted.map {
186+
milkLocation(
187+
file = it,
188+
id = id++
189+
)
190+
}
191+
}
192+
193+
return (rootPresets + categoryPresets).mapIndexed() { index, location ->
194+
Preset.Milkdrop(
195+
name = location.name,
196+
id = location.id,
197+
effects = null,
198+
overlay = null,
199+
comments = null,
200+
location = location
201+
)
202+
}
203+
// .also {
204+
// it.forEach {
205+
// logger.info { it }
206+
// }
207+
// }
208+
}
209+
210+
fun scanFileSystemQueueForImgSprites(path: String): List<Preset.ImageSprite> {
211+
val spritesFolder = File(path)
212+
// id is sequential, starting at 1
213+
var id = 1
214+
logger.info { "scanning $spritesFolder" }
215+
216+
fun imgLocation(
217+
file: File,
218+
id: Int,
219+
): PresetLocation.Img {
220+
val relative = file.relativeToOrSelf(spritesFolder)
221+
val path = relative.path
222+
223+
return PresetLocation.Img(
224+
name = relative.nameWithoutExtension,
225+
nameWithExtension = relative.name,
226+
id = id,
227+
path = path,
228+
categoryPath = relative.parentFile?.toPath()?.map { it.name }.orEmpty(),
229+
)
230+
}
231+
232+
val rootPresets =
233+
spritesFolder.listFiles().orEmpty().filter { it.isFile && it.extension == "png" || it.extension == "jpg" }
234+
.sortedBy { it.name.lowercase() }.map { file ->
235+
imgLocation(
236+
file = file,
237+
id = id++
238+
)
239+
}
240+
241+
val categoryPresets = spritesFolder.listFiles().orEmpty().filter { it.isDirectory }.flatMap { categoryFolder ->
242+
val files =
243+
categoryFolder.walkTopDown().filter { it.isFile && it.extension == "png" || it.extension == "jpg" }.map {
244+
it.relativeToOrSelf(spritesFolder)
245+
}
246+
val sorted = files.sortedBy { it.path.lowercase() }
247+
sorted.map {
248+
imgLocation(
249+
file = it,
250+
id = id++
251+
)
252+
}
253+
}
254+
255+
return (rootPresets + categoryPresets).mapIndexed() { index, location ->
256+
// logger.info { location.path }
257+
Preset.ImageSprite(
258+
name = location.name,
259+
id = location.id,
260+
effects = null,
261+
overlay = null,
262+
comments = null,
263+
location = location,
264+
)
265+
}
249266
}

src/desktopMain/kotlin/beatCounter.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ val beatProgress = MutableStateFlow(0f)
2222

2323
private val logger = KotlinLogging.logger { }
2424

25-
val bpmSynced = OscSynced.ValueSingle<Float>(
25+
val controlBeat = OscSynced.ValueSingle<Float>(
26+
"/Controls/sBeat",
27+
64f, send = false,
28+
target = OscSynced.Target.Nestdrop
29+
).also {
30+
it.logReceived = false
31+
}
32+
33+
val controlBpm = OscSynced.ValueSingle<Float>(
2634
"/Controls/sBpm",
2735
120f, send = false,
2836
target = OscSynced.Target.Nestdrop
@@ -36,9 +44,8 @@ val secondsPerBeat = MutableStateFlow(0.5f)
3644
val beatCounter = MutableStateFlow(0.0)
3745

3846
suspend fun startBeatCounter() {
39-
4047
@OptIn(FlowPreview::class)
41-
bpmSynced
48+
controlBpm
4249
.flow
4350
.sample(100.milliseconds)
4451
.map { bpm ->
@@ -55,11 +62,11 @@ suspend fun startBeatCounter() {
5562

5663
combine(
5764
secondsPerBeat,
58-
beatFrame,
65+
// beatFrame,
5966
deck.presetSwitching.transitTimeSync,
6067
// deck.presetSwitching.transitTimeBeatframeFraction,
6168
deck.presetSwitching.transitTimeBeats,
62-
) { secondsPerBeat, beatFrame, enabled, transitTimeBeats ->
69+
) { secondsPerBeat, enabled, transitTimeBeats ->
6370
if(enabled) {
6471
secondsPerBeat * transitTimeBeats
6572
} else {
@@ -129,7 +136,7 @@ suspend fun startBeatCounter() {
129136
val now = Clock.System.now()
130137
val timeDelta = now - lastLoop
131138
lastLoop = now
132-
val beatsPerMillisecond = bpmSynced.flow.value / 60_000.0
139+
val beatsPerMillisecond = controlBpm.flow.value / 60_000.0
133140
val beatsInDuration = beatsPerMillisecond * timeDelta.inWholeMilliseconds
134141
beatCounter.value += beatsInDuration
135142
}

0 commit comments

Comments
 (0)