diff --git a/.gitignore b/.gitignore index 72b8d7c..0dbf9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/ /run/ /.idea/ +/src/main/resources/ui/* diff --git a/build.gradle.kts b/build.gradle.kts index a353de1..17617fb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") kotlin("plugin.serialization") + id("com.github.johnrengelman.shadow") version "7.1.2" application } @@ -11,7 +12,7 @@ version = "1.0-SNAPSHOT" allprojects { tasks.withType { - kotlinOptions.jvmTarget = "16" + kotlinOptions.jvmTarget = "19" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.contracts.ExperimentalContracts" diff --git a/src/main/kotlin/de/moritzruth/lampenfieber/Main.kt b/src/main/kotlin/de/moritzruth/lampenfieber/Main.kt index e7c7616..07a0986 100644 --- a/src/main/kotlin/de/moritzruth/lampenfieber/Main.kt +++ b/src/main/kotlin/de/moritzruth/lampenfieber/Main.kt @@ -1,8 +1,9 @@ package de.moritzruth.lampenfieber +import de.moritzruth.lampenfieber.device.CoemarProWash +import de.moritzruth.lampenfieber.device.FuturelightDmh160 import de.moritzruth.lampenfieber.device.SimpleDimmer import de.moritzruth.lampenfieber.device.StairvilleTriLedBar -import de.moritzruth.lampenfieber.device.Wash import de.moritzruth.theaterdsl.dmx.DmxAddress import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb import de.moritzruth.theaterdsl.show.StepCue @@ -12,11 +13,12 @@ import de.moritzruth.theaterdsl.value.Color import de.moritzruth.theaterdsl.value.degrees import de.moritzruth.theaterdsl.value.percent import kotlinx.collections.immutable.persistentSetOf +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds -val bar = StairvilleTriLedBar(DmxAddress(400u)) - object FrontLights { val left = listOf(SimpleDimmer(DmxAddress(1u)), SimpleDimmer(DmxAddress(2u))) val center = listOf(SimpleDimmer(DmxAddress(4u)), SimpleDimmer(DmxAddress(5u))) @@ -27,62 +29,174 @@ object FrontLights { val spotLeft = SimpleDimmer(DmxAddress(10u)) val spotRight = SimpleDimmer(DmxAddress(11u)) -// Nebel: SF-1500 - -object Washs { - val left = Wash(DmxAddress(85u)) - val right = Wash(DmxAddress(101u)) - val all = listOf(left, right) +object Tops { + val left = FuturelightDmh160(DmxAddress(37u)) + val right = FuturelightDmh160(DmxAddress(53u)) + val both = listOf(left, right) } -val devices = persistentSetOf(bar, *FrontLights.all.toTypedArray(), spotLeft, spotRight, *Washs.all.toTypedArray()) +object Washs { + val left = CoemarProWash(DmxAddress(85u), false) + val right = CoemarProWash(DmxAddress(101u), true) + val both = listOf(left, right) +} +val bar = StairvilleTriLedBar(DmxAddress(121u)) // TODO: Change address on the device + +val devices = persistentSetOf(*FrontLights.all.toTypedArray(), spotLeft, spotRight, *Tops.both.toTypedArray(), *Washs.both.toTypedArray(), bar) + +@Suppress("DuplicatedCode") val show = createShow { act("Erster Akt") { - scene("I") { - step { - trigger = StepCue.MusicStart("Lampenfieber", 69.seconds) + scene("Intro") { + step(StepCue.MusicStart("Lampenfieber", 5.minutes + 30.seconds)) { + props { + + } onRun { - // Lichteffekte in der Aula - spotRight.brightness.static(100.percent) + Washs.both.forEach { + it.pointAtCeiling() + it.colorWheelMode.static(CoemarProWash.ColorWheelMode.White) + } + } + } + + lightStep(StepCue.Custom("Gitarren-Einsatz (Takt 17)")) { + Washs.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + + FrontLights.all.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds, start = 20.percent) } + + Tops.both.forEach { it.startRoomMovement(5.5) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + launch { + delay(500) + Washs.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + } + + Tops.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + Washs.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + + FrontLights.all.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds, start = 20.percent) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + launch { + delay(500) + Washs.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + } + + Tops.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + Washs.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + + FrontLights.all.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds, start = 20.percent) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + launch { + delay(500) + Washs.both.forEach { + it.brightness.pulseOnce(500.milliseconds, 5.seconds) + } + } + + Tops.both.forEach { it.brightness.pulseOnce(500.milliseconds, 5.seconds) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + Washs.both.forEach { it.brightness.fade(100.percent, 500.milliseconds) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + Tops.both.forEach { it.brightness.fade(100.percent, 500.milliseconds) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + FrontLights.center.forEach { it.brightness.pulseOnce(500.milliseconds, 3.seconds, end = 50.percent) } + } + + lightStep(StepCue.Custom("Gitarren-Ton")) { + FrontLights.center.forEach { it.brightness.fade(0.percent, 30.seconds) } + Washs.both.forEach { it.colorWheelMode.static(CoemarProWash.ColorWheelMode.Rotate(20.percent)) } + Tops.both.forEach { it.colorWheelMode.static(FuturelightDmh160.ColorWheelMode.Rotate(20.percent)) } + } + + step(StepCue.Text("Chor", "fangen wir an")) { + onRun { + FrontLights.center.forEach { it.brightness.fade(0.percent, 3.seconds) } + Washs.both.forEach { it.brightness.fade(0.percent, 3.seconds) } + Tops.both.forEach { it.brightness.fade(0.percent, 3.seconds) } } } } - scene("I.1") { - step { - trigger = StepCue.MusicEnd + scene("1.1") { + step(StepCue.MusicEnd) { + actors { + +"Richy / durch den Mittelgang" + +"Christine / steht auf der Vorbühne" + +"Andreas / steht auf der Vorbühne" + +"Steffi / steht auf der Vorbühne" + +"Jakob / steht auf der Vorbühne" + +"Tina / steht auf der Vorbühne" + } onRun { - spotRight.brightness.static(0.percent) - + spotRight.brightness.fade(100.percent, 6.seconds) FrontLights.center.forEach { it.brightness.fade(75.percent, 10.seconds) } + Washs.both.forEach { + it.pointAtStageCenter() + it.colorWheelMode.static(CoemarProWash.ColorWheelMode.White) + } } } - step { - trigger = StepCue.MusicStart("Rap", 69.seconds) + step(StepCue.MusicStart("Rap", 2.minutes + 30.seconds)) { + actors { + // Rapper, Tänzer + } onRun { - // sehr viel + // Nebel, rot-orange faden FrontLights.center.forEach { it.brightness.fade(20.percent, 10.seconds) } - bar.color.static(Color(100.degrees)) - bar.brightness.fade(75.percent, 10.seconds) + bar.brightness.fade(50.percent, 10.seconds) + bar.color.fadeRandomAround(20.degrees, 10.degrees, 1500.milliseconds) } } - step { - trigger = StepCue.MusicEnd + lightStep(StepCue.Custom("T5 S2")) { + Washs.both.forEach { + it.brightness.static(100.percent) + it.beamAngle.sine(0.percent, 100.percent, 3.seconds) + } + } - onRun { - FrontLights.center.forEach { it.brightness.fade(0.percent, 10.seconds) } - bar.brightness.fade(0.percent, 2.seconds) + lightStep(StepCue.Custom("Schlussschlag")) { + Washs.both.forEach { + it.beamAngle.fade(100.percent, 200.milliseconds) + it.brightness.fade(0.percent, 4.seconds) + } + + bar.brightness.fade(0.percent, 1.seconds) + FrontLights.center.forEach { it.brightness.pulseOnce(200.milliseconds, 4.seconds, end = 50.percent) } + } + + step(StepCue.MusicEnd) { + actors { + // Rapper, Tänzer ab } } } - scene("I.2") { + scene("1.2") { step { trigger = StepCue.Stub @@ -100,7 +214,7 @@ val show = createShow { } } - scene("I.3") { + scene("1.3") { step { trigger = StepCue.Stub @@ -111,7 +225,7 @@ val show = createShow { } } - scene("I.3") { + scene("1.3") { step { trigger = StepCue.Custom("Bühne erreicht") @@ -124,7 +238,7 @@ val show = createShow { // David spielt: Licht links } - scene("I.5") { + scene("1.5") { step { trigger = StepCue.Stub @@ -132,7 +246,7 @@ val show = createShow { FrontLights.all.forEach { it.brightness.fade(0.percent, 10.seconds) } FrontLights.right.forEach { it.brightness.fade(50.percent, 10.seconds) } - Washs.all.forEach { + Washs.both.forEach { it.brightness.fade(75.percent, 10.seconds) // Blau } @@ -148,7 +262,7 @@ val show = createShow { onRun { FrontLights.right.forEach { it.brightness.fade(0.percent, 10.seconds) } - Washs.all.forEach { + Washs.both.forEach { it.brightness.fade(100.percent, 10.seconds) } @@ -161,13 +275,13 @@ val show = createShow { onRun { FrontLights.right.forEach { it.brightness.off() } - Washs.all.forEach { it.brightness.off() } + Washs.both.forEach { it.brightness.off() } // Nebel aus } } } - scene("II.1") { + scene("2.1") { step { trigger = StepCue.MusicStart("Computerspiel", 69.seconds) @@ -177,7 +291,7 @@ val show = createShow { } } - scene("II.2") { + scene("2.2") { step { trigger = StepCue.Stub @@ -188,7 +302,7 @@ val show = createShow { } } - scene("II.3") { + scene("2.3") { step { trigger = StepCue.Stub @@ -240,7 +354,7 @@ val show = createShow { } } - scene("III.1") { + scene("3.1") { step { trigger = StepCue.Stub @@ -253,7 +367,7 @@ val show = createShow { } } - scene("III.2") { + scene("3.2") { step { trigger = StepCue.Stub @@ -272,14 +386,14 @@ val show = createShow { } } - scene("III.3") { + scene("3.3") { step { trigger = StepCue.MusicStart("Tischballet", 69.seconds) // Instrumental, Umbau zu Musiksaal } } - scene("III.4") { + scene("3.4") { step { trigger = StepCue.MusicEnd // Vorhang auf @@ -298,7 +412,7 @@ val show = createShow { } } - scene("III.5") { + scene("3.5") { step { trigger = StepCue.Custom("Ende") @@ -311,7 +425,7 @@ val show = createShow { // Umbau-Musik } - scene("III.6") { + scene("3.6") { step { trigger = StepCue.Custom("Vorhang auf, Musik Ende") @@ -322,7 +436,7 @@ val show = createShow { } } - scene("III.7") { + scene("3.7") { step { trigger = StepCue.Stub @@ -333,7 +447,7 @@ val show = createShow { } } - scene("III.8") { + scene("3.8") { step { trigger = StepCue.MusicEnd @@ -372,7 +486,7 @@ val show = createShow { // RnR entfällt - scene("IV.1") { + scene("4.1") { step { trigger = StepCue.MusicStart("Pause", 69.seconds) } @@ -413,7 +527,7 @@ val show = createShow { } } - scene("IV.2") { + scene("4.2") { step { trigger = StepCue.Stub @@ -432,7 +546,7 @@ val show = createShow { } } - scene("IV.3") { + scene("4.3") { step { trigger = StepCue.Stub @@ -443,7 +557,7 @@ val show = createShow { } } - scene("IV.4") { + scene("4.4") { step { trigger = StepCue.Custom("Auftritt David") @@ -475,7 +589,7 @@ val show = createShow { } } - scene("IV.5") { + scene("4.5") { step { trigger = StepCue.Stub @@ -493,7 +607,7 @@ val show = createShow { } } - scene("IV.6") { + scene("4.6") { step { trigger = StepCue.MusicEnd @@ -516,7 +630,7 @@ val show = createShow { } } - scene("IV.7") { + scene("4.7") { step { trigger = StepCue.Stub @@ -545,7 +659,7 @@ val show = createShow { } } - scene("IV.8") { + scene("4.8") { step { trigger = StepCue.MusicStart("Angstballet", 69.seconds) @@ -559,7 +673,7 @@ val show = createShow { } } - scene("IV.9") { + scene("4.9") { step { trigger = StepCue.Stub @@ -577,7 +691,7 @@ val show = createShow { } } - scene("IV.10") { + scene("4.10") { step { trigger = StepCue.Stub diff --git a/src/main/kotlin/de/moritzruth/lampenfieber/device/CoemarProWash.kt b/src/main/kotlin/de/moritzruth/lampenfieber/device/CoemarProWash.kt new file mode 100644 index 0000000..e5f8fc6 --- /dev/null +++ b/src/main/kotlin/de/moritzruth/lampenfieber/device/CoemarProWash.kt @@ -0,0 +1,92 @@ +package de.moritzruth.lampenfieber.device + +import de.moritzruth.theaterdsl.device.AngleDV +import de.moritzruth.theaterdsl.device.ConcreteDV +import de.moritzruth.theaterdsl.device.Device +import de.moritzruth.theaterdsl.device.PercentageDV +import de.moritzruth.theaterdsl.dmx.DmxAddress +import de.moritzruth.theaterdsl.dmx.DmxDataWriter +import de.moritzruth.theaterdsl.dmx.DmxValue +import de.moritzruth.theaterdsl.value.Percentage +import de.moritzruth.theaterdsl.value.degrees +import de.moritzruth.theaterdsl.value.percent +import kotlinx.collections.immutable.persistentSetOf +import kotlin.math.roundToInt + +class CoemarProWash(override val firstChannel: DmxAddress, private val isRight: Boolean) : Device { + companion object { + private const val PAN_LIMIT = 530f // degrees + private const val TILT_LIMIT = 284f // degrees + } + + override val numberOfChannels: UInt = 16u + + override fun writeDmxData(writer: DmxDataWriter) { + val pan = (pan.getCurrentValue().degrees.mod(PAN_LIMIT) / PAN_LIMIT * 65536).roundToInt().toUShort() + val tilt = (tilt.getCurrentValue().degrees.mod(TILT_LIMIT) / TILT_LIMIT * 65536).roundToInt().toUShort() + + writer.writeHighByte(pan) + writer.writeHighByte(tilt) + writer.writeLowByte(pan) + writer.writeLowByte(tilt) + writer.writeRaw(DmxValue(0u)) // pan/tilt speed = maximum + writer.writeRaw(DmxValue(255u)) // fan speed and lamp control = no function + writer.writeRaw(colorWheelMode.getCurrentValue().getDmxValue()) + writer.writePercentage(cyan.getCurrentValue()) + writer.writePercentage(magenta.getCurrentValue()) + writer.writePercentage(yellow.getCurrentValue()) + writer.writeRaw(DmxValue(0u)) // dimmer and color speed = maximum + writer.writeRaw(DmxValue(0u)) // colour macro = off + writer.writeRaw(DmxValue((beamAngle.getCurrentValue().value * 109 + 71).roundToInt().toUByte())) + writer.writeRaw(DmxValue(0u)) // no function + writer.writeRaw(DmxValue((strobeSpeed.getCurrentValue().value * 32 + 63).roundToInt().toUByte())) + writer.writePercentage(brightness.getCurrentValue()) + } + + sealed interface ColorWheelMode { + fun getDmxValue(): DmxValue + sealed class Simple(dmxValue: Int) : ColorWheelMode { + val dmxValue = DmxValue(dmxValue.toUByte()) + override fun getDmxValue() = dmxValue + } + + class Rotate(val speed: Percentage, val backwards: Boolean = false) : ColorWheelMode { + override fun getDmxValue() = run { + if (backwards) ((1f - speed.value) * 62).roundToInt() + 193 + else (speed.value * 62).roundToInt() + 128 + }.let { DmxValue(it.toUByte()) } + } + + object White : Simple(0) + object Red : Simple(20) + object DarkBlue : Simple(48) + object Green : Simple(70) + object WarmWhite : Simple(81) + object Violet : Simple(118) + } + + val pan = AngleDV() + val tilt = AngleDV() + val brightness = PercentageDV() + val beamAngle = PercentageDV(100.percent) + val strobeSpeed = PercentageDV(0.percent) + + val colorWheelMode = ConcreteDV(ColorWheelMode.White) + val cyan = PercentageDV() + val magenta = PercentageDV() + val yellow = PercentageDV() + + override val dvs = persistentSetOf( + pan, tilt, brightness, beamAngle, strobeSpeed, colorWheelMode, cyan, magenta, yellow + ) + + fun pointAtStageCenter() { + tilt.static(80.degrees) + pan.static(if (isRight) 270.degrees else 90.degrees) + } + + fun pointAtCeiling() { + tilt.static(180.degrees) + pan.static(0.degrees) + } +} \ No newline at end of file diff --git a/src/main/kotlin/de/moritzruth/lampenfieber/device/FuturelightDmh160.kt b/src/main/kotlin/de/moritzruth/lampenfieber/device/FuturelightDmh160.kt index 631b4a7..ce1b9cf 100644 --- a/src/main/kotlin/de/moritzruth/lampenfieber/device/FuturelightDmh160.kt +++ b/src/main/kotlin/de/moritzruth/lampenfieber/device/FuturelightDmh160.kt @@ -1,13 +1,18 @@ package de.moritzruth.lampenfieber.device import de.moritzruth.theaterdsl.device.AngleDV +import de.moritzruth.theaterdsl.device.ConcreteDV import de.moritzruth.theaterdsl.device.Device import de.moritzruth.theaterdsl.device.PercentageDV import de.moritzruth.theaterdsl.dmx.DmxAddress import de.moritzruth.theaterdsl.dmx.DmxDataWriter -import de.moritzruth.theaterdsl.dmx.get16BitChannelValues +import de.moritzruth.theaterdsl.dmx.DmxValue +import de.moritzruth.theaterdsl.value.Percentage +import de.moritzruth.theaterdsl.value.degrees import kotlinx.collections.immutable.persistentSetOf import kotlin.math.roundToInt +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds class FuturelightDmh160(override val firstChannel: DmxAddress) : Device { companion object { @@ -18,22 +23,72 @@ class FuturelightDmh160(override val firstChannel: DmxAddress) : Device { override val numberOfChannels: UInt = 16u override fun writeDmxData(writer: DmxDataWriter) { - val fullPan = (pan.getCurrentValue().degrees.mod(PAN_LIMIT) / PAN_LIMIT * 65536).roundToInt() - val (mPan, lPan) = fullPan.get16BitChannelValues() + val pan = (pan.getCurrentValue().degrees.mod(PAN_LIMIT) / PAN_LIMIT * 65536).roundToInt().toUShort() + val tilt = (tilt.getCurrentValue().degrees.mod(TILT_LIMIT) / TILT_LIMIT * 65536).roundToInt().toUShort() - val fullTilt = (tilt.getCurrentValue().degrees.mod(TILT_LIMIT) / TILT_LIMIT * 65536).roundToInt() - val (mTilt, lTilt) = fullTilt.get16BitChannelValues() - - writer.writeRaw(mPan) - writer.writeRaw(lPan) - writer.writeRaw(mTilt) - writer.writeRaw(lTilt) + writer.writeHighByte(pan) + writer.writeLowByte(pan) + writer.writeHighByte(tilt) + writer.writeLowByte(tilt) + writer.writeRaw(DmxValue(0u)) // pan/tilt speed + writer.writeRaw(DmxValue(0u)) // blackout while moving = off + writer.writeRaw(DmxValue((strobeSpeed.getCurrentValue().value * 32 + 63).roundToInt().toUByte())) writer.writePercentage(brightness.getCurrentValue()) + writer.writeRaw(colorWheelMode.getCurrentValue().getDmxValue()) + writer.writeRaw(DmxValue(0u)) // gobo wheel = open + writer.writeRaw(DmxValue(0u)) // gobo wheel = open + writer.writeRaw(prismMode.getCurrentValue().dmxValue) + writer.writeRaw(DmxValue((prismRotationSpeed.getCurrentValue().value * 123 + 4).roundToInt().toUByte())) + writer.writeRaw(DmxValue(255u)) // focus = maximum distance + writer.writeRaw(DmxValue(0u)) // iris = ? + writer.writeRaw(DmxValue(0u)) // functions = noop + } + + sealed interface ColorWheelMode { + fun getDmxValue(): DmxValue + sealed class Simple(dmxValue: Int) : ColorWheelMode { + val dmxValue = DmxValue(dmxValue.toUByte()) + override fun getDmxValue() = dmxValue + } + + class Rotate(val speed: Percentage, val backwards: Boolean = false) : ColorWheelMode { + override fun getDmxValue() = run { + if (backwards) (speed.value * 15).roundToInt() + 240 + else (speed.value * 15).roundToInt() + 224 + }.let { DmxValue(it.toUByte()) } + } + + object White : Simple(0) + object Pink : Simple(0) + object Red : Simple(0) + object Orange : Simple(0) + object Green : Simple(0) + object LightBlue : Simple(0) + object DarkBlue : Simple(0) + object Violet : Simple(0) + } + + enum class PrismMode(val dmxValue: DmxValue) { + OPEN(DmxValue(0u)), + FACETS_3(DmxValue(64u)), + FACETS_8(DmxValue(128u)), + FROST(DmxValue(192u)) } val pan = AngleDV() val tilt = AngleDV() val brightness = PercentageDV() + val colorWheelMode = ConcreteDV(ColorWheelMode.White) + val strobeSpeed = PercentageDV() + val prismMode = ConcreteDV(PrismMode.OPEN) + val prismRotationSpeed = PercentageDV() - override val dvs = persistentSetOf(brightness) + fun startRoomMovement(rotationsPerMinute: Double) { + pan.sine(0.degrees, 360.degrees, 1.minutes / rotationsPerMinute) + tilt.fade(120.degrees, 3.seconds) + } + + override val dvs = persistentSetOf( + pan, tilt, brightness, colorWheelMode, strobeSpeed + ) } \ No newline at end of file diff --git a/src/main/kotlin/de/moritzruth/lampenfieber/device/Wash.kt b/src/main/kotlin/de/moritzruth/lampenfieber/device/Wash.kt deleted file mode 100644 index 2d86e42..0000000 --- a/src/main/kotlin/de/moritzruth/lampenfieber/device/Wash.kt +++ /dev/null @@ -1,67 +0,0 @@ -package de.moritzruth.lampenfieber.device - -import de.moritzruth.theaterdsl.device.AngleDV -import de.moritzruth.theaterdsl.device.Device -import de.moritzruth.theaterdsl.device.PercentageDV -import de.moritzruth.theaterdsl.dmx.DmxAddress -import de.moritzruth.theaterdsl.dmx.DmxDataWriter -import de.moritzruth.theaterdsl.dmx.DmxValue -import de.moritzruth.theaterdsl.dmx.get16BitChannelValues -import de.moritzruth.theaterdsl.value.percent -import kotlinx.collections.immutable.persistentSetOf -import kotlin.math.roundToInt - -class Wash(override val firstChannel: DmxAddress) : Device { - companion object { - private const val PAN_LIMIT = 630f // degrees - private const val TILT_LIMIT = 270f // degrees - } - - override val numberOfChannels: UInt = 16u - - override fun writeDmxData(writer: DmxDataWriter) { - val fullPan = (pan.getCurrentValue().degrees.mod(PAN_LIMIT) / PAN_LIMIT * 65536).roundToInt() - val (mPan, lPan) = fullPan.get16BitChannelValues() - - val fullTilt = (tilt.getCurrentValue().degrees.mod(TILT_LIMIT) / TILT_LIMIT * 65536).roundToInt() - val (mTilt, lTilt) = fullTilt.get16BitChannelValues() - - writer.writeRaw(DmxValue(130u)) - writer.writeRaw(DmxValue(200u)) - writer.writeRaw(lPan) - writer.writeRaw(lTilt) - writer.writeRaw(DmxValue(0u)) - writer.writeRaw(DmxValue(0u)) - writer.writePercentage(white.getCurrentValue()) - writer.writePercentage(cyan.getCurrentValue()) - writer.writePercentage(magenta.getCurrentValue()) - writer.writePercentage(yellow.getCurrentValue()) - writer.writeRaw(DmxValue(0u)) - writer.writeRaw(DmxValue(0u)) - writer.writePercentage(beamSize.getCurrentValue()) - writer.writeRaw(DmxValue(0u)) - writer.writeRaw(DmxValue(47u)) - writer.writePercentage(brightness.getCurrentValue()) - } - - val pan = AngleDV() - val tilt = AngleDV() - val brightness = PercentageDV() - - val white = PercentageDV() - val cyan = PercentageDV() - val magenta = PercentageDV() - val yellow = PercentageDV() - val beamSize = PercentageDV(100.percent) - - override val dvs = persistentSetOf( - pan, - tilt, - brightness, - white, - cyan, - magenta, - yellow, - beamSize - ) -} \ No newline at end of file diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/device/DynamicValue.kt b/src/main/kotlin/de/moritzruth/theaterdsl/device/DynamicValue.kt index 2d61fc4..66d5135 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/device/DynamicValue.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/device/DynamicValue.kt @@ -8,6 +8,7 @@ import kotlin.math.PI import kotlin.math.asin import kotlin.math.min import kotlin.math.sin +import kotlin.random.Random import kotlin.time.Duration import kotlin.time.ExperimentalTime import kotlin.time.TimeSource @@ -40,10 +41,13 @@ abstract class FloatDV(private val initialStaticValue: Float) : DynamicValue< } data class Step(val steps: ImmutableList, val interval: Duration, val startIndex: Int) : State + data class PulseOnce(val rampUpDuration: Duration, val rampDownDuration: Duration, val peakValue: Float, val start: Float, val end: Float) : State } protected abstract fun toDomain(value: Float): T protected abstract fun fromDomain(value: T): Float + protected abstract val minimumValue: T + protected abstract val maximumValue: T private var stateChangeMark = TimeSource.Monotonic.markNow() private var state: State = State.Static(initialStaticValue) @@ -74,6 +78,18 @@ abstract class FloatDV(private val initialStaticValue: Float) : DynamicValue< val index = (elapsedTime / s.interval).toInt() + s.startIndex s.steps[index.mod(s.steps.size)] } + + is State.PulseOnce -> { + if (elapsedTime <= s.rampUpDuration) { + val progress = elapsedTime / s.rampUpDuration + val delta = s.peakValue - s.start + (progress * delta + s.start).toFloat() + } else { + val progress = min(elapsedTime / s.rampDownDuration, 1.0) + val delta = s.end - s.peakValue + (progress * delta + s.end).toFloat() + } + } } return toDomain(float) @@ -107,16 +123,44 @@ abstract class FloatDV(private val initialStaticValue: Float) : DynamicValue< steps(persistentListOf(a, b), interval, startIndex) } + + fun pulseOnce(rampUpDuration: Duration, rampDownDuration: Duration, peakValue: T = maximumValue, end: T = minimumValue, start: T = getCurrentValue()) { + state = State.PulseOnce(rampUpDuration, rampDownDuration, fromDomain(peakValue), fromDomain(start), fromDomain(end)) + } } class PercentageDV(initialStaticValue: Percentage = 0.percent) : FloatDV(initialStaticValue.value) { override fun fromDomain(value: Percentage): Float = value.value override fun toDomain(value: Float): Percentage = Percentage(value) + override val minimumValue: Percentage = 0.percent + override val maximumValue: Percentage = 100.percent } class AngleDV(initialStaticValue: Angle = 0.degrees) : FloatDV(initialStaticValue.degrees) { override fun fromDomain(value: Angle): Float = value.degrees override fun toDomain(value: Float): Angle = Angle(value) + override val minimumValue: Angle = 0.degrees + override val maximumValue: Angle = 360.degrees +} + +class ConcreteDV(private val initialStaticValue: T) : DynamicValue { + sealed interface State { + class Static(val value: T) : State + } + + var state: State = State.Static(initialStaticValue) + + override fun reset() { + state = State.Static(initialStaticValue) + } + + override fun getCurrentValue(): T = when (val s = state) { + is State.Static -> s.value + } + + fun static(value: T) { + state = State.Static(value) + } } @OptIn(ExperimentalTime::class) @@ -128,6 +172,8 @@ class ColorDV(private val initialStaticValue: Color = Color.WHITE) : DynamicValu val deltaSaturation = end.saturation.value - start.saturation.value val deltaBrightness = end.brightness.value - start.brightness.value } + + data class FadeRandomAround(val hue: Angle, val deviation: Angle, val interval: Duration) : State } private var stateChangeMark = TimeSource.Monotonic.markNow() @@ -155,6 +201,11 @@ class ColorDV(private val initialStaticValue: Color = Color.WHITE) : DynamicValu brightness = Percentage((s.start.brightness.value + s.deltaBrightness * progress).toFloat()) ) } + + is State.FadeRandomAround -> { + val random = Random((elapsedTime / s.interval).toInt()) + Color(hue = Angle(s.hue.degrees - (s.deviation.degrees / 2) + random.nextFloat() * s.deviation.degrees)) + } } } @@ -165,4 +216,8 @@ class ColorDV(private val initialStaticValue: Color = Color.WHITE) : DynamicValu fun fade(end: Color, duration: Duration, start: Color = getCurrentValue()) { state = State.Fade(start, end, duration) } + + fun fadeRandomAround(hue: Angle, deviation: Angle, interval: Duration) { + state = State.FadeRandomAround(hue, deviation, interval) + } } \ No newline at end of file diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxDataWriter.kt b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxDataWriter.kt index 9592bdd..80767c4 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxDataWriter.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxDataWriter.kt @@ -12,4 +12,7 @@ interface DmxDataWriter { */ fun writeInRange(range: ClosedFloatingPointRange, value: Float, startAtOne: Boolean = false) = writeRaw(Percentage((value - range.start) / (range.endInclusive - range.start)).roundToDmxValue(startAtOne)) + + fun writeHighByte(value: UShort) = writeRaw(DmxValue(value.toUInt().shr(8).toUByte())) + fun writeLowByte(value: UShort) = writeRaw(DmxValue(value.toUByte())) } diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxValue.kt b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxValue.kt index e38fa35..b46a121 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxValue.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/DmxValue.kt @@ -17,9 +17,4 @@ value class DmxValue(val value: UByte) : Comparable { */ fun Percentage.roundToDmxValue(startAtOne: Boolean = false): DmxValue = if (startAtOne) DmxValue(((value * (DmxValue.VALUE_RANGE.last.toFloat() - 1f)).roundToInt() + 1).toUByte()) - else DmxValue((value * DmxValue.VALUE_RANGE.last.toFloat()).roundToInt().toUByte()) - -fun Int.get16BitChannelValues(): Pair = Pair( - DmxValue((this.rotateRight(7) and 0x11111111).toUByte()), - DmxValue((this and 0x11111111).toUByte()) -) \ No newline at end of file + else DmxValue((value * DmxValue.VALUE_RANGE.last.toFloat()).roundToInt().toUByte()) \ No newline at end of file diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/EnttecOpenDmxUsb.kt b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/EnttecOpenDmxUsb.kt index 8cf9780..3070d8e 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/dmx/EnttecOpenDmxUsb.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/dmx/EnttecOpenDmxUsb.kt @@ -66,8 +66,7 @@ object EnttecOpenDmxUsb { Thread.sleep(23) } } catch (exception: SerialPortIOException) { - exception.printStackTrace() - port.closePort() + // only thrown when the program is stopped } } diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/show/Dsl.kt b/src/main/kotlin/de/moritzruth/theaterdsl/show/Dsl.kt index 4f627a7..c26d439 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/show/Dsl.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/show/Dsl.kt @@ -22,11 +22,13 @@ interface ActBuilderContext { @TheaterDslMarker interface SceneBuilderContext { - fun step(build: StepDataBuilderContext.() -> Unit) + fun step(cue: StepCue = StepCue.Stub, build: StepDataBuilderContext.() -> Unit) + fun lightStep(cue: StepCue, runner: StepRunner) } @TheaterDslMarker interface StepDataBuilderContext { + @Deprecated("") var trigger: StepCue val props: PropsBuilderMap @@ -88,8 +90,13 @@ private fun buildAct(actIndex: Int, name: String, build: ActBuilderContext.() -> val steps = mutableListOf() object : SceneBuilderContext { - override fun step(build: StepDataBuilderContext.() -> Unit) { - var nullableTrigger: StepCue? = null + override fun lightStep(cue: StepCue, runner: StepRunner) { + step(cue) { + onRun(runner) + } + } + + override fun step(cue: StepCue, build: StepDataBuilderContext.() -> Unit) { val changedProps = mutableMapOf() val actorEntrances = mutableSetOf() val actorExits = mutableSetOf() @@ -97,10 +104,8 @@ private fun buildAct(actIndex: Int, name: String, build: ActBuilderContext.() -> object : StepDataBuilderContext { override var trigger: StepCue - get() = nullableTrigger ?: throw IllegalStateException("trigger was not set yet") - set(value) { - nullableTrigger = value - } + get() = cue + set(value) {} override val props = PropsBuilderMap(changedProps) @@ -113,9 +118,7 @@ private fun buildAct(actIndex: Int, name: String, build: ActBuilderContext.() -> } }.build() - @Suppress("KotlinConstantConditions") - val trigger = nullableTrigger ?: throw IllegalStateException("No trigger was specified") - val logger = KotlinLogging.logger("createAct / $name / #${steps.size + 1} ${trigger.format()}") + val logger = KotlinLogging.logger("createAct / $name / #${steps.size + 1} ${cue.format()}") val actorEntrancesNames = actorEntrances.map { it.main } val actorExitsNames = actorExits.map { it.main } @@ -152,7 +155,7 @@ private fun buildAct(actIndex: Int, name: String, build: ActBuilderContext.() -> steps.add( Step( ShowPosition(actIndex, scenes.size, steps.size), - trigger, + cue, actorEntrances.toImmutableSet(), actorExits.toImmutableSet(), actorsOnStage.toImmutableList(), diff --git a/src/main/kotlin/de/moritzruth/theaterdsl/show/Runner.kt b/src/main/kotlin/de/moritzruth/theaterdsl/show/Runner.kt index ab4300c..59a7748 100644 --- a/src/main/kotlin/de/moritzruth/theaterdsl/show/Runner.kt +++ b/src/main/kotlin/de/moritzruth/theaterdsl/show/Runner.kt @@ -141,7 +141,7 @@ fun CoroutineScope.startStepRunning(context: ShowContext) = launch { val step = context.show.acts[lastPosition] lastStepJob?.cancelAndJoin() - lastStepJob = launch(SupervisorJob(currentCoroutineContext().job)) { + lastStepJob = launch(SupervisorJob(currentCoroutineContext().job), CoroutineStart.UNDISPATCHED) { val runContext = object : StepRunContext, CoroutineScope by this {} step.runner?.let { runContext.it() } @@ -170,6 +170,7 @@ private fun CoroutineScope.startWebsocketServer(context: ShowContext) = launch(D } val showJson = Json.encodeToString(context.show) + val indexHtmlContent = this::class.java.getResourceAsStream("/ui/index.html")!!.reader().use { it.readText() } embeddedServer(CIO, port = 8000) { install(WebSockets) { @@ -227,9 +228,13 @@ private fun CoroutineScope.startWebsocketServer(context: ShowContext) = launch(D } } - staticResources("/", "ui") { + staticResources("/assets", "ui/assets") { enableAutoHeadResponse() } + + get("/{...}") { + call.respondText(indexHtmlContent, ContentType.Text.Html, HttpStatusCode.OK) + } } }.start(wait = true) } \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index bc3f99a..9339f80 100755 --- a/ui/package.json +++ b/ui/package.json @@ -5,8 +5,7 @@ "license": "Apache-2.0", "scripts": { "dev": "vite --port 3000 --host", - "build": "vite build", - "start": "vite preview --port 3000 --host" + "build": "vite build --emptyOutDir --outDir ../src/main/resources/ui" }, "devDependencies": { "@iconify-json/ph": "^1.1.5", diff --git a/ui/server/package.json b/ui/server/package.json deleted file mode 100755 index df182c5..0000000 --- a/ui/server/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "main": "index.js", - "scripts": { - "dev": "tsx watch src/main.ts" - }, - "devDependencies": { - "@types/node": "^18.0.1", - "@types/ws": "^8.5.3", - "tsx": "^3.7.1", - "typescript": "^4.7.4" - }, - "dependencies": { - "bufferutil": "^4.0.6", - "utf-8-validate": "^5.0.9", - "ws": "^8.8.0" - } -} diff --git a/ui/server/pnpm-lock.yaml b/ui/server/pnpm-lock.yaml deleted file mode 100755 index 92a22f2..0000000 --- a/ui/server/pnpm-lock.yaml +++ /dev/null @@ -1,344 +0,0 @@ -lockfileVersion: 5.4 - -specifiers: - '@types/node': ^18.0.1 - '@types/ws': ^8.5.3 - bufferutil: ^4.0.6 - tsx: ^3.7.1 - typescript: ^4.7.4 - utf-8-validate: ^5.0.9 - ws: ^8.8.0 - -dependencies: - bufferutil: 4.0.6 - utf-8-validate: 5.0.9 - ws: 8.8.0_22kvxa7zeyivx4jp72v2w3pkvy - -devDependencies: - '@types/node': 18.0.1 - '@types/ws': 8.5.3 - tsx: 3.7.1 - typescript: 4.7.4 - -packages: - - /@esbuild-kit/cjs-loader/2.3.0: - resolution: {integrity: sha512-KInrVt8wlKLhWy7+y4a+E+0uBJoWgdx6Xupy+rrF4MFHA/dEt22ACvvChOZSyiqtQieYPtbPkVYSjbC7mOrFVw==} - dependencies: - '@esbuild-kit/core-utils': 2.0.2 - get-tsconfig: 4.1.0 - dev: true - - /@esbuild-kit/core-utils/2.0.2: - resolution: {integrity: sha512-clNYQUsqtc36pzW5EufMsahcbLG45EaW3YDyf0DlaS0eCMkDXpxIlHwPC0rndUwG6Ytk9sMSD5k1qHbwYEC/OQ==} - dependencies: - esbuild: 0.14.48 - source-map-support: 0.5.21 - dev: true - - /@esbuild-kit/esm-loader/2.4.0: - resolution: {integrity: sha512-zS720jXh06nfg5yAzm6oob4sWN9VTP2E1SonhFgEb6zCBswa4S8fOQ/4Bksz1flDgn56NPqoTTDn2XmWRyMG9Q==} - dependencies: - '@esbuild-kit/core-utils': 2.0.2 - get-tsconfig: 4.1.0 - dev: true - - /@types/node/18.0.1: - resolution: {integrity: sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==} - dev: true - - /@types/ws/8.5.3: - resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} - dependencies: - '@types/node': 18.0.1 - dev: true - - /buffer-from/1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true - - /bufferutil/4.0.6: - resolution: {integrity: sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==} - engines: {node: '>=6.14.2'} - requiresBuild: true - dependencies: - node-gyp-build: 4.5.0 - dev: false - - /esbuild-android-64/0.14.48: - resolution: {integrity: sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-android-arm64/0.14.48: - resolution: {integrity: sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-64/0.14.48: - resolution: {integrity: sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-arm64/0.14.48: - resolution: {integrity: sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-64/0.14.48: - resolution: {integrity: sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-arm64/0.14.48: - resolution: {integrity: sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-32/0.14.48: - resolution: {integrity: sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-64/0.14.48: - resolution: {integrity: sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm/0.14.48: - resolution: {integrity: sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm64/0.14.48: - resolution: {integrity: sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-mips64le/0.14.48: - resolution: {integrity: sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le/0.14.48: - resolution: {integrity: sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64/0.14.48: - resolution: {integrity: sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x/0.14.48: - resolution: {integrity: sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64/0.14.48: - resolution: {integrity: sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64/0.14.48: - resolution: {integrity: sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-sunos-64/0.14.48: - resolution: {integrity: sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32/0.14.48: - resolution: {integrity: sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64/0.14.48: - resolution: {integrity: sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64/0.14.48: - resolution: {integrity: sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild/0.14.48: - resolution: {integrity: sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - esbuild-android-64: 0.14.48 - esbuild-android-arm64: 0.14.48 - esbuild-darwin-64: 0.14.48 - esbuild-darwin-arm64: 0.14.48 - esbuild-freebsd-64: 0.14.48 - esbuild-freebsd-arm64: 0.14.48 - esbuild-linux-32: 0.14.48 - esbuild-linux-64: 0.14.48 - esbuild-linux-arm: 0.14.48 - esbuild-linux-arm64: 0.14.48 - esbuild-linux-mips64le: 0.14.48 - esbuild-linux-ppc64le: 0.14.48 - esbuild-linux-riscv64: 0.14.48 - esbuild-linux-s390x: 0.14.48 - esbuild-netbsd-64: 0.14.48 - esbuild-openbsd-64: 0.14.48 - esbuild-sunos-64: 0.14.48 - esbuild-windows-32: 0.14.48 - esbuild-windows-64: 0.14.48 - esbuild-windows-arm64: 0.14.48 - dev: true - - /fsevents/2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /get-tsconfig/4.1.0: - resolution: {integrity: sha512-bhshxJhpfmeQ8x4fAvDqJV2VfGp5TfHdLpmBpNZZhMoVyfIrOippBW4mayC3DT9Sxuhcyl56Efw61qL28hG4EQ==} - dev: true - - /node-gyp-build/4.5.0: - resolution: {integrity: sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==} - hasBin: true - dev: false - - /source-map-support/0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - - /source-map/0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true - - /tsx/3.7.1: - resolution: {integrity: sha512-dwl1GBdkwVQ9zRxTmETGi+ck8pewNm2QXh+HK6jHxdHmeCjfCL+Db3b4VX/dOMDSS2hle1j5LzQoo8OpVXu6XQ==} - hasBin: true - dependencies: - '@esbuild-kit/cjs-loader': 2.3.0 - '@esbuild-kit/core-utils': 2.0.2 - '@esbuild-kit/esm-loader': 2.4.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /typescript/4.7.4: - resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /utf-8-validate/5.0.9: - resolution: {integrity: sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==} - engines: {node: '>=6.14.2'} - requiresBuild: true - dependencies: - node-gyp-build: 4.5.0 - dev: false - - /ws/8.8.0_22kvxa7zeyivx4jp72v2w3pkvy: - resolution: {integrity: sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dependencies: - bufferutil: 4.0.6 - utf-8-validate: 5.0.9 - dev: false diff --git a/ui/server/src/main.ts b/ui/server/src/main.ts deleted file mode 100755 index c9aba23..0000000 --- a/ui/server/src/main.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { WebSocketServer } from "ws" - -const server = new WebSocketServer({ - clientTracking: true, - host: "0.0.0.0", - port: 8000 -}) - -let state = { - music: null, - message: "", - position: { - scene: 0, - step: 0 - } -} - -server.on("connection", (client, request) => { - const address = request.connection.remoteAddress - console.log(`Connected: ${address}`) - client.send(JSON.stringify({ - state, - timestamp: Date.now() - })) - - client.on("message", rawData => { - state = JSON.parse(rawData.toString()) - - console.log("Update: ", state) - - server.clients.forEach(c => { - if (c !== client) { - c.send(JSON.stringify({ - state, - timestamp: Date.now() - })) - } - }) - }) - - client.on("close", () => { - console.log(`Disconnected: ${address}`) - }) -}) - -console.log("Listening on ws://0.0.0.0:8000") diff --git a/ui/src/components/CueBox.vue b/ui/src/components/CueBox.vue index c1cd26b..baf7521 100755 --- a/ui/src/components/CueBox.vue +++ b/ui/src/components/CueBox.vue @@ -1,7 +1,7 @@