Add documentation and type checking.

This commit is contained in:
Pim Kunis 2020-10-11 13:40:12 +02:00
parent a8141f741e
commit 2dac1259b2
5 changed files with 95 additions and 9 deletions

View file

@ -1,8 +1,18 @@
defmodule MIDITools.Event do
@moduledoc """
Several musical events which can be converted to MIDI commands.
All timings are in milliseconds.
"""
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
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__{
channel: channel,
tone: tone,
@ -14,13 +24,27 @@ defmodule MIDITools.Event do
end
defmodule ChangeProgram do
@moduledoc """
An event which changes the current program of the given channel.
"""
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
%__MODULE__{channel: channel, time: time, program: program}
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{
channel: channel,
tone: tone,

View file

@ -1,39 +1,73 @@
defmodule MIDITools.Player do
use GenServer
@moduledoc """
A GenServer for playing a schedule of MIDI commands at certain times.
"""
# Client API
def start_link(arg \\ nil) do
GenServer.start_link(__MODULE__, arg, name: __MODULE__)
@doc """
Start the MIDI player.
"""
@spec start_link() :: GenServer.on_start()
def start_link do
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
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
GenServer.call(__MODULE__, {:set_schedule, schedule, duration})
end
@doc """
Play the current MIDI schedule from the start.
"""
@spec play() :: :ok
def play do
GenServer.call(__MODULE__, :play)
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})
end
@doc """
Stop the player and cancel the pause.
"""
@spec stop_playing() :: :ok
def stop_playing do
GenServer.call(__MODULE__, :stop_playing)
end
@doc """
Pause the player. See `MIDITools.Player.resume/0` for resuming playback.
"""
@spec pause() :: :ok | {:error, :already_paused | :not_started}
def pause do
GenServer.call(__MODULE__, :pause)
end
@doc """
Resume playback on the player after it has been paused.
"""
@spec resume() :: :ok, {:error, :not_paused}
def resume do
GenServer.call(__MODULE__, :resume)
end
# Server callbacks
@impl true
@impl GenServer
def init(_arg) do
{:ok, synth} = MIDISynth.start_link([])
@ -50,7 +84,7 @@ defmodule MIDITools.Player do
}}
end
@impl true
@impl GenServer
def handle_call({:set_schedule, schedule, duration}, _from, state) do
{:reply, :ok, %{state | schedule: schedule, schedule_left: schedule, duration: duration}}
end
@ -110,7 +144,7 @@ defmodule MIDITools.Player do
{:reply, :ok, %{state | timer: timer, start_time: start_time, pause_time: nil}}
end
@impl true
@impl GenServer
def handle_info(
:play,
%{

View file

@ -1,4 +1,18 @@
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
events
|> Enum.flat_map(&MIDITools.Event.convert/1)