keyboard device

This commit is contained in:
りき萌 2023-05-03 15:10:45 +02:00
parent 64162a30ff
commit da71bcf1ad
35 changed files with 558 additions and 172 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

After

Width:  |  Height:  |  Size: 553 B

Before After
Before After

Binary file not shown.

Binary file not shown.

View file

@ -1,10 +1,10 @@
// 1.19.2 2023-05-02T12:20:58.184760864 Models
// 1.19.2 2023-05-03T14:08:42.976687848 Models
0812a674d14cfc6fbb7c0e2ac1b473bf2afe1965 assets/dawd3/models/item/brown_patch_cable.json
6e01a1aa07f3a36d7950a1b00d1bc6e9045b9995 assets/dawd3/blockstates/knob.json
bf0e322e33123cb6873c2da4e8c6ab85688deb4e assets/dawd3/models/item/gray_patch_cable.json
215b221d48639e96de11914625a770a389d65b81 assets/dawd3/models/item/green_patch_cable.json
6a9fb209d82556f5941422a8d047a0ae2af1dc8f assets/dawd3/models/item/sine_oscillator.json
5919f5e0f79620a682f481a8e02995f95724b2ce assets/dawd3/models/block/amplifier.json
48ec7ba345d918e9cdd74c026dc5d705f1679a65 assets/dawd3/models/block/amplifier.json
8ba890b28c5ac57c59f19ccc8c72825caac10677 assets/dawd3/models/item/magenta_patch_cable.json
e34001d3c974aecfa347d435beb6bc0b9d325897 assets/dawd3/models/item/cyan_patch_cable.json
c3ea3e310d8fe7796b5b055d34db712cb8c7ac5a assets/dawd3/blockstates/triangle_oscillator.json
@ -19,20 +19,23 @@ a4e8bc89d39021eb8d56ad7735216cb851d67287 assets/dawd3/models/item/light_blue_pat
8c4b8147bfea2bdb8c1d348bc332a60d2cd82d68 assets/dawd3/models/item/phase.json
aa1a1807d2c46f1f25e3d0c507952fefb5b3cd9f assets/dawd3/models/item/knob.json
d65fd7b21da2adf55a0b076103103da7e7bb453a assets/dawd3/models/block/speaker.json
fe4987cffd1253462cc8440c90ff2341ac3026a4 assets/dawd3/models/block/triangle_oscillator.json
df1b924ee7f1a4f9a6496f52a27d74b449ef05fc assets/dawd3/models/block/triangle_oscillator.json
becb814a10bcf2d3e071a6479e9b2289165d0059 assets/dawd3/models/item/modulator.json
8aa966337109315240614d5257eb72f959eba5d8 assets/dawd3/models/item/orange_patch_cable.json
4748361fec856c3fe328ac0036ee642a5865fa99 assets/dawd3/models/block/pulse_oscillator.json
46e362f3f96c54438c29132f76441e6cddb94564 assets/dawd3/models/block/pulse_oscillator.json
12c4bfd825b2476955afcd3bb23c1f736ce68caa assets/dawd3/models/item/yellow_patch_cable.json
aab1bce7ec4e7c7dccd3d33d4242de12b63a981d assets/dawd3/models/item/white_patch_cable.json
6cdda36e539e23acf3db70d761338e88932a6ebb assets/dawd3/models/item/purple_patch_cable.json
e3c6aacd49a6395f37047d3df31f91a18a411267 assets/dawd3/models/item/speaker.json
f69a4acfdf715c64f64830ce0a79aa452ad4760a assets/dawd3/models/item/saw_oscillator.json
851149820d465dc203cc75e8808a5387e8eb5426 assets/dawd3/models/block/modulator.json
e289094bba4daa5de7873e6af154a25f4defc16b assets/dawd3/models/block/modulator.json
32bb0e6e3bf75b9005602e8fb1042ad5d41286ad assets/dawd3/models/item/lime_patch_cable.json
bb8b84a5a98c77aaf9ee25760dfc352b760fc2d3 assets/dawd3/models/item/keyboard.json
b5cc6f4b4af952380a1539489f2e406bc6ebe5fa assets/dawd3/blockstates/amplifier.json
1358574aaed0a49a6a4fdaba4ceb330463dcc421 assets/dawd3/models/block/phase.json
0e38af638352ff27a8ee1e42c72266ce34578db7 assets/dawd3/models/block/phase.json
292685c025f28911bca9252da3e4ac72998b3c7a assets/dawd3/blockstates/pulse_oscillator.json
63c9e9521285c0a1041709a42b2232517debee38 assets/dawd3/blockstates/keyboard.json
5d03886e98a219deed68eecf53a2453916401fc0 assets/dawd3/models/block/keyboard.json
956d8f117df95cf62c8cac375cff853df96840d6 assets/dawd3/models/item/pink_patch_cable.json
2adb4f854a9f13090dffaf8ab9dfe0553cc59a80 assets/dawd3/models/item/triangle_oscillator.json
3b1811bab3ba394ba03b67ac2efc72cb35316dc8 assets/dawd3/blockstates/sine_oscillator.json

