Init repository.
This commit is contained in:
commit
39f5f592a7
9 changed files with 191 additions and 0 deletions
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
midi_tools-*.tar
|
||||
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# MIDITools
|
||||
|
||||
**TODO: Add description**
|
||||
|
||||
## Installation
|
||||
|
||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||
by adding `midi_tools` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:midi_tools, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at [https://hexdocs.pm/midi_tools](https://hexdocs.pm/midi_tools).
|
||||
|
18
lib/midi_tools.ex
Normal file
18
lib/midi_tools.ex
Normal file
|
@ -0,0 +1,18 @@
|
|||
defmodule MIDITools do
|
||||
@moduledoc """
|
||||
Documentation for `MIDITools`.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> MIDITools.hello()
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
end
|
||||
end
|
73
lib/midi_tools/player.ex
Normal file
73
lib/midi_tools/player.ex
Normal file
|
@ -0,0 +1,73 @@
|
|||
defmodule Player do
|
||||
use GenServer
|
||||
|
||||
# Client API
|
||||
|
||||
def start_link(arg \\ nil) do
|
||||
GenServer.start_link(__MODULE__, arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
def set_schedule(schedule) do
|
||||
GenServer.call(__MODULE__, {:set_schedule, schedule})
|
||||
end
|
||||
|
||||
def play do
|
||||
GenServer.call(__MODULE__, :play)
|
||||
end
|
||||
|
||||
# Server callbacks
|
||||
|
||||
@impl true
|
||||
def init(_arg) do
|
||||
{:ok, synth} = MIDISynth.start_link([])
|
||||
epoch = Timex.epoch() |> Timex.to_datetime()
|
||||
{:ok, %{timer: nil, schedule: [], schedule_left: [], start_time: epoch, synth: synth}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:set_schedule, schedule}, _from, state) do
|
||||
{:reply, :ok, %{state | schedule: schedule, schedule_left: schedule}}
|
||||
end
|
||||
|
||||
def handle_call(:play, _from, %{schedule_left: schedule_left} = state) do
|
||||
start_time = Timex.now()
|
||||
state = %{state | start_time: start_time}
|
||||
timer = start_timer(schedule_left, start_time)
|
||||
{:reply, :ok, %{state | timer: timer}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(
|
||||
:play,
|
||||
%{schedule_left: schedule_left, start_time: start_time, synth: synth} = state
|
||||
) do
|
||||
schedule_left = play_next_midi(schedule_left, start_time, synth)
|
||||
{:noreply, %{state | schedule_left: schedule_left}}
|
||||
end
|
||||
|
||||
defp play_next_midi([], _start_time, _synth), do: []
|
||||
|
||||
defp play_next_midi([{offset, command} | next_schedule] = schedule_left, start_time, synth) do
|
||||
next_time = DateTime.add(start_time, offset, :millisecond)
|
||||
micro_diff = Timex.diff(next_time, Timex.now(), :microsecond)
|
||||
|
||||
if micro_diff < 500 do
|
||||
# Play command, and try to play next command too.
|
||||
MIDISynth.midi(synth, command)
|
||||
play_next_midi(next_schedule, start_time, synth)
|
||||
else
|
||||
# Command is too far in the future, schedule next timer.
|
||||
delay = ceil(micro_diff / 1000)
|
||||
Process.send_after(self(), :play, delay)
|
||||
schedule_left
|
||||
end
|
||||
end
|
||||
|
||||
defp start_timer([], _start_time), do: nil
|
||||
|
||||
defp start_timer([{offset, _command} | _], start_time) do
|
||||
next_time = DateTime.add(start_time, offset, :millisecond)
|
||||
delay = Timex.diff(next_time, Timex.now(), :millisecond)
|
||||
Process.send_after(self(), :play, delay)
|
||||
end
|
||||
end
|
26
mix.exs
Normal file
26
mix.exs
Normal file
|
@ -0,0 +1,26 @@
|
|||
defmodule MIDITools.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :midi_tools,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:midi_synth, "~> 0.4.0"},
|
||||
{:timex, "~> 3.6"}
|
||||
]
|
||||
end
|
||||
end
|
16
mix.lock
Normal file
16
mix.lock
Normal file
|
@ -0,0 +1,16 @@
|
|||
%{
|
||||
"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"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"},
|
||||
"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"},
|
||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||
"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"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"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"},
|
||||
"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"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||
}
|
8
test/midi_tools_test.exs
Normal file
8
test/midi_tools_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
|||
defmodule MIDIToolsTest do
|
||||
use ExUnit.Case
|
||||
doctest MIDITools
|
||||
|
||||
test "greets the world" do
|
||||
assert MIDITools.hello() == :world
|
||||
end
|
||||
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Reference in a new issue