View Source Midiex.Listener (Midiex v0.5.3)

GenServer for subscribing to MIDI input ports and responding to the MIDI messages (Midiex.MidiMessage) received.

how-this-works

How this works

This GenServer works by:

  • Subscribing to one or more MIDI input ports (using Midiex.subscribe/1). For each MIDI input port, Midiex.subscribe/1 will create a new OS thread (in Rust) which establishes a connection to the port and listens to messages. Incoming messages are then forwarded to the calling Elixir process (in this case, the Midiex.Listener process.)

    A subscription can be established on the start_link/1 or subscribe/2 functions, e.g.:

    # Get the first MIDI input port
    input_port = Midiex.ports(:input) |> List.first()
    
    # Start a lister for this MIDI input port
    {:ok, listener} =  Midiex.Listener.start_link(port: input_port)
  • Receieves MIDI messages in the form of a Midiex.MidiMessage struct, and passes it onto one or more Elixir handler functions. The handler takes one parameter representing the MIDI message, e.g.:

      # Add a simple message handler which inspects each message received:
      Midiex.Listener.add_handler(listener, fn (midi_msg) -> IO.inspect(midi_msg) end)

Midiex

example

Example

alias Midiex.Listener

# Get the first MIDI input port
input_port = Midiex.ports(:input) |> List.first()

# Start a lister for this MIDI input port
{:ok, listner} = Listener.start_link(port: input_port)

# Create a handler than inspects the MIDI messages received:
my_msg_hander = fn (midi_msg) -> IO.inspect(midi_msg, label: "MIDI message") end
Listener.add_handler(listener, &my_msg_hander/1)

# Stop listening to the input port
Listener.unsubscribe(listner, input_port)

Link to this section Summary

Functions

Add one or more callback function(s) which will recieve and handle MIDI messages.

Returns a specification to start this module under a supervisor.

Gets the servers state, returns %Midiex.Listener{} struct.

Creates a new %Midiex.Server{} struct.

Start the Midiex.Server GenServer.

Subscribe to one or more MIDI input ports.

Stops listening to the MIDI input port by unsubscribing to it.

Link to this section Functions

Link to this function

add_handler(pid, handler_fn)

View Source
@spec add_handler(pid(), function() | [function()]) :: :ok

Add one or more callback function(s) which will recieve and handle MIDI messages.

A single callback function or multiple callback functions can be provided in a list.

example

Example

# Start your Listener process
{:ok, listener} = Listener.start_link(port: input_port)

# Add a single handler
Listener.add_handler(listener, fn msg -> IO.inspect msg, label: "Inspecting msg" end)

# Add multiple handlers in a list
Listener.add_handler(
  listener,
  [
    fn msg -> IO.inspect msg, label: "Msg handler 1" end,
    fn msg -> IO.inspect msg, label: "Msg handler 1" end,
  ]
)

# If you've defined your hander function in a module function, pass it the usual way:
Listener.add_handler(listener, &MyModule.function_name/1)

Returns a specification to start this module under a supervisor.

See Supervisor.

@spec get_state(pid()) :: %Midiex.Listener{callback: term(), port: term()}

Gets the servers state, returns %Midiex.Listener{} struct.

Creates a new %Midiex.Server{} struct.

Takes an optional keyword list as the first parameter which can be used to populate individual struct keys.

The struct holds the following key-values:

  • :port which holds a list of MIDI input ports to listen to. These can be device ports %Midiex.MidiPort{direction: :input} or virtual ports %Midiex.VirtualMidiPort{}
  • :callback which holds a list of functions called when a message is received for an input port. The callback must be of single arity and take it's first parameter a message. See add_handler/2 for an example.
@spec start_link(keyword()) :: :ignore | {:error, any()} | {:ok, pid()}

Start the Midiex.Server GenServer.

Takes an optional keyword list as the first parameter which can be used to populate individual %Midiex.Listener{} struct keys. See new/1 for informaton.

examples

Examples

# Start with no options
{:ok, listener} = Midiex.Listener.start_link()

# Start, already passing the first available input port to listen to
first_port = Midiex.ports(:input) |> List.first()
{:ok, listener} = Midiex.Listener.start_link(ports: first_port)

# Start, already passing a list of all input ports available to listen to
{:ok, listener} = Midiex.Listener.start_link(ports: Midiex.ports(:input))
Link to this function

subscribe(pid, midi_input_port)

View Source
@spec subscribe(
  pid(),
  %Midiex.MidiPort{
    direction: :input,
    name: term(),
    num: term(),
    port_ref: term()
  }
  | %Midiex.VirtualMidiPort{direction: term(), name: term(), num: term()}
  | [
      %Midiex.MidiPort{
        direction: :input,
        name: term(),
        num: term(),
        port_ref: term()
      }
      | %Midiex.VirtualMidiPort{direction: term(), name: term(), num: term()}
    ]
) :: :ok

Subscribe to one or more MIDI input ports.

This accepts both ports listed on your device %Midiex.MidiPort{direction: :input} and virtual ports %Midiex.VirtualMidiPort{} you've created.

It accepts as it's second parameter either:

  • a single MIDI input port
  • a list of MIDI input ports
  • :all atom which will stop all MIDI input ports subscribed to.

example

Example

# Subscribe to the input port of the Arturia KeyStep Pro keyboard
keystep_in_port = Midiex.port("KeyStep Pro", :input)

# Returns a list with matching port names, in this case:
[
  %Midiex.MidiPort{
      direction: :input,
      name: "KeyStep Pro",
      num: 2,
      port_ref: #Reference<0.3139841870.4103995416.58432>
    }
]

# Create and start a listener process
{:ok, keyboard} = Midiex.Listener.start_link()

# Listen to MIDI messages from the keyboard
Midiex.Listener.subscribe(keyboard, keystep_in_port)

# Any keys you push on the keyboard will be listened to. Add one or more handlers with Midiex.Listener.add_handler/2 to process messages.
Link to this function

unsubscribe(pid, midi_input_ports)

View Source
@spec unsubscribe(
  pid(),
  %Midiex.MidiPort{
    direction: :input,
    name: term(),
    num: term(),
    port_ref: term()
  }
  | %Midiex.VirtualMidiPort{direction: term(), name: term(), num: term()}
  | [
      %Midiex.MidiPort{
        direction: :input,
        name: term(),
        num: term(),
        port_ref: term()
      }
      | %Midiex.VirtualMidiPort{direction: term(), name: term(), num: term()}
    ]
  | :all
) :: :ok

Stops listening to the MIDI input port by unsubscribing to it.

This accepts both ports listed on your device %Midiex.MidiPort{direction: :input} and virtual ports %Midiex.VirtualMidiPort{} you've created.

It accepts as it's second parameter either:

  • a single MIDI input port
  • a list of MIDI input ports
  • :all atom which will stop all MIDI input ports subscribed to.

Important

This stops the Rust OS thread from sending messages from that MIDI input port. If other Elixir processes have also subscribed to that port, they will also stop recieving messages.