Page 1 of 2

Writing a latent function.

Posted: Sat Sep 02, 2017 10:46 pm
by atrey65789
Hi there, during the course of the last few months, I have been studying C++ on my own time and experimenting with importing different functions natively into the Unreal Engine. right now I am experimenting with connecting different USB devices to the Unreal Engine, especially midi devices. The thing is though, I have a function that I made, void GetMidiInput(), it waits for input from a midi device and prompts the input to the screen via BroadcastMessage ().. it works and all, but between detecting input, the entire game window freezes completely.

So, but I am asking for help with, is that how can I modify this function to where it can wait for input from the device without freezing the entire game? I know that's the main purpose of latent functions, but I can't find any documentation on how to wrote these types of functions for unrealed. any help would be appreciated. I will paste the functions code if I have to.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 2:38 pm
by Hanfling
I never tried writing latent functions before, so let me see if I get it right how they are implemented, and write the below a bit more generalized.

Basic idea behind latent functions if you can have code inside your state code (the thing with the labels in your states), which can block unrealscript state code execution. Samples of this are Sleep(), StrafeTo() or WaitForLanding(). Not entire sure about this, but state code execution seems to be only useable on Actors and is executed when the actor is ticked.

Latent functions may be declared with the latent keyword, but as far as I can tell this is just to give you an hint that it's a latent function and prevents you from calling them in non state code, otherwise the latent keyword seems to have no effect.

To implement an UnrealScript latent function you basically need to native functions, one I will call entry function and a polling functions. The entry function is what you will call in UnrealScript, the second one is typically not declared in UnrealScript to prevent direct uc calls to it. The entry function will set StateFrame->LatentAction to the number of the polling native. During ticking if StateFrame->LatentAction is non zero this will be executed instead of continuing the normal state code flow. DeltaTime will be passed in inside the Result variable of the polling native. So basically at some point you need to set StateFrame->LatentAction to zero so normal code execution in UnrealScript continues, e.g. your latent functions "returns". Both of these native function need to be non blocking.

Actor also has some predeclared variables intended to be used with latent functions, for example to implement a timeout.

Code: Select all

var const byte            LatentByte;    // Internal latent function use.
var const int             LatentInt;     // Internal latent function use.
var const float           LatentFloat;   // Internal latent function use.
var const actor           LatentActor;   // Internal latent function use.
Now some example how this should work:

Code: Select all

// UC:
class LatentSampleActor extends Actors
  native;

native(3524) final latent function LatentSample();

event Receive( string Data );
event Timeout();

auto state Stuff
{

Begin:
  LatentSample();
}

// C++
#define LATENTSAMPLE_POLL_ID 3525

// Entry function.
void ALatentSampleActor::execLatentSample( FFrame& Stack, RESULT_DECL )
{
  guard(ALatentSampleActor::execLatentSample);
  P_FINISH;

  GetStateFrame->LatentAction() = LATENTSAMPLE_POLL_ID;
  LatentFloat = 0.f;
  unguardexec;
}
IMPLEMENT_NATIVE(ALatentSampleActor,3524,execLatentSample);

// Polling function.
void ALatentSampleActor::execLatentSamplePoll( FFrame& Stack, RESULT_DECL )
{
  guard(ALatentSampleActor::execLatentSamplePoll);
  // No P_FINISH as this isn't called by uc and hence can't serve it's purpose to eat up the EX_EndFunctionParms code.

  // Grab DeltaTime.
  FLOAT DeltaTime = *(FLOAT*)Result;

  // Increase timer used for timeout detection.
  LatentFloat += DeltaTime;

  // Fire event if data is ready
  if ( HAS_DATA() )
  {
    eventReceive( GET_DATA() );
    LatentFloat = 0.f;
  }
  // Timeout.
  else if ( LatentFloat>30.f )
  {
    eventTimeout();

    // Make "LatentSample function return" as viewed from UnrealScript.
    GetStateFrame->LatentAction() = 0;
  }
  unguardexec;
}
IMPLEMENT_NATIVE(ALatentSampleActor,LATENTSAMPLE_POLL_ID,execLatentSamplePoll);
So basically for your problem you will still have to either move your blocking i/o calls to another thread or use non blocking i/o calls for reading the input in your code.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 3:21 pm
by atrey65789
Very interesting. That explains why what I'm doing currently prevents the rest of the engine's code from executing. Very informative, Hanfling. Thank you. I will try this method today and let you know how it goes.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 6:41 pm
by atrey65789
I seem to be getting some errors here.

error C2227: left of '->LatentAction' must point to class/struct/union

