The EEGsynth, especially the modules (see below), are programmed mainly in Python, which is the open-source programming language of choice for programming by many scientists, professions and hobbyists. We use a minimum of compiled C-code. We are currently in the process of documenting the EEGsynth so that anyone should be able to understand it enough to use it, and that those who have a bit of experience with programming and hardware will be able to make their own. Here we explain how we designed the EEGsynth. If you want to install your own EEGsynth, and go into more detail, you can follow the more technical and specific documentaion and installation instructions on our GitHub page.
For many reasons, not the least to allow easy distributed collaboration, we developed a code architecture where the functions of the EEGsynth are split up into parallel modules, directly inspired by the modular synthesizer. Similarly as in a modular synthesizer, simple modules are connected, or “patched” together, to create complex and flexible behavior. The idea is that each module runs in parallel, performing a particular function. As an example, imagine module A is responsible for analyzing ECG (voltages from the heart) to determine the heart rate, while Module B sends out a signal to a synthesizer at every n milliseconds, i.e. functioning as a simple sequencer. By connecting (patching) the input of module B with the output of module A, the EEGsynth can be made to control a synthesizer at the speed of the heart rate.
So, how have we implemented the patching of modules? Indeed, there are many ways of doing this. Importantly, there it has to be a way by which modules can be connected in a flexible way, allowing many-to-many connections. Take a google-image look at analog modular synthesizers, and you know what I mean. We have opted to use the open-source Redis database which stores attribute-value pairs. Attribute-value pairs are nothing more than an attribute name with a value assigned to it, such as [“Name”, “John”] or [“Height”, 1.82]. A module can put anything it wants into the database, such as [“Heartrate”, 92]. Another module can ask the database to bring back the value belonging to [“Heartrate”]. Of course, the modules will need to know what to ask for, and where to (out)put their own values for other modules (if any) to use.
What is important to understand, is that there has to be a place to do all this book-keeping of the patches. It is not enough to know what modules are patched with what other modules, as modules can have more than one input and more than one output. So, in other words, we need a place where we specify which input attributes are connected to which output attributes. Furthermore, because modules are fully independent and don’t know anything about the other modules we need several places to do this – one for each module. Each modules therefor has it’s own .ini file (initialization file).
The .ini file is a basic text file with simple human-understandable formatting (according to Python’s ConfigParser class) where we define the attribute names for input and output. In addition, we will need to specify default values for the input attributes, in case the other module it depends on hasn’t supplied any yet. Finally, several modules need settings that have to be changes for different setups, e.g. which USB port to send MIDI codes to, or to receive control signals from. The .ini files can be edited with any text editor, or via a web interface the EEGsynth supplies.
The essential modules
Some modules you will need in most, if not all possible uses of the EEGsynth. Please note that we are in a time of development so many details are changing as you read this. Still, it is good to run by the most essential ones now so that we can talk more about how it they all fit together.
If you haven’t yet, take at least a look at the description of the FieldTrip realtime buffer. The FieldTrip realtime buffer was originally intended for use with MATLAB, but is platform and language independent. In short, it is a server that takes care of temporarily storing data in a ring-buffer (first in, first out), and takes care of receiving and returning data. In practice, the FieldTrip realtime buffer is the place where modules can ask for new data that has become available during measurements.
The openbci2ft (OpenBCI-to-FieldTrip realtime buffer) module retrieves data from the OpenBCI EEG acquisition hardware, and sends it to the FieldTrip realtime buffer (buffer module) using the FieldTrip openbci2ft function. This is specific for a setup that uses the OpenBCI hardware. FieldTrip has other [devicename]2ft functions that can take care of data acquisition by many other devices. For more information about the other devices that are supported take a look at the specific implementations.
Finally, we want the EEGsynth to control sound devices such as synthesizers, in particular modular synthesizers, and more specifically analog modular synthesizers. Analogue modular synthesizers use analogue signals to communicate between modules. CV stands for Control Voltage and is a voltage that can continuously fluctuate between 0 and 10 volts (or from 0 to 5, or from 0 to 12, or -5 to +5 Volts. Yes, its a hassle). The voltage typically determines the strength of the effect. So CV is best used to control changes in time, while gate is used to signal events. A gate is on when it is at 5 (or 10 or 12) Volts, and off when it is at 0 Volts. It is typically on for a shorter time, for instance signalling the press of a keyboard key, triggering a burst of activity or start of some other process.
It is important to realize that CV and gate function differently in time. Gate is something a module might be waiting for, ready to respond, while a CV is something that is used continuously. We have implemented these two different causal relationships in the code as well. In fact, it is similar to two different ways of database communication: publish-subscribe and set-get. The set-get way of doing things considers the Redis database as an archive in which modules can store attribute-value pairs which will stay there indefinitely (persistently), at least until it is updated or restarted. This would be used when you want to create a CV signal that drives an oscillator on a synthesizer, and that you want to change according to an EMG signal. In contrast, the publish-subscribe (pub-sub) approach does not store any values persistently, but signals events that last shortly but have to be responded to directly. For instance when you press a button on the LaunchControl or MIDI keyboard, or when detecting an eyeblink, that then has to result in an event. This would typically be in the form of a gate signal, or MIDI event.
This page is not the place to discuss all modules in detail, especially since we already see the number of modules being extended and developed independently from us by an enthusiastic community. Detailed information about each module can be found in the README.md that is included in each module directory. You can also read them online on the GitHub site: