Script Editor

When the script editor window is open, the main application window is temporarily disabled. However, any child windows (custom control panels, etc.) that are already open can still be modified. For example, to debug a script that watches for parameter changed events, first open or create a control panel with a control attached to the watched parameter. Then, with that control panel open, open the script editor window and set up the breakpoints. The controls on the open panel can still be changed, allowing the breakpoint to be hit and the script to be debugged.

Debugging Toolbar

Toggle Breakpoint: Adds or deletes a breakpoint on the line where the cursor is currently located

Toggle Enabled: Enables or disables a breakpoint on the line where the cursor is currently located

Clear Breakpoints: Removes all breakpoints

Run:  Executes the scripts. The script will continue to run and only stops when ‘Stop’ is pressed

Pause:  Pauses the execution. Yellow arrow will show the next line to execute

Continue:  Continues running the script after it has been paused either manually or by a breakpoint

Step:  When paused the script executes only the next line and pauses again. This can also be used to start the script and have the script pause on the first line it executes

Output window

Inside the Lua script the ‘print’ command can be used to send output to the output window. The output window only stores the most recent 200 lines of output. Once 200 lines has been exceeded the older lines will start to be deleted.

 

The fly-out button on the far right of the editor window , expands a selection of buttons that provide the function stubs that will provide a framework for executing all areas of the scripting language API.  These function stubs serve both as skeleton code that can easily be replaced or added-to, as well as a tutorial in the use of Audio Architect's Lua API.  NOTE: The Logic Source Script displays only the Add Logic Stub.

The lower section of the editor window is for output from the script. Anything written using Lua’s default ‘print’ function will show up in this pane. For example, if I were tracking the number of times a Timer’s tick callback has been called (see implementation details below), I could write the following:

count = 0

function OnTimer()

  count = count + 1

  print(“Tick count :=” ..  count )

end

The tick count would show up in the bottom pane of the editor window each time the tick callback method is called.

 

Add Logic Stub

Add Timer Stub

Add Event Log Stub

Add Watched Parameter Stub

Add Internal Parameter Stub

Add TCP/IP Server Stub

Add TCP/IP Client Stub

Add UDP Broadcaster Stub

Add UDP Receiver Stub

Byte Arrays

 

Load: Opens a file from disk with the .lua extension.

Save: Saves the current script.

Apply: Performs a preliminary parsing of the script, indicates errors if any, closes the Script Editor and applies the script.

Cancel: Closes the Script Editor without applying any changes.

 

A Comprehensive List of API Functions

A HiQnet Logic/System script will be written using the Audio Architect script editor for the purpose of automatically accomplishing tasks or creating other control mechanisms by associating the script with controls and features within the venue file. The Logic/System Object component allows you to execute scripts to perform operations on values, positions, strings, colors and other metadata, and timing of controls in the venue.

Logic Object input/output

For script-based logic objects, GetInputState and SetOutputState methods will be registered with the Lua script when the object is created.  Thus, these functions will have no need for the object-function syntax and can simply be called directly from the script.  For example, to swap inputs from input1 to output2 and from output2 to input1, the script simply reads:

SetOutputState(0, GetInputState(1))

SetOutputState(1, GetInputState(0))

Here, the numeric parameters to each function represent the zero-based index of the input-to or output-from the logic object executing the Lua script.

For the Logic Script object, the Logic Stub shows the implementation of a self-styled logic gate as follows.  Screen shots are used instead of entering the text into this document so that syntax coloring is preserved.

 

Unlike the Logic Script object, the System Script object can contain callback functions that interact with other callback methods.  It has been learned through experience that, while the SetInptuState and SetOutputState methods demonstrated above may be called throughout the Lua script for the System Object, it is Best Practice to implement a Logic Tick callback function and consolidate all calls to the Get Input and Set Output methods in this callback.  The basic System Script Logic Tick callback implementation is stubbed in as shown below.

The Timer Callback

Custom timers may be created that execute a callback either as a single-call method or as a method that will repeat each time the timer "ticks."

 

 

Event Logging

Event Logging, by itself, is a single-line API call. However, in the function stub, shown here is a simple function that will place a string in the event log with a common logging option: an Info type message with Low priority.

 

 

Watched Parameters

State Variables are handled by the API in two different ways.  SVs may be added to the System Script object and then be modified internally, or they may be state variables that are monitored by the System Script from an external source.  Adding parameters for monitoring may be done either through an Advanced Parameter Search dialog or by simply dragging the state variable to the System Script object on the wiring surface from the Venue Explorer.

 

 

