<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.bci2000.org/mediawiki/index.php?title=Special:NewPages&amp;feed=atom</id>
	<title>BCI2000 Wiki - New pages [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.bci2000.org/mediawiki/index.php?title=Special:NewPages&amp;feed=atom"/>
	<link rel="alternate" type="text/html" href="https://www.bci2000.org/mediawiki/index.php/Special:NewPages"/>
	<updated>2026-06-10T04:56:28Z</updated>
	<subtitle>From BCI2000 Wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://www.bci2000.org/mediawiki/index.php/Contributions:ScreenCaptureLogger</id>
		<title>Contributions:ScreenCaptureLogger</title>
		<link rel="alternate" type="text/html" href="https://www.bci2000.org/mediawiki/index.php/Contributions:ScreenCaptureLogger"/>
		<updated>2026-05-06T15:01:01Z</updated>

		<summary type="html">&lt;p&gt;Mellinger: /* ScreenCaptureAreas */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Synopsis==&lt;br /&gt;
An extension that records video from one or more screen areas to mp4 files, and stores frame numbers as (event-type) state variables.&lt;br /&gt;
&lt;br /&gt;
==Location==&lt;br /&gt;
http://{{SERVERNAME}}/svn/trunk/src/contrib/Extensions/ScreenCaptureLogger&amp;lt;br&amp;gt;&lt;br /&gt;
http://{{SERVERNAME}}/svn/trunk/src/contrib/Extensions/ScreenCaptureLogger_Wayland&lt;br /&gt;
&lt;br /&gt;
==Versioning==&lt;br /&gt;
===Authors===&lt;br /&gt;
Juergen Mellinger (mellinger@neurotechcenter.org)&lt;br /&gt;
&lt;br /&gt;
===Version History===&lt;br /&gt;
May 6, 2026: initial version&lt;br /&gt;
&lt;br /&gt;
===Source Code Revisions===&lt;br /&gt;
*Initial development: 9343&lt;br /&gt;
*Tested under: 9357&lt;br /&gt;
*Known to compile under: 9357&lt;br /&gt;
&lt;br /&gt;
===Known Issues===&lt;br /&gt;
On Linux, the Wayland version has never been working. It connects to the XDG screen capturing portal and displays a screen selector dialog but subsequently fails to establish a connection to the PipeWire daemon. Due to lack of error information and documentation on the PipeWire side, it is currently not possible to fix the issue with reasonable effort.&lt;br /&gt;
&lt;br /&gt;
==Functional Description==&lt;br /&gt;
For many experiments, a documentation of screen content is desired. While the content of the user&amp;#039;s task window is available in the ApplicationWindow visualization, this has its limitations, especially if the participant&amp;#039;s screen is not displayed by a BCI2000 application module but a third party stimulus presentation kit such as &amp;#039;&amp;#039;PsychoPy&amp;#039;&amp;#039; or &amp;#039;&amp;#039;PsychToolBox&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
This extension allows capturing of any number of desktop areas, and saving each into its own MP4 video file. It also records the current frame number as a state variable, so that the video can be synchronized with brain data at sample resolution (max. 1ms). &lt;br /&gt;
&lt;br /&gt;
The video file bears the name of the BCI2000 data file, with the extension removed and &amp;lt;tt&amp;gt;_ScreenCaptureM.mp4&amp;lt;/tt&amp;gt; appended where &amp;lt;tt&amp;gt;M&amp;lt;/tt&amp;gt; is the one-based index of the screen area in question.&lt;br /&gt;
For the format of the video file, see [[Contributions:ScreenCaptureLogger#Format_of_the_Video_File|the notes below]].&lt;br /&gt;
&lt;br /&gt;
==Integration into BCI2000==&lt;br /&gt;
Compile the extension into your source module by enabling contributed extensions in your CMake configuration. In your root build folder, run &amp;lt;tt&amp;gt;Configure.sh.cmd&amp;lt;/tt&amp;gt; in order to bring up the CMake GUI; there, enable EXTENSIONS_SCREENCAPTURELOGGER for the Windows/macOS/Linux-X11 version of the module.&lt;br /&gt;
In order to use the Linux-Wayland version &amp;#039;&amp;#039;&amp;#039;disable&amp;#039;&amp;#039;&amp;#039; EXTENSIONS_SCREENCAPTURELOGGER, and &amp;#039;&amp;#039;&amp;#039;enable&amp;#039;&amp;#039;&amp;#039; EXTENSIONS_SCREENCAPTURELOGGER_WAYLAND.&lt;br /&gt;
You cannot enable both the default and the Wayland version.&lt;br /&gt;
&lt;br /&gt;
Further requirements are:&lt;br /&gt;
===Windows===&lt;br /&gt;
No further requirements must be met.&lt;br /&gt;
===macOS===&lt;br /&gt;
On macOS, the extension requires at least macOS 15.2 (Sequoia) to compile and run.&lt;br /&gt;
===Linux-X11===&lt;br /&gt;
The X11 version of the extension requires the xcb (X11 C bindings) and the xcb-shm (XCB shared memory extension) libraries and headers. On Debian-based distributions, this resolved by installing the &amp;#039;&amp;#039;libxcb-dev&amp;#039;&amp;#039; and &amp;#039;&amp;#039;libxcd-shm0-dev&amp;#039;&amp;#039; packages. On other distributions, installing &amp;#039;&amp;#039;libxcb&amp;#039;&amp;#039; may be sufficient.&lt;br /&gt;
===Linux-Wayland===&lt;br /&gt;
To build the (currently broken) Wayland version of the extension, it its necessary to install the sdbus-c++ library (&amp;#039;&amp;#039;libsdbus-c++-dev&amp;#039;&amp;#039; on Debian-based systems, &amp;#039;&amp;#039;sdbus-cpp&amp;#039;&amp;#039; on Arch) as well as the PipeWire library (&amp;#039;&amp;#039;libpipewire-dev&amp;#039;&amp;#039; on Debian-based distributions, &amp;#039;&amp;#039;libpipewire&amp;#039;&amp;#039; on Arch).&lt;br /&gt;
&lt;br /&gt;
Once the extension is built into the source module, enable it by starting the source module with the &amp;lt;code&amp;gt;--LogScreenCapture=1&amp;lt;/code&amp;gt; command line argument.&lt;br /&gt;
&lt;br /&gt;
==Parameters==&lt;br /&gt;
The ScreenCapture logger is configured in the &amp;#039;&amp;#039;Source&amp;#039;&amp;#039; tab within the ScreenCaptureLogger section.  The configurable parameters are:&lt;br /&gt;
&lt;br /&gt;
===LogScreenCapture===&lt;br /&gt;
Enables/Disables the logger extension.&lt;br /&gt;
===DisplayCaptureStream===&lt;br /&gt;
Display the video stream from each captured screen area. Display stream is identical to what is being recorded in the .mp4 file.&lt;br /&gt;
===ScreenCaptureAreas===&lt;br /&gt;
A matrix parameter with each row representing an independently capture area (capture areas may overlap).&lt;br /&gt;
In each row, you may specify a video width, height, spatial decimation, fps, encoder profile, and a compression factor. &lt;br /&gt;
The &amp;#039;&amp;#039;encoding profile&amp;#039;&amp;#039; column allows you to choose an H.264 encoding profile for video output, which may be a number from 1 to 3. &amp;#039;&amp;#039;&amp;#039;1&amp;#039;&amp;#039;&amp;#039; corresponds to the &amp;quot;baseline&amp;quot; profile, &amp;#039;&amp;#039;&amp;#039;2&amp;#039;&amp;#039;&amp;#039; corresponds to the &amp;quot;main&amp;quot; profile, and &amp;#039;&amp;#039;&amp;#039;3&amp;#039;&amp;#039;&amp;#039; corresponds to the &amp;quot;high&amp;quot; profile as defined in the &amp;#039;&amp;#039;ITU-T H.264&amp;#039;&amp;#039; standard.&lt;br /&gt;
If the encoder uses ffmpeg’s &amp;#039;&amp;#039;x264&amp;#039;&amp;#039; codec (on non-Windows systems), the additional option &amp;#039;&amp;#039;tune=animation&amp;#039;&amp;#039; is applied to optimize encoding for screen content. On Windows, the &amp;#039;&amp;#039;mf_h264&amp;#039;&amp;#039; codec is used which does not provide such an option.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;compression factor&amp;#039;&amp;#039; determines the amount of data compression applied when encoding the stream of images into an .mp4 file. The smallest possible compression setting is 1 (no compression). Use a setting of 100 to compress the data stream to 1/100 of its original size.&lt;br /&gt;
&lt;br /&gt;
==Event Variables==&lt;br /&gt;
&lt;br /&gt;
===ScreenCaptureFrame&amp;lt;1-n&amp;gt;===&lt;br /&gt;
The frame number of the screen capture area with row i. The default value of n is 4, allowing for capturing of up to 4 areas. To record more areas, change the &amp;lt;code&amp;gt;NUM_OF_SCREEN_CAPTURE_EVENTS&amp;lt;/code&amp;gt; macro in ScreenCaptureLogger.cpp to the number of areas you want to record, and recompile. Event values can be used to synchronize the video with the recorded neural data. It is a 32-bit value, allowing 4294967295 frames before overflowing. If screen capturing is done at 30 frames/sec, this allows more than four years of video before it will go back to frame 0. In other words, this should not be an issue for most users.&lt;br /&gt;
&lt;br /&gt;
==Format of the Video File==&lt;br /&gt;
Each capture area writes its own video file. Files in mp4 format (and related formats) do not allow to seek to locations that represent original frames when writing the file. Rather, the file&amp;#039;s nominal frame rate and length are used in conjunction with interpolation to construct a frame at an ideal location.&lt;br /&gt;
Thus, it is not possible to write individual frames using the screen capture’s original timestamp, at the original frame rate.&lt;br /&gt;
&lt;br /&gt;
Instead, the file is written with the nominal framerate specified in the &amp;#039;&amp;#039;fps&amp;#039;&amp;#039; column of the &amp;#039;&amp;#039;ScreenCaptureAreas&amp;#039;&amp;#039; parameter, with differences between frame timestamps matching ideal frame duration.&lt;br /&gt;
This way, loading the video into e.g. Matlab, you will obtain a sequence of frames where each frame index corresponds to the frame index in the &amp;lt;tt&amp;gt;ScreenCaptureFrameN&amp;lt;/tt&amp;gt; state (minus 1).&lt;br /&gt;
&lt;br /&gt;
For a strategy how to synchronize video file frames with brain signal data, and possibly audio data, see [[User_Reference:BCI2000Viewer#AV_Synchronization_Strategies|this page]].&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[User Reference:Logging Input]], [[Contributions:Extensions]], [[Programming Reference:Events]], [[User Reference:BCI2000Viewer]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Contributions]][[Category:Extension]]&lt;/div&gt;</summary>
		<author><name>Mellinger</name></author>
	</entry>
	<entry>
		<id>https://www.bci2000.org/mediawiki/index.php/Contributions:AVStreamingTask</id>
		<title>Contributions:AVStreamingTask</title>
		<link rel="alternate" type="text/html" href="https://www.bci2000.org/mediawiki/index.php/Contributions:AVStreamingTask"/>
		<updated>2026-04-10T13:24:12Z</updated>

		<summary type="html">&lt;p&gt;Mellinger: /* Example Batch File */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Synopsis==&lt;br /&gt;
This extension displays video from a remote location, plus local video in a user application window.&lt;br /&gt;
It works in conjunction with the &amp;#039;&amp;#039;SimpleRelayServer&amp;#039;&amp;#039; application running within a local network, or on the internet.&lt;br /&gt;
Audio may be transferred in one direction as well. Bidirectional audio is possible but will lead to feedback unless earphones are used.&lt;br /&gt;
&lt;br /&gt;
===Authors===&lt;br /&gt;
Jürgen Mellinger (mellinger@neurotechcenter.org)&lt;br /&gt;
&lt;br /&gt;
==SimpleRelayServer==&lt;br /&gt;
To allow connection between machines which are located at different locations, and possibly in different local networks,&lt;br /&gt;
it is necessary to use a bridge that is accessible to both machines.&lt;br /&gt;
AVMeetingExtension is designed to use a relay server for this bridge.&lt;br /&gt;
In order to bridge different local networks, the relay server must be located somewhere in the open internet, on a Linux server under the experimenter&amp;#039;s control.&lt;br /&gt;
&lt;br /&gt;
Follow these steps to build and run the necessary &amp;#039;&amp;#039;SimpleRelayServer&amp;#039;&amp;#039; executable on a Linux machine:&lt;br /&gt;
* Download &amp;lt;tt&amp;gt;SimpleRelayServer.cpp&amp;lt;/tt&amp;gt; from&lt;br /&gt;
https://bci2000.org/svn/trunk/src/contrib/Extensions/AVMeetingExtension/SimpleRelayServer/SimpleRelayServer.cpp&lt;br /&gt;
* cd to the download&amp;#039;s directory and execute&lt;br /&gt;
 g++ SimpleRelayServer.cpp -o SimpleRelayServer&lt;br /&gt;
* Start the server with&lt;br /&gt;
 ./SimpleRelayServer 2&amp;gt;/dev/null&lt;br /&gt;
or just&lt;br /&gt;
 ./SimpleRelayServer &lt;br /&gt;
if you want to see log messages on the console.&lt;br /&gt;
By default, the server process will listen on all of the machine&amp;#039;s interfaces, at port 1915.&lt;br /&gt;
A different port may be specified by providing a command line argument:&lt;br /&gt;
 ./SimpleRelayServer 1928 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NOTE:&amp;#039;&amp;#039;&amp;#039; For testing purposes, BCI2000 provides a SimpleRelayServer instance at &amp;lt;tt&amp;gt;upload.bci2000.org:1915&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Example Batch File==&lt;br /&gt;
The following example batch file starts up BCI2000 with the &amp;#039;&amp;#039;SignalGenerator&amp;#039;&amp;#039; source module, the &amp;#039;&amp;#039;DummySignalProcessing&amp;#039;&amp;#039; module, and the &amp;#039;&amp;#039;GuiltTask&amp;#039;&amp;#039; with the &amp;#039;&amp;#039;AVMeetingExtension&amp;#039;&amp;#039; enabled.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 #! ../prog/BCI2000Shell&lt;br /&gt;
@cls &amp;amp; ..\prog\BCI2000Shell %0 %* #! &amp;amp;&amp;amp; exit /b 0 || exit /b 1\n&lt;br /&gt;
&lt;br /&gt;
Change directory $BCI2000LAUNCHDIR&lt;br /&gt;
Show window; Set title ${Extract file base $0}&lt;br /&gt;
Reset system&lt;br /&gt;
Startup system localhost&lt;br /&gt;
Start executable SignalGenerator --local --LogKeyboard=1&lt;br /&gt;
Start executable DummySignalProcessing --local &lt;br /&gt;
Start executable GuiltTask --local --EnableAVMeeting=1 --Port=1234 --IPAddress=3.216.172.59 --SharedStates=isReadyStart0,8&amp;amp;isReadyStart1,8&amp;amp;ClientNumber,8&amp;amp;CompensationTrans,32&amp;amp;CompensationVictim,32&lt;br /&gt;
Wait for Connected&lt;br /&gt;
&lt;br /&gt;
Load parameterfile &amp;quot;../parms/GuiltTask/guiltTaskLocal.prm&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Parameters==&lt;br /&gt;
===EnableAVMeeting===&lt;br /&gt;
If nonzero, enables the AVMeeting extension.&lt;br /&gt;
&lt;br /&gt;
===StreamingServer=== &lt;br /&gt;
The streaming server&amp;#039;s address with port, as in &amp;#039;&amp;#039;upload.bci2000.org:1915&amp;#039;&amp;#039;. This must match the &amp;#039;&amp;#039;SimpleRelayServer&amp;#039;s&amp;#039;&amp;#039; listening address.&lt;br /&gt;
For testing purposes, an instance of the streaming server is running at the BCI2000 server&amp;#039;s address given above.&lt;br /&gt;
&lt;br /&gt;
===CameraIndex===&lt;br /&gt;
The local camera&amp;#039;s 0-based index into the video backend&amp;#039;s list of detected cameras. Typically 0 unless multiple cameras are attached.&lt;br /&gt;
A value of -1 disables video streaming.&lt;br /&gt;
&lt;br /&gt;
===AudioDeviceIndex===&lt;br /&gt;
The local audio input device&amp;#039;s 0-based index into the audio backend&amp;#039;s list of detected audio devices.&lt;br /&gt;
Specify -1 to disable audio streaming from the local machine.&lt;br /&gt;
&lt;br /&gt;
===VideoEncoder===&lt;br /&gt;
The name of the desired video encoder to use, e.g. h264_mf or libx264.&lt;br /&gt;
Specify &amp;#039;&amp;#039;auto&amp;#039;&amp;#039; to use h264_mf on Windows, and libx264 on non-Windows systems.&lt;br /&gt;
&lt;br /&gt;
===RemoteVideoRect===&lt;br /&gt;
The display rectangle for remote video, in relative coordinates. Values are &amp;#039;&amp;#039;left&amp;#039;&amp;#039;, &amp;#039;&amp;#039;top&amp;#039;&amp;#039;, &amp;#039;&amp;#039;bottom&amp;#039;&amp;#039;, &amp;#039;&amp;#039;right&amp;#039;&amp;#039;. Defaults to &amp;lt;tt&amp;gt;0 0 1 1&amp;lt;/tt&amp;gt;, i.e. the entire window.&lt;br /&gt;
&lt;br /&gt;
===RemoteVideoZ===&lt;br /&gt;
The remote video display&amp;#039;s Z coordinate. Objects with a smaller Z coordinate are displayed on top objects with larger Z coordinates.&lt;br /&gt;
&lt;br /&gt;
===RemoteVideoVisibility===&lt;br /&gt;
A BCI2000 expression which may entail BCI2000 state names. Whenever this expression evaluates to a nonzero value, remote video is displayed.&lt;br /&gt;
Otherwise, remote video is hidden.&lt;br /&gt;
&lt;br /&gt;
===LocalVideoRect===&lt;br /&gt;
The display rectangle for local video, in relative coordinates. Values are &amp;#039;&amp;#039;left&amp;#039;&amp;#039;, &amp;#039;&amp;#039;top&amp;#039;&amp;#039;, &amp;#039;&amp;#039;bottom&amp;#039;&amp;#039;, &amp;#039;&amp;#039;right&amp;#039;&amp;#039;. Defaults to &amp;lt;tt&amp;gt;0.75 0 1 0.25&amp;lt;/tt&amp;gt;, i.e. the upper right corner of the display window.&lt;br /&gt;
&lt;br /&gt;
===LocalVideoZ===&lt;br /&gt;
The local video display&amp;#039;s Z coordinate. Objects with a smaller Z coordinate are displayed on top objects with larger Z coordinates.&lt;br /&gt;
&lt;br /&gt;
Note that by default the local video rectangle overlaps with the remote video rectangle, and local video is on top of the remote.&lt;br /&gt;
&lt;br /&gt;
===LocalVideoVisibility===&lt;br /&gt;
A BCI2000 expression which may entail BCI2000 state names. Whenever this expression evaluates to a nonzero value, local video is displayed.&lt;br /&gt;
Otherwise, local video is hidden.&lt;br /&gt;
&lt;br /&gt;
==States==&lt;br /&gt;
&lt;br /&gt;
===VideoStreamingLocalTotalFrames===&lt;br /&gt;
A 32-bit state holding the current local frame number since the beginning of the current recording (BCI2000 run).&lt;br /&gt;
&lt;br /&gt;
===VideoStreamingRemoteTotalFrames===&lt;br /&gt;
A 32-bit state holding the current remote frame number since the beginning of the current recording (BCI2000 run).&lt;br /&gt;
&lt;br /&gt;
===VideoStreamingLocalFrame===&lt;br /&gt;
A 32-bit state holding the current local frame number since the beginning of the current file.&lt;br /&gt;
&lt;br /&gt;
===VideoStreamingRemoteFrame===&lt;br /&gt;
A 32-bit state holding the current remote frame number since the beginning of the current file.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
Both local and remote video is being recorded into files in mp4 format.&lt;br /&gt;
These files bear the current BCI2000 file&amp;#039;s basename, followed with &amp;lt;tt&amp;gt;_VideoStreamingLocal&amp;lt;/tt&amp;gt; and&lt;br /&gt;
&amp;lt;tt&amp;gt;_VideoStreamingRemote&amp;lt;/tt&amp;gt;, respectively.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NOTE:&amp;#039;&amp;#039;&amp;#039; Seeking to individual frames is not possible for mp4 files unless frame time stamps exactly match the frame rate specified at the beginning of the file. To work around this, video files are created with the camera&amp;#039;s nominal frame rate, and artificial time stamps proportional to frame number are used when writing frames to a file.&lt;br /&gt;
In analysis, it will be possible to reconstruct actual time stamps using one of the frame count states.&lt;br /&gt;
&lt;br /&gt;
However, replaying mp4 files written by the &amp;#039;&amp;#039;AVMeetingExtension&amp;#039;&amp;#039; with a standard video player may not result in proper playback speed.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[Contributions:Extensions]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Contributions]]&lt;/div&gt;</summary>
		<author><name>Mellinger</name></author>
	</entry>
	<entry>
		<id>https://www.bci2000.org/mediawiki/index.php/Programming_Reference:SignalSharingClientLibDemo</id>
		<title>Programming Reference:SignalSharingClientLibDemo</title>
		<link rel="alternate" type="text/html" href="https://www.bci2000.org/mediawiki/index.php/Programming_Reference:SignalSharingClientLibDemo"/>
		<updated>2026-03-12T16:00:19Z</updated>

		<summary type="html">&lt;p&gt;Mellinger: /* Client application code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Location==&lt;br /&gt;
&amp;lt;tt&amp;gt;src/core/SignalProcessing/SignalSharingDemo/CppLibraryClient&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Synopsis==&lt;br /&gt;
The &amp;#039;&amp;#039;SignalSharingClientLibDemo&amp;#039;&amp;#039; client demonstrates how to receive signal data from BCI2000, using the [[Technical Reference:SignalSharingClient Library|SignalSharingClient Library]].&lt;br /&gt;
SignalSharing allows to tap into BCI2000 processing by receiving any filter output signal through a combination of a TCP connection, and shared memory.&lt;br /&gt;
&lt;br /&gt;
==Function==&lt;br /&gt;
The &amp;#039;&amp;#039;SignalSharing&amp;#039;&amp;#039; component in BCI2000 shares its input signal through a &amp;lt;tt&amp;gt;GenericSignal&amp;lt;/tt&amp;gt; object which has been linked to a shared memory block using &amp;lt;tt&amp;gt;GenericSignal::ShareAcrossModules()&amp;lt;/tt&amp;gt;. A dedicated thread waits for signal updates, and sends signal data out to a separate application waiting on a TCP/IP connection.&lt;br /&gt;
&lt;br /&gt;
When the client application is running on a separate machine, full signal data are sent over the network. When the client is running on the same machine, only a reference to a shared memory block is sent. On the application side, unserializing the signal will transparently bind it to the shared memory block if available.&lt;br /&gt;
&lt;br /&gt;
The simple client application receives BCI2000 parameters and data blocks, and writes them into a parameter file, and a signal file.&lt;br /&gt;
&lt;br /&gt;
==Client application code==&lt;br /&gt;
The client application uses the [[Technical Reference:SignalSharingClient Library|SignalSharingClient Library]] to receive data from BCI2000. This way, it does not have to deal with BCI2000 messages but will see the raw data directly. The [[Technical Reference:SignalSharingClient Library|SignalSharingClient Library]] is contained in the file &amp;lt;tt&amp;gt;prog/SignalSharingClientLib.dll&amp;lt;/tt&amp;gt; for Windows, and carrying the appropriate file extension for shared libraries on other platforms. For convenience, header file and &amp;lt;tt&amp;gt;.lib&amp;lt;/tt&amp;gt; file (if any) are also located at &amp;lt;tt&amp;gt;prog&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simple example consists of a single source file.&lt;br /&gt;
&lt;br /&gt;
Note how the callback functions call the appropriate &amp;lt;tt&amp;gt;SSC_Lock*()&amp;lt;/tt&amp;gt; function, followed with processing of the data, and calling &amp;lt;tt&amp;gt;SSC_Release*()&amp;lt;/tt&amp;gt; as soon as possible. Also note that care is taken to call &amp;lt;tt&amp;gt;SSC_Release*()&amp;lt;/tt&amp;gt; only if the call to &amp;lt;tt&amp;gt;SSC_Lock*()&amp;lt;/tt&amp;gt; was successful.&lt;br /&gt;
&lt;br /&gt;
Similarly, note how &amp;lt;tt&amp;gt;main()&amp;lt;/tt&amp;gt; calls &amp;lt;tt&amp;gt;SSC_DeleteConnector()&amp;lt;/tt&amp;gt; no matter whether an exception occurred or not.&lt;br /&gt;
&lt;br /&gt;
To avoid clutter, and throw an exception when an unexpected error has occurred, we define a &amp;lt;tt&amp;gt;SSC_SucceedOrThrow&amp;lt;/tt&amp;gt; class that can be used in place of a plain numeric variable to hold a function&amp;#039;s return value.&lt;br /&gt;
Note how exceptions are caught in the &amp;lt;tt&amp;gt;main()&amp;lt;/tt&amp;gt; function before calling the &amp;lt;tt&amp;gt;SSC_DeleteConnector()&amp;lt;/tt&amp;gt; function. This is possible because &amp;lt;tt&amp;gt;SSC_CreateConnector()&amp;lt;/tt&amp;gt; guarantees to null the resulting handle if any error occurs. This implies that we can call &amp;lt;tt&amp;gt;SSC_DeleteConnector()&amp;lt;/tt&amp;gt; without using a dangling or otherwise invalid pointer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;SignalSharingClientLib.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;fstream&amp;gt;&lt;br /&gt;
#include &amp;lt;string&amp;gt;&lt;br /&gt;
#include &amp;lt;mutex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static std::string SSC_ResultToString(SSC_RESULT result)&lt;br /&gt;
{&lt;br /&gt;
    switch (result) {&lt;br /&gt;
    case SSC_SUCCESS:&lt;br /&gt;
        return &amp;quot;Success&amp;quot;;&lt;br /&gt;
    case SSC_INVALID_ARG:&lt;br /&gt;
        return &amp;quot;Invalid argument&amp;quot;;&lt;br /&gt;
    case SSC_CANNOT_LISTEN:&lt;br /&gt;
        return &amp;quot;Cannot listen on given address&amp;quot;;&lt;br /&gt;
    case SSC_CANNOT_FULFILL_REQUEST:&lt;br /&gt;
        return &amp;quot;Cannot fulfill request at this time&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return &amp;quot;Unknown error&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Translate an error code into an exception&lt;br /&gt;
struct SSC_SucceedOrThrow&lt;br /&gt;
{&lt;br /&gt;
    SSC_SucceedOrThrow(SSC_RESULT result)&lt;br /&gt;
    {&lt;br /&gt;
        if (result != SSC_SUCCESS) {&lt;br /&gt;
            throw std::runtime_error(SSC_ResultToString(result));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// The data we need inside the callback functions&lt;br /&gt;
struct ConnectorData&lt;br /&gt;
{&lt;br /&gt;
    SSC_HANDLE h = nullptr;&lt;br /&gt;
    std::ofstream paramfile, datafile;&lt;br /&gt;
    std::mutex mutex;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Callback function for handling parameters&lt;br /&gt;
static void HandleParameters(void* pData)&lt;br /&gt;
{&lt;br /&gt;
    std::cout &amp;lt;&amp;lt; &amp;quot;Received parameters ...&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
    auto pConnectorData = static_cast&amp;lt;ConnectorData*&amp;gt;(pData);&lt;br /&gt;
    const char* const* parameterLines = nullptr;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    if (SSC_SUCCESS == ::SSC_LockParameters(pConnectorData-&amp;gt;h, &amp;amp;parameterLines, &amp;amp;count)) {&lt;br /&gt;
        std::unique_lock lock(pConnectorData-&amp;gt;mutex);&lt;br /&gt;
        for (int i = 0; i &amp;lt; count; ++i) {&lt;br /&gt;
            pConnectorData-&amp;gt;paramfile &amp;lt;&amp;lt; parameterLines[i] &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        lock.unlock();&lt;br /&gt;
        ::SSC_ReleaseParameters(pConnectorData-&amp;gt;h);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Callback function for handling signals&lt;br /&gt;
static void HandleSignal(void* pData)&lt;br /&gt;
{&lt;br /&gt;
    std::cout &amp;lt;&amp;lt; &amp;quot;Received signal ...&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
    auto pConnectorData = static_cast&amp;lt;ConnectorData*&amp;gt;(pData);&lt;br /&gt;
    union { const double* d; const char* c; } pSignal = { nullptr };&lt;br /&gt;
    int channels = 0, samples = 0;&lt;br /&gt;
    if (SSC_SUCCESS == ::SSC_LockSignal(pConnectorData-&amp;gt;h, &amp;amp;pSignal.d, &amp;amp;channels, &amp;amp;samples, nullptr)) {&lt;br /&gt;
        std::unique_lock lock(pConnectorData-&amp;gt;mutex);&lt;br /&gt;
        // Transpose data so we have a continuous stream of samples across channels&lt;br /&gt;
        for (int sample = 0; sample &amp;lt; samples; ++sample) {&lt;br /&gt;
            for (int ch = 0; ch &amp;lt; channels; ++ch) {&lt;br /&gt;
                // Data comes in channel-major format&lt;br /&gt;
                int idx = ch * samples + sample;&lt;br /&gt;
                pConnectorData-&amp;gt;datafile.write(pSignal.c + idx * sizeof(double), sizeof(double));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        lock.unlock();&lt;br /&gt;
        ::SSC_ReleaseSignal(pConnectorData-&amp;gt;h);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    std::string address = &amp;quot;localhost:1879&amp;quot;;&lt;br /&gt;
    if (argc &amp;gt; 1)&lt;br /&gt;
        address = argv[1];&lt;br /&gt;
&lt;br /&gt;
    std::string paramfile = &amp;quot;sscparams.prm&amp;quot;;&lt;br /&gt;
    if (argc &amp;gt; 2)&lt;br /&gt;
        paramfile = argv[2];&lt;br /&gt;
&lt;br /&gt;
    std::string datafile = &amp;quot;sscdata.raw&amp;quot;;&lt;br /&gt;
    if (argc &amp;gt; 3)&lt;br /&gt;
        datafile = argv[3];&lt;br /&gt;
&lt;br /&gt;
    // The main function&amp;#039;s return value&lt;br /&gt;
    int result = 0;&lt;br /&gt;
&lt;br /&gt;
    ConnectorData data;&lt;br /&gt;
    // Callback functions are called from a separate thread, so let&amp;#039;s lock the mutex&lt;br /&gt;
    // (not really necessary here because the thread will first start inside SSC_CreateConnector() below)&lt;br /&gt;
    std::unique_lock lock(data.mutex);&lt;br /&gt;
    data.paramfile.open(paramfile);&lt;br /&gt;
    data.datafile.open(datafile, std::ios::binary | std::ios::out);&lt;br /&gt;
    lock.unlock();&lt;br /&gt;
    try {&lt;br /&gt;
        SSC_SucceedOrThrow result = ::SSC_CreateConnector(address.c_str(), &amp;amp;data.h);&lt;br /&gt;
        result = ::SSC_RegisterParametersCallback(data.h, &amp;amp;HandleParameters, &amp;amp;data);&lt;br /&gt;
        result = ::SSC_RegisterSignalCallback(data.h, &amp;amp;HandleSignal, &amp;amp;data);&lt;br /&gt;
&lt;br /&gt;
        // Callback functions are called from a separate thread, so just wait for user input&lt;br /&gt;
        std::cout &amp;lt;&amp;lt; &amp;quot;Press &amp;lt;Enter&amp;gt; to quit&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
        std::string ignored;&lt;br /&gt;
        std::getline(std::cin, ignored);&lt;br /&gt;
    }&lt;br /&gt;
    catch (const std::exception&amp;amp; exc) {&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;Error: &amp;quot; &amp;lt;&amp;lt; exc.what() &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
        result = -1;&lt;br /&gt;
    }&lt;br /&gt;
    ::SSC_DeleteConnector(data.h);&lt;br /&gt;
    return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Parameters==&lt;br /&gt;
===ShareTransmissionFilter===&lt;br /&gt;
IP address and port number of the client application. The client&amp;#039;s default address is &amp;lt;tt&amp;gt;localhost:1879&amp;lt;/tt&amp;gt; but may be changed on the client&amp;#039;s command line.&lt;br /&gt;
&lt;br /&gt;
The example uses the &amp;#039;&amp;#039;ShareTransmissionFilter&amp;#039;&amp;#039; parameter but any other filter&amp;#039;s &amp;#039;&amp;#039;Share&amp;lt;FilterName&amp;gt;&amp;#039;&amp;#039; parameter under the &amp;#039;&amp;#039;SignalSharing&amp;#039;&amp;#039; tab will work as well to visualize the chosen filter&amp;#039;s output signal.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[User Reference:SignalSharing]], [[Programming Reference:GenericSignal Class]],  [[Programming Reference:SignalSharing Python Demo]], [[Programming Reference:SignalSharingClientLibDemo]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Framework API]][[Category:Howto]][[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Mellinger</name></author>
	</entry>
	<entry>
		<id>https://www.bci2000.org/mediawiki/index.php/Technical_Reference:SignalSharingClient_Library</id>
		<title>Technical Reference:SignalSharingClient Library</title>
		<link rel="alternate" type="text/html" href="https://www.bci2000.org/mediawiki/index.php/Technical_Reference:SignalSharingClient_Library"/>
		<updated>2026-03-11T14:07:49Z</updated>

		<summary type="html">&lt;p&gt;Mellinger: /* SSC_RESULT SSC_DeleteConnector(SSC_HANDLE) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The SignalSharingClient library provides a [[User Reference:SignalSharing|SignalSharing]] client, wrapped into a C interface. It may be used to tap into signal and state data at any point of processing from external applications. The SignalSharingClient library can be used from any programming language that allows using a shared library (DLL). Although the SignalSharingClient library is written in C++, its DLL interface is plain C, and can be used with compiled languages such as C, C++, C#, Pascal, but also with interpreted languages that can call functions in a DLL, such as Python, Matlab, VisualBasic, etc. Interfacing with Java requires a JNI (Java Native Interface) wrapper, which is currently not provided by BCI2000.&lt;br /&gt;
&lt;br /&gt;
==Functions provided by the Library==&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_CreateConnector(const char* listeningAddress, SSC_HANDLE*)===&lt;br /&gt;
Create a connector object, and obtain a handle to it. A connector object is necessary to handle a connection to BCI2000,&lt;br /&gt;
and is specified as the first argument of the remaining &amp;#039;&amp;#039;SignalSharingClient&amp;#039;&amp;#039; library functions.&lt;br /&gt;
The listening address is in the format &amp;lt;IP&amp;gt;:&amp;lt;port&amp;gt; with &amp;lt;IP&amp;gt; being a name or numeric address.&lt;br /&gt;
&amp;lt;tt&amp;gt;SSC_CreateConnector()&amp;lt;/tt&amp;gt; will immediately start an internal server thread, and listen at the given port.&lt;br /&gt;
Only one client can connect to the server at a time (note that BCI2000 acts as a client).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;SSC_CreateConnector()&amp;lt;/tt&amp;gt; guarantees that the handle it returns will be either a valid handle, or nullptr (if an error occurs).&lt;br /&gt;
This implies that &amp;lt;tt&amp;gt;SSC_DeleteConnector()&amp;lt;/tt&amp;gt; may be called unconditionally on a handle returned by &amp;lt;tt&amp;gt;SSC_CreateConnector()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_DeleteConnector(SSC_HANDLE)===&lt;br /&gt;
Stop listening, terminate the internal server thread, and delete the given connector object. Must be called when connector object is no longer used.&lt;br /&gt;
&lt;br /&gt;
Providing a nullptr handle to &amp;lt;tt&amp;gt;SSC_DeleteConnector()&amp;lt;/tt&amp;gt; will result in an &amp;lt;tt&amp;gt;SSC_INVALID_ARG&amp;lt;/tt&amp;gt; error but will otherwise cause no harm.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_RegisterSignalCallback(SSC_HANDLE, SSC_CALLBACK, void* refdata)===&lt;br /&gt;
Register a callback to be called whenever new signal data is available.&lt;br /&gt;
For each connector object, it is only possible to register one signal callback at a time.&lt;br /&gt;
To unregister a signal callback, call &amp;lt;tt&amp;gt;SSC_RegisterSignalCallback()&amp;lt;/tt&amp;gt; with a &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt; argument.&lt;br /&gt;
Use the &amp;lt;refdata&amp;gt; argument to specify auxiliary data which will be provided to the callback function when it is called.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_RegisterStatesCallback(SSC_HANDLE, SSC_CALLBACK, void* refdata)===&lt;br /&gt;
Register a callback to be called whenever new state data is available.&lt;br /&gt;
For each connector object, it is only possible to register one states callback at a time.&lt;br /&gt;
To unregister a signal callback, call &amp;lt;tt&amp;gt;SSC_RegisterStatesCallback()&amp;lt;/tt&amp;gt; with a &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt; argument.&lt;br /&gt;
Use the &amp;lt;refdata&amp;gt; argument to specify auxiliary data which will be provided to the callback function when it is called.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_RegisterParametersCallback(SSC_HANDLE, SSC_CALLBACK, void* refdata)===&lt;br /&gt;
Register a callback to be called whenever new parameter data is available.&lt;br /&gt;
For each connector object, it is only possible to register one parameters callback at a time.&lt;br /&gt;
To unregister a signal callback, call &amp;lt;tt&amp;gt;SSC_RegisterParametersCallback()&amp;lt;/tt&amp;gt; with a &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt; argument.&lt;br /&gt;
Use the &amp;lt;refdata&amp;gt; argument to specify auxiliary data which will be provided to the callback function when it is called.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_LockSignal(SSC_HANDLE, const double** pSignal, int* channels, int* samples, const char* const** channelNames)===&lt;br /&gt;
Lock signal data and obtain signal information. Typically, this function will be called from a signal callback.&lt;br /&gt;
Locking blocks the arrival of new signal data, so call &amp;lt;tt&amp;gt;SCC_ReleaseSignal()&amp;lt;/tt&amp;gt; as soon as possible.&lt;br /&gt;
All pointer arguments are optional, may be &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;pSignal&amp;#039;&amp;#039; argument receives a pointer to an array in channel-major order, i.e. to compute a linear index into that array from both&lt;br /&gt;
channel and sample index, use&lt;br /&gt;
 idx = channel * samples + sample&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_ReleaseSignal(SSC_HANDLE)===&lt;br /&gt;
Releases a locked signal. Call this function as soon as you are done with forwarding or processing signal data.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_LockStates(SSC_HANDLE, const double** pStateData, int* count, int* samples, const char* const** pStateNames)===&lt;br /&gt;
Lock state data and obtain state information. Typically, this function will be called from a state callback.&lt;br /&gt;
Locking blocks the arrival of new state data, so call &amp;lt;tt&amp;gt;SCC_ReleaseStates()&amp;lt;/tt&amp;gt; as soon as possible.&lt;br /&gt;
All pointer arguments are optional, may be &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;pStateData&amp;#039;&amp;#039; argument receives a pointer to an array in channel-major format, i.e. use the following formula to compute a linear index from state and sample index:&lt;br /&gt;
 idx = state * samples + sample&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_ReleaseStates(SSC_HANDLE)===&lt;br /&gt;
Releases locked state information. Call this function as soon as you are done with forwarding or processing state data.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_LockParameters(SSC_HANDLE, const char* const** parameterLines, int* count)===&lt;br /&gt;
Lock parameter data and obtain parameter information. Typically, this function will be called from a parameter callback.&lt;br /&gt;
Locking blocks the arrival of new parameter data, so call &amp;lt;tt&amp;gt;SCC_ReleaseParameters)&amp;lt;/tt&amp;gt; as soon as possible.&lt;br /&gt;
All pointer arguments are optional, may be &amp;lt;tt&amp;gt;nullptr&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_ReleaseParameters(SSC_HANDLE)===&lt;br /&gt;
Releases locked parameter information. Call this function as soon as you are done with forwarding or processing parameter data.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_GetSignalDimensions(SSC_HANDLE, int* channels, int* samples)===&lt;br /&gt;
Obtain signal dimensions for use with &amp;lt;tt&amp;gt;SSC_PushSignal()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_PushSignal(SSC_HANDLE, const double* pSignal)===&lt;br /&gt;
Push signal data to BCI2000. The &amp;#039;&amp;#039;pSignal&amp;#039;&amp;#039; pointer must point to a valid memory area that is at least as large as determined by a previous call to &amp;lt;tt&amp;gt;SSC_GetSignalDimensions&amp;lt;/tt&amp;gt;. Pushing signal data will have no effect unless &amp;lt;tt&amp;gt;SSC_Commit()&amp;lt;/tt&amp;gt; is called.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_PushState(SSC_HANDLE, const char* name, unsigned int value)===&lt;br /&gt;
Push a state value to BCI2000. Pushing a state value will have no effect unless &amp;lt;tt&amp;gt;SSC_Commit()&amp;lt;/tt&amp;gt; is called.&lt;br /&gt;
&lt;br /&gt;
===SSC_RESULT SSC_Commit(SSC_HANDLE)===&lt;br /&gt;
Commit pushed signal and state data to the BCI2000 pipeline. Any previously pushed signal or state data will be inserted into the BCI2000 pipeline when this function is called.&lt;br /&gt;
&lt;br /&gt;
==Callback Function Type==&lt;br /&gt;
To be registered using &amp;lt;tt&amp;gt;SSC_Register*Callback()&amp;lt;/tt&amp;gt;, a function must be a free (non-member) or static function, and have the following signature:&lt;br /&gt;
 void MyCallback(void* refdata)&lt;br /&gt;
The &amp;#039;&amp;#039;refdata&amp;#039;&amp;#039; argument will hold the struct or object pointer specified as &amp;#039;&amp;#039;refdata&amp;#039;&amp;#039; when calling &amp;lt;tt&amp;gt;SSC_Register*Callback()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Error Codes==&lt;br /&gt;
All of the above functions return an error code of type &amp;lt;tt&amp;gt;SSC_ERROR&amp;lt;/tt&amp;gt;.&lt;br /&gt;
The following codes are defined:&lt;br /&gt;
{|    &lt;br /&gt;
|    SSC_SUCCESS || The operation completed successfully. &lt;br /&gt;
|-&lt;br /&gt;
|    SSC_INVALID_ARG || One or more of the function&amp;#039;s arguments were invalid.&lt;br /&gt;
|-&lt;br /&gt;
|    SSC_CANNOT_LISTEN || The connector was created but could not bind to/listen on the given address.&lt;br /&gt;
|-&lt;br /&gt;
|    SSC_CANNOT_FULFILL_REQUEST || The connector is in a state in which it cannot fulfill the request, try again later.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[Programming Reference:SignalSharingClientLibDemo]],&lt;br /&gt;
[[Technical Reference:BCI2000Remote Library]], [[Programming Reference:BCI2000Remote Class]], [[Contributions:BCI2000Command]], [[Contributions:BCI2000PresentationLink]], &amp;lt;!--[[Contributions:BCI2000Automation]],--&amp;gt;&lt;br /&gt;
[[Contributions:Applications]]&lt;br /&gt;
&lt;br /&gt;
[[Category:External Interfaces]][[Category:User Application]]&lt;/div&gt;</summary>
		<author><name>Mellinger</name></author>
	</entry>
</feed>