It shows this error two times, so, I don't think it's finding anything for 'GetStateFrame'.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 7:18 pm
by Hanfling
Missed the () after GetStateFrame() which is a function.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 9:34 pm
by atrey65789
Neat, fixed that. Also, "LatentAction()" was supposed to be "LatentAction". Gonna fix the other simple errors on my part and see if it works.

Re: Writing a latent function.

Posted: Sun Sep 03, 2017 10:10 pm
by atrey65789
error C2059: syntax error : 'constant'

I got this on the IMPLEMENT_NATIVE definitions. Could I use AUTOGENERATE_FUNCTION instead since it has the exact same parameters?

Re: Writing a latent function.

Posted: Mon Sep 18, 2017 2:22 pm
by atrey65789
Bump. Anybody know that error? Still messing with it and can't get it to work.

Re: Writing a latent function.

Posted: Thu Sep 21, 2017 2:03 pm
by Hanfling
Post project you want to compile.

You can use the AUTOGENERATE_FUNCTION macro to avoid having to manually put your IMPLEMENT_FUNCTION into cpp files, see NativeSamplePackage in ut/rune pubsrc for example use.

Re: Writing a latent function.

Posted: Thu Sep 21, 2017 3:16 pm
by atrey65789
This is the header

Code: Select all

/*===========================================================================
    C++ class definitions exported from UnrealScript.
    This is automatically generated by the tools.
    DO NOT modify this manually! Edit the corresponding .uc files instead!
===========================================================================*/
#if _MSC_VER
#pragma pack (push,4)
#endif

#ifndef UNREALMIDI_API
#define UNREALMIDI_API DLL_IMPORT
#endif

#ifndef NAMES_ONLY
#define AUTOGENERATE_NAME(name) extern UNREALMIDI_API FName UNREALMIDI_##name;
#define AUTOGENERATE_FUNCTION(cls,idx,name)
#endif

AUTOGENERATE_NAME(MidiMessage)

#ifndef NAMES_ONLY


struct AUnrealMIDI_eventMidiMessage_Parms
{
    FString midi_msg;
    FString midi_velocity;
};
class UNREALMIDI_API AUnrealMIDI : public AActor
{
public:
    DECLARE_FUNCTION(execGetMidiInput);
    DECLARE_FUNCTION(execMidiPoll);

    void eventMidiMessage(const FString& midi_msg, const FString& midi_velocity)
    {
        AUnrealMIDI_eventMidiMessage_Parms Parms;
        Parms.midi_msg=midi_msg;
        Parms.midi_velocity=midi_velocity;
        ProcessEvent(FindFunctionChecked(UNREALMIDI_MidiMessage),&Parms);
    }
    DECLARE_CLASS(AUnrealMIDI,AActor,0)
    NO_DEFAULT_CONSTRUCTOR(AUnrealMIDI)
};

#endif

AUTOGENERATE_FUNCTION(AUnrealMIDI,3524,execGetMidiInput);
AUTOGENERATE_FUNCTION(AUnrealMIDI,3525,execMidiPoll);

#ifndef NAMES_ONLY
#undef AUTOGENERATE_NAME
#undef AUTOGENERATE_FUNCTION
#endif NAMES_ONLY

#if _MSC_VER
#pragma pack (pop)
#endif

This is the source
// ===========================================================================
// C++ Class to implement full MIDI-Input Support for the UnrealEd (V 1.0).. cool, eh?
//
// By: Brenden Dane, Dane Audio Productions
//
// ===========================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include "UnrealMIDIPrivate.h"

FString Msg;
FString midi_velocity;
bool bDone;


struct midi_in_list_node
{
int midi_in_number;
HMIDIIN midi_in;
struct midi_in_list_node *next_midi_in_list_node;
};

midi_in_list_node *opennode; //Current node for the current MIDI device.
HMIDIIN opendevice;

struct midi_in_list_node *first_midi_in_list_node = NULL;

#define LATENTSAMPLE_POLL_ID 3525

#define NAMES_ONLY
#define AUTOGENERATE_NAME(name) UNREALMIDI_API FName UNREALMIDI_##name;
#define AUTOGENERATE_FUNCTION(cls,idx,name) IMPLEMENT_FUNCTION(cls,idx,name)
#include "UnrealMIDIClasses.h"
#undef AUTOGENERATE_FUNCTION
#undef AUTOGENERATE_NAME
#undef NAMES_ONLY
void RegisterNames()
{
static INT Registered=0;
if(!Registered++)
{
#define NAMES_ONLY
#define AUTOGENERATE_NAME(name) extern UNREALMIDI_API FName UNREALMIDI_##name; UNREALMIDI_##name=FName(TEXT(#name),FNAME_Intrinsic);
#define AUTOGENERATE_FUNCTION(cls,idx,name)
#include "UnrealMIDIClasses.h"
#undef DECLARE_NAME
#undef NAMES_ONLY
}
}



