prototype lua midi generators & filters and port event-rewrite
authorRobin Gareus <robin@gareus.org>
Sun, 29 May 2016 18:36:16 +0000 (20:36 +0200)
committerRobin Gareus <robin@gareus.org>
Sun, 29 May 2016 18:36:32 +0000 (20:36 +0200)
libs/ardour/luabindings.cc
libs/ardour/luaproc.cc
scripts/midi_rewite.lua [new file with mode: 0644]
scripts/midifilter.lua [new file with mode: 0644]
scripts/midigenerator.lua [new file with mode: 0644]
scripts/synth1.lua

index 6c687de56cf20c10cef2e59ae5f08f7bf75b6781..23dc0d26f06ea0962ff94282f363a111760e0666 100644 (file)
@@ -171,6 +171,7 @@ CLASSKEYS(boost::weak_ptr<ARDOUR::Route>);
 CLASSKEYS(std::list<boost::shared_ptr<ARDOUR::Region> >);
 CLASSKEYS(std::list<ARDOUR::AudioRange>);
 CLASSKEYS(Evoral::Beats);
+CLASSKEYS(ARDOUR::PortEngine);
 CLASSKEYS(ARDOUR::PortManager);
 CLASSKEYS(ARDOUR::AudioEngine);
 CLASSKEYS(void);
@@ -459,6 +460,8 @@ LuaBindings::common (lua_State* L)
                .endClass ()
 
                .beginWSPtrClass <Port> ("Port")
+               .addCast<MidiPort> ("to_midiport")
+               .addCast<AudioPort> ("to_audioport")
                .addFunction ("name", &Port::name)
                .addFunction ("pretty_name", &Port::pretty_name)
                .addFunction ("receives_input", &Port::receives_input)
@@ -471,6 +474,7 @@ LuaBindings::common (lua_State* L)
                .addFunction ("connected_to", (bool (Port::*)(Port*)const)&Port::connected_to)
                .addFunction ("connect", (int (Port::*)(Port*))&Port::connect)
                .addFunction ("disconnect", (int (Port::*)(Port*))&Port::disconnect)
+               //.addStaticFunction ("port_offset", &Port::port_offset) // static
                .endClass ()
 
                .deriveWSPtrClass <AudioPort, Port> ("AudioPort")
@@ -479,6 +483,7 @@ LuaBindings::common (lua_State* L)
                .deriveWSPtrClass <MidiPort, Port> ("MidiPort")
                .addFunction ("input_active", &MidiPort::input_active)
                .addFunction ("set_input_active", &MidiPort::set_input_active)
+               .addFunction ("get_midi_buffer", &MidiPort::get_midi_buffer) // DSP only
                .endClass ()
 
                .beginWSPtrClass <PortSet> ("PortSet")
@@ -956,6 +961,9 @@ LuaBindings::common (lua_State* L)
                .addFunction ("set_output_device_name", &AudioBackend::set_output_device_name)
                .endClass()
 
+               .beginClass <PortEngine> ("PortEngine")
+               .endClass()
+
                .beginClass <PortManager> ("PortManager")
                .addFunction ("port_engine", &PortManager::port_engine)
                .addFunction ("connected", &PortManager::connected)
@@ -1107,8 +1115,10 @@ LuaBindings::dsp (lua_State* L)
                .beginClass <MidiBuffer> ("MidiBuffer")
                .addEqualCheck ()
                .addFunction ("silence", &MidiBuffer::silence)
-               .addFunction ("empty", &MidiBuffer::empty)
+               .addFunction ("push_event", (bool (MidiBuffer::*)(const Evoral::MIDIEvent<framepos_t>&))&MidiBuffer::push_back)
+               .addFunction ("push_back", (bool (MidiBuffer::*)(framepos_t, size_t, const uint8_t*))&MidiBuffer::push_back)
                // TODO iterators..
+               .addExtCFunction ("table", &luabridge::CFunc::listToTable<const Evoral::MIDIEvent<framepos_t>, MidiBuffer>)
                .endClass()
 
                .beginClass <BufferSet> ("BufferSet")
