PioneerDjのDDJ-FLX4でシーケンシャルウインカーを実装します
前提と基礎知識
の続編です
ElixirでPadのシーケンシャルウインカー
プログラム仕様
- Pad1〜Pad8を使って表現します
- 上段、下段のPadを使います
- デッキ1、デッキ2のPadは内側から光ます
表示例
□□□□ □□□□
□□□□ □□□□
□□□■ ■□□□
□□□■ ■□□□
□□■■ ■■□□
□□■■ ■■□□
□■■■ ■■■□
□■■■ ■■■□
■■■■ ■■■■
■■■■ ■■■■
前回のコラムでDDJ-FLX4固有の部分をモジュール化しました
前回のコラムに通信仕様を書いた為説明は省略
lib/ddj.ex
defmodule Ddj do
@moduledoc """
Documentation for `Ddj`.
"""
def led_on, do: "7F"
def led_off, do: "00"
def pad1, do: "00"
def pad2, do: "01"
def pad3, do: "02"
def pad4, do: "03"
def pad5, do: "04"
def pad6, do: "05"
def pad7, do: "06"
def pad8, do: "07"
def deck1, do: "97"
def deck2, do: "99"
def pad_list, do: [pad1(), pad2(), pad3(), pad4(), pad5(), pad6(), pad7(), pad8()]
def open do
{_, output} = PortMidi.open(:output, "DDJ-FLX4 MIDI 1")
output
end
def close(output) do
PortMidi.close(:output, output)
end
def send_midi(v, output) do
Enum.map(v, fn x -> hex_to_dec(x) end)
|> List.to_tuple()
|> then(&PortMidi.write(output, &1))
end
def hex_to_dec(hex), do: String.to_integer(hex, 16)
def pad_led(output, deck, pad, led_sw) do
[deck, pad, led_sw]
|> send_midi(output)
end
end
ウインカープログラムの本体です
lib/turn_signal.ex
defmodule TurnSignal do
@moduledoc """
Documentation for `TurnSignal`.
"""
import Ddj
@doc """
左側のウインカーの光るリスト
"""
def deck1_signal do
[
{pad4(), pad8()},
{pad3(), pad7()},
{pad2(), pad6()},
{pad1(), pad5()}
]
end
@doc """
右側のウインカーの光るリスト
"""
def deck2_signal do
# 左側と逆になる
deck1_signal()
|> Enum.reverse()
end
def start() do
output = open()
# ウインカーは5回
1..5
|> Enum.each(fn _ -> task_deck(output) end)
close(output)
end
@doc """
左右ウインカー1回分
"""
def task_deck(output) do
# 非同期で左ウインカーを処理
task_deck1 =
Task.async(fn ->
deck1_signal()
|> signal_process(deck1(), output)
end)
# 非同期で右ウインカーを処理
task_deck2 =
Task.async(fn ->
deck2_signal()
|> signal_process(deck2(), output)
end)
# 左右のウインカーを処理が終わるまで待機
Task.await(task_deck1)
Task.await(task_deck2)
end
@doc """
片方のウインカーを処理
"""
def signal_process(pattern, deck, output) do
pattern
|> Enum.each(&signal_on(&1, deck, output))
Process.sleep(300)
pattern
|> Enum.each(&signal_off(&1, deck, output))
Process.sleep(300)
end
@doc """
上段、下段のPadの表示/非表示
"""
def signal({pad_up, pad_down}, deck, led_sw, output) do
pad_led(output, deck, pad_up, led_sw)
pad_led(output, deck, pad_down, led_sw)
end
@doc """
上段、下段のPadの表示
表示後100ミリ秒待つ
"""
def signal_on({pad_up, pad_down}, deck, output) do
signal({pad_up, pad_down}, deck, led_on(), output)
Process.sleep(100)
end
@doc """
上段、下段のPadの非表示
"""
def signal_off({pad_up, pad_down}, deck, output),
do: signal({pad_up, pad_down}, deck, led_off(), output)
end
補足
左右のウインカーは、非同期で動きます
但し、左右のウインカーが終わるまで次の処理を実行しません
データを作るのがめんどくさいので非同期処理しました(手抜き)
実行
$ mix run --eval "TurnSignal.start()"