Record generated MIDI events from Plugin to Clip?

Hey there,

I wrote a custom te::Plugin that generates new MIDI events (basically, a note repeater plugin). It plays back fine but these events are not recorded within the currently recorded clip.

How can this be achieved? Do I need to manually retrieve the ClipTrack from the Plugin and add the events manually on the Clip?

Thanks in advance!

After some more digging, a custom te::Plugin doesn’t seem to be the right way to write a quantized MIDI repeater.
I simply need to schedule MIDI events precisely that can either be played back or recorded. To playback, it works perfectly, but as said earlier, the generated events aren’t recorded.

I could either:

  • Schedule these events on the te::AudioTrack's MidiInputDevice (the trackDevice), but I’d need to play with timestamping of events (as
  • Insert and merge a new MidiClip just like MidiInputDevice::addMidiToTrackAsTransaction does after stopping a recording.
  • Write a custom Node?

What do you think?
Thanks in advance!
E.

Hey!
So far I see you doing it right making it te::Plugin.
Look at Midi RecordingDemo in examples. It shows how recording works.
I think what is wrong is position of you Plugin on the te::Track. You Plugin produces Midi and they are played by plugin positioned after your NoteRepeter Plugin.
So Midi is played but not recorded. I would advice you to checkout how you can record Midi produced by Arperggiator plugin in Waveform.

Also checkout this thread. I ran into same issues in the past. Configure Track A as MidiTrack input for Track B

1 Like

Hey @dikadk13 !

Thanks for your answer!

I’ve read the thread you linked to, where you set up a track that hosts an arpeggiator, which then feeds to another track’s MIDI input. Clever. I was able to set up Waveform as you did and record a clip with the generated notes.

I could have one generic (hidden) te::Track just for that purpose, with my MIDI repeater plugin instantiated, and dynamically connect the MIDI output to any MIDI input track I want to record these patterns. I’ll try this and report back.

Another idea I was also considering is to write a custom VirtualMidiInputDevice with my note repeat feature built in.

Thanks !
E.

1 Like

The other option is to render the clip through the note repeater plugin in to a new clip.
MIDI effects are always tricky as once you’ve “recorded” them to a clip, you don’t want to apply the processing any more or you’ll get double the effect.

1 Like

Hey @dave96 !

Right, in my case that’s not a problem, as I have a FIFO of events that I push from the UI directly to the plugin so I don’t rely on the event data received in applyToBuffer. I implemented it that way to avoid repeating notes over and over again when they are played back.

My initial issue was to find a way to record the new, generated MIDI events into the PluginRenderContext's MIDI buffer to a new clip. Right now I’m following @dikadk13 's suggestion to hook up the MIDI output of a “ghost” track with the MIDI repeater plugin into the input of another track MIDI input.

Ideally, I’d rather add these generated notes to the scratch record buffer that serves to create new transactional Clips.

I also investigated pushing the events on the track’s virtual MIDI device too but had a couple of issues with timestamping. I could investigate again if that seems like a good solution.

So, there are no ways to have certain events generated from a Pluginto be recorded into a new/existing Clip?

Thanks in advance!

E.

Hey @dikadk13 !

Were you able to record the incoming event coming from the source track to the destination track? They get routed properly but they are not recorded into the clip.
My source track has the MIDI repeater plugin, it adds the event to the PluginRenderContext MIDI buffer, my destination track has a sampler on it; it plays back, but nothing is recorded into a new Clip on the destination track.
I tried investigating but I must be missing something. Were you able to record the generated notes on the destination track at all?
Thanks!
E.

@eggnog, yeah there are few things.

I have MidiInput Track with Arrpegiator plugin and OutputTrack with Sampler/Synth plugin making sound.

I’m creating VirtualMidiInputDevice to trigger Midi on btn click.

te::VirtualMidiInputDevice::handleIncomingMidiMessage(juce::MidiMessage::noteOn(1, 60, 1.f));
te::VirtualMidiInputDevice *DrumPanel::createAndSetupVirtualMidiDevice(te::AudioTrack::Ptr &inputTrack,
                                                                       te::AudioTrack::Ptr &destTrack) {
    te::DeviceManager &deviceManager = edit.engine.getDeviceManager();

    juce::String virtualDeviceName("MidiInput_" + juce::String(inputTrack->getName()));

    te::InputDeviceInstance *matchedInstance = findVirtualDeviceByName(virtualDeviceName);

    //Create *virtualDevice if not already exists
    if (matchedInstance == nullptr) {
        auto result = deviceManager.createVirtualMidiDevice(virtualDeviceName);
        DBG(result.getErrorMessage());
        matchedInstance = findVirtualDeviceByName(virtualDeviceName);
    }
    jassert(matchedInstance);

    matchedInstance->setTargetTrack(*inputTrack, 0, true);

    //Route audio input to destTrack
    // (in order to play without Midi Generating plugin)
    inputTrack->getOutput().setOutputToTrack(destTrack.get());
    //Setup MidiTrackInput
    te::InputDevice &id = inputTrack->getMidiInputDevice();

    // Make the track's InputDeviceInstance visible to the EditPlaybackContext
    edit.getEditInputDevices().getInstanceStateForInputDevice(id);

    if (auto epc = edit.getCurrentPlaybackContext()) {
        if (te::InputDeviceInstance *sourceTrackInputDeviceInstance = epc->getInputFor(&id)) {
            sourceTrackInputDeviceInstance->setTargetTrack(*destTrack, 0, false);
            //sourceTrackInputDeviceInstance->setRecordingEnabled(*destTrack, true);
            for (auto dev: edit.getEditInputDevices().getDevicesForTargetTrack(*destTrack)) {
                dev->setRecordingEnabled(*destTrack, true);
            }

            DBG(virtualDeviceName << " was configured to pass midi and play inputTrack:" <<
                                  inputTrack->getName() << " destTrack:" << destTrack->getName());
        }
    }

    return dynamic_cast<te::VirtualMidiInputDevice * >(&matchedInstance->getInputDevice());
}
te::InputDeviceInstance *DrumPanel::findVirtualDeviceByName(juce::StringRef virtualDeviceName) const {
    edit.getTransport().ensureContextAllocated();
    for (auto instance : edit.getAllInputDevices()) {
        if (instance->getInputDevice().getDeviceType() == te::InputDevice::virtualMidiDevice &&
            instance->getInputDevice().getName() == virtualDeviceName) {
            return instance;
        }
    }
    return {};
}

