Configure Track A as MidiTrack input for Track B

I cannot figure out how to configure Midi Input for tracks in project I’m working on.
I have te::VirtualMidiInputDevice on te::AudioTrack A ("Drum 1 Input"on screenshot) and Arrpegiator plugin on same track.
I would like to use te::AudioTrack B (“Drum 1” on screenshot) as destination for Midi data sent from te::AudioTrack A, so it get’s recorded into te::MidiClip on te::AudioTrack B (“Drum 1” on screenshot).
I tried setting te::AudioTrack output and it seem to affect only audio, not Midi

trackA ->getOutput().setOutputToTrack(trackB)

Also tried:

 if (auto* virMidi = dynamic_cast<te::VirtualMidiInputDevice*>(&track->getMidiInputDevice())){
        virMidi->setEnabled(true);
        if(auto instance = edit.getCurrentInstanceForInputDevice(virMidi)){ // and track->getMidiInputDevice() as argument as well
            instance->setRecordingEnabled(*track, true);
        }
    }

But I’m not getting any instances for this InputDevice

Screenshot from Waveform shows exactly was I want to achieve “Drum 1 Input” track as MidiTrack Input for “Drum 1” and be able to record all generated Midi from Arpeggiator.

Thanks for all help in advance!

This is where the myriad of routing options becomes confusing :wink:

setOutputToTrack sets the “Track Destination” property in Waveform which send the track’s output to the start of the plugin chain of the destination track.

I think what you’re after is using "Drum Input 1"s WaveInputDevice as an input to track “Drum 1”.
This should be achievable with the following:

                InputDevice& id = sourceTrack->getWaveInputDevice();
                
                // Make the track's InputDeviceInstance visible to the EditPlaybackContext
                edit.getEditInputDevices().getInstanceStateForInputDevice (id);

                if (auto epc = edit.getCurrentPlaybackContext())
                    if (auto sourceTrackInputDeviceInstance = epc->getInputFor (&id))
                        sourceTrackInputDeviceInstance->setTargetTrack (destTrack, inputSlotIndex, moveToTrack);

Hmmm. I tried and didn’t quite work for me.

I’m only receiving Midi Events on “Destination track” if I use inputTrack->“getMidiInputDevice()” instead of “getWaveInputDevice()”, but Midi Messages are original from te::VirtualMidiInputDevice, not modified by my Arpeggiator.

I modified MidiRecording Example to demonstrate issue. Added function with all setup I’m doing.

void createVirtualMidiAndAssignToTrack()

Sorry, the audio/midi device was my mistake, as you say it should be the MIDI device if you want MIDI message :man_facepalming:

There’s a few problems with your code though, I’m not sure if they are the source of the problem though (I’ve also added some spacing to make it a bit more readable):

            for (const auto instance : edit->getAllInputDevices())
            {
                DBG(instance->getInputDevice().getName());

                if (instance->getInputDevice().getDeviceType() == te::InputDevice::virtualMidiDevice
                    && instance->getInputDevice().getName() == virtualDeviceName)
                {
                    if (auto inputTrack = EngineHelpers::getOrInsertAudioTrackAt(*edit, 0))
                    {
                        instance->setTargetTrack(*inputTrack, 0, true);
                        instance->isLivePlayEnabled(*inputTrack); //!!! This call doesn't make any sense, it's a const method that returns a bool so won't actually do anything...
                        
                        inputTrack->setName("InputTrack");
                    
                        if (auto destTrack = EngineHelpers::getOrInsertAudioTrackAt(*edit, 1))
                        {
                            destTrack->setName("DestTrack");
                            
                            //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 (auto sourceTrackInputDeviceInstance = epc->getInputFor (&id))
                                {
                                    sourceTrackInputDeviceInstance->setRecordingEnabled (*destTrack, true);
                                    sourceTrackInputDeviceInstance->setTargetTrack (*destTrack, 0, true); //!!! This probably needs to go above the setRecordingEnabled call or the setRecordingEnabled will fail as it hasn't been assigned to the track yet
                                    sourceTrackInputDeviceInstance->isLivePlayEnabled(*destTrack); //!!! As above: This call doesn't make any sense, it's a const method that returns a bool so won't actually do anything...
                                }
                            }
                        }
                    }

                    virtualMidi = dynamic_cast<te::VirtualMidiInputDevice * >(&instance->getInputDevice());
                }
            }

With those changes, does it work as expected?
If not, have you stepped through that method to check all your ifs are actually being entered?

Thanks for fast reply and sorry for formatting. I usually use CLion and it takes care of it, unlike xCode, which I’ve used for modifying my example.

I updated my code after your remarks. But it still doesn’t work as expected. I don’t see any midi or audio passed “inputTrack” -> “destTrack”.
te::InputDeviceInstance* state looks correct though.

<?xml version="1.0" encoding="UTF-8"?>
<INPUTDEVICE sourceTrack="1012" type="MIDI">
  <INPUTDEVICEDESTINATION targetTrack="1002" targetIndex="0" armed="1"/>
</INPUTDEVICE> 

                   for (const auto instance : edit->getAllInputDevices())
                    {
                        DBG(instance->getInputDevice().getName());

                        if (instance->getInputDevice().getDeviceType() == te::InputDevice::virtualMidiDevice
                            && instance->getInputDevice().getName() == virtualDeviceName)
                        {
                            if (auto inputTrack = EngineHelpers::getOrInsertAudioTrackAt(*edit, 0))
                            {
                                instance->setTargetTrack(*inputTrack, 0, true);
                                inputTrack->setName("InputTrack");
                            
                                if (auto destTrack = EngineHelpers::getOrInsertAudioTrackAt(*edit, 1))
                                {
                                    destTrack->setName("DestTrack");
                                    
                                    //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 (auto sourceTrackInputDeviceInstance = epc->getInputFor (&id))
                                        {
                                            sourceTrackInputDeviceInstance->setTargetTrack (*destTrack, 0, true);
                                            sourceTrackInputDeviceInstance->setRecordingEnabled (*destTrack, true);
                                            
                                            DBG(sourceTrackInputDeviceInstance->state.toXmlString());
                                            DBG(inputTrack->state.toXmlString());
                                            DBG(destTrack->state.toXmlString());
                                        }
                                    }
                                }
                            }

                            virtualMidi = dynamic_cast<te::VirtualMidiInputDevice * >(&instance->getInputDevice());
                        }
                    }

It’s easy to reproduce by running my modified MidiRecording example from above.
Anything else I can check or try?

Woohoo! Looks like code above gets me to result I want. I actually had to add Arpeggiator plugin to inputTrack, once it was added it generates and sends Midi to destTrack.
For scenarios where I don’t have Midi Plugin (arpeggiator plugin in my case) on inputTrack, but still want to route Midi to destTrack, I used function below which routed output to destTrack.

inputTrack->getOutput().setOutputToTrack(destTrack);

Thank’s @dave96 for help!