View file

@ -40,30 +40,6 @@
16.0
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
4.0
]
}
},
"from": [
3.0,
3.0,
-0.01
],
"to": [
7.0,
7.0,
0.01
]
},
{
"faces": {
"north": {
@ -88,6 +64,30 @@
0.01
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
4.0
]
}
},
"from": [
3.0,
3.0,
-0.01
],
"to": [
7.0,
7.0,
0.01
]
},
{
"faces": {
"north": {

View file

@ -40,30 +40,6 @@
16.0
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
0.0,
0.0,
4.0,
4.0
]
}
},
"from": [
9.0,
6.0,
-0.01
],
"to": [
13.0,
10.0,
0.01
]
},
{
"faces": {
"north": {
@ -87,6 +63,30 @@
10.0,
0.01
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
0.0,
0.0,
4.0,
4.0
]
}
},
"from": [
9.0,
6.0,
-0.01
],
"to": [
13.0,
10.0,
0.01
]
}
],
"textures": {

View file

@ -46,21 +46,21 @@
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
0.0,
4.0,
4.0
]
}
},
"from": [
9.0,
3.0,
6.0,
-0.01
],
"to": [
13.0,
7.0,
10.0,
0.01
]
},
@ -79,11 +79,35 @@
},
"from": [
9.0,
6.0,
9.0,
-0.01
],
"to": [
13.0,
13.0,
0.01
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
4.0
]
}
},
"from": [
3.0,
6.0,
-0.01
],
"to": [
7.0,
10.0,
0.01
]

View file