Hey @dikadk13 ! Thank you so much.

I am doing something similar, it plays perfectly but the new, generated notes are never recorded into the new clip. I double-checked everything. The source and destination tracks are armed, recording is enabled, etc.

It seems feasible because I can set up something similar in Tracktion:

Screenshot 2021-12-22 at 11.18.56

I see the single notes recorded into my source track (which would be ARP on the screenshot), but nothing ends up don’t the recorded clip on the destination track (4OSC on the screenshot).

I will keep debugging what’s happening at the input level of the destination track.

Thanks!
E.

Another question for @dave96 - in Waveform, it is possible to change the number of inputs on a track, like this:

Screenshot 2021-12-23 at 11.02.44

I believe this is the purpose of index argument of InputDeviceInstance::setTargetTrack() ?

TIA!
E.

Yes, but I think you have to call AudioTrack::setMaxNumOfInputs first to enable more inputs. I would typically avoid doing this as it gets confusing having multiple inputs to the same track.

Have you set up the track-to-track input? You do this with AudioTrack::getMidiInputDevice() and assign that as an input to another track, you can then record from it like a normal MIDI input.

Also, make sure you’re on the latest tip of develop as there was a problem with event timing a few weeks ago that caused notes to not be recorded.

1 Like

Hey @dave96 !

Thanks for your reply. I’m manually managing the internal routing of each track so enabling 2 inputs is OK for my use case.

Alright, so I have set up track-to-track input, and everything records as expected, great! I manually create the instance from the “MIDI repeater” AudioTrack's MIDI Input Device and connect it to my instrument track.

One last question, is it possible to feed multiple destination tracks from one single track? I’d like to avoid creating a new “MIDI repeater” track for each track I want to feed to. I don’t think creating a new MIDI input device instance per destination track will work as it refers to a single track/virtual input device.

If I absolutely need to create a “MIDI repeater” track for each destination track, I could rely on enabling/disabling processing to avoid unnecessary processing, in the worst-case scenario, but maybe there’s a better way to achieve this?

Thanks for your help and happy holidays!
E.

No, you can’t route to multiple track destinations at the moment.
I can’t really visualise what you’re app looks like now as it’s got a bit complex but maybe you want to route the signal after the “MIDI repeater” with an aux send or Rack? That would enable you to send it to multiple synths on different tracks.

1 Like

Hey @dave96!

Happy New Year!

Thanks for your reply.

Basically, I have a bunch of instrument tracks (with a sampler on each), each have a virtual MIDI device to play notes. The virtual MIDI controller UI has different play modes like repeat, arpeggios, etc. Right now my initial design works, that is, I have a hidden MIDI “mod” track with the MIDI plugin and connects to the input of the instrument/sampler track.

I just tried your AUX send/return idea in Waveform, basically, I have one track with the MIDI plugin, followed by an AUX send plugin. My instrument tracks have an AUX return (same bus as the AUX send) before the instrument plugin, they do receive the modified MIDI notes but these notes are not recorded to a new clip. With a rack, I cannot connect the MIDI output of the plugin to the input of the destination tracks (audio only).

So far my initial design work, that is, one extra “MIDI mod” track per instrument track. I need to benchmark the overhead and try to save any useless processing when the MIDI repeater isn’t active.

Maybe it’s possible to record incoming MIDI from an AUX return but I didn’t find a way so far!

Thanks again,
Best,
E.

No, it’s not possible to record from an aux at the moment.
I think having two tracks per instrument is probably your simplest bet right now.

1 Like

Thanks @dave96 ! OK.

The pro’s of having such a MIDI mod track is that I can add/write plenty of MIDI modifier plugins which brings tons of possibilities for live play and sequencing.

Another alternative would be a write a custom Virtual MIDI device with the repeat/arp features built-in. I think I should be able to subclass MidiInputDevice just like VirtualMidiInputDevice but I’m not sure if that’s the right approach. Any thoughts?

Thanks again!
E.

I don’t think that would be possible because there’s no way for the engine to instantiate your derived class.

1 Like

Yeah, that’s what I was concluding. No worries – I’ll keep the original design (which is also pretty cool to host AUv3 MIDI plugins so their output can be recorded).