BEYOND SDK
April 2015.
Intro
The SDK designed to extend functionality ot BEYOND software. The SDK
can not be used without BEYOND. This SDK is not for access to
Pangolin hardware without Pangolin software. If you need only the
hardware, then stop reading this file.
Why we keep access to Pangolin hardware closed. There are multiple
reasons. One of this is design of new models. With some period of
time we rework internals because of new ideas, new methods.
Sometimes the change is radical, and keep a compatibility with old
methods is problematic. With closed interface we have a freedom to
change it any time we need. If the interface become open, then we
partially lose this freedom. We count on your understanding.
What this SDK give me? Everything in BEYOND can be separated on two
groups. The first group is "Image". Image generate points. As
example - frames, text, abstract, clocks, they are Images. The
second group of "Effect". Effect take points generated by Image, and
modify them, producing output points. The Image has only "out", the
effect has "in" and "out". The SDK give you ability to stream
the frames into special Image inside BEYOND.
BEYOND has special "SDK Image". DLL communicate with images. Right
now for communication we use WM_COPYDATA and a couple of simple
messages. At BEYOND side there is special window that receive
messages generated by you (inside DLL) and does corresponding
actions. In addition to that, each SDK Image create own window. All
SDK Image use same class, and personal window name. We use
FindWindow() call for search. After that, functions use
SendMessage().
SDK Image
The image receive the frame from you over WM_COPYDAYA and keep it
inside itself. Everytime as BEYOND calculate the next laser output,
then it comes to SDK Image and take a copy of your frame. This part
of code is pretty short and protected by critical section. By the
nature, SDK Image is a buffer between your data and BEYOND
calculation core. There are a few ways how SDK Image can be used.
SDK Image inside Cue.
Cue is a container of various types of Images. SDK Image work same
as any other Image types. You place it into the Cue and after that
you may use it inside Grid and inside Timeline. All rules for Image
inside the Cue should be applicable to SDK Image too. The only what
you need to understand. Each SDK Window has own communication window
(see CreateWindow()) with a window name, and this name used for
FindWindow(). Do not create duplicates of this Image. If you use it
inside Timeline - keep it inside Cue List. In this case the timeline
will not create a copies of this SDK Image.
SDK Image inside Projection Zone.
This very untypical way of how Image can be used. In fact, we simply
reuse the functionality of SDK Image - a buffer. You have 3
commands. Create Image inside Zone, Send frame to this Image, and
delete the image. Yes, it is simple as that, only 3 commands. When
you create the image inside Zone, you specify zone index, and name
of image (window name). Use some specific name to avoid name
conflicts with other applications. Each Projection zone has list for
such Images. You may add more than one. Other applications can do
that too. BEYOND core unite output from all parts of BEYOND and send
to the hardware.
BEYOND process SDK Images before "Also-To" (see in Projection Zones
dilaog). Same as other frames, the output from SDK images will be
modified by geometric correction, BAM and so on.
SDK Image inside Projector.
The third, and final place where you can stream is Projector. At
first BEYOND calculate Cues, later it calculate Zones, and at final
place it apply Projector settings. You have ability to insert your
stream ignoring all zone settings, projector settings....except
color and color shift. But, you definitely skip window clipping,
vector to point transformation, zone related settings and multiple
projector related settings.
Quick resume.
This model give you ability to insert your stream in very early
stage (as Cue/Image), in the middle at Zone level, and also at the
most final place - Projector phase.
API
General functions
function ldbCreate:integer;
int ldbCreate();
Function initialize a few internal DLL such as events ( CreateEven()
) that you can use later inside WaitForSingleObject() function. Call
ldCreate once, before you will use other functions. If function
successful then result is 1. Otherwise 0.
function ldbDestroy:integer;
int ldbDestroy();
Function release allocated resources. Use it once at the end use.
function ldGetDllVersion:integer;
int ldGetDllVersion();
Function return version of DLL. This is a constant that will be
changed if something in DLL will be changed. Current value is 100.
function ldbBeyondExeStarted:integer;
int ldbBeyondExeStarted();
Function return 1 if BEYOND.EXE started the execution. This is a
detection of very early phase of execution. After that BEYOND
load workspace, settings files, start hardware and so on. The
function confirm that execution started. See below
function ldbBeyondExeReady:integer;
int ldbBeyondExeReady();
The function return 1 of BEYOND SDK ready to be used. The SDK class
created at very end of startup process what guarantee that all
systems started and initialized. If you get result of 1, then go
ahead and use other function. Otherwise, check ldbBeyondExeStarted
and if BEYOND is not started then you need to start
BEYOND.EXE.Otherwise, wait for "ready" state.
function ldbGetBeyondVersion:integer;
int ldbGetBeyondVersion();
Function return build number of BEYOND. If BEYOND is not started,
then result is zero. Build number is one integer value, currently it
is 712. If you need more detailed info, then use
GetFileVersionInfo from Win API.
This function work pretty fast, one SendMessage(), and you may use
it for detection of started and ready for use BEYOND.
function ldbEnableLaserOutput:integer;
int ldbEnableLaserOutput();
Function enable laser output, what is equal to click on Enable Laser
Output button. If the output already enabled the function does
nothing. In case of successful execution result of function is 1,
otherwise 0.
function ldbDisableLaserOutput:integer;
int ldbDisableLaserOutput();
Function disable laser output, what is equal to click on Disable
Laser Output button. If the output already disbled the function does
nothing. In case of successful execution result of function is 1,
otherwise 0.
function ldbBlackout:integer;
int ldbBlackout();
Function does a click on Blackout button of the main toolbar. The
blackout stop all players as well as clear SDK images output.
function ldbGetProjectorCount:integer;
int ldbGetProjectorCount();
Function return number of Projectors in BEYOND. If zero then BEYOND
is not active;
function ldbGetZoneCount:integer;
int ldbGetZoneCount();
Function return number of Projection Zones in BEYOND. If zero then
BEYOND is not active;
function ldGetProjectorEvent(AIndex:integer):integer;
int ldGetProjectorEvent(int AIndex);
DLL create named Event (see CreateEvent() in MSDN) for each
Projector. BEYOND create events with same names too. At the end of
calculation for the projector BEYOND does PulseEvent. From your side
you may use WaitForSingleObject or WaitForMultipleObjects with event
handle. Function return Event handle (THandle). Argument of function
is index of Projector (counting from 0);
Create and delete Image
function ldbCreateZoneImage(AZoneIndex:integer;
AImageName:PChar):integer;
int ldbCreateZoneImage(int AZoneIndex; pchar
AImageName:PChar);
Function create new SDK Image and put it into image-list of
corresponding Projection Zone.
AZoneIndex - Projection Zones index. Counting from 0. If more or
less - operation will be ignored.
If the Image already exists - operation will be ignored. BEYOND use
AImageName, check list of all existing SDK Images inside Projection
zones, and if the Image with such name found, then BEYOND ignore
this call
AImageName- zero terminated, ansii string, PChar, non unicode.
Define a name of window, and this name will be used for
identification of your Image. See ldSendFrameToImage(), the first
parameter is AImageName.
function ldbCreateProjectorImage(AProjIndex:integer;
AImageName:PChar):integer;
int ldbCreateProjectorImage(int AProjIndex; pchar
AImageName);
Function create new SDK Image and put it into image-list of
corresponding Projector.
AProjIndex - Projector index. counting from 0. If more or less -
operation will be ignored.
If the Image already exists - operation will be ignored. BEYOND use
AImageName, check list of all existing SDK Images inside Projector
list, and if the Image with such name found, then BEYOND ignore this
call.
AImageName- zero terminated, ansii string, PChar, non unicode.
Define a name of window, and this name will be used for
identification of your Image. See ldSendFrameToImage(), the first
parameter is AImageName.
function ldbDeleteZoneImage(AImageName:PChar):integer;
int ldbDeleteZoneImage(pchar AImageName);
Function delete SDK Image from Projection Zone image-list. Name of
Image defined in zero terminated AImageName string.
function ldbDeleteProjectorImage(AImageName:PChar):integer;
int ldbDeleteProjectorImage(pchar AImageName);
Function delete SDK Image from Projector image-list. Name of Image
defined in zero terminated AImageName string.
Send frame to BEYOND
function ldbSendFrameToImage(AImageName:PChar;
ACount:integer; AFrame:pointer; AZones:pointer;
ARate:integer):integer;
int ldbSendFrameToImage(pchar AImageName; int ACount;
void*AFrame; void*AZones; int ARate);
Function deliver frame to corresponding SDK Image in BEYOND. This
function work with SDK Image independently on the type.
AImageName - zero terminated string, PChar. This is a name
of Image. You define the name in ldCreate.. functions or when you
create SDK Image in BEYOND Cue.
ACount - number of points in the frame. Maximum 8192 points.
This is max size of internal BEYOND buffer for a frame. Do not
exceed.
AFrame - pointer on array with points. Details below.
AZones - pointed on array with Zone information. Details
below.
ARate - Scan rate or sample rate. Scan rate is a
relative value, percents of defalt sample rate. 100 means 100% of
default projector sample rate. If value of ARate is negative, this
this is sample rate. Sorry, a bit tricky, the idea is use one
variable for two possible options. So, of value is negative then it
is sample rate. If you want 30K, then value should be -30000 (minus
thirty k). Sample rate means - points per second.
Under the hood it work this way. DLL has global variable - a
structure. Inside it a field for header, after zone array, and then
points array. This static struct used for WM_COPYDATA. The function
use critical section - code of function is inside critical section.
Point format:
TSdkImagePoint=packed record
X,Y,Z
:single; // 32bit float point, Coordinate system -32K to +32K
Color
:integer; // RGB in Windows style
RepCount
:byte; // Repeat counter
Focus
:byte; // Beam brush reserved, leave it zero
Status
:byte; // bitmask - attributes
Zero
:byte; // Leave it zero
end;
typedef struct
{
float X,Y,Z; // 32bit float point, Coordinate
system -32K to +32K
int Color; // RGB in Windows style
BYTE RepCount; // Repeat
counter
BYTE Focus; // Beam brush reserved,
leave it zero
BYTE Status; // bitmask -
attributes
BYTE Zero; // Leave it zero
} TSdkImagePoint;
X,Y,Z - 32 bit float point value. Standard "single". The coordinate
system is -32K...+32K. Please fit your data in the range.
Color - 32 bit integer number. Color is 24bit RGB, standard encoding
in windows format. Red bytes comes low (00..FF), Green after that,
Blue the most signification. It exactly as in GDI.
RepCount - usigned byte. Repeat counter of the point. 0 - no
repeats. 1 - one repeat and so on. - usigned byte.
Focus - usigned byte. Now it unused
Status - flags, now eave it zero.
Zero - usigned byte. leave it zero.
You need have array with points and supply pointed on this array
into ldSendFrameToImage.
Zone data.
This part is a bit tricky. BEYOND may have up to 200 projection
zones. Sometimes one frame goes to one zones, but may go to many,
so, we need a way to address many zones. LD2000 SDK use a bit mask,
but we do not fit into 32 bit or even 64 bit. Second detail - order
of zones is also important, because of time-shift functions, where
order of zones used for time shift calculation. The solution is
this. Declare array of 256 unsigned bytes, and it will contain a
sequence of zone indexes. Counting from 1. Value 0 is a termination,
like in strings. As example if you need send frame to zones one, two
and five, then the array must have values 1,2,5,0. You should supply
a pointer on this array as argument of ldSendFrameToImage.
Quick resume.
ldbSendFrameToImage use a few pointers - Image name, point array,
and zone indexes. The scanrate / sample rate is a simple number,
nothing special. ACount is number of points inside points array.
Code inside function use WM_COPYDATA. Seems like work relatively
fast, ~2500 times per second in a dead loop on MacBook Pro under Win
7, x64. The speed may variate, in the past I had much lower results.
Timecode
We have a couple of functions that allow you read and write the
timecode of BEYOND form the place where timecode goes in and out.
BEYOND work with multiple input types. All input device types,
at the end of code flow, call the same function that deliver to
Timeline, Grid or Play list. Two functions defined below allow write
timecode to BEYOND, same as it comes from TC2000, MIDI or ArtNet.
Or, you can read the last value of incoming timecode.
function ldbGetTimeCode:integer;
int ldbGetTimeCode();
BEYOND support multiple types of timecode - from MIDI, from SMPTE,
and so on. When the time code value comes the last value stored in
local variable. Function ldGetTimeCode return value of this
variable. Value measured in milliseconds.
function ldbSetTimeCode(AValueMS:integer):integer;
int ldbSetTimeCode(int AValueMS);
BEYOND support multiple types of timecode - from MIDI, from SMPTE,
and so on. Time information may come from different places but to
same point. Function ldSetTimeCode() supply time information to the
same place as any other time code sources. Value in milliseconds.
Result value must be equal to value you supply as argument.
MIDI
function ldbSetMidiIn(ACmd, AData1, AData2,
ADevIndex:byte):integer;
int ldbSetMidiIn(BYTE ACmd, AData1, AData2, ADevIndex);
Function allow write MIDI message into BEYOND same as it would come
from real MIDI IN device. So, this is simulation of MIDI IN.
ACmd - MIDI command
AData1, AData2 - MIDI command parameters
ADevIndex - device pair index. BEYOND has four MIDI IN/OUT pairs.
ADevIndex parameter define index of the pair. Value in range of 0
and 3.
function ldbSetMidiOut(ACmd, AData1, AData2,
ADevIndex:byte):integer;
int ldbSetMidiOut(BYTE ACmd, AData1, AData2, ADevIndex);
Function allow to send MIDI OUT message. Technically, BEYDON receive
your message and immediately call send to MIDI out command. This
function might help if you need to send MIDI OUT message to device
that already in use by BEYOND
ACmd - MIDI command
AData1, AData2 - MIDI command parameters
ADevIndex - device pair index. BEYOND has four MIDI IN/OUT pairs.
ADevIndex parameter define index of the pair. Value in range of 0
and 3.
Kinect
function ldbSetKinect(AIndex:integer;
AData:pointer):integer;
int ldbSetKinect(int AIndex; void* AData);
Function allow supply the Skeleton data into BEYOND. BEYOND can use
two types of data from Kinect controller - skeleton node coordinates
and the raster image for tracing. This function allow supply
skeleton information same as it would come from the controller.
AIndex - index of skeleton. BEYOND support 2. Index of skeleton must
be 0 or 1
AData - pointer on array of 20 skeleton node coordinates structures.
Array must contain 20 elements (nodes). The node definition
TKPoint=packed record
X,Y,Z:single;
Active:boolean;
end;
typedef struct
{
float X,Y,Z;
BYTE Active; // value must be 0 or 1. It will be
translated into Delphi boolean
} TKPoint;
X,Y,Z are coorinates of the node. Value in meters, 1 means one
meter.
Active - 1 if the node is active, and 0 if node inactive.
DMX
function ldbSetDmx(AIndex:integer; AData:pointer):integer;
int ldbSetDmx(int AIndex; void* AData);
Function supply one DMX array of 512 values into input buffer of
BEYOND. This is emulation of DMX IN.
AIndex - index of the input, value must be in range of 0 and 3.
AData - pointer on array of 512 bytes. Do not supply less than 512,
it will cause an incorrect data in the incoming buffer.
Channels
function ldbSetChannels(AChannels:pointer;
ACount:integer):integer;
int ldbSetChannels(void* AChannels; int ACount);
Function allow control up to channels of BEYOND.
AChannels - pointer on array of Single.
ACount - number of channels (number of elements in the array), must
be between 1 and 255.
The Channel is array of normalized variables, value must be in range
of 0 and 1. Maximum channel number is 255. AData point on the
beginning of array with channel values. If you do not want to supply
value of some channel, the set the value of corresponding item of
array to -1.
Timeline editor.
There are a few calls that allow to communicate with the timeline
editor of BEYOND.
function ldbTimelinePlay:integer;
int ldbTimelinePlay();
Start playback of current show in the timeline editor. If timeline
already in play mode then no action
function ldbTimelineStop:integer;
int ldbTimelineStop();
Stop playback of current show in the timeline editor. If timeline
already in stop/pause mode then no action
function ldbTimelineGetPlaying:integer;
int ldbTimelineGetPlaying();
Function return 1 of timeline is on play mode. Otherwise 0.
function ldbTimelineSetPos(ATime:integer):integer;
int ldbTimelineSetPos(int ATime);
Set the timeline position.
ATime - time in milliseconds.
function ldbTimelineGetPos:integer;
int ldbTimelineGetPos();
Function return current timeline position. Value in milliseconds.
Function can be used in playback as well as in edit/pause mode.
function ldbTimelineGetDuration:integer;
int ldbTimelineGetDuration();
Function return duration of timeline/show. Value in milliseconds.
function ldbTimelineSetOnline(AEnabled:integer):integer;
int ldbTimelineSetOnline(int AEnabled);
Function control timeline editor state. AEnabled equal to 1 means
enable output (online mode). Value 0 means offline mode (no laser
output)
function ldbTimelineGetOnline:integer;
int ldbTimelineGetOnline();
Function return current state of timeline editor. Result value 1
means that laser output enabled.