VRPN Tutorial : Sending data from OpenViBE to an external application

  • NB: Document cursorily updated for OpenViBE 0.18 (19.Nov.2014).
The VRPN interfaces and the example program are part of the OpenViBE source tree since 0.18. See under applications/examples/openvibe-to-vrpn/ for the most up-to-date versions. The visual studio subproject is openvibe-examples-openvibe-to-vrpn/. The source code in this documentation is for illustrative purposes only and may be outdated.

This tutorial deals with the VRPN connections. It describes how to send data from OpenViBE to an external application (receiving data from an application by OpenViBE is described in this tutorial). Both analog and button connections are explained.

This part describes the usage of VRPN Server boxes. It will show you how to send data from OpenViBE to an external application. As an example application we will write a program which outputs the data it receives.

The OpenViBE scenario

As a first step we will create a scenario which generates some data. This simple scenario will generate a new signal sample every second. Additionally a keyboard stimulator is present to generate stimulations. Samples and stimulations are sent by VRPN to the client application.

Settings of the Keyboard stimulator and Signal display remain unchanged. Configure other boxes as follows:

Sinus oscillator

We only want very few samples to be sent out. These settings will produce 1 sample on 1 channel every 1 second.

Button VRPN Server

The Button VRPN Server box can send stimulations to external programs. These stimulations are presented as buttons. Each Button Server can contain several button objects.

Each VRPN server has to have a name by which it is identified. This name has to be unique.

For our sample scenario we will use two buttons (you have to right click the box and select modify inputs -> new). The first one (A) will be simply captured by the C++ application, the other (Z) will close the application.

By default the keyboard stimulator sends out the stimulation Label_01 when the A key is pressed and Label_00 when it is released. The Z key will produce stimulations Label_02 and Label_00. We will bind these stimulations for VRPN button 1 and 2.

Analog VRPN Server

The Analog VRPN Server can send the data of any stream matrix to a VRPN client in the program. Each Analog server can have several inputs. We are only going to use one in this tutorial.

The C++ client program

A basic C++ program will be used to capture the output from OpenViBE and print out the received values. If it receives input from the button 2 it will exit.

Note: The most recent version of the following code is included in the OpenViBE source code distribution.

#include <iostream>

#include <vrpn_Button.h>
#include <vrpn_Analog.h>

void VRPN_CALLBACK vrpn_button_callback(void* user_data, vrpn_BUTTONCB button)
{
    std::cout << "Button ID : " << button.button << " / Button State : " << button.state << std::endl;

    if (button.button == 1)
    {
        *(bool*)user_data = false;
    }
}

void VRPN_CALLBACK vrpn_analog_callback(void* user_data, vrpn_ANALOGCB analog)
{
    for (int i = 0; i < analog.num_channel; i++)
    {
        std::cout << "Analog Channel : " << i << " / Analog Value : " << analog.channel[i] << std::endl;
    }
}

int main(int argc, char** argv)
{
    /* flag used to stop the program execution */
    bool running = true;

    /* VRPN Button object */
    vrpn_Button_Remote* VRPNButton;

    /* Binding of the VRPN Button to a callback */
    VRPNButton = new vrpn_Button_Remote( "openvibe_vrpn_button@localhost" );
    VRPNButton->register_change_handler( &running, vrpn_button_callback );

    /* VRPN Analog object */
    vrpn_Analog_Remote* VRPNAnalog;

    /* Binding of the VRPN Analog to a callback */
    VRPNAnalog = new vrpn_Analog_Remote( "openvibe_vrpn_analog@localhost" );
    VRPNAnalog->register_change_handler( NULL, vrpn_analog_callback );

    /* The main loop of the program, each VRPN object must be called in order to process data */
    while (running)
    {
        VRPNButton->mainloop();
    }

    return 0;
}

Compilation and linking

In order to compile this program you will have to add the vrpn headers to the include directory. The best way is to use VRPN directly from the OpenViBE dependencies. The path is dependencies/vrpn/include. On Windows, you will get the dependencies/ directory by running scripts/win32-install-dependencies.exe of the OpenViBE source distribution.

You will have to link the executable against the vrpn library as well. On Linux you need to add -lvrpn and -lvrpnserver, on Windows vrpn.a is sufficient.

Detailed description of the client program code

Writing a VRPN client is pretty simple. Each of the servers has to have a client object associated.

Includes

To receive input from VRPN the code needs to include VRPN headers: vrpn_Button.h for buttons and vrpn_Analog.h for analog input.

Object creation

The line to create a VRPN client object is the following one:

    VRPNButton = new vrpn_Button_Remote( "openvibe_vrpn_button@localhost" );

In this case we are creating a Button client handler. You can see on the line 40 that creating an Analog handler is done the same way. The important thing is to pass the right address to the constructor. Here we use “openvibe_vrpn_button@localhost” the part before @ is the name of the peripheral specified inside the OpenViBE box. The part after the @ is the hostname of the computer where the server is running. If you run OpenViBE and the client application on the same machine then use localhost as the hostname.

Binding the object to a handler

Once the client object is created we have to bind it to a handler callback. Every time the client receives an input the handler callback will be executed. The callback’s signature for a Button client is

void VRPN_CALLBACK vrpn_button_callback(void* user_data, vrpn_BUTTONCB button)

You can put anything you want to the user_data pointer, usually this parameter can be used for an object handler. In this example we will put a pointer to a boolean variable which determines whether the program is running or should exit.

The vrpn_BUTTONCB is a vrpn structure containing two members

  • button : The id of the button which changed state. This index starts at 0, so the first button in the OpenViBE box will be indexed as 0 in the client application and so on.
  • state : The state of the button. This is either 1 (pressed) or 0 (released)

The code of the callback in this example is as follows:

{
    std::cout << "Button ID : " << button.button << " / Button State : " << button.state << std::endl;

    if (button.button == 1)
    {
        *(bool*)user_data = false;
    }
}

This callback will simply output the content of the button structure each time a key (A or Z) is pressed in OpenViBE keyboard handler box. If the button 1 (Z) is pressed the boolean variable passed as user_data will be set to false.

To bind the callback to a client object use the following code:

    VRPNButton->register_change_handler( &running, vrpn_button_callback );

Mainloop

Finally, the VRPN object has to be polled permanently to check for new incoming data. This should be done in your program’s main loop.

In our case we will simply create and endless while loop:

    while (running)
    {
        VRPNButton->mainloop();
    }

Particularity of the VRPN Analog callback

The Analog callback uses a different structure and its handling is different from the Buttons.

The function signature is almost identical, the only thing which changes is the structure of the :

void VRPN_CALLBACK vrpn_button_callback(void* user_data, vrpn_ANALOGCB analog)

The structure itself however contains different members:

  • num_channel : The number of channels received by the VRPN client. Important! This value is NOT necessarily the number of inputs in the VRPN Analog Server box in OpenViBE.
  • channel[] : An array containing all received values.

OpenViBE sends streamed matrix data by chunks containing several values. Also, each streamed matrix can have several channels. The VRPN Analog Server box can have several inputs. All the data has to be sent to the VRPN client at the same time so it can be processed in one run. OpenViBE will flatten all of the data into a one-dimensional array and send it over VRPN.

This entry was posted in Connecting OpenViBE and other applications and tagged , , . Bookmark the permalink.