Unity's "new" input system and Keijiro's Minis midi stuff

Many of you might be used to using the old Klak/MidiJack libraries for midi input into Unity, however they're getting a little long in the tooth now & Unity has changed up its input system to supposedly be more flexible & modern.

The new midi input library is called Minis, I'm pretty sure it's short for Midi New Input System.. but that doesn't really matter :D
There isn't a whole lot of easy-to-understand documentation on how to use it in scripts, so I thought I'd share what I'd figured out through digging around Keijiro's example files.

I'm kind of assuming you know how to use the Input Action menu - as it would seem you don't even need it, for the method I'm about to show you... In any case if you do setup some midi inputs there (or indeed any other kinds of inputs - and it seems like you can only setup simple single note button presses there? - you would use code resembling this (following) to trigger things functions as you would have before, with GetInputKeyDown(etc).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class placeholder : MonoBehaviour
{
    public Box_inputs control;
    public float test=0;
    Vector2 movement;
    Transform thing;

    private void Awake()
    {
        control = new Box_inputs();
        control.player.onePress.performed += _ => onePressDone();
        control.player.move.performed += ctx => movement = ctx.ReadValue<Vector2>();
        control.player.move.canceled += ctx => movement = ctx.ReadValue<Vector2>();
        thing = GetComponent<Transform>();

        control.player.midi.performed += _ => midiKey();
        control.player.cc.performed += _ => ccKey();

    }
    void ccKey()
    {
        Debug.Log("cc turned");
    }

    void midiKey()
    {
        Debug.Log("C key pressed");
    }

    void onePressDone()
    {

        Debug.Log("one pressed pressed");
    }


    private void FixedUpdate()
    {
        Vector3 cat =new Vector3 (movement.x, 0, movement.y );
        thing.transform.Translate(cat*Time.deltaTime);
    }

    private void OnEnable()

    {
        control.Enable();
    }

    private void OnDisable()
    {
        control.Disable();
    }
}
 

Firstly note you need to be Using Unity.InputSystem; Oh  - and you should have generated the C# class for your input actions by selecting the Input Action, then clicking that button in the Inspector. This lets you create an instance of the class in this script - public BoxInputs control; and control=new Box_inputs();

Here's a screenshot of my input actions..it might help

control.player.onePress.performed += _ => onePressDone();
control.player.move.performed += ctx => movement = ctx.ReadValue<Vector2>();
control.player.move.canceled += ctx => movement = ctx.ReadValue<Vector2>();

as you can see we're accessing the "onePress" action within the "player" action map. "Performed"is when the action is performed... The opposite would be cancelled. This can be useful for when you release the key.
What we're doing with the += ctx => is something called "deferred" or lambda functions.
The ctx can be called anything... it can even just be an underscore if you're not passing any data through and are just calling a function. With the "move" action, we're storing the vector2 values received from the action into the vector2 value "movement". In the update function later on, we transform the "transform" by these values.
I don't fully understand the deferred/lambda stuff. But I know it's supposed to keep your code a bit more concise, as you can call and define functions in a line...

The last important thing with this code - is you need to enable and disable your input action stuff for it to work. So in OnEnable and OnDisable, we do exactly that. It's all housekeeping sort of stuff.


Ok - next up is the Keijiro Minis specific code. It's cobbled together from one of the example files he lists on his github & it seems to ignore the Input Action editor completely - Input Actions must still be installed though, as Minis extends a bunch of the classes/actions/stuff there.

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;

public class note_sender_d : MonoBehaviour
{
    Material midi_mat;
    private void Awake()
    {
        midi_mat = GetComponent<Renderer>().material;
    }
    void Start()
    {
        InputSystem.onDeviceChange += (device, change) =>
          {
              if (change != InputDeviceChange.Added) return;

              var midiDevice = device as Minis.MidiDevice;
              if (midiDevice == null) return;
           

               midiDevice.onWillNoteOn += (note,velocity) => sendToMaterial(note.noteNumber,(note.device as Minis.MidiDevice).channel);
              //midiDevice.onWillNoteOn += (note, velocity) => sendToMaterial(note.noteNumber, velocity);

          };
                            
    }
    void sendToMaterial(int noteNumber,int channel) {
        midi_mat.SetFloat("_notenum", noteNumber);
        midi_mat.SetFloat("_channel",channel);

   } 
}

One thing - I don't know if I'm supposed to be disabling something somewhere? It all seems to work without enabling........ so I don't really know if we're creating memory leaks here!!

Anyway - I'm grabbing the material that's attached to the gameobject, that this script is attached to, using GetComponent..
Then in Start() I think I'm checking for a new midi device and assigning it to a variable called "midiDevice". When this device does a note-on, I then pass the note and velocity values (it seems you must pass both) to a custom function called sendToMaterial. In the code, I send the note number and also the midi channel
which seems to be accessed in a bit of a weird way. I think it's casting the note device as a MidiDevice and *then* accessing the channel data.  On the Minis github, it says that each channel is treated as a different device. You can see this happening if you check it out on the Input Debugger window.
Anyway!!! The sendToMaterial function sets a value in the material's shader, parameters I've named _notenum and _channel.
The commented out line was where I tested out sending velocity over to the shader. Hopefully this stuff makes enough sense for you to get something working.

Comments

  1. Hi Dave! Thanks for writing about this.. I've barely found anything on working with Minis and I'm completely stuck. It looks like you've been through the confusion I'm going through right now and made it out alive. Is there any chance you'd be able to help me out with this shit? ;)

    ReplyDelete

Post a Comment

Popular posts from this blog

setting VFX graph properties using C#

scripting custom render texture creation, assignment, shaders