Elixir hot-reloading & MIDI events generation

Elixir hot-reloading & MIDI events generation

Lightning talk at ElixirConfEU 2017. Demonstrates how to use Elixir "hot-reloading" feature, together with MIDI events generation.

Video: https://www.youtube.com/watch?v=_VgcUatTilU&feature=youtu.be&t=2m2s
Repo: https://github.com/thbar/demo-elixir-reloading-music

91eb330fb36d1e03c856574dfb77d2bc?s=128

Thibaut Barrère

May 04, 2017
Tweet

Transcript

  1. ELIXIR HOT-RELOADING & MIDI NOTES GENERATION https://github.com/thbar/demo-elixir-reloading-music

  2. HOW TO IMPLEMENT A VERY SIMPLE ELIXIR CODE HOT-RELOADING EXAMPLE?

  3. HOW TO MAKE IT INTERESTING?

  4. LIVE SOUND EVENTS GENERATION !

  5. HOW TO GENERATE ONE NOTE?

  6. RENOISE (MUSIC PRODUCTION SYSTEM)

  7. PORTMIDI (C LIBRARY)

  8. ELIXIR BINDINGS FOR PORTMIDI

  9. # Start a process for MIDI event queue {:ok, pid}

    = PortMidi.open(:output, "Renoise MIDI-In") note = 48 # C-4 velocity = 127 # Send "NOTE ON" PortMidi.write(pid, {0x90, note, velocity}) # Send "NOTE OFF" PortMidi.write(pid, {0x80, note})
  10. HOW TO BUILD A MUSIC LOOP?

  11. ▸ GenServer ▸ Process.send_after(xxx)

  12. defmodule MidiPlayer do use GenServer def start_link do {:ok, device}

    = PortMidi.open(:output, "Renoise MIDI-In") tick_period = 50 Process.send_after(:midi, {:tick}, tick_period) GenServer.start_link(__MODULE__, %{ current_tick: -1, device: device, tick_period: tick_period }, name: :midi) end # SNIP end
  13. defmodule MidiPlayer do def handle_info({:tick}, state) do Process.send_after(:midi, {:tick}, state.tick_period)

    current_tick = Map.fetch!(state, :current_tick) + 1 show_visual_feedback(current_tick) play_notes(state.device, current_tick) {:noreply, %{state | current_tick: current_tick}} end end
  14. def play_notes(device, current_tick) do notes = [0x54, 0x57, 0x5B, 0x60]

    delay = 4 if rem(current_tick, delay) == 0 do index = rem(div(current_tick, delay), Enum.count(notes)) note = Enum.at(notes, index) PortMidi.write(device, {0x90, note, volume}) Process.send_after(:midi, {:note_off, note}, 50 * 2) end end
  15. I CAN HAZ RELOADING? Code.eval_file("music.exs")

  16. HOW TO REACT TO FILE CHANGE? defmodule Monitor do use

    ExFSWatch, dirs: ["music.exs"], listener_extra_args: "--latency=0.0" def callback(_file_path, _events) do Code.eval_file("music.exs") end end Monitor.start
  17. AHA MOMENT GenServer reloading keeps the state across reloads. =>

    We can keep the "current music tick" between reloads.
  18. +-----------------+ +------------------------+ | (reloable) code | + | preserved state

    (tick) | +-----------------+ +------------------------+ | | \ / +---------------------+ +----------+ +---------+ | ex-portmidi process | -> | portmidi | -> | renoise | +---------------------+ +----------+ +---------+
  19. DEMO Youtube Link