In this instance the APS dialog was used to add the Logic Source state variables to the watch-list for the System Script object. These state variables now report any value changes to the script via a callback method and they may have their values set by the script.

 

 

 

Internal Parameters

The State Variables that are created internally within the System Script Object are accessible in the same way as the Watched Parameters. The main difference between the two is that the Watched Parameters are in a list that is zero-based and are accessed by the index, where the Internal Parameters are accessed by their ID, which, due to already-existing state variables that are not exposed to the end user, begin at index 1000 and go up from there as they are created. The state variables may be given custom names and IDs (greater than 1000).

 

 

The SV type is limited to be either a long ( interger), float (decimal) or string.

 

Ethernet Communication API

Retrieving IP Addresses

For the function stubs provided in the Syntax Editor, it is assumed that the user knows and can hard-code the IP address of the UDP and TCP/IP sockets.  However, while it is not demonstrated in the function stubs, there is a programmatic way to retrieve the local IP addresses from the TCP and UDP Lua API objects.

 

 

This uses the TCP creation object, but it is perfectly acceptable to also use UDP:GetDefaultIP(

TCP/IP Clients

The TCP Client APi object creates a socket connection to a server (either scripted or otherwise, based on an IPAddress and a port number). Once connected, messages may be passed back and forth from client to server. Callback methods are provided for connections and connection failures, and for messages received.  The big caveat about using TCP objects, either client or server, is that the buffer size must be specified at which the callback method will receive data.  The data stream will remain open until the specified sized data chunk has been received then it will all be handed to the callback method at once.

 

 

 

 

 

 

TCP/IP Servers

The TCP Server API object behaves much the same way as the client, except the IP and port provided are local. The TCP Server also has a notification callback that is fired every time a client disconnects.

 

 

 

 

UDP Broadcasters

UDP messages are much easier to send than TCP/IP messages. The UDP Broadcaster API object exists to simply send a UDP message from a local IP at a specified port.

 

 

UDP Receivers

UDP Receivers watch for UDP messages to be sent from a specified IPAddress at a specified port.

 

Byte Arrays

Lua strings, within the Lua script, are essentially byte arrays. However, passing a Lua string to Audio Architect converts the string to Unicode/UTF-8. In the networking objects, data values to be sent or received may experience data loss due to this conversion processs. To fix this problem, we have introduced a ByteArray interface in the Lua API which may be preferable to the default String messaging.

A new instance of the ByteArray will be created by calling MakeByteArray(size), where ‘size’ is the integer value representing the length of the byte array to be created. ByteArray objects contain Get and Set methods which are 1-based to align with the default indexing scheme of the Lua language.

local foo = MakeByteArray(32)

value = foo:Get( index_to_get )

foo:Set( index_to_set, value )

To prevent crashing, if the user attempts to set a value at a non-existent index (i.e., a negative index or an index beyond the length of the byte array), the byte array will remain unchanged. Similarly, if the user attempts to get a value at an invalid index, the code returns 255 instead of crashing or throwing an exception.

The ByteArray class also contains a Resize method that will change the size of the internal byte array, but does not keep any data currently stored in the array. Any values already set will be lost.

foo:Resize(8)


There is also a Length method that reports the current number of bytes in the ByteArray. This is especially useful in the UDP Receiver, where the size of the data received may be unknown.

size = foo:Length()


The Message (string)-based methods for the TCP/IP and UDP communications objects have been supplemented with nearly identical methods for ByteArray-based networking. The only difference is that the ‘Message’ in the function name has been replaced with ‘ByteArray’ (e.g., udp:RegisterMessageCallback( callback ) becomes udp:RegisterByteArrayCallback( callback ); tcpClient:SendMessage( string_msg ) becomes tcpClient:SendByteArray( bytearray_object ); etc.). The full list of ByteArray functions is shown in the Comprehensive List, below.

It’s important to note that only one of the Message or ByteArray callback is active at a time. If you have registered a Message callback function and then, later, register a ByteArray callback function, any new data received will be passed to the ByteArray callback instead of the Messge callback, unless or until RegisterMessageCallback is executed again.

 

Comprehensive List of API Functions

In many of the functions that follow the Lua names are set by the API and cannot be modified. Where parameters can be given different names by the user, the function or variable will appear in italicized text.

Logic Interaction

GetInputState( int index )  – returns a boolean value based on the state of the input at the specified index to the logic object.

SetOutputState( int index, bool state) – sets the output from the logic object at the specified index to the specified state.  No return value.

System Script Logic Callbacks

RegisterLogicTickCallback( LuaFunction function ) – the specified Lua function takes no parameters and has no return value. The Register Callback method also has no return value.

Event Logging

The API specifies a Log object whose functions are accessed via the "Log:" syntax.

Log:Priority( string priority ) -- The priority string passed to the function is one of "Low", "Medium" and "High". The return value is a c# enumerated value representing the priority with which the message will be sent to the event log.

Log:MessageType( string type ) –  The type string passed to the function is one of "Error", "Info", and "Warning".  The return value is a c# enumerated value representing the type of message sent to the event log.

Log:Log( string message, enumeration priority, enumeration log ) – The priority and log enumerated values are retrieved from Log:Priority and Log:MessageType as shown above. The message string is what is sent to the event log. No return value.

Timers

Timer creation is handled through the Timer API object, accessed via the "Timer:" syntax. only one function exists from this interface

Timer:CreateTimer(int miliseconds, bool repeating, LuaFunction callback) – miliseconds represents the frequency with which the timer will tick. repeating specifies whether the timer will continue to run repeatedly after the tick event fires, and the callback function is a Lua method that takes no parameters and has no return value.  CreateTimer returns a C# LuaTimer object with the following functions.

timer:Start().  Takes no parameters and returns nothing. Starts the timer for the number of milliseconds specified in the Timer:CreateTimer function.

timer:Stop().  Stops the timer from continuing to run.  Once stopped, the timer may be restarted.

timer:Destroy() – following a call to timer:Destroy, the C# timer is disposed and the timer is no longer valid within Lua. Using it again will cause a script error.

Parameters

Both the Watched Parameters and the Internal Parameters API objects use C# Parameter objects that are returned to the Lua script as variables passed to LuaFunctions. These Parameter objects have the following methods:

parameter:SetValue( [int|float] value) – the parameter is set to the specified value.

parameter:GetValue() – returns the value of the parameter. (All Lua values are doubles, so distinguishing between int and double here is meaningless).

parameter:Name() – returns a string value containing the name of the parameter within Audio Architect.

Watched Parameters

Functions that interact with the script object's Watched Parameters are accessed or registered through the WatchedParameters API object.

WatchedParameters:RegisterValueChangedCallback( LuaFunction callback ) – Registers a callback which will be called any time the value of a watched parameter changes. The callback function takes two parameters and has the signature " function functionName (int index, Parameter parameter)".The index is the 0-based index of the parameter within the System Script object's Watched Parameter list. The Lua API Parameter object has its own set of functions that will be listed momentarily.

WatchedParameters also contains two functions which iterate over all of the Watched Parameters:

WatchedParameters:ForAllParameters( LuaFunction function ) – the LuaFunction provided to this method has the signature: "functionfunctionName ( Parameter parameter )".  This function will be called once for each parameter in the Watched Parameters list.

WatchedParameters:ForAllParametersByIndex( LuaFunction function ) – the LuaFunction provided to this method has the signature "function functionName( int index, Parameter parameter)"  This function will be called for each parameter in the Watched Parameters list. The index of the specified parameter is the first argument passed to the LuaFunction.

WatchedParameters:AtIndex( int index ) – Returns the API Parameter object of the parameter at the specified index within the Watched Parameters list. If an invalid index is provided, the return value will be nil.

Internal Parameters

The API InternalParemeters object contains exactly the same set of functions as the WatchedParameters API object.  The difference between the two is that, instead of retrieving the 0-based index of the Parameter like we do with WatchedParameters, for InternalParameters we use the ID of the State Variable within the System Script object.  By default these begin at 1000 and go up from there, but they can be customized to any value greater or equal to 1000 in the range of a C# ushort.

ByteArray

Note: Unlike most of the other objects provided in the interface to Audio Architect, the ByteArray object uses 1-based indexing instead of 0-based indexing to better match the Lua string indexing default.

MakeByteArray(int size) – the entry point for creation of a new ByteArray object. The ‘size’ parameter is the length of the array.

ByteArray:Resize( int newSize ) – Resizes the byte array. This method will erase any data currently in the array

ByteArray:Length() – returns the length of the ByteArray

ByteArray:Get( int index_to_get ) – returns the byte-value at the requested (1-based) index. If the index is invalid (i.e., less than 1 or greater than the length of the array), 255 is returned.

ByteArray:Set( int index_to_set, byte value) – sets the value of the ByteArray at the specified index to be the value specified. If the index is invalid, nothing in the ByteArray is changed.

 

UDP Broadcaster/Receiver creation

The UDP API object contains two methods for creating Broadcaster and Receiver objects. It also contains a third function that will programmatically retrieve the possible IPAddresses from which the local Contrio Server can send messages.

UDP:GetDefaultIP() – returns a LuaTable that acts like a 0-based array (i.e., the table keys begin at 0 and increment by one), containing strings with the set of local IP address(es).

UDP:MakeBroadcaster(string address, int port) – returns an API Broadcaster object configured to send messages at the specified IPAddress from the specified port.

UDP:MakeReceiver(string address, int port) – returns an API Receiver object configured to listen for UDP messages sent from the specified IPAddress at the specified port.

UDP Broadcaster

broadcaster:SendMessage( string message ) – Lua strings are essentially byte arrays. To see how the message can be populated with non-character data, see the Broadcaster stub demo.

broadcaster:SendByteArray( ByteArray message ) – Sends the message using the ByteArray class instead of a Lua string. This may be necessary to prevent data loss when Lua strings are converted to Unicode/UTF-8 and back.

UDP Receiver

receiver:RegisterMessageCallback( LuaFunction messageCallback ) – Has no return value.  The messageCallback function takes a string as a parameter.  For info on decomposing the string into byte data, see the UDP Receiver stub.

receiver:RegisterByteArrayCallback( LuaFunction byteArrayCallback ) – Has no return value.  The byteArrayCallback function takes a ByteArray object as a parameter.  Using this function instead of the MessageCallback may be necessary to prevent data loss when Lua strings are converted to Unicode/UTF-8 and back.

receiverStartReceiving() – No return value. Begins listening to UDP messages. Until this is called, the registered callback function will not be called.

receiverStopReceiving() – No return value. Stops listening to UDP messages. After this is called, the registered callback function will not be called.

UDP Multicast

The broadcaster can be initialized to send to an IP address in the range of 224.X.X.X to 239.X.X.X.  These addresses are specifically designated as multicast addresses – multiple receivers can pick up the messages broadcast to these IP addresses.  In this case, both the broadcaster and the receiver(s) are initialized to the same multicast IP address.

There is needed a mechanism for the script to know if it is being developed on a local Audio Architect machine that was simulating the logic or if the script is actually being run on a Contrio Server.  The difficulty here comes in specifying exact IP addresses – the local device machine is going to have a different IP address than the server, and if you accept and simulate a script with the server’s IP, both TCP and UDP communication break down – the simulation can’t set up a TCP server or a UDP receiver if they’re configured to an IP other than the address of the host machine.

To solve this problem, an IsServer method to the Lua script has been created.

IsServer usage is as follows:

 

ipAddress = “10.34.5.85” --the development PC ‘s IP address

 

if IsServer() then

 

  ipAddress = “10.34.5.120”  -- the IP address of the Contrio Server receiving the loaded script.

 

end

TCP/IP Client/Server creation

The TCP API object contains two methods for creating Server and Client objects. It also contains a third function that will programmatically retrieve the possible IPAddresses from which the local Contrio Server can set up a scripted Server.

TCP:GetDefaultIP() – does the same thing as UDP:GetDefaultIP.

TCP:MakeServer( string address, int port, int numClients ) – returns a TCP/IP Server object configured to set up a socket connection at the specified IPAddress from the specified port. The numclients parameter specifies the maximum number of socket connections that the server will support.

TCP:MakeClient( string address, int port ) – returns a TCP/IP Client object configured to establish a connection with an already existing socket at the specified IPAddress, at the specified port.

TCP/IP Client

client:SetBufferSize( int size ) – Both the client and the server will read chunks of data that are then passed to their respective message received callbacks.  SetBufferSize specifies the size of the buffer to be read. Until the specified number of bytes are received, the message received callback function will not be called.

client:RegisterMessageCallback( LuaFunction messageReceivedCallback ) – Specifies the callback method through which received data is passed. The LuaFunction takes a single parameter as a string. For details on processing the string as a set of bytes, see the sample stub function.

client:RegisterByteArrayCallback( LuaFunction byteArrayReceivedCallback ) – Specifies the callback method through which received data is passed. The byteArrayReceivedCallback function takes a single parameter as a ByteArray object. It may be necessary to use this function instead of the messageCallback to prevent data loss that occurs when Lua strings are converted to Unicode/UTF-8 and back as they’re passed to/from Audio Architect.

client:Connect( LuaFunction connected, LuaFunction connectionFailed ) – Specifies callback methods through which the script is notified of success or failure in the attempt to connect to the server. No return value.  Until Connect is called on the client, no messages will be received and it will not be possible to send messages back to the server.  The connected callback takes no parameters and has no return value.  The connectionFailed callback has no return value, but takes a string parameter as a message explaining the reason the connection failed.

client:Disconnect() – No return value. Closes the socket connection to the server. Once this is called no more messages will be passed to the messageReceivedCallback.

client:SendMessage( string message ) – May only be called between the time Connect is called and the time Disconnect is called.  As Lua strings are essentially byte arrays, the string may actually contain any type of data.  See the function stub for examples of padding the buffer to the expected size and for examples of sending data other than string data.

client:SendByteArray( ByteArray bArray ) – May only be called between the time Connect is called and the time Disconnect is called. It may be necessary to use this function instead of the messageCallback to prevent data loss that occurs when Lua strings are converted to Unicode/UTF-8 and back as they’re passed to/from Audio Architect.

TCP/IP Server

server:SetBufferSize( int size ) – See the TCP Client SetBufferSize for details

server:RegisterMessageReceivedCallback( LuaFunction messageReceivedCallback )  See client:RegisterMessageReceivedCallback for details.

server:RegisterByteArrayReceivedCallback( LuaFunction byteArrayReceivedCallback )  See client:RegisterByteArrayReceivedCallback for details.

server:StartServer( LuaFunction clientConnection, LuaFunction connectionLost, LuaFunction connectionFailure ) – three callback functions are used to start the server. The first is notification that a client has connected to the server and receives as parameters an int representing the index of the new client and a string representing the IPAddress of the client.  The second callback is called when a client connection to the server is closed or is lost and takes two parameters; an int containing the index of the lost client and a string containing a message containing any error information about why the connection was lost.  Finally, the connectionFailure callback takes a single string as a parameter and is fired in the event that something has gone wrong with the server itself. The string contains any relevant error information.

server:CloseSocket( int index ) – closes the socket connection to the client at the specified index.

server:CloseAllSockets() – closes all socket connections to all clients.

server:SendMessageToClient( int index, string message )  – sends a string message to a single client as specified by the index parameter.  As Lua strings are essentially byte data, a string may be either an actual string or a representation of a different data type. See the function stub for an example of padding the data out to a specified buffer size and for an example of sending non-string data to the client.

server:SendByteArrayToClient( int index, ByteArray byte_array )  – sends a byte array to a single client as specified by the index parameter. It may be necessary to use this function instead of the messageCallback to prevent data loss that occurs when Lua strings are converted to Unicode/UTF-8 and back as they’re passed to/from Audio Architect.

server:SendMessageToAllClients( string message ) – the string buffer passed to this function will be sent to all connected clients.

server:SendByteArrayToAllClients( ByteArray byte_array ) – the ByteArray buffer passed to this function will be sent to all connected clients.

Implementation details

In almost all cases, the behind-the-scenes implementation for these API calls represents a c# object that is assigned to the Lua script. The entirety of the Event Log class, called the LuaAPIEventLogger class in the code, is as follows:

              private class LuaAPIEventLogger

              {

                     private Dictionary<string, EventLog.logPriority> priorityMap = new Dictionary<string, EventLog.logPriority>()

                     {

                           {"High", EventLog.logPriority.High },

                           {"Medium", EventLog.logPriority.Medium },

                           {"Low", EventLog.logPriority.Low }

                     };

 

                     private Dictionary<string, EventLog.logType> logTypeMap = new Dictionary<string, EventLog.logType>()

                     {

                           {"Error", EventLog.logType.Error},

                           {"Info", EventLog.logType.Info},

                           {"Warning", EventLog.logType.Warning}

                     };

 

                     public EventLog.logPriority Priority(string priority)

                     {

                           if (priorityMap.ContainsKey(priority))

                                  return priorityMap[priority];

                           return EventLog.logPriority.Low; //default

                     }

 

                     public EventLog.logType MessageType(string messageType)

                     {

                           if (logTypeMap.ContainsKey(messageType))

                                  return logTypeMap[messageType];

                           return EventLog.logType.Info;

                     }

 

                     /// <summary>

                     /// Pass-through to the static Trace method of the HProLogger

                     /// </summary>

                     public void Log(string message, EventLog.logPriority priority, EventLog.logType messageType)

                     {

                            EventLog.HProLog.Log(new EventLog.LogEntry()

                           {

                                  Category = EventLog.logCategory.Application,

                                  DateTime = DateTime.Now,

                                  EventName = "Scripting Message",

                                  EventDetail = message,

                                  Priority = priority,

                                  Type = messageType

                           });

                     }

              }

In the Lua initialization, the LuaAPIEventLogger is registered with the Lua script.

                     _lua["Log"] = new LuaAPIEventLogger();

Thus, in the Lua script, what appears to be the “Log” namespace actually identifies the LuaAPIEventLogger, through which the API for event logging is handled.

A similar pattern is used for the rest of the API.