local ml = require 'melisma.lazy' ml.util.openmodule(ml.functional) local argv = ml.internal.argv local device = argv[1] if not device then print('Usage: melisma -C synth main.lua DEVICE') print('Available devices are:') for _,source in ipairs(ml.port.midi.sources()) do print('\t'..source) end os.exit(1) end local mport = ml.port.midi.input.open('midi-in') print(string.format('Connecting device \'%s\'', device)) mport:connect(device) local wt = ml.oscil.wavetable.saw() wt = wt + wt:scale(2) local size = 8 local active = replicate(false, size) local allocs = replicate(nil, 128) local oscil = replicate(bind(ml.oscil.init, wt, 0, 0), size) local interp = replicate(bind(ml.interp.init, 0), size) zip(ml.oscil.mapa, oscil, interp) local mix = ml.mixer.init(table.unpack(oscil)) local delay = ml.delay.init(mix) :delay(0.2) :feedback(0.4) :wetdrymix(0.5) :gain(0.8) local lowpass = ml.biquad.lowpass.init(delay) :cutoff(4000) :resonance(0.7) local aport = ml.port.audio.output.open('audio-out', lowpass) local dests = ml.port.audio.destinations() fmap(bind(aport.connect, aport), dests) local attack = 0.025 local release = 0.05 local function handle_on(key, vel) print('on', key) -- Check if this note is already on if allocs[key] then return end -- Find unused oscillator local i = nil for j = 1,size do if not active[j] then i = j break end end if not i then return end -- Play the note print('activate oscillator', i) active[i] = true allocs[key] = i interp[i]:linear(vel / 127.0, attack) local f = 440 * 2^((key - 57) / 12.0) print('frequency', f) oscil[i]:setf(f) end local function free(args) -- Mark the oscillator as unused after the note is released local i, key = table.unpack(args) print('free oscillator', i) active[i] = false allocs[key] = nil end local function handle_off(key) print('off', key) -- Find the oscillator local i = allocs[key] if not i then return end -- Stop playing the note print('deactivate oscillator', i) interp[i]:linear(0, release) ml.event.schedule(release, free, { i, key }) end mport:callback(function(msg) local status = msg.status & 0xf0 local key = msg.data1 local vel = msg.data2 print('received', status, key, vel) local off = 0x80 local on = 0x90 if status == off then handle_off(key) return end if status == on then handle_on(key, vel) return end end)