View Source Midiex (Midiex v0.1.1)
This is the main Midiex module.
It's built around three basic concepts:
- Ports:
- list or count MIDI ports availble (for example, a keyboard or synth)
- Connections:
- open or close connections to MIDI ports
- create a virtual input or output connections so your Elixir application appears as a MIDI device
- Messages:
- send or receive messages to and from connections.
examples
Examples
# List MIDI ports
Midiex.ports()
# Lists MIDI ports discoverable on your system
# [
# %Midiex.MidiPort{
# direction: :input,
# name: "IAC Driver Bus 1",
# num: 0,
# port_ref: #Reference<0.2239960018.1937899544.176288>
# },
# %Midiex.MidiPort{
# direction: :output,
# name: "IAC Driver Bus 1",
# num: 0,
# port_ref: #Reference<0.2239960018.1937899544.176289>
# }
# ]
# Create a virtual output connection
piano = Midiex.create_virtual_output_conn("piano")
# Returns an output connection:
# %Midiex.OutConn{
# conn_ref: #Reference<0.1633267383.3718381569.210768>,
# name: "piano",
# port_num: 0
# }
# Send MIDI messages to a connection
# In the message below, the note 60 is equivalent to Middle C and 127 means maximum velocity
note_on = <<0x90, 60, 127>>
note_off = <<0x80, 60, 127>>
Midiex.send_msg(piano, note_on)
:timer.sleep(3000) # wait three seconds
Midiex.send_msg(piano, note_off)
livebook-tour
Livebook tour
Also see the introductory tour in LiveBook at /livebook/midiex_notebook.livemd.
Link to this section Summary
Port discovery
Returns the count of the number of input and output MIDI ports in as a map.
Lists MIDI ports availabile on the system.
List MIDI ports matching the specified direction (e.g. input or output)
Lists MIDI ports matching the name. This can be either a string match or a regex pattern.
Virtual ports & connections
Creates a virtual input port struct.
Creates a virtual output connection.
Link to this section Port discovery
Returns the count of the number of input and output MIDI ports in as a map.
Midiex.port_count()
# Returns a map in the following format:
# %{input: 2, output: 0}
@spec ports() :: [ %Midiex.MidiPort{ direction: term(), name: term(), num: term(), port_ref: term() } ]
Lists MIDI ports availabile on the system.
Midiex.ports()
# Returns a list of input or output ports:
# [
# %Midiex.MidiPort{
# direction: :input,
# name: "Piano",
# num: 0,
# port_ref: #Reference<0.249304305.242352152.40090>
# },
# %Midiex.MidiPort{
# direction: :input,
# name: "Drums",
# num: 1,
# port_ref: #Reference<0.249304305.242352152.40091>
# }
# ]
@spec ports(:input | :output) :: [ %Midiex.MidiPort{ direction: term(), name: term(), num: term(), port_ref: term() } ]
List MIDI ports matching the specified direction (e.g. input or output)
Takes an atom as the first parameter representing the direction:
- :input - lists input ports only
- :output - lists output ports only.
Midiex.ports(:input)
# Returns a list of input or output MIDI ports:
# [
# %Midiex.MidiPort{
# direction: :input,
# name: "Piano",
# num: 0,
# port_ref: #Reference<0.249304305.242352152.40090>
# },
# %Midiex.MidiPort{
# direction: :input,
# name: "Drums",
# num: 1,
# port_ref: #Reference<0.249304305.242352152.40091>
# }
# ]
@spec ports(binary() | map(), (:input | :output) | nil) :: [ %Midiex.MidiPort{ direction: term(), name: term(), num: term(), port_ref: term() } ]
Lists MIDI ports matching the name. This can be either a string match or a regex pattern.
Optionally takes a direction (:input or :output) can be given.
Examples:
# Regex examples
# List ports containing the word 'Arturia' and ignore case
Midiex.ports(~r/Arturia/i)
# List output ports starting the word 'Arturia' and ignore case
Midiex.ports(~r/^Arturia/i, :output)
# String matching examples
# List input ports with the name 'KeyStep Pro'
Midiex.ports("KeyStep Pro", :input)
# List output ports with the name 'Arturia MicroFreak'
Midiex.ports("Arturia MicroFreak", :output)
Link to this section Output connections
@spec close( %Midiex.OutConn{conn_ref: term(), name: term(), port_num: term()} | [%Midiex.OutConn{conn_ref: term(), name: term(), port_num: term()}] ) :: any()
Closes a MIDI output connection.
Accepts as the first parameter either a:
- MIDI output connection, e.g. a
%Midiex.OutConn{}
struct - List of output connections.
example
Example
# Connect to the first output port
out_port = Midiex.ports(:output) |> List.first()
out_conn = Midiex.open(out_port)
Midiex.close_out_conn(out_conn)
# Will return :ok if successful
@spec open( %Midiex.MidiPort{ direction: :output, name: term(), num: term(), port_ref: term() } | [ %Midiex.MidiPort{ direction: :output, name: term(), num: term(), port_ref: term() } ] ) :: %Midiex.OutConn{conn_ref: term(), name: term(), port_num: term()} | [%Midiex.OutConn{conn_ref: term(), name: term(), port_num: term()}]
Creates a connection to the MIDI port.
Accepts one of the following as a parameter:
- MIDI output port, e.g. a
%Midiex.MidiPort{direction: :output}
struct - List of MIDI output ports.
Returns an output connection (%Midiex.OutConn{)
) or a list of output connections if a list was output ports was given as the first parameter.
example
Example
connect-to-a-single-output-port
Connect to a single output port
# Get the first available MIDI output port
out_port = Midiex.ports(:output) |> List.first()
# Open an output connection
out_conn = Midiex.open(out_port)
# Returns an output connection struct, for example:
%Midiex.OutConn{
conn_ref: #Reference<0.3613027763.2067398660.163505>,
name: "IAC Driver Bus 1",
port_num: 0
}
You can now send messages to the connection:
# Send a note on message for D4
Midiex.msg_send(out_conn, Midiex.Message.note_on(:D4))
# Let note play for 2 seconds
:timer.sleep(2000)
# Send a note off message for D4
Midiex.msg_send(out_conn, Midiex.Message.note_off(:D4))
connect-to-a-list-of-output-ports
Connect to a list of output ports
# Get a list of available output ports
out_ports = Midiex.ports(:output)
# Returns a list of available MIDI output ports, e.g.:
[
%Midiex.MidiPort{
direction: :output,
name: "Arturia MicroFreak",
num: 1,
port_ref: #Reference<0.3139841870.4103995416.58431>
},
%Midiex.MidiPort{
direction: :output,
name: "KeyStep Pro",
num: 2,
port_ref: #Reference<0.3139841870.4103995416.58432>
},
%Midiex.MidiPort{
direction: :output,
name: "MiniFuse 2",
num: 3,
port_ref: #Reference<0.3139841870.4103995416.58433>
}
]
# Connect to each port
out_conns = Midiex.open(out_ports)
# Returns a list of MIDI output connections, e.g.:
[
%Midiex.OutConn{
conn_ref: #Reference<0.1633267383.3718381569.210768>,
name: "Arturia MicroFreak",
port_num: 1
},
%Midiex.OutConn{
conn_ref: #Reference<0.3139841870.4103995416.58432>,
name: "KeyStep Pro",
port_num: 2
},
%Midiex.OutConn{
conn_ref: #Reference<0.3139841870.4103995416.58433>,
name: "MiniFuse 2",
port_num: 3
}
]
Link to this section Virtual ports & connections
@spec create_virtual_input(String.t()) :: %Midiex.VirtualMidiPort{ direction: term(), name: term(), num: term() }
Creates a virtual input port struct.
Takes a name as the first parameter.
This is only available on platforms that support virtual ports (currently every platform but Windows).
Important
Even though this creates an input port, beacause it's a virtual port it is listed as an 'output' when querying the OS for available devices.
That means other software or devices will discover it and use it as a an output and can send messages to it.
It also means it will show as
%Midiex.MidiPort{direction: :output}
when callingMidiex.ports()
.Note that it won't be discoverable until the input port is subscribed.
example
Example
# Create a virtual MIDI input by giving it a name. MIDIex will also assign it an input port number (`num`).
my_virtual_in = Midiex.create_virtual_input("My Virtual Input")
# This will return a VirtualMidiPort struct in the following format
# %Midiex.VirtualMidiPort{direction: :input, name: "My Virtual Input", num: 1}
The %Midiex.VirtualMidiPort{}
struct can then be passed to MIDI input port listener functions, such as:
Midiex.subscribe(my_virtual_in)
- If using a Listener GenServer:
Midiex.Listener.start(port: my_virtual_in)
Midiex.Listener.subscribe(listener, my_virtual_in)
Likewise, once subscribed to, the virtual input port can be unsubscribed to:
Midiex.unsubscribe(my_virtual_in)
- If using a Listener GenServer:
Midiex.Listener.unsubscribe(my_virtual_in)
@spec create_virtual_output(String.t()) :: %Midiex.OutConn{ conn_ref: term(), name: term(), port_num: term() }
Creates a virtual output connection.
This allows your Elixir application to be seen as a MIDI device.
Note this is only available on platforms that support virtual ports (currently every platform but Windows).
# Create an output connection called "piano"
piano_conn = Midiex.create_virtual_output("piano")
You can send messages to MIDI software or hardware connected to this virtual device in the standard way, e.g.:
note_on = <<0x90, 60, 127>>
note_off = <<0x80, 60, 127>>
Midiex.send_msg(piano, note_on)
:timer.sleep(3000) # wait three seconds
Midiex.send_msg(piano, note_off)
Important
Even though this creates an output port, beacause it's a virtual port it is listed as an 'input' when querying the OS for available devices.
That means other software or devices will discover it and use it as a an input, such as to receive messages.
It also means it will show as
%Midiex.MidiPort{direction: :input}
when callingMidiex.ports()
.