Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
ShirasawaSama committed Dec 10, 2023
1 parent 6ad911b commit c6070f3
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.eimsound.daw.commons.actions.Restorable
import com.eimsound.daw.commons.json.*
import com.eimsound.dsp.data.DefaultEnvelopePointList
import com.eimsound.dsp.data.EnvelopePointList
import com.eimsound.dsp.data.fromJson
import com.eimsound.dsp.data.putNotDefault
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -78,8 +77,7 @@ data class DefaultHandledParameter(
}

override fun fromJson(json: JsonElement) {
json as JsonObject
points.fromJson(json["points"])
json.jsonObject["points"]?.let { points.fromJson(it) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ inline fun JsonElement.asFloat() = jsonPrimitive.float
inline fun JsonElement.asColor() = Color(jsonPrimitive.long.toULong())
val JsonIgnoreDefaults = Json { ignoreUnknownKeys = true; encodeDefaults = false }
@OptIn(ExperimentalSerializationApi::class)
val JsonPrettier = Json { ignoreUnknownKeys = true; encodeDefaults = false; prettyPrint = true; prettyPrintIndent = " " }
val JsonPrettier = Json { ignoreUnknownKeys = true; encodeDefaults = false; prettyPrint = true; prettyPrintIndent = "" }

interface JsonSerializable {
fun toJson(): JsonElement
Expand Down Expand Up @@ -56,6 +56,35 @@ fun JsonSerializable.fromJsonFile(file: Path) {
fromJson(Json.parseToJsonElement(file.inputStream().use { it.reader().readText() }))
}

inline fun <T: JsonSerializable> MutableCollection<T>.fromJson(json: JsonArray?, block: () -> T) {
clear()
if (json != null) addAll(json.map { block().apply { fromJson(it) } })
}
inline fun <K, T: JsonSerializable> MutableMap<K, T>.fromJson(json: JsonObject?, block: (String) -> Pair<K, T>) {
clear()
if (json != null) putAll(json.map { (k, v) -> block(k).apply { second.fromJson(v) } })
}
inline fun <T: JsonSerializable> MutableMap<Int, T>.fromIntKeyJson(json: JsonObject?, block: () -> T) {
clear()
if (json != null) putAll(json.map { (k, v) -> k.toInt() to block().apply { fromJson(v) } })
}
inline fun <T: JsonSerializable> MutableMap<Float, T>.fromFloatKeyJson(json: JsonObject?, block: () -> T) {
clear()
if (json != null) putAll(json.map { (k, v) -> k.toFloat() to block().apply { fromJson(v) } })
}
inline fun <T: JsonSerializable> MutableMap<Double, T>.fromDoubleKeyJson(json: JsonObject?, block: () -> T) {
clear()
if (json != null) putAll(json.map { (k, v) -> k.toDouble() to block().apply { fromJson(v) } })
}
inline fun <T: JsonSerializable> MutableMap<String, T>.fromStringKeyJson(json: JsonObject?, block: () -> T) {
clear()
if (json != null) putAll(json.map { (k, v) -> k to block().apply { fromJson(v) } })
}
fun <T: JsonSerializable> Collection<T>.toJsonArray() = JsonArray(map { it.toJson() })
fun <K : Any> Map<K, JsonSerializable>.toJson() = JsonObject(mutableMapOf<String, JsonElement>().also { map ->
this@toJson.forEach { (k, v) -> map[k.toString()] = v.toJson() }
})

fun Path.toJsonElement() = Json.parseToJsonElement(inputStream().use { it.reader().readText() })
@OptIn(ExperimentalSerializationApi::class)
inline fun <reified T> File.toJson() = Json.decodeFromStream<T>(inputStream())
Expand All @@ -70,6 +99,9 @@ inline fun JsonObjectBuilder.putNotDefault(key: String, value: Collection<String
inline fun JsonObjectBuilder.putNotDefault(key: String, value: List<JsonElement>?) {
if (!value.isNullOrEmpty()) put(key, JsonArray(value))
}
inline fun <K: Any> JsonObjectBuilder.putNotDefault(key: String, value: Map<K, JsonSerializable>?) {
if (!value.isNullOrEmpty()) put(key, value.toJson())
}
inline fun JsonObjectBuilder.putNotDefault(key: String, value: Collection<JsonSerializable>?) {
if (!value.isNullOrEmpty()) put(key, JsonArray(value.map(JsonSerializable::toJson)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fun EditorGrid(
var stepsOutlineColor = beatsOutlineColor.copy(0.8F)
if (barsOutlineColor == beatsOutlineColor) {
beatsOutlineColor = stepsOutlineColor
stepsOutlineColor = stepsOutlineColor.copy(0.4F)
stepsOutlineColor = stepsOutlineColor.copy(0.3F)
}
val eightBarsOutlineColor = MaterialTheme.colorScheme.outline.copy(0.7F)
val rangeColor = MaterialTheme.colorScheme.primary.copy(0.07F)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastForEach
import com.eimsound.daw.api.EchoInMirror
import com.eimsound.daw.api.EditorTool
import com.eimsound.daw.commons.json.JsonIgnoreDefaults
import com.eimsound.daw.commons.MultiSelectableEditor
import com.eimsound.daw.commons.SerializableEditor
import com.eimsound.daw.commons.json.fromJsonString
import com.eimsound.daw.commons.json.toJsonString
import com.eimsound.daw.components.utils.EditAction
import com.eimsound.daw.components.utils.calculateContrastRatio
import com.eimsound.daw.utils.*
import com.eimsound.dsp.data.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlin.collections.ArrayDeque
import kotlin.math.*

Expand Down Expand Up @@ -129,6 +129,9 @@ private fun Density.getSelectedPoint(
if (checkIsSelectedPoint(points.getOrNull(pointIndex - 1))) pointIndex - 1 else -1
}

/**
* @see com.eimsound.daw.actions.GlobalEnvelopeEditorEventHandler
*/
interface EnvelopeEditorEventHandler {
fun onAddPoints(editor: EnvelopeEditor, points: BaseEnvelopePointList)
fun onPastePoints(editor: EnvelopeEditor, points: BaseEnvelopePointList): BaseEnvelopePointList
Expand Down Expand Up @@ -199,13 +202,12 @@ class EnvelopeEditor(
selectedPoints.clear()
selectedPoints.addAll(result)
}
override fun copyAsString() = if (selectedPoints.isEmpty()) "" else JsonIgnoreDefaults.encodeToString(
SerializableEnvelopePointList(EchoInMirror.currentPosition.ppq, copyAsObject())
)
override fun copyAsString() = if (selectedPoints.isEmpty()) ""
else SerializableEnvelopePointList(EchoInMirror.currentPosition.ppq, copyAsObject()).toJsonString()

override fun pasteFromString(value: String) {
try {
val paste = JsonIgnoreDefaults.decodeFromString<SerializableEnvelopePointList>(value)
val paste = SerializableEnvelopePointList().apply { fromJsonString(value) }
val factor = EchoInMirror.currentPosition.ppq.toDouble() / paste.ppq
paste.points.fastForEach { it.time = (it.time * factor).roundToInt() }
val result = eventHandler?.onPastePoints(this, paste.points) ?: return
Expand Down Expand Up @@ -263,14 +265,17 @@ class EnvelopeEditor(
event.changes[0].position,
if (isPointExists) points[pointId] else null,
)
continue
return
}
if (isPencil) {
selectedPoints.clear()
action = EditAction.BRUSH
break
} else if (isPointExists) {
if (!selectedPoints.contains(points[pointId])) selectedPoints.clear()
if (!selectedPoints.contains(points[pointId])) {
selectedPoints.clear()
selectedPoints.add(points[pointId])
}
currentAdjustingPoint = pointId
action = EditAction.RESIZE
break
Expand All @@ -287,13 +292,15 @@ class EnvelopeEditor(
hoveredIndex = tmpId
if (event.buttons.isSecondaryPressed) {
floatingLayerProvider.openMenu(event.changes[0].position, points[tmpId])
continue
return
}
action = EditAction.MOVE
break
}
continue
return
}

else -> return
}
} while (!event.changes.fastAll(PointerInputChange::changedToDownIgnoreConsumed))
val down = event.changes[0]
Expand Down Expand Up @@ -492,6 +499,8 @@ class EnvelopeEditor(
val range = valueRange.range
val hoveredId = hoveredIndex

println(selectedPoints.firstOrNull())

// draw points
val tmpOffsetX = offsetX
val tmpOffsetY = offsetY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.eimsound.audioprocessor.convertSamplesToPPQ
import com.eimsound.daw.api.*
import com.eimsound.daw.api.clips.*
import com.eimsound.daw.api.processor.Track
import com.eimsound.daw.commons.json.fromIntKeyJson
import com.eimsound.daw.components.EnvelopeEditor
import com.eimsound.daw.impl.clips.midi.editor.DefaultMidiClipEditor
import com.eimsound.daw.utils.lowerBound
Expand Down Expand Up @@ -46,7 +47,7 @@ class MidiClipImpl(factory: ClipFactory<MidiClip>) : AbstractClip<MidiClip>(fact
put("id", id)
put("factory", factory.name)
putNotDefault("notes", notes)
putNotDefault("events", Json.encodeToJsonElement<MidiCCEvents>(events))
putNotDefault("events", events)
}

override fun fromJson(json: JsonElement) {
Expand All @@ -60,9 +61,7 @@ class MidiClipImpl(factory: ClipFactory<MidiClip>) : AbstractClip<MidiClip>(fact
}
json["events"]?.let {
val e2: MutableMidiCCEvents = hashMapOf()
Json.decodeFromJsonElement<MidiCCEvents>(it).forEach { (id, points) ->
e2[id] = DefaultEnvelopePointList().apply { addAll(points) }
}
events.fromIntKeyJson(it.jsonObject) { DefaultEnvelopePointList() }
events.putAll(e2)
}
}
Expand Down
74 changes: 47 additions & 27 deletions dsp/src/commonMain/kotlin/com/eimsound/dsp/data/Envelope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package com.eimsound.dsp.data

import androidx.compose.runtime.mutableStateOf
import com.eimsound.daw.commons.IManualState
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import com.eimsound.daw.commons.json.*
import kotlinx.serialization.json.*
import kotlin.math.absoluteValue

Expand Down Expand Up @@ -54,47 +51,68 @@ enum class EnvelopeType {
}
}

@Serializable
data class EnvelopePoint(
var time: Int,
var value: Float,
var tension: Float = 0F,
class EnvelopePoint(
var time: Int = 0,
var value: Float = 0F,
tension: Float = 0F,
var type: EnvelopeType = EnvelopeType.SMOOTH
): Comparable<EnvelopePoint> {
): Comparable<EnvelopePoint>, JsonSerializable {
override fun compareTo(other: EnvelopePoint) = time.compareTo(other.time)
var tension = tension
set(value) { field = value.coerceIn(-1F, 1F) }
fun copy(time: Int = this.time, value: Float = this.value, tension: Float = this.tension, type: EnvelopeType = this.type) =
EnvelopePoint(time, value, tension, type)

override fun toJson() = buildJsonArray {
add(time)
add(value)
add(tension)
if (type != EnvelopeType.SMOOTH) add(type.ordinal)
}

override fun fromJson(json: JsonElement) {
val array = json.jsonArray
time = array[0].asInt()
value = array[1].asFloat()
tension = array[2].asFloat()
type = if (array.size > 3) EnvelopeType.entries[array[3].asInt()] else EnvelopeType.SMOOTH
}

override fun toString() = "EnvelopePoint(time=$time, value=$value, tension=$tension, type=$type)"
}

typealias BaseEnvelopePointList = List<EnvelopePoint>
typealias MutableBaseEnvelopePointList = MutableList<EnvelopePoint>

@Serializable
data class SerializableEnvelopePointList(val ppq: Int, val points: BaseEnvelopePointList) {
@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@EncodeDefault
val className = "EnvelopePointList"
class SerializableEnvelopePointList(
var ppq: Int = 96, var points: BaseEnvelopePointList = emptyList()
): JsonSerializable {
override fun toJson() = buildJsonObject {
put("ppq", ppq)
put("points", points)
put("className", "EnvelopePointList")
}
override fun fromJson(json: JsonElement) {
val obj = json.jsonObject
ppq = obj["ppq"]?.asInt() ?: 96
points = obj["points"]?.let { mutableListOf<EnvelopePoint>().apply { fromJson(it) } } ?: emptyList()
}
}

fun JsonObjectBuilder.put(key: String, value: BaseEnvelopePointList?) {
put(key, Json.encodeToJsonElement(value))
if (value == null) put(key, JsonArray(emptyList()))
else put(key, value.toJsonArray())
}
fun JsonObjectBuilder.putNotDefault(key: String, value: BaseEnvelopePointList?) {
if (!value.isNullOrEmpty()) put(key, Json.encodeToJsonElement(value))
if (!value.isNullOrEmpty()) put(key, value.toJsonArray())
}

@Serializable
sealed interface EnvelopePointList : MutableBaseEnvelopePointList, IManualState, BaseEnvelopePointList {
sealed interface EnvelopePointList : MutableBaseEnvelopePointList, IManualState, JsonSerializable {
fun copy(): EnvelopePointList
fun split(time: Int, offsetStart: Int = 0): Pair<BaseEnvelopePointList, BaseEnvelopePointList>
fun getValue(position: Int, defaultValue: Float = 0F): Float
}

fun EnvelopePointList.fromJson(json: JsonElement?) {
clear()
if (json != null) addAll(Json.decodeFromJsonElement<BaseEnvelopePointList>(json))
update()
}

fun BaseEnvelopePointList.toMutableEnvelopePointList() = DefaultEnvelopePointList().apply { addAll(this@toMutableEnvelopePointList) }

val PAN_RANGE = -1F..1F
Expand All @@ -112,7 +130,6 @@ private inline fun <T> List<T>.lowerBound(comparator: (T) -> Boolean): Int {
return l
}

@Serializable
class DefaultEnvelopePointList : EnvelopePointList, ArrayList<EnvelopePoint>() {
@Transient
private var modification = mutableStateOf(0)
Expand Down Expand Up @@ -164,6 +181,9 @@ class DefaultEnvelopePointList : EnvelopePointList, ArrayList<EnvelopePoint>() {

override fun update() { modification.value++ }
override fun read() { modification.value }

override fun fromJson(json: JsonElement) { fromJson(json.jsonArray) { EnvelopePoint() } }
override fun toJson() = toJsonArray()
}

@Suppress("unused")
Expand Down

0 comments on commit c6070f3

Please sign in to comment.