Add documentation and type checking.
This commit is contained in:
parent
a8141f741e
commit
2dac1259b2
5 changed files with 95 additions and 9 deletions
|
@ -1,8 +1,18 @@
|
||||||
defmodule MIDITools.Event do
|
defmodule MIDITools.Event do
|
||||||
|
@moduledoc """
|
||||||
|
Several musical events which can be converted to MIDI commands.
|
||||||
|
All timings are in milliseconds.
|
||||||
|
"""
|
||||||
|
|
||||||
defmodule Note do
|
defmodule Note do
|
||||||
|
@moduledoc """
|
||||||
|
An event which plays the given tone for the given timespan.
|
||||||
|
"""
|
||||||
|
|
||||||
defstruct channel: 0, tone: 0, start_time: 0, end_time: 0, velocity: 0
|
defstruct channel: 0, tone: 0, start_time: 0, end_time: 0, velocity: 0
|
||||||
|
|
||||||
def new(channel, tone, start_time, end_time, velocity) do
|
@spec new(MIDISynth.Command.channel(), non_neg_integer(), non_neg_integer(), non_neg_integer(), MIDISynth.Command.velocity()) :: %Note{}
|
||||||
|
def new(channel, tone, start_time, end_time, velocity) when start_time >= 0 and end_time > start_time do
|
||||||
%__MODULE__{
|
%__MODULE__{
|
||||||
channel: channel,
|
channel: channel,
|
||||||
tone: tone,
|
tone: tone,
|
||||||
|
@ -14,13 +24,27 @@ defmodule MIDITools.Event do
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule ChangeProgram do
|
defmodule ChangeProgram do
|
||||||
|
@moduledoc """
|
||||||
|
An event which changes the current program of the given channel.
|
||||||
|
"""
|
||||||
|
|
||||||
defstruct channel: 0, time: 0, program: 0
|
defstruct channel: 0, time: 0, program: 0
|
||||||
|
|
||||||
|
@spec new(MIDISynth.Command.channel(), non_neg_integer(), non_neg_integer()) :: %ChangeProgram{}
|
||||||
def new(channel, time, program) do
|
def new(channel, time, program) do
|
||||||
%__MODULE__{channel: channel, time: time, program: program}
|
%__MODULE__{channel: channel, time: time, program: program}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@typedoc """
|
||||||
|
A musical event.
|
||||||
|
"""
|
||||||
|
@type t :: %Note{} | %ChangeProgram{}
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Converts the event to a list of MIDI commands.
|
||||||
|
"""
|
||||||
|
@spec convert(t()) :: MIDITools.Schedule.t()
|
||||||
def convert(%Note{
|
def convert(%Note{
|
||||||
channel: channel,
|
channel: channel,
|
||||||
tone: tone,
|
tone: tone,
|
||||||
|
|
|
@ -1,39 +1,73 @@
|
||||||
defmodule MIDITools.Player do
|
defmodule MIDITools.Player do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
A GenServer for playing a schedule of MIDI commands at certain times.
|
||||||
|
"""
|
||||||
|
|
||||||
# Client API
|
# Client API
|
||||||
|
|
||||||
def start_link(arg \\ nil) do
|
@doc """
|
||||||
GenServer.start_link(__MODULE__, arg, name: __MODULE__)
|
Start the MIDI player.
|
||||||
|
"""
|
||||||
|
@spec start_link() :: GenServer.on_start()
|
||||||
|
def start_link do
|
||||||
|
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Set the current schedule and total duration for the MIDI player.
|
||||||
|
The duration makes sure the player plays a (potential) pause after the last
|
||||||
|
midi command.
|
||||||
|
"""
|
||||||
|
@spec set_schedule(MIDITools.Schedule.t(), non_neg_integer()) :: :ok
|
||||||
def set_schedule(schedule, duration) do
|
def set_schedule(schedule, duration) do
|
||||||
GenServer.call(__MODULE__, {:set_schedule, schedule, duration})
|
GenServer.call(__MODULE__, {:set_schedule, schedule, duration})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Play the current MIDI schedule from the start.
|
||||||
|
"""
|
||||||
|
@spec play() :: :ok
|
||||||
def play do
|
def play do
|
||||||
GenServer.call(__MODULE__, :play)
|
GenServer.call(__MODULE__, :play)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_repeat(repeat) do
|
@doc """
|
||||||
|
Set the player on repeat or not.
|
||||||
|
"""
|
||||||
|
@spec set_repeat(boolean()) :: :ok
|
||||||
|
def set_repeat(repeat) when is_boolean(repeat) do
|
||||||
GenServer.call(__MODULE__, {:set_repeat, repeat})
|
GenServer.call(__MODULE__, {:set_repeat, repeat})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Stop the player and cancel the pause.
|
||||||
|
"""
|
||||||
|
@spec stop_playing() :: :ok
|
||||||
def stop_playing do
|
def stop_playing do
|
||||||
GenServer.call(__MODULE__, :stop_playing)
|
GenServer.call(__MODULE__, :stop_playing)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Pause the player. See `MIDITools.Player.resume/0` for resuming playback.
|
||||||
|
"""
|
||||||
|
@spec pause() :: :ok | {:error, :already_paused | :not_started}
|
||||||
def pause do
|
def pause do
|
||||||
GenServer.call(__MODULE__, :pause)
|
GenServer.call(__MODULE__, :pause)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Resume playback on the player after it has been paused.
|
||||||
|
"""
|
||||||
|
@spec resume() :: :ok, {:error, :not_paused}
|
||||||
def resume do
|
def resume do
|
||||||
GenServer.call(__MODULE__, :resume)
|
GenServer.call(__MODULE__, :resume)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Server callbacks
|
# Server callbacks
|
||||||
|
|
||||||
@impl true
|
@impl GenServer
|
||||||
def init(_arg) do
|
def init(_arg) do
|
||||||
{:ok, synth} = MIDISynth.start_link([])
|
{:ok, synth} = MIDISynth.start_link([])
|
||||||
|
|
||||||
|
@ -50,7 +84,7 @@ defmodule MIDITools.Player do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl GenServer
|
||||||
def handle_call({:set_schedule, schedule, duration}, _from, state) do
|
def handle_call({:set_schedule, schedule, duration}, _from, state) do
|
||||||
{:reply, :ok, %{state | schedule: schedule, schedule_left: schedule, duration: duration}}
|
{:reply, :ok, %{state | schedule: schedule, schedule_left: schedule, duration: duration}}
|
||||||
end
|
end
|
||||||
|
@ -110,7 +144,7 @@ defmodule MIDITools.Player do
|
||||||
{:reply, :ok, %{state | timer: timer, start_time: start_time, pause_time: nil}}
|
{:reply, :ok, %{state | timer: timer, start_time: start_time, pause_time: nil}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl GenServer
|
||||||
def handle_info(
|
def handle_info(
|
||||||
:play,
|
:play,
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -1,4 +1,18 @@
|
||||||
defmodule MIDITools.Schedule do
|
defmodule MIDITools.Schedule do
|
||||||
|
@moduledoc """
|
||||||
|
Functions for using a MIDI schedule.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@typedoc """
|
||||||
|
A list of tuples which indicate that the MIDI binary should play at the given time.
|
||||||
|
"""
|
||||||
|
@type t :: [{non_neg_integer(), binary()}]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Convert a list of events to MIDI schedule.
|
||||||
|
See `MIDITools.Event` for creating these events.
|
||||||
|
"""
|
||||||
|
@spec convert_events([MIDITools.Event.t()]) :: t()
|
||||||
def convert_events(events) do
|
def convert_events(events) do
|
||||||
events
|
events
|
||||||
|> Enum.flat_map(&MIDITools.Event.convert/1)
|
|> Enum.flat_map(&MIDITools.Event.convert/1)
|
||||||
|
|
13
mix.exs
13
mix.exs
|
@ -7,7 +7,15 @@ defmodule MIDITools.MixProject do
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
elixir: "~> 1.10",
|
elixir: "~> 1.10",
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
deps: deps()
|
deps: deps(),
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
name: "MIDITools",
|
||||||
|
source_url: "https://github.com/pizzapim/midi_tools",
|
||||||
|
homepage_url: "https://github.com/pizzapim/midi_tools",
|
||||||
|
docs: [
|
||||||
|
main: "MIDITools"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -20,7 +28,8 @@ defmodule MIDITools.MixProject do
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:midi_synth, "~> 0.4.0"},
|
{:midi_synth, "~> 0.4.0"},
|
||||||
{:timex, "~> 3.6"}
|
{:timex, "~> 3.6"},
|
||||||
|
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
5
mix.lock
5
mix.lock
|
@ -1,13 +1,18 @@
|
||||||
%{
|
%{
|
||||||
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
|
"earmark_parser": {:hex, :earmark_parser, "1.4.10", "6603d7a603b9c18d3d20db69921527f82ef09990885ed7525003c7fe7dc86c56", [:mix], [], "hexpm", "8e2d5370b732385db2c9b22215c3f59c84ac7dda7ed7e544d7c459496ae519c0"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"},
|
"elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"},
|
||||||
|
"ex_doc": {:hex, :ex_doc, "0.22.6", "0fb1e09a3e8b69af0ae94c8b4e4df36995d8c88d5ec7dbd35617929144b62c00", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "1e0aceda15faf71f1b0983165e6e7313be628a460e22a031e32913b98edbd638"},
|
||||||
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
||||||
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||||
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.15.0", "98312c9f0d3730fde4049985a1105da5155bfe5c11e47bdc7406d88e01e4219b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "75ffa34ab1056b7e24844c90bfc62aaf6f3a37a15faa76b07bc5eba27e4a8b4a"},
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
"midi_synth": {:hex, :midi_synth, "0.4.0", "58e88aee3fdaf4f60e99847fdbf8921576dce5820966f0d58a4c0f4a96198740", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "789152ba66603e5c358c7f6971ceac6f014e7605e2367cc6be7a9af36fc2cc70"},
|
"midi_synth": {:hex, :midi_synth, "0.4.0", "58e88aee3fdaf4f60e99847fdbf8921576dce5820966f0d58a4c0f4a96198740", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "789152ba66603e5c358c7f6971ceac6f014e7605e2367cc6be7a9af36fc2cc70"},
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
"timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
|
"timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
|
||||||
|
|
Loading…
Reference in a new issue