Lua arpeggiator plugin anyone?

BTW, Robin, I think that raptor_arp.lua is also testament to how well Ardour-Lua works. In contrast to the other two arps, this one really has a substantial amount of complicated code and composes most of the note output on the fly, employing lexical closures in Lua.

The test sessions use three raptor tracks, each with a separate instance of the plugin, which are fed with MIDI input from the same track. You can also play the session live if you switch that track to “In”. This all works amazingly well, without any hiccups in the playback, and I actually find Raptor to be a lot easier to use in Ardour than the Pd version. I can just throw together a bunch of raptors in a session and start jamming. The Pd implementation lets me do that, too, but there is a lot more preparations and hoops to jump through.

Also, porting the Lua code from Pd-Lua wasn’t really hard. Having to inline required modules is a minor annoyance, but I understand why it’s necessary.

I miss kikito’s inspect, though. It’s great for debugging purposes, so it would be nice if Ardour-Lua would preload it. Yes, I could include the code in my plugins, but it’s some 370 LOC, so I currently use a rather simplistic bare-bones replacement in my modules instead.

I’d have suggested

local rolling = time["sample"] ~= time["sample_end"]

but your solution also works

Will time["sample"] ~= time["sample_end"] already yield true even if the transport isn’t actually moving yet?

EDIT: Well, I guess so, otherwise you wouldn’t have suggested it. :wink:

Anyway, that’s good to know that I can get that information without another method call of the Session object. Thanks!

Latest changes just pushed to my ardour-lua repo:

barlow_arp, simple_arp: Fix plugin crash when bbt.ticks happens to be negative. (Fallout from previous fix.)

raptor_arp: Non-MIDI-related control values are normalized values in the -1…1 or 0…1 range now, for consistency with the other arp plugins.

BTW, Robin, what’s up with all these synthetic all-notes-off (CC 123) events being generated when loop playback wraps around to the start of the loop? It seems that Ardour sends these to all MIDI tracks. I guess the intention is to get rid of potential hanging notes at the end of the loop, but CC 123 is a rather sharp knife, which is giving me woes.

In particular, these messages were affecting latch mode with live MIDI input in Raptor, so it now only processes these if transport is stopped. But those messages also cause issues further down in the signal chain, cutting notes short in the synth at the beginning of the loop. It seems that the all-notes-offs may actually arrive right after a note-on being output by the arp, cutting the note short.

I can get rid of the cut-short notes by placing a MIDI filter before the synth, so that CC 123 gets filtered out there. But maybe there should be an option to just disable these on a per-track basis?

BTW, the same applies to CC 64 (hold pedal) which also gets sent at each loop start/end. Again, the intent seems to be to get rid of a potential “hanging hold CC” at loop end, but it should be possible to disable those messages if they get in the way.

To be clear, I’m not talking about transport start/stop, I think it’s fine to send these messages at that time. But those messages being emitted on all MIDI tracks for each loop wraparound can definitely get in the way of live MIDI input (and arpeggiator output). Obviously, the issue mostly arises when playing live. With MIDI coming from a track, everything seems to work fine.

Ok, looking at the code, it seems that this actually happens at the port level, in MidiPort::resolve_notes() which is being called from MidiPort::flush_buffers(), and gets primed by the MidiPort::realtime_locate() call which sets the _resolve_required flag that is being checked by MidiPort::flush_buffers() to determine whether to emit those CC 123+64 messages. MidiPort::flush_buffers() in turn is being called from a number of places, among them AsyncMIDIPort::cycle_start() and Delivery::flush_buffers().

This looks complicated and I wouldn’t want to touch any of that code in order not to break it. :wink: Except maybe MidiPort::flush_buffers(), it would be straightforward to check a secondary flag there in order to decide whether to emit the messages. I’m a bit worried, though, that this might break assumptions elsewhere. I guess that we’d be seeing hanging notes galore, in case a MIDI region extends past the loop end marker?

Robin, does the above make any sense to you? In any case, I can try to work around this as best as I can in the plugin, and use midi filters for the rest if I need to.

It is intentional at transport stop to send “all notes off” “all sound off”, “raise pedal”. I hazard a guess that the current loop model dates back to JACK where this is always “stop, locate, play”. @paul?