@@ -1135,8 +1145,8 @@ LuaBindings::dsp (lua_State* L)
                // add Ctor?
                .addFunction ("type", &Evoral::MIDIEvent<framepos_t>::type)
                .addFunction ("channel", &Evoral::MIDIEvent<framepos_t>::channel)
-               .addFunction ("set_type", &Evoral::MIDIEvent<framepos_t>::type)
-               .addFunction ("set_channel", &Evoral::MIDIEvent<framepos_t>::channel)
+               .addFunction ("set_type", &Evoral::MIDIEvent<framepos_t>::set_type)
+               .addFunction ("set_channel", &Evoral::MIDIEvent<framepos_t>::set_channel)
                .endClass ()
                .endNamespace ();
 
index eb67d5c384b053fe39d5324bb28fe3123a8412a1..fc899ba4add1cb5e2720cc575a1a0b4ffa1b92c6 100644 (file)
@@ -212,15 +212,24 @@ LuaProc::load_script ()
                }
        }
 
-       luabridge::LuaRef lua_dsp_midi_in = luabridge::getGlobal (L, "dsp_midi_input");
-       if (lua_dsp_midi_in.type () == LUA_TFUNCTION) {
+       // query midi i/o
+       luabridge::LuaRef lua_dsp_has_midi_in = luabridge::getGlobal (L, "dsp_has_midi_input");
+       if (lua_dsp_has_midi_in.type () == LUA_TFUNCTION) {
                try {
-                       _has_midi_input = lua_dsp_midi_in ();
+                       _has_midi_input = lua_dsp_has_midi_in ();
+               } catch (luabridge::LuaException const& e) {
+                       ;
+               }
+       }
+
+       luabridge::LuaRef lua_dsp_has_midi_out = luabridge::getGlobal (L, "dsp_has_midi_output");
+       if (lua_dsp_has_midi_out.type () == LUA_TFUNCTION) {
+               try {
+                       _has_midi_output = lua_dsp_has_midi_out ();
                } catch (luabridge::LuaException const& e) {
                        ;
                }
        }
-       lpi->_is_instrument = _has_midi_input;
 
        _ctrl_params.clear ();
 
@@ -352,10 +361,10 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
        bool found = false;
        bool exact_match = false;
        const int32_t audio_in = in.n_audio ();
-       int32_t midi_out = 0; // TODO handle  _has_midi_output
+       int32_t midi_out = _has_midi_output ? 1 : 0;
 
        // preferred setting (provided by plugin_insert)
-       assert (out.n_audio () > 0);
+       assert (out.n_audio () > 0 || midi_out > 0);
        const int preferred_out = out.n_audio ();
 
        for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
@@ -406,8 +415,17 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                int possible_out = io["audio_out"];
 
                if (possible_out == 0) {
+                       if (possible_in == 0) {
+                               if (_has_midi_output && audio_in == 0) {
+                                       // special case midi filters & generators
+                                       audio_out = 0;
+                                       found = true;
+                                       break;
+                               }
+                       }
                        continue;
                }
+
                if (possible_in == 0) {
                        /* no inputs, generators & instruments */
                        if (possible_out == -1) {
@@ -527,6 +545,15 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                        int possible_in = io["audio_in"];
                        int possible_out = io["audio_out"];
 
+                       if (possible_out == 0 && possible_in == 0 && _has_midi_output) {
+                               assert (audio_in > 0); // no input is handled above
+                               // TODO hide audio input from plugin
+                               imprecise->set (DataType::AUDIO, 0);
+                               audio_out = 0;
+                               found = true;
+                               continue;
+                       }
+
                        assert (possible_in > 0); // all other cases will have been matched above
                        assert (possible_out !=0 || possible_in !=0); // already handled above
 
@@ -550,10 +577,10 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
        }
 
        if (exact_match) {
-               out.set (DataType::MIDI, midi_out); // currently always zero
+               out.set (DataType::MIDI, midi_out);
                out.set (DataType::AUDIO, preferred_out);
        } else {
-               out.set (DataType::MIDI, midi_out); // currently always zero
+               out.set (DataType::MIDI, midi_out);
                out.set (DataType::AUDIO, audio_out);
        }
        return true;
@@ -651,7 +678,7 @@ LuaProc::connect_and_run (BufferSet& bufs,
                                }
                        }
 
-                       luabridge::LuaRef lua_midi_tbl (luabridge::newTable (L));
+                       luabridge::LuaRef lua_midi_src_tbl (luabridge::newTable (L));
                        int e = 1; // > 1 port, we merge events (unsorted)
                        for (uint32_t mp = 0; mp < midi_in; ++mp) {
                                bool valid;
@@ -668,20 +695,52 @@ LuaProc::connect_and_run (BufferSet& bufs,
                                                luabridge::LuaRef lua_midi_event (luabridge::newTable (L));
                                                lua_midi_event["time"] = 1 + (*m).time();
                                                lua_midi_event["data"] = lua_midi_data;
-                                               lua_midi_tbl[e] = lua_midi_event;
+                                               lua_midi_src_tbl[e] = lua_midi_event;
                                        }
                                }
                        }
 
                        if (_has_midi_input) {
                                // XXX TODO This needs a better solution than global namespace
-                               luabridge::push (L, lua_midi_tbl);
-                               lua_setglobal (L, "mididata");
+                               luabridge::push (L, lua_midi_src_tbl);
+                               lua_setglobal (L, "midiin");
                        }
 
