Refactor KeyboardComponent with constants for note and key handling

Replaced hardcoded values for octaves, keys, and dimensions with named constants for improved readability and maintainability. Simplified calculations and loops using these constants. Enhanced clarity in key rendering and MIDI note calculations.
This commit is contained in:
2025-06-07 11:39:21 +02:00
parent 9ab909cf6c
commit 5c16b57ae9

View File

@@ -47,8 +47,8 @@ class KeyboardComponent(
get() = _octave get() = _octave
set(value) { set(value) {
_octave = when { _octave = when {
value < 0 -> 0 value < MIN_OCTAVE -> MIN_OCTAVE
value > 9 -> 9 value > MAX_OCTAVE -> MAX_OCTAVE
else -> value else -> value
} }
requestUpdate() requestUpdate()
@@ -57,14 +57,14 @@ class KeyboardComponent(
// Set to track which notes are currently pressed // Set to track which notes are currently pressed
private val pressedNotes = mutableSetOf<Int>() private val pressedNotes = mutableSetOf<Int>()
// MIDI note numbers for C4 to B4 (one octave) // MIDI note numbers for one octave
private val whiteKeys = listOf(60, 62, 64, 65, 67, 69, 71) private val whiteKeys = BASE_WHITE_KEYS
private val blackKeys = listOf(61, 63, 66, 68, 70) private val blackKeys = BASE_BLACK_KEYS
// Key dimensions // Key dimensions
private val whiteKeyWidth = keyboardWidth / 7 private val whiteKeyWidth = keyboardWidth / WHITE_KEYS_PER_OCTAVE
private val blackKeyWidth = (keyboardWidth / 9) private val blackKeyWidth = (keyboardWidth / BLACK_KEY_WIDTH_DIVISOR)
private val blackKeyHeight = keyboardHeight * 60 / 100 private val blackKeyHeight = keyboardHeight * BLACK_KEY_HEIGHT_PERCENTAGE / 100
// Calculate positions for black keys // Calculate positions for black keys
private val blackKeyPositions = listOf( private val blackKeyPositions = listOf(
@@ -97,12 +97,12 @@ class KeyboardComponent(
pressedNotes.clear() pressedNotes.clear()
} }
private fun getOctaveOffset(): Int = (octave - 5) * 12 private fun getOctaveOffset(): Int = (octave - OCTAVE_BASE) * NOTES_PER_OCTAVE
private fun getMidiNoteFromMousePosition(x: Double, y: Double): Int? { private fun getMidiNoteFromMousePosition(x: Double, y: Double): Int? {
// Check if click is on a black key (black keys are on top of white keys) // Check if click is on a black key (black keys are on top of white keys)
if (y <= blackKeyHeight) { if (y <= blackKeyHeight) {
for (j in 0 until 5) { for (j in 0 until BLACK_KEYS_PER_OCTAVE) {
if (x >= blackKeyPositions[j] && x <= blackKeyPositions[j] + blackKeyWidth) { if (x >= blackKeyPositions[j] && x <= blackKeyPositions[j] + blackKeyWidth) {
return blackKeys[j] + getOctaveOffset() return blackKeys[j] + getOctaveOffset()
} }
@@ -110,13 +110,13 @@ class KeyboardComponent(
// If no black key was pressed, check for white key // If no black key was pressed, check for white key
val keyIndex = (x / whiteKeyWidth).toInt() val keyIndex = (x / whiteKeyWidth).toInt()
if (keyIndex in 0..6) { if (keyIndex in 0 until WHITE_KEYS_PER_OCTAVE) {
return whiteKeys[keyIndex] + getOctaveOffset() return whiteKeys[keyIndex] + getOctaveOffset()
} }
} else { } else {
// If y > blackKeyHeight, it's definitely a white key // If y > blackKeyHeight, it's definitely a white key
val keyIndex = (x / whiteKeyWidth).toInt() val keyIndex = (x / whiteKeyWidth).toInt()
if (keyIndex in 0..6) { if (keyIndex in 0 until WHITE_KEYS_PER_OCTAVE) {
return whiteKeys[keyIndex] + getOctaveOffset() return whiteKeys[keyIndex] + getOctaveOffset()
} }
} }
@@ -125,7 +125,7 @@ class KeyboardComponent(
override fun HtmlBuilder.render() { override fun HtmlBuilder.render() {
div(KeyboardCls.name) { div(KeyboardCls.name) {
style = "width: ${keyboardWidth}px; height: ${keyboardHeight + 60}px" style = "width: ${keyboardWidth}px; height: ${keyboardHeight + KEYBOARD_CONTROLS_HEIGHT}px"
onMouseLeaveFunction = { event -> onMouseLeaveFunction = { event ->
if (event is MouseEvent) { if (event is MouseEvent) {
@@ -196,7 +196,7 @@ class KeyboardComponent(
} }
// Draw white keys // Draw white keys
for (i in 0 until 7) { for (i in 0 until WHITE_KEYS_PER_OCTAVE) {
val midiNote = whiteKeys[i] + getOctaveOffset() val midiNote = whiteKeys[i] + getOctaveOffset()
val isPressed = pressedNotes.contains(midiNote) val isPressed = pressedNotes.contains(midiNote)
rect( rect(
@@ -210,7 +210,7 @@ class KeyboardComponent(
} }
// Draw black keys // Draw black keys
for (i in 0 until 5) { for (i in 0 until BLACK_KEYS_PER_OCTAVE) {
val midiNote = blackKeys[i] + getOctaveOffset() val midiNote = blackKeys[i] + getOctaveOffset()
val isPressed = pressedNotes.contains(midiNote) val isPressed = pressedNotes.contains(midiNote)
rect( rect(
@@ -228,6 +228,7 @@ class KeyboardComponent(
} }
companion object : CssId("keyboard") { companion object : CssId("keyboard") {
// CSS class names
object KeyboardCls : CssName() object KeyboardCls : CssName()
object KeyboardControlsCls : CssName() object KeyboardControlsCls : CssName()
object KeyboardInfoCls : CssName() object KeyboardInfoCls : CssName()
@@ -240,6 +241,21 @@ class KeyboardComponent(
object WhiteKeyPressedCls : CssName() object WhiteKeyPressedCls : CssName()
object BlackKeyPressedCls : CssName() object BlackKeyPressedCls : CssName()
// Constants
private const val OCTAVE_BASE = 5
private const val NOTES_PER_OCTAVE = 12
private const val BLACK_KEY_HEIGHT_PERCENTAGE = 60
private const val MIN_OCTAVE = 0
private const val MAX_OCTAVE = 9
private const val WHITE_KEYS_PER_OCTAVE = 7
private const val BLACK_KEYS_PER_OCTAVE = 5
private const val BLACK_KEY_WIDTH_DIVISOR = 9
private const val KEYBOARD_CONTROLS_HEIGHT = 60
// MIDI note numbers for C5 to B5 (one octave)
private val BASE_WHITE_KEYS = listOf(60, 62, 64, 65, 67, 69, 71) // C, D, E, F, G, A, B
private val BASE_BLACK_KEYS = listOf(61, 63, 66, 68, 70) // C#, D#, F#, G#, A#
init { init {
defineCss { defineCss {
select(cls(KeyboardCls)) { select(cls(KeyboardCls)) {