Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
inputs: ["mix.exs", "{lib,test}/**/*.{ex, exs}"]
]
8 changes: 4 additions & 4 deletions lib/mix/tasks/portmidi.devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ defmodule Mix.Tasks.Portmidi.Devices do
@shortdoc "Shows the connected devices"

def run(_args) do
IO.puts "Input:"
IO.puts("Input:")
list_devices(:input)

IO.puts "Output:"
IO.puts("Output:")
list_devices(:output)
end

defp list_devices(type) do
PortMidi.devices[type]
PortMidi.devices()[type]
|> Enum.each(&print_device/1)
end

defp print_device(device) do
IO.puts " - #{device.name}"
IO.puts(" - #{device.name}")
end
end
12 changes: 5 additions & 7 deletions lib/portmidi.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ defmodule PortMidi do
"""
@spec open(:input, <<>>) :: {:ok, pid()} | {:error, atom()}
def open(:input, device_name) do
Input.start_link device_name
Input.start_link(device_name)
end

@doc """
Expand All @@ -63,7 +63,7 @@ defmodule PortMidi do
"""
@spec close(atom, pid()) :: :ok
def close(device_type, device)
def close(:input, input), do: Input.stop(input)
def close(:input, input), do: Input.stop(input)
def close(:output, output), do: Input.stop(output)

@doc """
Expand All @@ -72,8 +72,7 @@ defmodule PortMidi do
events in its mailbox as soon as they are emitted from the device.
"""
@spec listen(pid(), pid()) :: :ok
def listen(input, pid), do:
Input.listen(input, pid)
def listen(input, pid), do: Input.listen(input, pid)

@doc """
Writes a MIDI event to the given `output` device. `message` can be a tuple
Expand All @@ -86,13 +85,12 @@ defmodule PortMidi do
@spec write(pid(), message) :: :ok
@spec write(pid(), {message, timestamp}) :: :ok
@spec write(pid(), [{message, timestamp}, ...]) :: :ok
def write(output, message), do:
Output.write(output, message)
def write(output, message), do: Output.write(output, message)

@doc """
Returns a map with input and output devices, in the form of
`PortMidi.Device` structs
"""
@spec devices() :: %{input: [%PortMidi.Device{}, ...], output: [%PortMidi.Device{}, ...]}
def devices, do: Devices.list
def devices, do: Devices.list()
end
4 changes: 1 addition & 3 deletions lib/portmidi/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@ defmodule PortMidi.Device do
|> make_struct()
end

defp make_struct(map), do:
struct(__MODULE__, map)
defp make_struct(map), do: struct(__MODULE__, map)
end

7 changes: 3 additions & 4 deletions lib/portmidi/devices.ex
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
defmodule PortMidi.Devices do
import PortMidi.Nifs.Devices
alias PortMidi.Device
alias PortMidi.Device

def list do
do_list()
|> Map.update(:input, [], &do_build_devices/1)
|> Map.update(:input, [], &do_build_devices/1)
|> Map.update(:output, [], &do_build_devices/1)
end

defp do_build_devices(devices), do:
Enum.map(devices, &Device.build/1)
defp do_build_devices(devices), do: Enum.map(devices, &Device.build/1)
end
3 changes: 1 addition & 2 deletions lib/portmidi/input.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule PortMidi.Input do
alias PortMidi.Listeners

def start_link(device_name) do
Server.start_link device_name
Server.start_link(device_name)
end

def listen(input, pid) do
Expand All @@ -14,4 +14,3 @@ defmodule PortMidi.Input do
Server.stop(input)
end
end

17 changes: 8 additions & 9 deletions lib/portmidi/input/reader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,35 @@ defmodule PortMidi.Input.Reader do
@buffer_size Application.get_env(:portmidi, :buffer_size, 256)

def start_link(server, device_name) do
Agent.start_link fn -> start(server, device_name) end
Agent.start_link(fn -> start(server, device_name) end)
end

# Client implementation
#######################

def listen(agent), do:
Agent.get_and_update agent, &do_listen/1
def listen(agent), do: Agent.get_and_update(agent, &do_listen/1)

def stop(agent) do
Agent.get agent, &do_stop/1
Agent.get(agent, &do_stop/1)
Agent.stop(agent)
end

# Agent implementation
######################
defp start(server, device_name) do
case device_name |> String.to_char_list |> do_open do
{:ok, stream} -> {server, stream}
case device_name |> String.to_char_list() |> do_open do
{:ok, stream} -> {server, stream}
{:error, reason} -> exit(reason)
end
end

defp do_listen({server, stream}) do
task = Task.async fn -> loop(server, stream) end
task = Task.async(fn -> loop(server, stream) end)
{:ok, {server, stream, task}}
end

defp loop(server, stream) do
if do_poll(stream) == :read, do: read_and_send(server,stream)
if do_poll(stream) == :read, do: read_and_send(server, stream)
loop(server, stream)
end

Expand All @@ -44,7 +43,7 @@ defmodule PortMidi.Input.Reader do
end

defp do_stop({_server, stream, task}) do
task |> Task.shutdown
task |> Task.shutdown()
stream |> do_close
end
end
15 changes: 6 additions & 9 deletions lib/portmidi/input/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ defmodule PortMidi.Input.Server do
# Client implementation
#######################

