Yeah, that came to my mind, too, because the only way turning on the automation affects the plugin is that some extra recomputations of the patterns are done, thus potentially using extra memory.
But that doesn’t seem to be it. This session is just 4/4, thus a single Barlow table with around 112 total entries of integer values. It’s not big by any means. To make sure, I actually measured the memory use with collectgarbage("count") at the end of dsp_run, and it’s consistently in the 1650 to 1680 KB range, way below the 3 MB limit.
I also tested my other theory about simply copying events from midiin to midiout, and made sure that I take deep copies instead, but that didn’t get rid of the bug either.
I think that I’ll just leave it at that for now. It only affects live usage of the arpeggiator in specific circumstances. I’ll surely stumble on the root cause of the issue at some point. For the time being, I can make the arpeggiator react to controller 123 on the input channel by flushing the chord and latched notes cache, so that you can at least get rid of the hanging notes by hitting the panic button.
There is also the possibility that his is a bug in Ardour.
When automation is present, Ardour can split the process-cycle. Say process 595, change the value and then process the remaining 429 samples for a total of 1024.
It looks like MIDI events are not offset correctly: In the first cycle (0…595) the DSP function receives data for the complete cycle, and due to in-place processing the same happens again in any later sub-cycle.
Thanks, will do. (I have to teach courses tomorrow, so I probably won’t get to this before tomorrow night.)
Another idea: Could there also be a latency issue at play here? Currently I don’t have a latency setting for the plugin, so it defaults to zero, which probably isn’t right. If I just copy midiin to midiout with a zero latency setting, then the notes will sound one cycle late, right? So the latency setting should probably be something like Session:get_block_size(), no?
I noticed this issue while experimenting with a ‘Bypass’ toggle in the plugin params. Specifically, with a zero latency setting notes passed through at the beginning of the loop range would not sound, but increasing the latency to the block size would fix that. So it seems to me that there must be some kind of timing issue at play here. (Not sure whether that’s also related in any way to the hanging notes bug, though, which only seems to happen with live input.)
That did it! While I was at it, I also applied the earlier debian Pastezone, so that I don’t have to sort the midiout events on the Lua side anymore.
With that, I don’t see any hanging notes with live input anymore, and the latency issues with the bypass option also seem to have sorted themselves out. I can run the plugin at latency 0 now, and everything works as expected.
Robin, thank you so much for this! This bug was driving me nuts. I’m a happy camper now Any chance that these two patches get committed to Ardour git soon?
Done, except the latter was just a workaround to investigate the issue. Ardour now correctly offsets MIDI events when cycles are split for automation changes.
I am glad that it works reliably now and am sorry that you spent some time trying to fix an issue with your plugin when it was instead an Ardour bug.
Do you expect more development is is the plugin ready to be bundled with Ardour?
The next minor version release is due in ~7 weeks, so there is still some time.
No worries, complex plugins shake out the bugs of complex APIs, that’s just normal business. This has actually been a lot of fun, even the debugging.
Both plugins are pretty much finished at this point, so feel free to pull them over whenever it’s convenient. In any case I can give you a quick heads up if they need updating because of substantial bugfixes or new features.
For barlow_arp, now that the MIDI timing issues have been sorted out, I still have an idea in my head, a delay parameter which is a generalization of the swing parameter in simple_arp, with modulation by the Barlow pulse strength. Not sure yet whether that will be musically interesting, but I’d like to give it a shot.
Also, I have plans to port the big one, raptor, which is like barlow_arp but heavily on steroids. This has Barlow harmonicity controls and lots of built-in modulations and randomization, so it’s really a full-blown algorithmic composition program pretending to be an arpeggiator. The Pd version is kind of hard to play because there are so many parameters, but with Ardour’s preset system that should become much more manageable, and I think I’m also going to simplify it a bit to make it easier to use. But that’s still on the horizon right now, I don’t know when I’ll get to this.
Robin, I pushed a new revision of both arps which fixes a rather annoying bug (input notes were slipping through at the very start, which I remedied by using the TFSM to detect transport status).
There’s also a very first version of the Raptor implementation in Ardour, see raptor_arp.lua. It sounds very close to the Pd version, so it seems that it works alright, but consider it experimental at this point. I’ve included a few factory presets from the Pd version for illustration purposes, which should hopefully help anyone get started with this one. Some test sessions can be found here. They use gmsynth and setbfree as instruments. Short soundbites from each session can also be found there.
Note that, unlike the other arps I’ve made, Raptor will play different notes each time due to its built-in randomization and modulation capabilities. That’s by design, and not a bug. Also, the harmonicity parameters (hmin, hmax, and pref, the harmonic preference, the latter can also be negative), along with the corresponding modulation parameters (hmod and prefmod, these are also bipolar) have a substantial impact on the amount of tonality (or absence thereof) in the output.
To prove this, the test sessions I pointed to above range from the almost purely tonal (raptor-test) to almost completely atonal (raptor-go-barlow). The latter may sound familiar if you have listened to any of Barlow’s pieces, which is no accident, because Raptor is basically Barlow’s Autobusk in the guise of an arpeggiator and with a bunch of automatic modulation parameters thrown in, although it can do run-of-the-mill deterministic and randomized arpeggios as well.
The go-barlow session also illustrates how to use Ardour’s automation on the plugin parameters to get some interesting “harmonicity sweeps”, also a hallmark feature of many of Barlow’s compositions. So you could use Raptor in Ardour as a replacement for Autobusk to compose entire pieces by providing note input along with automation curves for the various parameters.
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.
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. 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.