This is absolutely about hanging notes, which can be caused by:

  1. no note off delivered because it is beyond the end of the loop
  2. no note off from synth because sustain is engaged

So we have to take actions to prevent this.

We do have a more subtle knife available: the port should know which notes are on because it has a state tracker.

But the less subtle knife is quicker and more reliable.

1 Like

I have to agree. Hanging notes are the worst. Notes being cut short are equally bad for an arpeggiator, though. :slight_smile: But, as I said, I’ll just deal with that as best as I can in the plugin.

Robin, I’ve just released version 0.3 of the scripts at https://github.com/agraef/ardour-lua. Raptor now has some proper instructions in the README, and the arpeggiator plugins all have version information in their descriptions now, so that it becomes easier to know what version a user is running in case of bug reports.

I think that I am finished with these for now (bugfixes notwithstanding).

@aggraef
I’m again hugely grateful for the effort you’ve put in here, even if for my compositional style / usage patterns I’m unlikely to use the plugins:

  • The examples you have provided have opened my ears (at least a bit) to this kind of generative / algorithmic composition, which is a gift.
  • That you have put in the effort to grok the Ardor Lua API to this extent warms the cockles of my heart, because it gives me confidence that I might be able to use that API myself for some vague / notional future need.
  • That @x42 et. aliae have engaged so well with your effort is unsurprising, but still gratifying, as a justification for the F/LOSS community-based development model I love so much. I defy anybody to demonstrate a similar openness / flexibility in any similarly complex, proprietary software ecosystem.
1 Like

@tseaver, thanks for the kind words. And a big shoutout goes to Robin, without his involvement this effort probably wouldn’t have gone anywhere. The Lua API has advanced a lot since I last looked at it, and the ability to write MIDI plugins in Lua is a great boon IMHO.

BTW, Robin, one thing I’d really like to see added some time is an API for control surfaces. I don’t know whether that’s feasible, and I think that I’ve read somewhere that Paul prefers to have these things written in C++. But I have ready-to use Lua code lying around for interfacing the AKAI APC mini (both mk1 and mk2) to Ardour’s new clip launcher. This currently runs in Pd and utilizes Ardour’s OSC interface, but presumably it would be easy to port this over if the API was available in the Lua interface.

(The APC mini is a great budget option for a clip launcher, and the new version is a lot better, featuring true RGB colors for the pads and built-in drum and isomorphic keyboard modes. IMHO this is the best clip launcher that you can get right now at the $100 price point.)

If you’re interested, I can start a new thread about this, just let me know.

If not, well then I’ll wait for Paul’s Novation Launchpad driver and use that as a blueprint for an APC mini driver in C++, but frankly it would be much more fun for me to write this in Lua. :slight_smile:

1 Like

We’re very unlikely to do this. As you note, we have the option to provide support for any surface via C++, and that’s almost certainly the way it is going to stay for the foreseeable future.

Although we did distill down several of the common elements of what a control surface needs to do into libs/ctrl-interface/midi_surface it remains the case the the basic physical design of these things seems to continue to dictate a relatively unique approach per device.

That said, obviously both Bitwig and Reaper allow control surface support via their own scripting languages, so to some extent I’m full of crap.

1 Like

Ok, I understand. I naively assumed that the API was already there and just needed to be wrapped. But looking at DrivenByMoss, I can see that the task of designing such an API is an enormous project in itself, and I have to agree that development work is better spent elsewhere.

This is getting off-topic now, but there’s another little snag concerning MIDI controllers I keep running into. As far as I can tell, it’s only possible to use a single Generic MIDI control surface right now. Would it be difficult to adapt Ardour to have multiple controllers with different binding maps active at the same time? That is, instead of one “Generic MIDI” interface under “Control Surfaces”, have, say, three of them, or as many as one needs? I can work around that limitation by creating a single custom binding map for all the controllers I want to use, but that’s a little inconvenient.

IF we can, can we go ahead and make a new topic for the multiple midi controllers question? Thanks!

Seablade

Done, see Multiple "Generic MIDI" control surfaces

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.