Elixirでも手軽に音を鳴らしたいです
今回はTone.jsを使います
Tone.jsの公式の説明では
「Tone.js は、ブラウザ上でインタラクティブな音楽を作成するための Web Audio フレームワークです。」
ざっくりTone.jsができること
- シンセサイザー
- FMSynth
- AMSynth
- NoiseSynth
- Synth
- エフェクト
- LFO
- リバーブ
など、上記は一部で省略してます、下記で確認できます
完成イメージ
前提
- LiveViewのhookを使ったことがある
- mix phx.newでプロジェクト作成済
Tone.jsインストール
$ cd assets
$ npm i tone
コードを書く
Hook
assets/js/tonex.js
import * as Tone from "tone"
Tonex = {
mounted() {
this.synth = new Tone.Synth().toDestination();
this.handleEvent("play_note", ({note, duration}) => {
this.synth.triggerAttackRelease(note, duration);
})
}
}
export default Tonex;
説明
- this.synth = new Tone.Synth().toDestination(); でシンセを初期化
- this.handleEvent("play_note", ({note, duration}) => はElixirからの受付をする
- this.synth.triggerAttackRelease(note, duration); でシンセの音を鳴らす
これは、公式のサンプルHello ToneをElixirで使えるようにラップしました
公式のサンプルHello Tone
//create a synth and connect it to the main output (your speakers)
const synth = new Tone.Synth().toDestination();
//play a middle 'C' for the duration of an 8th note
synth.triggerAttackRelease("C4", "8n");
操作画面
lib/live_tone_web/live/tone_live.ex
defmodule LiveToneWeb.ToneLive do
use LiveToneWeb, :live_view
def mount(_params, _session, socket) do
base_notes = ~w"C D E F G A B"
notes =
1..7
|> Enum.map(fn r -> Enum.map(base_notes, fn x -> "#{x}#{r}" end) end)
|> List.flatten()
socket =
assign(socket, notes: notes)
{:ok, socket}
end
def render(assigns) do
~H"""
<Layouts.app flash={@flash}>
<div id="audio-container" phx-hook="Tonex" class="w-[350px]">
<button
:for={note <- @notes}
phx-click="play"
phx-value-note={note}
class="btn w-[50px]"
>{note}</button>
</div>
</Layouts.app>
"""
end
def handle_event("play", %{"note" => note}, socket) do
{:noreply, play_note(socket, note)}
end
def play_note(socket, note) do
push_event(socket, "play_note", %{note: note, duration: "8n"})
end
end
説明
- mount
- 鳴らす音のノートを定美
- base_notes ノートを定美 ドレミファソラシ(CDEFGAB)
- notes 7オクターブ分定義 C1〜B7
- 鳴らす音のノートを定美
- render
- ボタンをC1〜B7を作成
-
phx-click="play"でdef handle_event("play"と関連付けする
- handle_event("play", %{"note" => note}, socket)
- ボタンを押したとき
play_note(socket, note)を呼ぶ
- ボタンを押したとき
- play_note(socket, note)
- tonex.jsの
this.handleEvent("play_note", ({note, duration}) => {を実行- つまり、ここでTone.jsの機能を実行できる
- tonex.jsの
ElixirでTone.jsを使うことができました
このコラムでは、Tone.jsの基礎の部分のみ使ってます
Tone.jsのAPIをHookに定義すると、さらにいろいろな表現ができます
ソース