def new_messages(server, messages), do:
GenServer.cast(server, {:new_messages, messages})
def new_messages(server, messages), do: GenServer.cast(server, {:new_messages, messages})

def stop(server), do:
GenServer.stop(server)
def stop(server), do: GenServer.stop(server)

# Server implementation
#######################
Expand All @@ -25,20 +23,19 @@ defmodule PortMidi.Input.Server do
{:ok, reader} ->
Reader.listen(reader)
{:ok, reader}

{:error, reason} ->
{:stop, reason}
end
end

def handle_cast({:new_messages, messages}, reader) do
self()
|> Listeners.list
|> Enum.each(&(send(&1, {self(), messages})))
|> Listeners.list()
|> Enum.each(&send(&1, {self(), messages}))

{:noreply, reader}
end

def terminate(_reason, reader), do:
reader |> Reader.stop

def terminate(_reason, reader), do: reader |> Reader.stop()
end
19 changes: 7 additions & 12 deletions lib/portmidi/listeners.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@ defmodule PortMidi.Listeners do
# Client implementation
#######################

def register(input, pid), do:
GenServer.cast(__MODULE__, {:register, input, pid})
def register(input, pid), do: GenServer.cast(__MODULE__, {:register, input, pid})

def list(input), do:
GenServer.call(__MODULE__, {:list, input})
def list(input), do: GenServer.call(__MODULE__, {:list, input})

# Server implementation
#######################

def init(:ok), do:
{:ok, {%{}, %{}}}
def init(:ok), do: {:ok, {%{}, %{}}}

def handle_call({:list, input}, _from, {listeners, _} = state) do
if Map.has_key?(listeners, input) do
Expand All @@ -43,19 +40,17 @@ defmodule PortMidi.Listeners do
{:noreply, {listeners, refs}}
end

def handle_info(_msg, state), do:
{:noreply, state}
def handle_info(_msg, state), do: {:noreply, state}

require Logger
def terminate(reason, _), do:
Logger.error(reason)
def terminate(reason, _), do: Logger.error(reason)

# Private implementation
########################

def do_update_listeners(listeners, pid) do
Enum.reduce(listeners, %{}, fn({input, listeners}, acc) ->
Map.put acc, input, do_find_new_listeners(listeners, pid)
Enum.reduce(listeners, %{}, fn {input, listeners}, acc ->
Map.put(acc, input, do_find_new_listeners(listeners, pid))
end)
end

Expand Down
5 changes: 2 additions & 3 deletions lib/portmidi/nifs/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ defmodule PortMidi.Nifs.Devices do
@on_load :init
def init do
:portmidi
|> :code.priv_dir
|> :code.priv_dir()
|> :filename.join("portmidi_devices")
|> :erlang.load_nif(0)
end

def do_list, do:
raise "NIF library not loaded"
def do_list, do: raise("NIF library not loaded")
end
21 changes: 9 additions & 12 deletions lib/portmidi/nifs/input.ex
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
defmodule PortMidi.Nifs.Input do
@on_load {:init, 0}
def init do
:ok = :portmidi
|> :code.priv_dir
|> :filename.join("portmidi_in")
|> :erlang.load_nif(0)
:ok =
:portmidi
|> :code.priv_dir()
|> :filename.join("portmidi_in")
|> :erlang.load_nif(0)
end

def do_poll(_stream), do:
raise "NIF library not loaded"
def do_poll(_stream), do: raise("NIF library not loaded")

def do_read(_stream, _buffer_size), do:
raise "NIF library not loaded"
def do_read(_stream, _buffer_size), do: raise("NIF library not loaded")

def do_open(_device_name), do:
raise "NIF library not loaded"
def do_open(_device_name), do: raise("NIF library not loaded")

def do_close(_stream), do:
raise "NIF library not loaded"
def do_close(_stream), do: raise("NIF library not loaded")
end
18 changes: 8 additions & 10 deletions lib/portmidi/nifs/output.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ defmodule PortMidi.Nifs.Output do
@on_load {:init, 0}

def init do
:ok = :portmidi
|> :code.priv_dir
|> :filename.join("portmidi_out")
|> :erlang.load_nif(0)
:ok =
:portmidi
|> :code.priv_dir()
|> :filename.join("portmidi_out")
|> :erlang.load_nif(0)
end

def do_open(_device_name, _latency), do:
raise "NIF library not loaded"
def do_open(_device_name, _latency), do: raise("NIF library not loaded")

def do_write(_stream, _message), do:
raise "NIF library not loaded"
def do_write(_stream, _message), do: raise("NIF library not loaded")

def do_close(_stream), do:
raise "NIF library not loaded"
def do_close(_stream), do: raise("NIF library not loaded")
end
10 changes: 3 additions & 7 deletions lib/portmidi/output.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ defmodule PortMidi.Output do
GenServer.start_link(__MODULE__, {device_name, latency})
end


# Client implementation
#######################
def write(server, message), do:
GenServer.call(server, {:write, message})

def stop(server), do:
GenServer.stop(server)
def write(server, message), do: GenServer.call(server, {:write, message})

def stop(server), do: GenServer.stop(server)

# Server implementation
#######################
def init({device_name, latency}) do
Process.flag(:trap_exit, true)

case do_open(to_charlist(device_name), latency) do
{:ok, stream} -> {:ok, stream}
{:ok, stream} -> {:ok, stream}
{:error, reason} -> {:stop, reason}
end
end
Expand Down
Loading