IMPLEMENT_PACKAGE(UnrealMIDI);

void CALLBACK midi_in_handler(HMIDIIN midi_in, UINT msg_type, DWORD user_data, DWORD midi_msg, DWORD param2)
{
UINT midi_in_number;

midiInGetID(midi_in, &midi_in_number);

if (msg_type == MIM_DATA)
{
union
{
DWORD dwData;
BYTE bData[4];
}
u;

u.dwData = midi_msg;


switch (u.bData[0] & 0xF0)
{
case 0x80:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0x90:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xA0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xB0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xC0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xD0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xE0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
}
}
}

void AUnrealMIDI::execGetMidiInput( FFrame& Stack, RESULT_DECL )
{
guard(AUnrealMIDI::execGetMidiInput);

P_GET_INT(port);
P_FINISH;

int midi_in_number = port;

GetStateFrame()->LatentAction = LATENTSAMPLE_POLL_ID;
LatentFloat = 0.f;

HMIDIIN hMidiDevice = NULL;
MMRESULT rv;

rv = midiInOpen(&hMidiDevice, midi_in_number, (DWORD)(void*)midi_in_handler, NULL,CALLBACK_FUNCTION);

midiInStart(hMidiDevice);

hMidiDevice = NULL;

unguardexec;
}

IMPLEMENT_NATIVE(AUnrealMIDI,3524,execGetMidiInput);


void AUnrealMIDI::execMidiPoll( FFrame& Stack, RESULT_DECL )
{

guard(AUnrealMIDI::execMidiPoll);

FString M;

// Grab DeltaTime.
FLOAT DeltaTime = *(FLOAT*)Result;

M = FString::Printf(TEXT("Poll called"));
eventBroadcastMessage(M,0,NAME_None);

// Increase timer used for timeout detection.
LatentFloat += DeltaTime;

// Fire event if data is ready
if (bDone)
{
LatentFloat = 0.f;
eventMidiMessage(Msg,midi_velocity);
GetStateFrame()->LatentAction = 0;
bDone=false;
}

unguardexec;
}

//IMPLEMENT_NATIVE(AUnrealMIDI,LATENTSAMPLE_POLL_ID,execMidiPoll);


IMPLEMENT_CLASS(AUnrealMIDI);

Re: Writing a latent function.

Posted: Thu Sep 21, 2017 10:51 pm
by Hanfling
Mh IMPLEMENT_NATIVE should have been IMPLEMENT_FUNCTION in the first place, in any case in your code you won't need those as you do this with the AUTOGENERATE_FUNCTION stuff with it been declared inside the header.

Re: Writing a latent function.

Posted: Fri Sep 22, 2017 3:58 am
by atrey65789
Hanfling wrote:Mh IMPLEMENT_NATIVE should have been IMPLEMENT_FUNCTION in the first place, in any case in your code you won't need those as you do this with the AUTOGENERATE_FUNCTION stuff with it been declared inside the header.

Alrighty alright. I did the AUTOGENERATE_FUNCTION way as you stated, It compiles and such, but it seems that there's no waiting for input before the function finishes its execution. (I have it looping with a 0.1 second sleep so I can see if it actually pauses until there is input)

Re: Writing a latent function.

Posted: Fri Sep 29, 2017 8:30 am
by atrey65789
Bump, code still compiles.. but it seems that the Poll function (which I assume is supposed to loop during the latent function) is not being called whatsoever.

Re: Writing a latent function.

Posted: Fri Sep 29, 2017 11:08 am
by Hanfling
You should post the uc part, and keep indention. It's horrible to read without. Also project file + download as a zip, so it's ready to build would be helpful.

Also, is bDone ever properly intialized? It should probably also be used a class variable rather than a global one. And you don't even get a first poll function call? If thats not the case you might just very early have your callback setting it to bDone.

Also, you never call RegisterNames(). You can probably just throw this into the actors ctor.

Re: Writing a latent function.

Posted: Sat Sep 30, 2017 1:51 am
by atrey65789
https://drive.google.com/file/d/0B_YWF0 ... sp=sharing

There's a link to the whole source, UC, private header, etc. Should be ready to compile except maybe the path of the LIBs and Headers.


Do I call RegisterNames() in the native functions I am importing?