Integrations
Engine library and source builds
Beta.
The neuromore Engine is the C++ processing core of neuromore Studio. You can also build it as a library and run classifiers inside your own application, without the Studio user interface. This page is for developers who want to do that, or who want to add a device driver in their own source build.
Most people do not need this page. To get data out of Studio, use OSC or the other interfaces in the integrations overview.
What the library does and does not do
Section titled “What the library does and does not do”Your application is the host. It reads the sensor, pushes samples into the Engine, calls the update function, and reads the feedback values.
- The API is a C interface in
src/Engine/neuromoreEngine.h, in the namespaceneuromoreEngine. - The library doesn’t connect to hardware through its API. Your application reads the sensor. The drivers that scan for devices are part of Studio, and
AddDevicecan only create the types listed under Device types. - It opens no OSC, WebSocket or Studio Server port. Those servers are part of Studio’s Qt layer, and the library does not link Qt.
- It does not sign in or contact the neuromore cloud. You pass the classifier in as a JSON string. For data upload and cloud parameters, it creates JSON for your application to send.
- It has no pause function and no Init Time step. For how a session behaves in Studio, see Running a session.
In 1.7.3: The function signatures are the same. Some behaviour differs. See Feeding samples and Reading feedback.
Lifecycle
Section titled “Lifecycle”Call the functions in this order:
-
Init(). Call this first. -
SetCallback(...). See Callbacks. -
SetPowerLineFrequencyType(POWERLINEFREQ_50HZ)orPOWERLINEFREQ_60HZ. -
LoadClassifier(json, uuid, revision). The UUID can be one you choose. Optionally, callLoadStateMachine(json, uuid, revision)as well. To get the classifier JSON, right-click the classifier file in Studio’s Back-End File System window and choose Download. You can also open the file in Studio, then right-click it and choose Copy JSON to clipboard.In 1.7.3: Download is named Save To HDD, and it also needs the file to be open in Studio.
-
SetBufferLength(seconds), if you need it. It needs a loaded classifier and applies to that classifier. The code default is 10 seconds. The comment in the header says 5. -
AddDevice(type)for each device. It returns the device index, or -1 on failure. -
Check that
IsDeviceRequiredByClassifier(type)is true only for devices you added, then checkIsReady(). -
Start(). Then push the first samples before your firstUpdate(). See Feeding samples. -
In your real-time loop, push samples, call
Update(), and read feedback.Update()takes no time argument. It measures the time since the last call, so call it often. -
Stop(), when your application ends the session or whenOnStopfires. In threaded mode, see Threaded mode. -
Reset(), before you run the classifier again from a clean state. -
Shutdown(), after the last Engine call.
Some of these calls do less than their header comments suggest. Follow this table:
| Function | What the code does |
|---|---|
IsReady |
Checks only that no session is running and a classifier is loaded. It does not check devices, so do step 7 yourself. |
IsRunning |
Reports whether a session is running. |
AddDevice, RemoveDevice, LoadClassifier, SetBufferLength, SetPowerLineFrequencyType |
Refused during a session. |
SetSessionLength |
Stores the length. It does not stop the session, so stop it yourself. |
Start |
Resets only output and file writer nodes in the classifier, not the whole classifier. |
Stop |
Stops the session and the state machine. It does not reset the classifier or devices. |
Reset |
Resets the session, classifier, state machine and device data. Devices stay added. |
ConnectDevice |
Not needed after AddDevice, and returns FALSE at that point, because these devices start out connected. It is only for use after a disconnect. The disconnect function is spelled DisonnectDevice. |
Threaded mode
Section titled “Threaded mode”StartThreaded() starts a session and runs Update() on its own thread, at up to 60 updates per second. End it with StopThreaded(), and don’t call Update() yourself. GetPerformanceStatistics only gets its values from this thread.
In threaded mode, callbacks run on the Engine’s update thread. Don’t call Stop() or StopThreaded() from OnStop. Set a flag in the callback, then call StopThreaded() from another thread. Don’t call Stop() first, because StopThreaded() then returns FALSE and the update thread keeps running.
Prefer Start() with your own loop. Threaded mode begins updating at once, so you can’t be sure that samples arrive before the first update (see below).
Feeding samples
Section titled “Feeding samples”AddInputSample(deviceIndex, inputIndex, value) pushes one value into one input of a device.
- It returns FALSE unless a session is running, so samples sent before
Start()are dropped, not queued. - It takes a value only, with no timestamp. Send at the input’s rate, listed in the table below, and at a steady pace.
- You can call it from another thread while
Update()runs. Samples wait in a locked queue until the next update. GetNumInputs(deviceIndex)returns the number of inputs on a device.SetBatteryChargeLevel(deviceIndex, normalizedCharge)stores the device’s battery level. The Battery low warning on a device node is not forwarded to your application, so read the level from your own sensor code.
Push the first samples before the first Update() after Start(). A device node connects to its device only when the classifier initializes, which is during the first update after Start(), and only if the device has already received data. Otherwise the node stays in its “Device not connected” error, and nothing in the library initializes it again during the session.
In 1.7.3: The classifier re-initializes on every update, so a device node connects on the first update after the device receives data. Pushing samples first works in both versions.
A device that stops receiving samples is not removed. The library turns off the automatic removal that Studio uses.
Device types
Section titled “Device types”The EDevice list is much shorter than Studio’s device list. It has no entry for OpenBCI, BrainFlow boards, Neurosity, eego, Mitsar or BrainMaster, and AddDevice can’t create them. See Devices for the full Studio list.
EDevice |
Value | Inputs | Input rate | Community builds |
|---|---|---|---|---|
DEVICE_ |
0 | BPM (0), RR interval in ms (1) | Variable, send as values arrive | Included |
DEVICE_ |
3 | X, Y, Z | 50 Hz | Included |
DEVICE_ |
4 | X, Y, Z | 10 Hz | Included |
DEVICE_ |
10 | See EMuseInputs in the header |
EEG 220 Hz | Included |
DEVICE_EMOTIV_EPOC |
11 | Not in public build | ||
DEVICE_ |
12 | Not in public build | ||
DEVICE_ |
13 | Not in public build | ||
DEVICE_ (spelled so) |
14 | Pz, C4, Fz, Cz, C3 | EEG 250 Hz | Included |
DEVICE_ |
15 | GSR in uSiemens | 10 Hz | Included |
The gyroscope comment in the header says “< 50 Hz”, but the device is set up for 10 Hz. The Muse DRL and Ref comments in the header say 220 Hz, but the device sets them up for 10 Hz.
- Emotiv EPOC and Insight: Not in public build. Available through a source build (community) or a custom build (commercial clients). The vendor SDK is not in the repository.
- NeuroSky MindWave: Not in public build. Available through a source build (community) or a custom build (commercial clients). The vendor SDK is not in the repository.
In a build without these drivers, AddDevice returns -1 for those types.
The Engine links a classifier’s device node to a device by type and by the node’s Device Number attribute, which defaults to 1. The first device you add of a type matches Device Number 1, the second matches 2. A node with no matching device stays in its “Device not connected” error. Use a classifier whose device nodes match the devices you add.
Reading feedback
Section titled “Reading feedback”The library copies feedback values at the end of each Update(), under a lock, so you can read them from another thread.
| Function | Returns |
|---|---|
GetNumFeedbacks() |
The number of feedback nodes in the classifier. Available right after LoadClassifier. Returns -1 only if Init() has not run. |
FindFeedbackIndexByName(name) |
The index of the first node with that name, or -1. |
GetFeedbackName(index) |
The node’s name. Empty if the index is out of range. |
GetCurrentFeedbackValue(index) |
The value from the last Update(). 0.0 if the index is out of range. |
GetFeedbackRange(index, |
For Custom Feedback nodes, the Range Min and Range Max attributes. For control nodes, always 0 and 1. 0.0 for both if the index is out of range. |
- Look nodes up by name. The list is sorted by each node’s vertical position in the classifier, so moving a node can change its index.
- The list holds Custom Feedback nodes and the control nodes built on them, including Volume Control and Brightness Control. Your application decides what to do with their values.
GetFeedbackNamereturns text in a per-thread buffer of 256 bytes. Copy it if you need to keep it, because the next call on that thread overwrites it. Longer names are cut off.
In 1.7.3:
GetFeedbackNamereturns a pointer to the Engine’s own copy of the name, with no per-thread buffer.
Callbacks
Section titled “Callbacks”The library uses a C struct of function pointers, Callback, passed to SetCallback.
- Set every function pointer. The library forwards events without checking for NULL.
- Allocate the struct with
malloc. A laterSetCallbackcall frees the previous struct withfree.
The library forwards these:
| Group | Callbacks |
|---|---|
| Session end | OnStop(status), when the state machine reaches an exit state. The status is STATUS_OK, STATUS_DEVICE_ERROR, STATUS_SIGNAL_ERROR or STATUS_ENGINE_ERROR. |
| Log | OnLog(message) |
| Audio and video | OnPlayAudio, OnStopAudio, OnPauseAudio, OnSetAudioVolume, OnSeekAudio, OnPlayVideo, OnStopVideo, OnPauseVideo, OnSetVideoVolume, OnSeekVideo |
| Screen | OnShowImage, OnHideImage, OnShowText, OnHideText, OnShowButton, OnClearButtons, OnSetFourZoneAVEColors, OnHideFourZoneAVE |
| Other | OnCommand |
OnStopdoes not stop the session. In aStart()session, callStop()in response. In threaded mode, see Threaded mode.OnError,OnClearError,OnWarning,OnClearWarning,OnInfo,OnLogBackendandOnSetBackgroundColorare in the struct, but this source never calls them. The header says a buffer overflow callsOnError. Don’t rely on that.- The Engine doesn’t play or show anything. Your application does that, then reports back with
AudioLooped(url)andVideoLooped(url). ButtonClicked(buttonId)returns FALSE while a session is running, so buttons fromOnShowButtondo nothing during a session in this source. Don’t rely on them.
To list a state machine’s assets, use GetNumAssets, GetAssetLocation and the related functions in the header.
Build targets
Section titled “Build targets”Build instructions are in the public repository: see Building. It covers prerequisites, build parameters and output folders. The public source does not yet include every 1.8.2 change.
| Make target | Output | In make default |
Purpose |
|---|---|---|---|
Engine |
Static library Engine-neuromore |
Yes | The core. The other targets link it, so build it first. |
EngineLIB |
Shared library EngineLIB-neuromore |
Yes | The C API described on this page. |
EngineJNI |
Shared library and JAR, EngineJNI-neuromore |
No | Java wrapper. |
QtBase |
Static library QtBase-neuromore |
Yes | Qt support code that Studio links. |
Studio |
Studio executable | Yes | The Studio application. |
EngineCLI |
Executable EngineCLI, 1.8.2 only |
No | Command-line interface for automated integration tests. Not a supported way to integrate, and not documented here. |
Run the commands from the root of the source tree. To build only the library:
make Dependencies-enginemake Enginemake EngineLIBTo build the Java wrapper, also run make EngineJNI. It needs a JDK. On Windows and Linux, set the JAVA_HOME environment variable to the JDK folder first. On macOS, the makefile finds the JDK with /usr/libexec/java_home.
To build everything, including Studio:
make DependenciesmakeIn 1.7.3:
Dependencies-enginedoesn’t buildstk, whichEngineLIBlinks. Usemake Dependenciesinstead.
Libraries are written to build/make/lib/<os>-<arch>/, with no lib prefix. Shared libraries use the extension .dll, .so or .dylib. Static libraries use .lib on Windows and .a on other platforms. EngineJNI also writes a .jar.
Release is the default. Debug builds (MODE=debug) add _d to the name. Use the same MODE for the dependencies and for every target, because the file names must match when the targets link.
In 1.7.3: Debug is the default. The outputs are named
Engine,EngineLIB,EngineJNIandQtBase, without-neuromore. There is noEngineCLItarget.
The makefiles have platform sections for Windows, macOS, Linux, Android and iOS. A section being there does not mean that the build works on that platform.
Java wrapper
Section titled “Java wrapper”EngineJNI wraps the same functions for Java, in the package com.neuromore.engine. The main class is Wrapper. The ICallback interface is in the same package. Enums are in com.neuromore.engine.enums. You pass the callback to Init.
-
Wrapperhas noFindFeedbackIndexByNameorGetFeedbackRange, so look up feedback withGetFeedbackName. -
The disconnect function is spelled
DisconnectDevicehere. -
WrappercallsSystem.loadLibrary("EngineJNI"), but the build writesEngineJNI-neuromore, with nolibprefix. Rename the shared library toEngineJNI.dllon Windows,libEngineJNI.soon Linux orlibEngineJNI.dylibon macOS, and put its folder onjava.library.path.In 1.7.3: The build writes
EngineJNI, orEngineJNI_din a debug build, which is the default. The same target names apply.
Adding drivers in a source build
Section titled “Adding drivers in a source build”These docs show what the neuromore system can do. Community builds on GitHub do not include every device driver. Developers can add those drivers in their own source build. Commercial clients can get the integration as part of their custom build. Each device is labelled in Devices and Biosensors, so check the label before you buy hardware.
Adding a driver is real work, and turning on a flag is not enough:
- Existing drivers that are switched off. The flags are in
src/Engine/Config.h. Most need a vendor SDK that is not in the repository, with files in place at compile, link and run time. See Customizing the Build. Some are Windows 32-bit only, and actiCHamp has only a driver skeleton with no data path. Devices lists what each one needs. - New hardware in Studio. Device types and their nodes are registered in
src/Engine/Devices/DeviceInventory.cpp. Drivers are registered insrc/Studio/Devices/DriverInventory.cpp. - New hardware in the library. Add a value to
EDeviceand handle it inneuromoreEngine.cpp, in the functions that switch on the device type. - A newer BrainFlow. This means bringing its sources, board descriptions and vendor libraries into the source tree. See BrainFlow boards.
To ask about a custom build, contact neuromore.
Licensing
Section titled “Licensing”Licensing: see neuromore Licensing, the Terms & Conditions, the End User Agreement and the Privacy Policy.
Vendor SDK licensing is not yet confirmed.