Trance gate for Ardour

Hi -

I’m trying to set a minimum time on my MIDI notes. (i.e, if MIDI Note Off comes less than X ms after the corresponding Note On, delay the Note Off until X ms have passed)

Some comments on other forums suggest using a trace gate for this purpose.

Do we have any kind of open source Ardour plugin that can do this?

I’m running on a Linux ARM architecture (Raspberry Pi 5), so I need it to be open source and not just freeware, since a Windows download is not suitable for my purposes.

Thanks!

I don’t know an existing plugin, the closest is perhaps “MIDI Sostenuto” from x42 MIDI Filter Collection (Raspbian packages it as x42-plugins):

MIDI Sostenuto: This filter delays note-off messages by a given time, emulating a piano sostenuto pedal. When the pedal is released, note-off messages that are queued will be sent immediately. The delay-time can be changed dynamically, changes do affects note-off messages that are still queued.

It would not be too hard to add a trace gate filter to the MIDI filter set to enforce a minimum note duration.

If it does not have to be in realtime, but editing already recorded MIDI on the timeline, you can could use a Lua Script to do this.

Quick hack:

  1. Select the MIDI region(s) in the editor that you want to change.
  2. Ardour Menu > Window > Scripting
  3. Copy/Paste the script below there
  4. optionally change required min. note length (line 16, 17)
  5. Press “Run”
local sel = Editor:get_selection ()
-- iterate over all selected regions
for r in sel.regions:regionlist ():iter () do
  local mr = r:to_midiregion ()
  if mr:isnil () then goto continue end

  -- get MIDI Model
  local mm = mr:midi_source(0):model ()
  -- Prepare Undo command
  local midi_command = mm:new_note_diff_command ("MIDI Note Duration")

  -- Iterate over all notes of the MIDI region
  for note in ARDOUR.LuaAPI.note_list (mm):iter () do
    -- len is in Temporal::Beats
    local old_len = note:length ()

    -- pick one of the following ..
    local new_len = old_len:round_up_to_beat ();
    --local new_len = Temporal.Beats (0, math.max (old_len:to_ticks (), 1920 / 4)) -- 16th note

    --print (old_len, new_len)

    if new_len ~= old_len then
      local new_note = ARDOUR.LuaAPI.new_noteptr (note:channel (), note:time (), new_len, note:note (), note:velocity ())
      midi_command:remove (note)
      midi_command:add (new_note)
    end
  end
  mm:apply_command (Session, midi_command)
  ::continue::
end
1 Like

Robin -

Thanks for the helpful suggestions.

I’m using it for live performance, so it does have to be in realtime.

“add a trace gate filter to the MIDI filter set” - sounds like something I could contribute to the project.

What kind of features should it have? Basic operation would be to drop all Note Off messages, and every Note On triggers a series of On/Off/On/Off messages. Timing would be similar to the “MIDI Delayline”, I think. I’ll have to study it to figure out what “Plugin Host” and “Control Port” mean for timing source.

My application is slightly different from a standard trace gate - I wouldn’t mind if my Note Off messages weren’t just dropped. If anything, I’d like a plugin to generate a delayed Note Off message, then another plugin to drop the first Note Off message and forward the second, so I get behavior like the commented out new_len calculation in your LUA script.

Somebody will probably want the thing to loop, too.

I’ll puzzle about it a little more.

I think the suggestion from the other forum had a typo. They probably meant that you should try a “Trance gate” effect. This was made popular in 90’s Trance music.

That is not a MIDI effect, but a sidechain technique:

You use a rhythmic pattern for example using a snare sample (disable routing to master so you don’t hear it in the mix). Then use that pattern to trigger a gate plugin on a seperate track that contains a sustained synth or vocal sound.

You can do this with the ACE Expander.

Hello,

Long-time lurker here, jumping to point to my own collection of plugins, that I did not expect to make public just yet (still lacking documentation, no UI yet, and so on), but if I understand correctly your use-case I think this is covered by my MIDI “Gate” plugin. It takes all incoming NoteOn, replace NoteOff with the desired delay (could be set in seconds, minimum 1ms, or in a ratio of the bar duration as advertised by the transport).

You can grab and test it from there: : GitHub - QualyaIO/obtuse.DPF: Audio plugins version of Obtuse DSP, thanks to DPF

Through DPF there are releases for many platforms, ARM included. Look for “ObtuseGate”. I quickly tested on ardour (8.4 installed at the moment), it seems that the inline controls for integers params are a buggy for LV2 and VST2 versions, prefer the VST3 one (I’ll investigate that later).

There is a tiny bit of documentation in the README of the main repo, presenting the various plugins and the overall goal (provide for a set of tools to do generate notes and sounds across DAW, VCV or Arduino): GitHub - QualyaIO/obtuse.DSP: Self-contained generative music with limited resources.

Hope it helps :slight_smile:

3 Likes

@mrMute,

The typo is mine. I meant “Trance Gate”, but misunderstood the effect. I looked at Kiloheart’s Trance Gate and saw the ADSR controls, but also saw the pattern effect and misunderstood it. I do want the MIDI effect, not the sidechain effect.

But I loaded ACE Expander and must admit that I don’t understand it. What is being graphed in it, for example? Is it decibels vs time somehow?

@jfrey ,

That’s a nice collection of plugins, that I didn’t know about!

I’m running Ubuntu 24, so I tried apt install dpf-plugins-vst3, and got a bunch of new plugins, but ObtuseGate was not included. I’ll probably have to try building from source.

