Hello everyone!
What I'm trying to do is create a visualization of a piano that responds to midi input. So far so good, but I am trying to figure out how to use the value of midi_event.pitch (which is a number corresponding to which note was pressed) and use that to pick an object (the correct piano key mesh) and move that object accordingly. Here's what I have so far, pretty straightforward, my node tree contains a node3d, with a child called KB, which has (for now) just two mesh3d instances, named 48 and 50, which corresponds to a C and D note respectively.

func _ready():
	OS.open_midi_inputs()
	print(OS.get_connected_midi_inputs())
	print(" ")

func _input(input_event):
	if input_event is InputEventMIDI:
		_keymove(input_event)
	
func _keymove(midi_event: InputEventMIDI):
	print (str(midi_event.pitch))
	if midi_event.message == 9:
		$"KB/50".rotation.x = deg*92.5
	elif midi_event.message == 8:
		$"KB/50".rotation.x = deg*90

Of course key #50 is the only one that moves, no matter which note is played, which is as expected.
Now, I could include a condition on the if/elif statements to see whether it is the correct key, and this does move key #50 only when the correct note is pressed:

func _keymove(midi_event: InputEventMIDI):
	print (str(midi_event.pitch))
	if midi_event.message == 9 && midi_event.pitch==50:
		$"KB/50".rotation.x = deg*92.5
	elif midi_event.message == 8:
		$"KB/50".rotation.x = deg*90

but obviously this is grossly inefficient, since I'd end up with an if/elif for every single key that can be pressed, and whether the signal is a "note on" or "note off" message. On an 88 key controller that would mean a few hundred lines of elif statements. That's like sending a postman with only one letter and having him knock at every single door in town asking if he's at the right address!

I have also tried many things to include the value in a string within the node path (such as having the value sent to a string as well as inluding it directly) but it doesn't seem like that is possible.

What I'd like to do is take the value of midi_event.pitch and use that to choose which key moves. Thanks for any advice =)

  • kuligs2 replied to this.
  • Quotes are only needed when using a string literal, not when using a string variable.

    # string literal
    my_function("hello")
    
    # string variable
    var s: String = "hello"
    my_function(s)
    
    func my_function(s: String) -> void:
        pass

    In _ready create a dictionary with the pitch value as the key and the node as the value. Then in your _keyymove function access the respective node via this dictionary.

    tofanimation
    nah its much simpler.
    If you have named your 3D nodes accordingly -> key 50scene = 50 then:

    https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html

    var key_num = midi_event.pitch
    var the_key_node = get_parent().get_node(key_num)
    if midi_event.message == 9:
        key_node.rotation.x = deg*92.5
    elseif midi_event.message == 8:
        key_node.rotation.x = deg*90

    something like that?

    Im a bit rusty with godot, but you can get node by name, and then do stuff with it.

    Thanks for the ideas.
    I have been playing around with kuligs2's idea since it seemed a little more streamlined to what I am trying to do, but I still haven't cracked the code.

    First I tried the following:

    func _keymove(midi_event: InputEventMIDI):
    	var key_num = str(midi_event.pitch)
    	var key_node = get_parent().get_node(key_num)
    	if midi_event.message == 9:
    		key_node.rotation.x = deg*92.5
    	elif midi_event.message == 8:
    		key_node.rotation.x = deg*90

    When I press a note, the debug fails and I get the following error message:

    Invalid access to property or key 'rotation' on a base object of type 'null instance'.

    I backed up a step and simply had it simply print my variables:

    func _keymove(midi_event: InputEventMIDI):
    	var key_num = str(midi_event.pitch)
    	var key_node = get_node(key_num)
    	print (midi_event.pitch)
    	print (key_num)
    	print (key_node)

    The debug console prints the following when note 50 is pressed:

    50
    50
    <Object#null>

    "print (midi_event.pitch)" prints the key number as expected.
    "print (key_num)" also prints the same number, which means I probably don't need to pass midi_event.pitch to another variable, but no harm done.
    "print (key_node)" prints <Object#null> which seems to be why I was getting that error message about 'null instance'.

    Looking at the documentation again, the examples on the get_node() page all ask for the node name in quotes. The examples they give are:

    get_node("Sword")
    get_node("Backpack/Dagger")
    get_node("../Swamp/Alligator")
    get_node("/root/MyGame")

    All in quotes. Which leads me back to thinking I'd have to be able to include quotes within the string variable, but I can't figure out any way to do that, as the second quote always closes the first one, so nested quotes doesn't seem to be possible

      tofanimation as the second quote always closes the first one, so nested quotes doesn't seem to be possible

      I'm not sure if it will help in your case, but you can try it out:

      Quotes

      Use double quotes unless single quotes make it possible to escape fewer characters in a given string. See the examples below:

      # Normal string.
      print("hello world")
      
      # Use double quotes as usual to avoid escapes.
      print("hello 'world'")
      
      # Use single quotes as an exception to the rule to avoid escapes.
      print('hello "world"')
      
      # Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
      print("'hello' \"world\"")

        Tomcat
        Thanks Tomcat,
        this was helpful in understanding how to print quotes from a string:

        func _keymove(midi_event: InputEventMIDI): #make function point to the correct object based on midi_event.pitch
        	var key_node = '"' + str(midi_event.pitch) + '"'
        	print (midi_event.pitch)
        	print (key_node)
        	print (get_node(key_node))

        which prints:

        57
        "57"
        <Object#null>

        The quotes print, but unfortunately doesn't seem to be able to pass the node name on to get_node() successfully, as I still get the <Object#null> message. I'll keep playing with combinations of these suggestions for now...

        Passing the node name to get_node works just fine. The node path is just wrong. The node your script is attached to does not have any children named 57 (or "57" as in your key_node variable). The correct node path would be KB/57, as you did correctly in your original code.

        just build up the path to the node get_node("KB/"+key_num). As i stated before, im rusty on the godot, it was a general concept

        Quotes are only needed when using a string literal, not when using a string variable.

        # string literal
        my_function("hello")
        
        # string variable
        var s: String = "hello"
        my_function(s)
        
        func my_function(s: String) -> void:
            pass

          DaveTheCoder Quotes are only needed when using a string literal, not when using a string variable.

          Aha, this did the trick! I removed the quotes and now the function finds the correct mesh in the node path. Here's what I ended up with:

          func _keymove(midi_event: InputEventMIDI): #make function point to the correct object based on midi_event.pitch
          	var key_node = str("KB/") + str(midi_event.pitch)
          	print (midi_event.pitch)
          	print (key_node)
          	print(get_node(key_node))
          	print(" ")
          	
          	if midi_event.message == 9:
          		get_node(key_node).rotation.x = deg*92.5
          	elif midi_event.message == 8:
          		get_node(key_node).rotation.x = deg*90

          Playing each note now prints an address to the console, something like 48:<Node3D#495870439857> for example, and of course moves the 3D mesh of the correct key.

          Thank you all for your ideas, each of which gave me a little insight into various aspects of how this works. I'm thankful to now have this as a handy part of my toolkit!