LoginSignup
24
7

More than 1 year has passed since last update.

Elixirで音を鳴らして遊ぼう

Last updated at Posted at 2022-11-17

Elixirで音を鳴らすライブラリー midi_synth
で遊びたいと思います

検証して動作に成功した環境はUbuntu20.04
同様のことを試してみましたが現時点ではmacOS 12はビルドが通らなかったです…(サポートされているはず)
Windows WSL2も同様ビルドが通らなかった(仮にビルド通ってもサウンドドライバー問題がきっとある…)

その為Ubuntu 20.04で動かすことを前提で書きました

ライブラリーはこちらを使いました

準備

FluidSynthをインストール

$ sudo apt install libfluidsynth-dev

さっそく遊びましょう

まず、iexを起動する

$ iex

Mix.install でライブラリーを読み込む

iex(2)> Mix.install([{:midi_synth, "~> 0.4.0"}])

サウンドフォント(FluidR3_GM.sf2)も自動でダウンロードされました

実行結果

Resolving Hex dependencies...
Dependency resolution completed:
New:
  elixir_make 0.6.3
  midi_synth 0.4.1
* Getting midi_synth (Hex package)
* Getting elixir_make (Hex package)
==> elixir_make
Compiling 1 file (.ex)
Generated elixir_make app
==> midi_synth
/usr/bin/make -C src all
make[1]: ディレクトリ '/home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/deps/midi_synth/src' に入ります
mkdir -p /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/priv
mkdir -p /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/obj
cc -c -O2 -Wall -Wextra -Wno-unused-parameter -std=c99 -D_GNU_SOURCE  -o /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/obj/midi_synth.o midi_synth.c
cc /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/obj/midi_synth.o  -L/usr/lib/x86_64-linux-gnu -lfluidsynth  -o /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/priv/midi_synth
curl -LO https://github.com/fhunleth/midi_synth/releases/download/v0.1.0/FluidR3_GM.sf2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  141M  100  141M    0     0  6412k      0  0:00:22  0:00:22 --:--:-- 2864k
cp FluidR3_GM.sf2 /home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/_build/dev/lib/midi_synth/priv/FluidR3_GM.sf2
make[1]: ディレクトリ '/home/user/.cache/mix/installs/elixir-1.13.4-erts-13.0.3/1481ec753c3c298f97311102a571a358/deps/midi_synth/src' から出ます
Compiling 3 files (.ex)
Generated midi_synth app
:ok

音を鳴らしてみよう

iex(3)> {:ok, synth} = MIDISynth.start_link([])
{:ok, #PID<0.295.0>}

ワーニングでますが、今回はそのままにしておきました

fluidsynth: warning: SDL2 not initialized, SDL2 audio driver won't be usable
                                                                                    fluidsynth: Using PulseAudio driver
                  fluidsynth: warning: Failed to set thread to high priority

MIDISynthのプロセスが立ち上がりsynthにpidが格納してます

ド(C4)を鳴らします

iex(4)> MIDISynth.Keyboard.play(synth, 60, 100)
:ok

第1引数: MIDISynth.start_linkのpid
第2引数: ノート番号(音階)
第3引数: 長さ(ミリ秒)

1オクターブ音を鳴らしてみる

ノート番号としては60(C4)〜72(C5)を連続に鳴らします

iex(4)> 60..72 |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 100) end)

半音も含めて1オクターブ音が鳴りました

ピコピコ鳴らしてみる

ノート番号の例

ド   C  60
    C# 61	
レ   D  62	
   D# 63	
ミ  E  64	
ファ F  65	
   F# 66	
ソ  G  67	
   G# 68	
ラ  A  69	
   A# 70
シ  B  71
ド   C  72

iex(9)> [60, 67] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 100) end)
:ok
iex(11)> [60, 67, 72, 64] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 100) end)
:ok

ピアノでは面白くないのでシンセの音にします

プログラムチェンジで81で Lead 1 (square)にします

iex(12)> MIDISynth.Keyboard.change_program(synth, 81)   
:ok

再度音を鳴らします

iex(13)> [60, 67, 72, 64] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 100) end)
:ok

ドラムを鳴らしてみよう

MIDIはドラムのチャンネルは10チャンネルです(1〜16チャンネルの場合)

まず、MIDISynth.Keyboard.playのヘルプを確認してみる
チャンネルは0から始まるようなので、9がドラムのチャンネルになる

iex(19)> h MIDISynth.Keyboard.play

        def play(server, note, duration, velocity \\ 127, channel \\ 0)         

  @spec play(
          GenServer.server(),
          MIDISynth.Command.note(),
          MIDISynth.Command.duration(),
          MIDISynth.Command.velocity(),
          MIDISynth.Command.channel()
        ) :: :ok

Play a note

This is a utility method for pressing a note down and then releasing it after a
duration.

## Example

iex> {:ok, synth} = MIDISynth.start_link([]) iex>
MIDISynth.Keyboard.play(synth, 60, 100) :ok iex> MIDISynth.Keyboard.play(synth,
60, 100, 80) :ok

ドラムのノート番号

36 バスドラム
38 スネアドラム
42 クローズハイハット

上記の前提で遊ぶ

まず、バスドラムを4つ打ち

iex(23)> [36] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 400, 127, 9) end)
:ok

裏拍にスネアを入れてみる

iex(24)> [36, 38] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 200, 127, 9) end)
:ok

ハイハットも追加してみる

iex(27)> [36, 42, 38, 42] |> List.duplicate(10) |> List.flatten() |> Enum.each(fn note -> MIDISynth.Keyboard.play(synth, note, 200, 127, 9) end)
:ok

リズムマシンぽくなりました

同時に複数音鳴らすのはちょっと難易度が上がるのでこの記事では触れません

実行例

24
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
7