View Source MIDIex notebook

Mix.install([{:midiex, path: "/Users/haubie/Development/midiex"}])
==> midiex
Compiling 1 file (.ex)
Compiling crate midiex in release mode (native/midiex)
   Compiling midiex v0.1.0 (/Users/haubie/Development/midiex/native/midiex)
    Finished release [optimized] target(s) in 0.55s
:ok

setup

Setup

finding-devices-ports

Finding devices (ports)

how-many-ports-are-there

How many ports are there?

Midiex.port_count()
%{input: 4, output: 4}

list-devices-ports

List devices (ports)

ports = Midiex.ports()
[
  %Midiex.MidiPort{
    direction: :input,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215786>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215787>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215788>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "MiniFuse 2",
    num: 3,
    port_ref: #Reference<0.522475107.1026162712.215789>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215790>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215791>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215792>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "MiniFuse 2",
    num: 3,
    port_ref: #Reference<0.522475107.1026162712.215793>
  }
]

filter-to-show-input-or-output-ports

Filter to show input or output ports

Midiex.ports(:output)
[
  %Midiex.MidiPort{
    direction: :output,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215798>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215799>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215800>
  },
  %Midiex.MidiPort{
    direction: :output,
    name: "MiniFuse 2",
    num: 3,
    port_ref: #Reference<0.522475107.1026162712.215801>
  }
]
out_port = Midiex.ports("Arturia", :output) |> List.first()
%Midiex.MidiPort{
  direction: :output,
  name: "Arturia MicroFreak",
  num: 1,
  port_ref: #Reference<0.522475107.1026162712.215807>
}
out_conn = Midiex.open(out_port)
%Midiex.OutConn{
  conn_ref: #Reference<0.522475107.1026162689.216660>,
  name: "Arturia MicroFreak",
  port_num: 1
}
Midiex.send_msg(out_conn, <<0x90, 60, 127>>)
%Midiex.OutConn{
  conn_ref: #Reference<0.522475107.1026162689.216660>,
  name: "Arturia MicroFreak",
  port_num: 1
}
Midiex.ports(:input)
[
  %Midiex.MidiPort{
    direction: :input,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215810>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215811>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215812>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "MiniFuse 2",
    num: 3,
    port_ref: #Reference<0.522475107.1026162712.215813>
  }
]

filter-by-port-name

Filter by port name

Midiex.ports("IAC", :input)
[
  %Midiex.MidiPort{
    direction: :input,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215818>
  }
]

virtual-devices

Virtual devices

virtual_conn = Midiex.create_virtual_output("My Virtual Connection")
%Midiex.OutConn{
  conn_ref: #Reference<0.522475107.1026162689.216746>,
  name: "My Virtual Connection",
  port_num: 4
}

Note that although you've created this virtual output, on your system it will appear as an import port to be discoverable by other MIDI software or devices.

If you call Midiex.ports/1 you'll see it as an input:

ports = Midiex.ports(:input)
[
  %Midiex.MidiPort{
    direction: :input,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.522475107.1026162712.215826>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215827>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215828>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "MiniFuse 2",
    num: 3,
    port_ref: #Reference<0.522475107.1026162712.215829>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "My Virtual Connection",
    num: 4,
    port_ref: #Reference<0.522475107.1026162712.215830>
  }
]

make-a-connection-to-a-device

Make a connection to a device

connect-to-an-output-device-and-send-a-message

Connect to an output device and send a message

out_conn =
  Midiex.ports("IAC", :output)
  |> List.first()
  |> Midiex.open()
%Midiex.OutConn{
  conn_ref: #Reference<0.522475107.1026162689.216766>,
  name: "IAC Driver Bus 1",
  port_num: 0
}
Midiex.Backend.get_subscribed_ports()
[]
Midiex.Backend.listen(Midiex.ports(:input) |> Enum.at(2))
[
  %Midiex.MidiPort{
    direction: :input,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215846>
  }
]
in_port = Midiex.ports("Key", :input) |> List.first()
%Midiex.MidiPort{
  direction: :input,
  name: "KeyStep Pro",
  num: 2,
  port_ref: #Reference<0.522475107.1026162712.215855>
}
1..10//1
|> Enum.map(fn i ->
  :timer.sleep(1000)
  Midiex.Backend.test(in_port)
end)
warning: variable "i" is unused (if the variable is not meant to be used, prefix it with an underscore)
  livebook/midiex_notebook.livemd#cell:7euaccszbergu2jit2isskr7ejpvc65q:2
[:ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok]
# Midiex.Backend.clear_subscribed_ports()
nil

connect-and-listen-to-an-input-device

Connect and listen to an input device

defmodule MyMidiServer do
  use GenServer

  @impl true
  def init(state \\ []) do
    IO.inspect(state, label: "INIT")
    {:ok, state}
  end

  @impl true
  def handle_info(msg, state) do
    case msg do
      msg ->
        IO.inspect(msg, label: "FROM RUST")
        msg

      _ ->
        msg
    end

    {:noreply, state}
  end

  def listen_to(device_name \\ "IAC Driver") do
    Midiex.ports()
    |> Midiex.filter_port_name_contains(device_name, direction: :input)
    |> List.first()
    |> Midiex.listen()
  end

  def listen(pid, device_name) do
    GenServer.cast(pid, {:listen, device_name})
  end

  @impl true
  def handle_cast({:listen, device_name}, state) do
    listen_to(device_name)
    {:noreply, state}
  end

  def subscribe(pid) do
    GenServer.cast(pid, :subscribe)
  end

  @impl true
  def handle_cast(:subscribe, state) do
    Midiex.subscribe()
    {:noreply, state}
  end
end
warning: clauses with the same name and arity (number of arguments) should be grouped together, "def handle_cast/2" was previously defined (livebook/midiex_notebook.livemd#cell:7rfguhfubcggnj6q6hoiiqg7lbqbjlab:36)
  livebook/midiex_notebook.livemd#cell:7rfguhfubcggnj6q6hoiiqg7lbqbjlab:46

warning: this clause cannot match because a previous clause at line 13 always matches
  livebook/midiex_notebook.livemd#cell:7rfguhfubcggnj6q6hoiiqg7lbqbjlab:17

warning: Midiex.filter_port_name_contains/3 is undefined or private
  livebook/midiex_notebook.livemd#cell:7rfguhfubcggnj6q6hoiiqg7lbqbjlab:26: MyMidiServer.listen_to/1
{:module, MyMidiServer, <<70, 79, 82, 49, 0, 0, 21, ...>>, {:handle_cast, 2}}
{:ok, pid} = GenServer.start_link(MyMidiServer, [])
INIT: []
{:ok, #PID<0.264.0>}
# MyMidiServer.listen(pid, "KeyStep")
# MyMidiServer.listen(pid, "MicroFreak")
nil
MyMidiServer.subscribe(pid)
:ok
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
Midiex.Backend.listen(Midiex.ports(:input) |> Enum.at(1))
[
  %Midiex.MidiPort{
    direction: :input,
    name: "Arturia MicroFreak",
    num: 1,
    port_ref: #Reference<0.522475107.1026162712.215863>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "KeyStep Pro",
    num: 2,
    port_ref: #Reference<0.522475107.1026162712.215846>
  }
]
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
FROM RUST: %{"port_name" => "Arturia MicroFreak", "port_num" => 1}
FROM RUST: %{"port_name" => "KeyStep Pro", "port_num" => 2}
Midiex.Backend.get_subscribed_ports()
[
  %Midiex.MidiPort{
    direction: :input,
    name: "IAC Driver Bus 1",
    num: 0,
    port_ref: #Reference<0.2084832073.4240834584.3914>
  },
  %Midiex.MidiPort{
    direction: :input,
    name: "My Virtual Connection",
    num: 1,
    port_ref: #Reference<0.2084832073.4240834584.3918>
  }
]
FROM RUST: %{"port_name" => "IAC Driver Bus 1", "port_num" => 0}
FROM RUST: %{"port_name" => "My Virtual Connection", "port_num" => 1}
FROM RUST: %{"port_name" => "IAC Driver Bus 1", "port_num" => 0}
FROM RUST: %{"port_name" => "My Virtual Connection", "port_num" => 1}
FROM RUST: %{"port_name" => "IAC Driver Bus 1", "port_num" => 0}
FROM RUST: %{"port_name" => "My Virtual Connection", "port_num" => 1}
Midiex.Backend.clear_subscribed_ports()
[]