+                       luabridge::LuaRef lua_midi_sink_tbl (luabridge::newTable (L));
+                       if (_has_midi_output) {
+                               luabridge::push (L, lua_midi_sink_tbl);
+                               lua_setglobal (L, "midiout");
+                       }
 
                        // run the DSP function
                        (*_lua_dsp)(in_map, out_map, nframes);
+
+                       // copy back midi events
+                       if (_has_midi_output && lua_midi_sink_tbl.isTable ()) {
+                               bool valid;
+                               const uint32_t idx = out.get(DataType::MIDI, 0, &valid);
+                               if (valid && bufs.count().n_midi() > idx) {
+                                       MidiBuffer& mbuf = bufs.get_midi(idx);
+                                       mbuf.silence(0, 0);
+                                       for (luabridge::Iterator i (lua_midi_sink_tbl); !i.isNil (); ++i) {
+                                               if (!i.key ().isNumber ()) { continue; }
+                                               if (!i.value ()["time"].isNumber ()) { continue; }
+                                               if (!i.value ()["data"].isTable ()) { continue; }
+                                               luabridge::LuaRef data_tbl (i.value ()["data"]);
+                                               framepos_t tme = i.value ()["time"];
+                                               if (tme < 1 || tme > nframes) { continue; }
+                                               uint8_t data[64];
+                                               size_t size = 0;
+                                               for (luabridge::Iterator di (data_tbl); !di.isNil () && size < sizeof(data); ++di, ++size) {
+                                                       data[size] = di.value ();
+                                               }
+                                               if (size > 0 && size < 64) {
+                                                       mbuf.push_back(tme - 1, size, data);
+                                               }
+                                       }
+
+                               }
+                       }
                }
        } catch (luabridge::LuaException const& e) {
                PBD::error << "LuaException: " << e.what () << "\n";
diff --git a/scripts/midi_rewite.lua b/scripts/midi_rewite.lua
new file mode 100644 (file)
index 0000000..35d2e1d
--- /dev/null
@@ -0,0 +1,36 @@
+ardour {
+       ["type"]    = "session",
+       name        = "Rewrite Midi",
+       license     = "MIT",
+       author      = "Robin Gareus",
+       email       = "robin@gareus.org",
+       site        = "http://gareus.org",
+       description = [[An example session script preprocesses midi buffers.]]
+}
+
+function factory ()
+       -- this function is called in every process cycle, before processing
+       return function (n_samples)
+               _, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ())
+               for p in t[2]:iter () do
+                       if not p:receives_input () then goto next end
+
+                       if not p:name () == "MIDI/midi_in 1" then goto next end
+
+                       midiport = p:to_midiport ()
+                       assert (not midiport:isnil ())
+                       mb = midiport:get_midi_buffer (n_samples);
+
+                       events = mb:table() -- copy event list into lua table
+                       mb:silence (n_samples, 0); -- clear existing buffer
+
+                       for _,e in pairs (events) do
+                               -- e is an http://ardourman/lua-scripting/class_reference/#Evoral:MidiEvent
+                               e:set_channel (2)
+                               mb:push_event (e)
+                       end
+
+                       ::next::
+               end
+       end
+end
diff --git a/scripts/midifilter.lua b/scripts/midifilter.lua
new file mode 100644 (file)
index 0000000..ceb115a
--- /dev/null
@@ -0,0 +1,43 @@
+ardour {
+       ["type"]    = "dsp",
+       name        = "Midi Filter",
+       category    = "Utility",
+       license     = "MIT",
+       author      = "Robin Gareus",
+       email       = "robin@gareus.org",
+       site        = "http://gareus.org",
+       description = [[An Example Midi Filter for prototyping.]]
+}
+
+function dsp_ioconfig ()
+       return { { audio_in = 0, audio_out = 0}, }
+end
+
+function dsp_has_midi_input () return true end
+function dsp_has_midi_output () return true end
+
+function dsp_run (_, _, n_samples)
+       assert (type(midiin) == "table")
+       assert (type(midiout) == "table")
+       local cnt = 1;
+
+       function tx_midi (time, data)
+               midiout[cnt] = {}
+               midiout[cnt]["time"] = time;
+               midiout[cnt]["data"] = data;
+               cnt = cnt + 1;
+       end
+
+       -- for each incoming midi event
+       for _,b in pairs (midiin) do
+               local t = b["time"] -- t = [ 1 .. n_samples ]
+               local d = b["data"] -- get midi-event
+
+               if (#d == 3 and bit32.band (d[1], 240) == 144) then -- note on
+                       tx_midi (t, d)
+               end
+               if (#d == 3 and bit32.band (d[1], 240) == 128) then -- note off
+                       tx_midi (t, d)
+               end
+       end
+end
diff --git a/scripts/midigenerator.lua b/scripts/midigenerator.lua
new file mode 100644 (file)
index 0000000..e9aa55d
--- /dev/null
@@ -0,0 +1,52 @@
+ardour {
+       ["type"]    = "dsp",
+       name        = "Midi Generator",
+       category    = "Utility",
+       license     = "MIT",
+       author      = "Robin Gareus",
+       email       = "robin@gareus.org",
+       site        = "http://gareus.org",
+       description = [[An Example Midi Generator for prototyping.]]
+}
+
+function dsp_ioconfig ()
+       return { { audio_in = 0, audio_out = 0}, }
+end
+
+function dsp_has_midi_output () return true end
+
+local tme = 0 -- sample-counter
+local seq = 1 -- sequence-step
+local spb = 0 -- samples per beat
+
+local midi_sequence = {
+       { 0x90, 64, 127 },
+       { 0x80, 64,   0 },
+}
+
+function dsp_init (rate)
+       local bpm = 120
+       spb = rate * 60 / bpm
+       if spb < 2 then spb = 2 end
+end
+
+function dsp_run (_, _, n_samples)
+       assert (type(midiout) == "table")
+       assert (spb > 1)
+       local m = 1
+
+       for time = 1,n_samples do -- not very efficient
+               -- TODO, timestamp the sequence in beats, calc/skip to next event
+               tme = tme + 1
+
+               if tme >= spb then
+                       midiout[m] = {}
+                       midiout[m]["time"] = time
+                       midiout[m]["data"] = midi_sequence[seq]
+
+                       tme = 0
+                       m = m + 1
+                       if seq == #midi_sequence then seq = 1 else seq = seq + 1 end
+               end
+       end
+end
index 99a5e4d6c81e12521f5b59d99744e45c61f19dea..9d04e29ec12216dff5d2a229faaba5667ca535af 100644 (file)
@@ -21,7 +21,7 @@ function dsp_ioconfig ()
        }
 end
 
-function dsp_midi_input ()
+function dsp_has_midi_input ()
        return true
 end
 
@@ -70,8 +70,8 @@ function dsp_run (ins, outs, n_samples)
 
        local tme = 1
        -- parse midi messages
-       assert (type(mididata) == "table") -- global table of midi events (for now)
-       for _,b in pairs (mididata) do 
+       assert (type(midiin) == "table") -- global table of midi events (for now)
+       for _,b in pairs (midiin) do
                local t = b["time"] -- t = [ 1 .. n_samples ]
 
                -- synth sound until event