Very helpful; very helpful!

Sorry, I should have been more specific; DPF is a framework to help develop plugins accross various formats and platforms at the same time. Obtuse is not packaged for any distribution, but you don’t have to compile yourself, you can download it from the github release page and copy to your plugins folder Releases · QualyaIO/obtuse.DPF · GitHub

An expander is like a compressor but instead of attenuating sounds above a a certain threshold it attenuates sounds below it.
It’s similar to a gate (exactly like a gate if you set the Ratio to 100) but you can control how much leakage you want below the threshold.

In this example everything below -15dB gets reduced by a factor of 2.6

Not MIDI, but for those interested in the audio FX:

@jfrey ,

Thanks, I got the pre-compiled tar bundle now.

Your Gate plugin is real close to what I want. I do want the notes to sustain if I hold the keys down; I’m not sure if that’s best done with a separate plugin or an option on yours.

The Gate plugin pegs the NoteOn velocity at 127, no matter what velocity I strike the key with. Looking at the source code, that’s clearly not what’s intended. Unless you know why it’s doing that, I’ll look into it a little bit more and try to figure it out.

Thanks again!

Actually, I think I see the hard-wired “127” now.

The noteOn() function relays the velocity correctly, but the call to sendNoteOn() in process() uses 127 for the velocity. But I need to study the code more to understand it better.

Thanks for having a try at the plugin :slight_smile:

The C code is actually transpiled from vult (GitHub - vult-dsp/vult: Vult is a transcompiler well suited to write high-performance DSP code) that I use underneath , so if you want to fully check the code you’d need to dig in the “obtuse.DSP” (sub)repo – here for instance.

I’ll have a look at preserving velocity if this is useful to you, however having the gate hold further while the note is held is not on my todo list, as this is not my intended use-case, and it would add in complexity (e.g. what to do if there is never a NoteOff event?).

…and I implemented keeping the velocity, check the latest release.

I tried to think of an easy way for you to also have notes to sustain, best so far would be run in parallel input and plugin output (track with flexible I/O, edit the pin connections of the plugin, add a MIDI out, connect to input), discard one of the noteOn, merge (e.g. MIDI join from FalkTX and carla package?)… and then find a way to discard the first noteOff event (if the note is held longer than gate then discard gate, run until note is released; if the note is short lived, discard the note release, wait until the end of the gate) :person_shrugging:

I picked up @jfrey 's latest obtuse Gate plugin; sure enough, it relays velocity now.

It turns out I didn’t really need obtuse Gate to relay velocity at all. Here’s how I got it all working.

I created two tracks. One takes input from my Input track and feeds obtuse Gate, followed by MIDI Event Filter set to discard all NoteOn messages. This is why I don’t really need velocity - those messages get discarded. That’s it for the first track. The second track takes input from the Input track as well as the obtuse Gate track, and thus I get two NoteOff messages for each NoteOn message. I run it through the Lua script/plugin below, and then on to sfizz (the instrument):

ardour {
  ["type"]    = "dsp",
  name        = "Discard Alternate NoteOff",
  category    = "Utility",
  author      = "Brent Baccala",
  description = [[use in conjunction with obtuse Gate plugin to implement a delay gate with a minimum but unbounded note time]]
}

function dsp_ioconfig ()
  return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, }
end

-- table to hold the NoteOff state; key is note number; nil means no NoteOff seen; t means NoteOff seen
state = { }

function process_midi_messages()

        kout = 1
        for k,b in pairs (midiin) do
                local t = b["time"] -- t = [ 1 .. n_samples ]
                local d = b["data"] -- midi-event data
                local event_type
                local channel
                if #d == 0 then
                    event_type = -1
                    channel = -1
                else
                    event_type = d[1] >> 4
                    channel = d[1] & 15
                end
		if (event_type == 8) then
                    -- only pass alternate NoteOff messages (on a per-key basis)
                    note = d[2]
                    if (state[note]) then
                        midiout[kout] = midiin[k]
                        kout = kout + 1
                        state[note] = nil
                    else
                        state[note] = t
                    end
                else
                    -- pass through all other MIDI messages
                    midiout[kout] = midiin[k]
                    kout = kout + 1
                end
        end
end

function dsp_run (_, _, n_samples)
        -- without pcall, any errors would crash Ardour; instead, just ignore errors
        -- doesn't seem to stop errors in process_midi_messages from crashing Ardour
        status, error = pcall(process_midi_messages)
        if not status then
            print(error)
        end
end

It’s been a nice learning experience, and I thank everyone for their suggestions. I’m starting to think that what I want is something like a drum machine triggered from the keybed. This is a very simple example of that, but ultimately I want NoteOn messages to be able to trigger either a one-shot sequence or start a repeating pattern playing. Pads are fine between songs, but too difficult to use while playing, that’s why I’m starting to think I want to trigger stuff from the keybed, and this is a very simple example of that.

No, I see now that it’s not a “Trance Gate”; it’s something else (a drum machine?).

I still don’t quite understand what a Trance Gate does, but at least I know that it exists.

2 Likes

It’s an unusual workflow, but I love that you managed to help yourself. Nice piece of creative work!

Thanks @Brent_Baccala for the follow-up, nice little script, and it demonstrates how powerful lua integration is :slight_smile: I should get more familiar with it at some point!

Here is a good explanation of a trance gate: