piano.gd 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. extends Control
  2. # A standard piano with 88 keys has keys from 21 to 108.
  3. # To get a different set of keys, modify these numbers.
  4. # A maximally extended 108-key piano goes from 12 to 119.
  5. # A 76-key piano goes from 23 to 98, 61-key from 36 to 96,
  6. # 49-key from 36 to 84, 37-key from 41 to 77, and 25-key
  7. # from 48 to 72. Middle C is pitch number 60, A440 is 69.
  8. const START_KEY = 21
  9. const END_KEY = 108
  10. const WhiteKeyScene := preload("res://piano_keys/white_piano_key.tscn")
  11. const BlackKeyScene := preload("res://piano_keys/black_piano_key.tscn")
  12. var piano_key_dict := Dictionary()
  13. @onready var white_keys: HBoxContainer = $WhiteKeys
  14. @onready var black_keys: HBoxContainer = $BlackKeys
  15. func _ready() -> void:
  16. assert(not _is_note_index_sharp(_pitch_index_to_note_index(START_KEY)), "The start key can't be a sharp note (limitation of this piano-generating algorithm). Try 21.")
  17. for i in range(START_KEY, END_KEY + 1):
  18. piano_key_dict[i] = _create_piano_key(i)
  19. if white_keys.get_child_count() != black_keys.get_child_count():
  20. _add_placeholder_key(black_keys)
  21. OS.open_midi_inputs()
  22. if not OS.get_connected_midi_inputs().is_empty():
  23. print(OS.get_connected_midi_inputs())
  24. func _input(input_event: InputEvent) -> void:
  25. if input_event is not InputEventMIDI:
  26. return
  27. var midi_event: InputEventMIDI = input_event
  28. if midi_event.pitch < START_KEY or midi_event.pitch > END_KEY:
  29. # The given pitch isn't on the on-screen keyboard, so return.
  30. return
  31. _print_midi_info(midi_event)
  32. var key: PianoKey = piano_key_dict[midi_event.pitch]
  33. if midi_event.message == MIDI_MESSAGE_NOTE_ON:
  34. key.activate()
  35. else:
  36. key.deactivate()
  37. func _add_placeholder_key(container: HBoxContainer) -> void:
  38. var placeholder := Control.new()
  39. placeholder.size_flags_horizontal = SIZE_EXPAND_FILL
  40. placeholder.mouse_filter = Control.MOUSE_FILTER_IGNORE
  41. placeholder.name = &"Placeholder"
  42. container.add_child(placeholder)
  43. func _create_piano_key(pitch_index: int) -> PianoKey:
  44. var note_index := _pitch_index_to_note_index(pitch_index)
  45. var piano_key: PianoKey
  46. if _is_note_index_sharp(note_index):
  47. piano_key = BlackKeyScene.instantiate()
  48. black_keys.add_child(piano_key)
  49. else:
  50. piano_key = WhiteKeyScene.instantiate()
  51. white_keys.add_child(piano_key)
  52. if _is_note_index_lacking_sharp(note_index):
  53. _add_placeholder_key(black_keys)
  54. piano_key.setup(pitch_index)
  55. return piano_key
  56. func _is_note_index_lacking_sharp(note_index: int) -> bool:
  57. # B and E, because no B# or E#
  58. return note_index in [2, 7]
  59. func _is_note_index_sharp(note_index: int) -> bool:
  60. # A#, C#, D#, F#, and G#
  61. return note_index in [1, 4, 6, 9, 11]
  62. func _pitch_index_to_note_index(pitch: int) -> int:
  63. pitch += 3
  64. return pitch % 12
  65. func _print_midi_info(midi_event: InputEventMIDI) -> void:
  66. print(midi_event)
  67. print("Channel: " + str(midi_event.channel))
  68. print("Message: " + str(midi_event.message))
  69. print("Pitch: " + str(midi_event.pitch))
  70. print("Velocity: " + str(midi_event.velocity))
  71. print("Instrument: " + str(midi_event.instrument))
  72. print("Pressure: " + str(midi_event.pressure))
  73. print("Controller number: " + str(midi_event.controller_number))
  74. print("Controller value: " + str(midi_event.controller_value))