@ -40,30 +40,6 @@
16.0
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
4.0
]
}
},
"from": [
3.0,
3.0,
-0.01
],
"to": [
7.0,
7.0,
0.01
]
},
{
"faces": {
"north": {
@ -87,6 +63,30 @@
7.0,
0.01
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
4.0,
0.0,
8.0,
4.0
]
}
},
"from": [
3.0,
3.0,
-0.01
],
"to": [
7.0,
7.0,
0.01
]
}
],
"textures": {

View file

@ -40,30 +40,6 @@
16.0
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
0.0,
0.0,
4.0,
4.0
]
}
},
"from": [
9.0,
6.0,
-0.01
],
"to": [
13.0,
10.0,
0.01
]
},
{
"faces": {
"north": {
@ -87,6 +63,30 @@
10.0,
0.01
]
},
{
"faces": {
"north": {
"cullface": "north",
"texture": "#port",
"uv": [
0.0,
0.0,
4.0,
4.0
]
}
},
"from": [
9.0,
6.0,
-0.01
],
"to": [
13.0,
10.0,
0.01
]
}
],
"textures": {

View file

@ -1,8 +1,11 @@
package net.liquidev.dawd3.audio.device
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.liquidev.dawd3.net.TweakControl
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtFloat
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicInteger
@ -85,3 +88,17 @@ class ControlMap(set: ControlSet) {
operator fun get(name: ControlName): Control? = map[name]
}
object BlockEntityControls {
fun setFloatControlValue(blockPosition: BlockPos, control: FloatControl, value: Float) {
control.value = value
ClientPlayNetworking.send(
TweakControl.id,
TweakControl(
blockPosition,
control.descriptor.name.id,
control.valueToBytes()
).serialize()
)
}
}

View file

@ -1,4 +1,4 @@
package net.liquidev.dawd3.audio.devices.math
package net.liquidev.dawd3.audio.devices.io
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.*

View file

@ -0,0 +1,61 @@
package net.liquidev.dawd3.audio.devices.io
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.*
import net.minecraft.util.Identifier
class KeyboardDevice : Device<KeyboardDevice.Controls> {
companion object : DeviceDescriptor {
override val id = Identifier(Mod.id, "keyboard")
val pitchControl = ControlDescriptor(id, "pitch", 0f)
val triggerControl = ControlDescriptor(id, "trigger", 0f)
val velocityControl = ControlDescriptor(id, "velocity", 0f)
val pitchOutput = OutputPortName(id, "pitch")
val triggerOutput = OutputPortName(id, "trigger")
val velocityOutput = OutputPortName(id, "velocity")
}
class Controls : ControlSet {
val pitch = FloatControl(pitchControl)
val trigger = FloatControl(triggerControl)
val velocity = FloatControl(velocityControl)
override fun visitControls(visit: (ControlName, Control) -> Unit) {
visit(pitchControl.name, pitch)
visit(triggerControl.name, trigger)
visit(velocityControl.name, velocity)
}
}
val pitch = OutputPort(bufferCount = 1)
val trigger = OutputPort(bufferCount = 1)
val velocity = OutputPort(bufferCount = 1)
override fun process(sampleCount: Int, controls: Controls) {
val pitchBuffer = pitch.buffers[0].getOrReallocate(sampleCount)
val triggerBuffer = trigger.buffers[0].getOrReallocate(sampleCount)
val velocityBuffer = velocity.buffers[0].getOrReallocate(sampleCount)
val pitch = controls.pitch.value
val trigger = controls.trigger.value
val velocity = controls.velocity.value
for (i in 0 until sampleCount) {
pitchBuffer[i] = pitch
}
for (i in 0 until sampleCount) {
triggerBuffer[i] = trigger
}
for (i in 0 until sampleCount) {
velocityBuffer[i] = velocity
}
}
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {
visit(pitchOutput, pitch)
visit(triggerOutput, trigger)
visit(velocityOutput, velocity)
}
}

View file

@ -4,6 +4,7 @@ import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.Audio
import net.liquidev.dawd3.audio.AudioBuffer
import net.liquidev.dawd3.audio.device.*
import net.liquidev.dawd3.audio.math.Trigger
import net.liquidev.dawd3.audio.unit.VOct
import net.minecraft.util.Identifier
@ -11,14 +12,17 @@ class PhaseDevice : Device<NoControls> {
companion object : DeviceDescriptor {
override val id = Identifier(Mod.id, "phase")
val frequencyCVPort = InputPortName(id, "frequency_cv")
val resetPort = InputPortName(id, "reset")
val outputPort = OutputPortName(id, "output")
}
private val frequencyCV = InputPort()
private val reset = InputPort()
private val output = OutputPort(bufferCount = 1)
private val frequencyBuffer = AudioBuffer()
private var phase = 0.0f
private val resetTrigger = Trigger()
override fun process(sampleCount: Int, controls: NoControls) {
val voctBuffer = frequencyCV.getConnectedOutputBuffer(0, sampleCount)
@ -27,17 +31,25 @@ class PhaseDevice : Device<NoControls> {
frequencyBuffer[i] = VOct(voctBuffer[i]).toFrequency(Audio.a4).value
}
val resetBuffer = reset.getConnectedOutputBuffer(0, sampleCount)
val outputBuffer = output.buffers[0].getOrReallocate(sampleCount)
for (i in 0 until sampleCount) {
val phaseStep = Audio.sampleRateFInv * frequencyBuffer[i]
phase += phaseStep
phase %= 1.0f
phase %= 1f
if (resetTrigger.stepEdge(resetBuffer[i]) == Trigger.Edge.Rising) {
println("resetting phase")
phase = 0f
}
outputBuffer[i] = phase
}
}
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
visit(frequencyCVPort, frequencyCV)
visit(resetPort, reset)
}
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {

View file

@ -2,6 +2,9 @@ package net.liquidev.dawd3.audio.devices.oscillator
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.*
import net.liquidev.dawd3.audio.math.PhaseDerivative
import net.liquidev.dawd3.audio.math.oversample
import net.liquidev.dawd3.audio.math.polyBLEP
import net.minecraft.util.Identifier
class PulseOscillatorDevice : Device<NoControls> {

View file

@ -2,6 +2,9 @@ package net.liquidev.dawd3.audio.devices.oscillator
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.*
import net.liquidev.dawd3.audio.math.PhaseDerivative
import net.liquidev.dawd3.audio.math.oversample
import net.liquidev.dawd3.audio.math.polyBLEP
import net.minecraft.util.Identifier
class SawOscillatorDevice : Device<NoControls> {

View file

@ -2,6 +2,9 @@ package net.liquidev.dawd3.audio.devices.oscillator
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.*
import net.liquidev.dawd3.audio.math.PhaseDerivative
import net.liquidev.dawd3.audio.math.oversample
import net.liquidev.dawd3.audio.math.polyBLEP
import net.minecraft.util.Identifier
import kotlin.math.abs

View file

@ -1,4 +1,4 @@
package net.liquidev.dawd3.audio.devices.oscillator
package net.liquidev.dawd3.audio.math
import kotlin.math.abs
@ -14,4 +14,4 @@ class PhaseDerivative {
previousDeltaPhase = deltaPhase
return smoothDeltaPhase
}
}
}

View file

@ -0,0 +1,23 @@
package net.liquidev.dawd3.audio.math
class Trigger {
var isTriggered = false
fun stepEdge(nextSample: Float): Edge {
val wasTriggered = isTriggered
isTriggered = nextSample > 0.5
return if (!wasTriggered && isTriggered) {
Edge.Rising
} else if (wasTriggered && !isTriggered) {
Edge.Falling
} else {
Edge.Constant
}
}
enum class Edge {
Constant,
Rising,
Falling,
}
}

View file

@ -1,4 +1,4 @@
package net.liquidev.dawd3.audio.devices.oscillator
package net.liquidev.dawd3.audio.math
inline fun oversample(
phase: Float,

View file

@ -11,8 +11,9 @@ import net.liquidev.dawd3.block.device.DeviceBlock
import net.liquidev.dawd3.block.device.DeviceBlockEntity
import net.liquidev.dawd3.block.device.DeviceBlockEntityRenderer
import net.liquidev.dawd3.block.devices.SpeakerBlockDescriptor
import net.liquidev.dawd3.block.devices.io.KeyboardBlockDescriptor
import net.liquidev.dawd3.block.devices.io.KnobBlockDescriptor
import net.liquidev.dawd3.block.devices.math.AmplifierBlockDescriptor
import net.liquidev.dawd3.block.devices.math.KnobBlockDescriptor
import net.liquidev.dawd3.block.devices.math.ModulatorBlockDescriptor
import net.liquidev.dawd3.block.devices.oscillator.*
import net.liquidev.dawd3.item.Items
@ -64,6 +65,7 @@ object Blocks {
val speaker = registerDeviceBlock(SpeakerBlockDescriptor)
val knob = registerDeviceBlock(KnobBlockDescriptor)
val keyboard = registerDeviceBlock(KeyboardBlockDescriptor)
val amplifier = registerDeviceBlock(AmplifierBlockDescriptor)
val modulator = registerDeviceBlock(ModulatorBlockDescriptor)

View file

@ -0,0 +1,44 @@
package net.liquidev.dawd3.block.devices.io
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.DeviceInstance
import net.liquidev.dawd3.audio.devices.io.KeyboardDevice
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
import net.liquidev.dawd3.block.device.PhysicalPort
import net.liquidev.dawd3.ui.widget.Window
import net.liquidev.dawd3.ui.widget.keyboard.Keyboard
import net.minecraft.client.world.ClientWorld
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec2f
object KeyboardBlockDescriptor : DeviceBlockDescriptor<KeyboardBlockDescriptor.ClientState, KeyboardDevice.Controls> {
override val id = Identifier(Mod.id, "keyboard")
override val portLayout = PhysicalPort.layout {
port(KeyboardDevice.pitchOutput, position = Vec2f(0.25f, 0.25f), PhysicalPort.Side.Front)
port(KeyboardDevice.triggerOutput, position = Vec2f(0.75f, 0.25f), PhysicalPort.Side.Front)
port(KeyboardDevice.velocityOutput, position = Vec2f(0.5f, 0.75f), PhysicalPort.Side.Front)
}
class ClientState(controls: KeyboardDevice.Controls) : DeviceBlockDescriptor.ClientState {
override val logicalDevice = DeviceInstance.create(KeyboardDevice(), controls)
}
override fun initControls() = KeyboardDevice.Controls()
override fun onClientLoad(controls: KeyboardDevice.Controls, world: ClientWorld) =
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<KeyboardDevice.Controls> {
override fun open(controls: KeyboardDevice.Controls, x: Int, y: Int) =
Window(
x, y,
width = 332,
height = 56,
title = Text.translatable("block.dawd3.keyboard")
).apply {
children.add(Keyboard(x = 8, y = 16, firstNote = -33, lastNote = 27, controls))
}
}
}

View file

@ -1,8 +1,8 @@
package net.liquidev.dawd3.block.devices.math
package net.liquidev.dawd3.block.devices.io
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.audio.device.DeviceInstance
import net.liquidev.dawd3.audio.devices.math.ConstantDevice
import net.liquidev.dawd3.audio.devices.io.ConstantDevice
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
import net.liquidev.dawd3.block.device.PhysicalPort
import net.liquidev.dawd3.block.device.descriptor.FaceTextures

View file

@ -6,7 +6,6 @@ import net.liquidev.dawd3.audio.device.NoControls
import net.liquidev.dawd3.audio.devices.oscillator.PhaseDevice
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
import net.liquidev.dawd3.block.device.PhysicalPort
import net.liquidev.dawd3.block.device.descriptor.FaceTextures
import net.minecraft.client.world.ClientWorld
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec2f
@ -15,18 +14,10 @@ object PhaseBlockDescriptor : DeviceBlockDescriptor<PhaseBlockDescriptor.ClientS
override val id = Identifier(Mod.id, "phase")
override val portLayout = PhysicalPort.layout {
port(
PhaseDevice.frequencyCVPort,
position = Vec2f(0.25f, 0.5f),
side = PhysicalPort.Side.Front,
)
port(
PhaseDevice.outputPort,
position = Vec2f(0.75f, 0.5f),
side = PhysicalPort.Side.Front
)
port(PhaseDevice.frequencyCVPort, position = Vec2f(0.25f, 0.25f), PhysicalPort.Side.Front)
port(PhaseDevice.resetPort, position = Vec2f(0.25f, 0.75f), PhysicalPort.Side.Front)
port(PhaseDevice.outputPort, position = Vec2f(0.75f, 0.5f), PhysicalPort.Side.Front)
}
override val faceTextures = FaceTextures.withFrontAndSide { id }
class ClientState(controls: NoControls) : DeviceBlockDescriptor.ClientState {
override val logicalDevice = DeviceInstance.create(PhaseDevice(), controls)

View file

@ -17,14 +17,14 @@ import kotlin.math.sin
import kotlin.math.sqrt
object Render {
fun icon(
fun sprite(
matrices: MatrixStack,
x: Int,
y: Int,
width: Int,
height: Int,
atlas: Atlas,
icon: Icon,
sprite: Sprite,
) {
RenderSystem.setShaderTexture(0, atlas.asset)
DrawableHelper.drawTexture(
@ -33,17 +33,17 @@ object Render {
y,
width,
height,
icon.u.toFloat(),
icon.v.toFloat(),
icon.width,
icon.height,
sprite.u.toFloat(),
sprite.v.toFloat(),
sprite.width,
sprite.height,
atlas.size,
atlas.size
)
}
fun icon(matrices: MatrixStack, x: Int, y: Int, atlas: Atlas, icon: Icon) {
icon(matrices, x, y, icon.width, icon.height, atlas, icon)
fun sprite(matrices: MatrixStack, x: Int, y: Int, atlas: Atlas, icon: Sprite) {
sprite(matrices, x, y, icon.width, icon.height, atlas, icon)
}
fun line(

View file

@ -1,6 +1,6 @@
package net.liquidev.dawd3.render
data class Icon(
data class Sprite(
val u: Int,
val v: Int,
val width: Int,

View file

@ -3,8 +3,8 @@ package net.liquidev.dawd3.ui
import net.liquidev.dawd3.Mod
import net.liquidev.dawd3.block.device.DeviceBlockEntity
import net.liquidev.dawd3.render.Atlas
import net.liquidev.dawd3.render.Icon
import net.liquidev.dawd3.render.Render
import net.liquidev.dawd3.render.Sprite
import net.liquidev.dawd3.ui.widget.Widget
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.util.math.MatrixStack
@ -52,18 +52,19 @@ class Rack(
openWidget.widget.draw(matrices, mouseX, mouseY, delta)
}
Render.icon(matrices, 8, 8, badge.width * 2, badge.height * 2, atlas, badge)
Render.sprite(matrices, 8, 8, badge.width * 2, badge.height * 2, atlas, badge)
}
private fun propagateEvent(event: Event): Boolean {
for (openWidget in openWidgets) {
if (openWidget.widget.event(
if (
openWidget.widget.event(
EventContext(world, openWidget.blockPosition),
event.relativeTo(
openWidget.widget.x.toDouble(),
openWidget.widget.y.toDouble(),
)
) == null
)
) return true
}
return false
@ -96,7 +97,7 @@ class Rack(
companion object {
val atlas = Atlas(asset = Identifier(Mod.id, "textures/ui/rack.png"), size = 64)
val badge = Icon(u = 0, v = 16, width = 6, height = 3)
val badge = Sprite(u = 0, v = 16, width = 6, height = 3)
val smallFont = Identifier(Mod.id, "altopixel")
val smallText = Style.EMPTY.withFont(smallFont)!!

View file

@ -0,0 +1,19 @@
package net.liquidev.dawd3.ui.widget
import net.liquidev.dawd3.ui.Event
import net.liquidev.dawd3.ui.EventContext
import net.minecraft.client.util.math.MatrixStack
abstract class Container(x: Int, y: Int) : Widget(x, y) {
abstract val children: List<Widget>
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
for (child in children) {
child.draw(matrices, mouseX, mouseY, deltaTime)
}
}
override fun event(context: EventContext, event: Event): Boolean {
return propagateEvent(context, event, children.reversed())
}
}

View file

@ -1,13 +1,13 @@
package net.liquidev.dawd3.ui.widget
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.liquidev.dawd3.audio.device.BlockEntityControls
import net.liquidev.dawd3.audio.device.FloatControl
import net.liquidev.dawd3.common.Degrees
import net.liquidev.dawd3.common.Radians
import net.liquidev.dawd3.common.clamp
import net.liquidev.dawd3.common.mapRange
import net.liquidev.dawd3.net.ControlTweaked
import net.liquidev.dawd3.net.TweakControl
import net.liquidev.dawd3.render.Render
import net.liquidev.dawd3.render.TextureStrip
import net.liquidev.dawd3.render.Tooltip
@ -28,12 +28,13 @@ class Knob(
val min: Float,
val max: Float,
val color: Color,
size: Int = normalSize,
val unit: FloatUnit = RawValue,
// Pick a default sensitivity such that for pitch ranges we move by steps of 0.25.
val sensitivity: Float = (max - min) * 0.25f / 96f,
) : Widget(x, y) {
override val width = 20
override val height = 24
override val width = size
override val height = size + 4
private data class DraggingInfo(var previousMouseY: Double)
@ -109,7 +110,7 @@ class Knob(
}
}
override fun event(context: EventContext, event: Event): Event? {
override fun event(context: EventContext, event: Event): Boolean {
val client = MinecraftClient.getInstance()
when (event) {
is MouseButton -> if (event.button == GLFW.GLFW_MOUSE_BUTTON_LEFT) {
@ -129,7 +130,7 @@ class Knob(
event.absoluteMouseX * guiScale,
event.absoluteMouseY * guiScale,
)
return null
return true
}
Action.Up -> {
val draggingInfo = draggingInfo
@ -154,22 +155,17 @@ class Knob(
if (draggingInfo != null) {
val guiScale = client.options.guiScale.value.toFloat()
val deltaY = (draggingInfo.previousMouseY - event.absoluteMouseY) * guiScale
// Reflect the change locally immediately for lower latency.
control.value = alterValue(control.value, by = deltaY.toFloat())
ClientPlayNetworking.send(
TweakControl.id,
TweakControl(
context.blockPosition,
control.descriptor.name.id,
control.valueToBytes()
).serialize()
BlockEntityControls.setFloatControlValue(
context.blockPosition,
control,
alterValue(control.value, by = deltaY.toFloat())
)
draggingInfo.previousMouseY = event.absoluteMouseY
}
}
}
return event
return false
}
private fun alterValue(value: Float, by: Float): Float =
@ -195,5 +191,8 @@ class Knob(
val u = 18f + color.index.toFloat()
return TextureStrip(u, 16f, u, 32f)
}
const val normalSize = 20
const val smallSize = 16
}
}

View file

@ -15,8 +15,8 @@ abstract class Widget(var x: Int, var y: Int) {
deltaTime: Float,
)
/** Returns non-null to propagate the event, or null to consume it. */
abstract fun event(context: EventContext, event: Event): Event?
/** Returns false to propagate the event, or true to consume it. */
abstract fun event(context: EventContext, event: Event): Boolean
fun draw(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
matrices.push()
@ -36,10 +36,11 @@ abstract class Widget(var x: Int, var y: Int) {
through: Iterable<Widget>,
): Boolean {
for (widget in through) {
if (widget.event(
if (
widget.event(
context,
event.relativeTo(widget.x.toDouble(), widget.y.toDouble())
) == null
)
) {
return true
}

View file

@ -2,16 +2,19 @@ package net.liquidev.dawd3.ui.widget
import net.liquidev.dawd3.render.NinePatch
import net.liquidev.dawd3.render.Render
import net.liquidev.dawd3.ui.Event
import net.liquidev.dawd3.ui.EventContext
import net.liquidev.dawd3.ui.Rack
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class Window(x: Int, y: Int, override val width: Int, override val height: Int, val title: Text) :
Widget(x, y) {
class Window(
x: Int,
y: Int,
override val width: Int,
override val height: Int,
val title: Text,
) : Container(x, y) {
val children = mutableListOf<Widget>()
override val children = mutableListOf<Widget>()
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
Render.ninePatch(matrices, 2, 2, width, height, Rack.atlas, windowShadow)
@ -25,13 +28,7 @@ class Window(x: Int, y: Int, override val width: Int, override val height: Int,
0x111111
)
for (child in children) {
child.draw(matrices, mouseX, mouseY, deltaTime)
}
}
override fun event(context: EventContext, event: Event): Event? {
return if (propagateEvent(context, event, children)) null else event
super.drawContent(matrices, mouseX, mouseY, deltaTime)
}
companion object {

View file

@ -0,0 +1,64 @@
package net.liquidev.dawd3.ui.widget.keyboard
import net.liquidev.dawd3.audio.devices.io.KeyboardDevice
import net.liquidev.dawd3.render.Render
import net.liquidev.dawd3.render.Sprite
import net.liquidev.dawd3.ui.Event
import net.liquidev.dawd3.ui.EventContext
import net.liquidev.dawd3.ui.Rack
import net.liquidev.dawd3.ui.widget.Widget
import net.minecraft.client.util.math.MatrixStack
class Key(
x: Int,
y: Int,
val type: Type,
val note: Float,
val controls: KeyboardDevice.Controls,
) : Widget(x, y) {
enum class Type(
val width: Int,
val height: Int,
val idleSprite: Sprite,
val pressedSprite: Sprite,
) {
White(
width = 10,
height = 32,
idleSprite = Sprite(u = 32, v = 16, width = 10, height = 32),
pressedSprite = Sprite(u = 42, v = 16, width = 10, height = 32),
),
Black(
width = 5,
height = 20,
idleSprite = Sprite(u = 52, v = 16, width = 7, height = 20),
pressedSprite = Sprite(u = 52, v = 36, width = 7, height = 20)
),
}
override val width = type.width
override val height = type.height
private var isPressed = false
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
Render.sprite(
matrices,
x = 0,
y = 0,
Rack.atlas,
if (isPressed) type.pressedSprite else type.idleSprite
)
}
override fun event(context: EventContext, event: Event) = false
internal fun triggerDown() {
isPressed = true
}
internal fun triggerUp() {
isPressed = false
}
}

View file

@ -0,0 +1,118 @@
package net.liquidev.dawd3.ui.widget.keyboard
import net.liquidev.dawd3.audio.device.BlockEntityControls
import net.liquidev.dawd3.audio.devices.io.KeyboardDevice
import net.liquidev.dawd3.ui.*
import net.liquidev.dawd3.ui.widget.Container
import org.lwjgl.glfw.GLFW
class Keyboard(
x: Int,
y: Int,
firstNote: Int,
lastNote: Int,
private val controls: KeyboardDevice.Controls,
) : Container(x, y) {
override val children = run {
var whiteX = 0
List(lastNote - firstNote) { index ->
val note = firstNote + index
val keyType = octave[note.mod(octave.size)]
val keyX = when (keyType) {
Key.Type.White -> {
val xx = whiteX
whiteX += keyType.width - 1
xx
}
Key.Type.Black -> whiteX - keyType.width / 2 - 1
}
Key(keyX, y = 0, keyType, note.toFloat(), controls)
// We want black keys to render after white keys.
}.sortedBy { it.type }
}
override val width = children.maxOf { it.x + it.width }
override val height = 24
private var pressedKey: Key? = null
override fun event(context: EventContext, event: Event): Boolean {
if (event is MouseButton && event.button == GLFW.GLFW_MOUSE_BUTTON_LEFT) {
mouseIsDown = event.action == Action.Down
if (mouseIsDown) {
setPressedKey(context, findPressedKey(event.mouseX.toInt(), event.mouseY.toInt()))
} else {
setPressedKey(context, null)
}
return mouseIsDown && pressedKey != null
}
if (event is MouseMove && mouseIsDown) {
setPressedKey(context, findPressedKey(event.mouseX.toInt(), event.mouseY.toInt()))
}
return false
}
private fun findPressedKey(mouseX: Int, mouseY: Int): Key? =
children.findLast { it.containsRelativePoint(mouseX - it.x, mouseY - it.y) }
private fun setPressedKey(context: EventContext, newKey: Key?) {
val previousKey = pressedKey
pressedKey = newKey
if (newKey != previousKey) {
previousKey?.triggerUp()
if (newKey != null) {
newKey.triggerDown()
BlockEntityControls.setFloatControlValue(
context.blockPosition,
controls.pitch,
newKey.note
)
BlockEntityControls.setFloatControlValue(
context.blockPosition,
controls.trigger,
1f
)
// TODO: Key velocity.
BlockEntityControls.setFloatControlValue(
context.blockPosition,
controls.velocity,
1f
)
} else {
BlockEntityControls.setFloatControlValue(
context.blockPosition,
controls.trigger,
0f
)
BlockEntityControls.setFloatControlValue(
context.blockPosition,
controls.velocity,
0f
)
}
}
}
companion object {
var mouseIsDown = false
val octave = arrayOf(
// For efficiency we start with A, since pitch signals represent it as 0.
Key.Type.White, // A
Key.Type.Black, // A#
Key.Type.White, // B
Key.Type.White, // C
Key.Type.Black, // C#
Key.Type.White, // D
Key.Type.Black, // D#
Key.Type.White, // E
Key.Type.White, // F
Key.Type.Black, // F#
Key.Type.White, // G
Key.Type.Black, // G#
)
}
}

View file

@ -17,6 +17,7 @@
"item.dawd3.red_patch_cable": "Red Patch Cable",
"item.dawd3.black_patch_cable": "Black Patch Cable",
"block.dawd3.amplifier": "Amplifier",
"block.dawd3.keyboard": "Keyboard",
"block.dawd3.knob": "Knob",
"block.dawd3.modulator": "Modulator",
"block.dawd3.phase": "Phase",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 344 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 480 B

Before After
Before After