Modifying the host application#
We welcome contributions to the main plugin-GUI repository, but these may require a bit more prior knowledge than building or modifying plugins. This page aims to provide a high-level overview of the GUI’s code base. However, if you’d like to make changes more substantial than a minor bug fix, we strong recommend submitting an issue on GitHub describing your plans, so the Open Ephys core team can help guide you in the right direction.
Recommended workflow#
If you’re planning to update the host application, or any of the plugins in the plugin-GUI repository, we encourage you to follow these steps:
Fork the GUI’s GitHub repository to your own account.
Clone the fork via the command line or the GitHub Desktop app.
Compile the GUI for your platform of choice according to the steps on this page.
Open an issue describing the changes you’d like to make, whether it’s fixing a bug or adding a new feature. This will ensure that we avoid any redundant development efforts.
Create a new branch for your changes based on the main branch:
git checkout -b <newbranchname>
Make your changes (and ensure that the GUI successfully compiles).
Push your changes to GitHub:
git push -u origin <newbranchname>
Open a pull request against the development branch of the main GUI repository.
If your changes look good, they will be merged and included in the next release!
Code organization#
The top-level directory of the plugin-GUI repository contains the following folders:
.github - GitHub Actions scripts for building the GUI on Linux, macOS, and Windows. Whenever the code is updated in the testing or master branches, these scripts will automatically regenerate the GUI’s binaries and upload them to Artifactory.
Build - empty directory where platform-specific build files will be located. Any files that end up in this directory will not be included in your git commits.
JuceLibraryCode - a customized version of the source code for the Juce Library. The Open Ephys GUI uses this library for a wide variety of functions, including drawing the user interface, real-time data processing, threading, and file I/O.
Plugins - source code for plugins that are included by default with the host application.
Resources - includes a variety of useful resources, including sample data files, scripts, and libraries used by the GUI.
Source - the source code for the host application. The key classes are described below.
Program structure#
This diagram showcases how key C++ classes interact within the host application:
Solid black arrows denote ownership: which objects are responsible for creating and destroying other objects?
Dashed black arrows indicate the precisely timed callbacks that drive data acquisition.
Red arrows show how data flows through the application. All of the data processing takes place within the ProcessorGraph, but may leave the graph to be stored to disk by the RecordNode, sent to audio monitors by the AudioNode, or visualized within the DataViewport.
Orange arrows denote the essential message-passing interactions. The messages sent between the EditorViewport and the ProcessorGraph are especially important, as they are responsible for constructing and verifying the signal chain.
Core application classes#
When the application starts, the first object created is an OpenEphysApplication, which is derived from the JUCEApplication
class. This object initializes the application and owns the MainWindow, but doesn’t do much else. When the MainWindow is created, it creates the three central objects of the application: the UIComponent, the AudioComponent, and the ProcessorGraph.
The UIComponent owns all of the objects the user can interact with directly. Editing the ProcessorGraph, starting and stopping acquisition, and receiving updates about the application’s state is all handled by objects created by the UIComponent. It is derived from Juce’s
Component
classThe AudioComponent communicates with the computer’s audio card, which drives the callbacks to the ProcessorGraph and enables audio monitoring of incoming data. It is derived from Juce’s
AudioDeviceManager
class.The ProcessorGraph stores information about the signal chain and ensure that data is processed efficiently. It is derived from Juce’s
AudioProcessorGraph
class.
User interface classes#
The UIComponent has five main regions, each of which is an object that it is responsible for creating and destroying:
The ControlPanel occupies the top of the application window. It contains a CPU usage meter, a disk space meter, a play button, and a record button. It also own the Clock, which displays the total time spent acquiring or recording data.
The ProcessorList sits on the left-hand side of the application window and contains a list of all the available modules for creating and processing data. Modules are selected from the list and dragged onto the EditorViewport to construct the signal chain. The ProcessorList can be collapsed to provide additional room for data visualization once the signal chain is in place.
The EditorViewport, near the bottom of the application window, displays the editors for all of the monitors currently in the signal chain. The editors provide a graphical interface for modifying the parameters of each module. Once they’re inside the EditorViewport, editors can be dragged and dropped to change their ordering within a signal chain, but only if acquisition is paused.
The MessageCenter lives at the bottom of the application window and displays messages that are relevant to the user. Most of these messages currently originate in the ProcessorGraph, but they could come from almost any object within the program.
The DataViewport contains tabs for different types of visualizers. Whenever a plugin with a visualizer is added to the signal chain, it can either claim a tab within the DataViewport or place its display in a separate window. This affords the GUI incredible flexibility. In a setup with multiple monitors, each visualizer can occupy a separate window on its own screen. If the GUI is being used on a laptop, all of the visualizers are organized into easy-to-find tabs.
The processor graph#
The ProcessorGraph manages user-defined data processing pipelines composed of different combinations of plugins. All of the modules that create the ProcessorGraph are derived from a custom GenericProcessor class.
There are five main types of plugins that can be placed in the ProcessorGraph:
Sources feed data into the graph. They can only have outputs, never inputs. Every valid signal chain must have at least one source.
Filters modify the data they receive. They can be as simple as bandpass filters or resamplers, or more complex spike detectors or ripple/spindle detectors. Filters can process continuous signals or discrete events.
Sinks send data outside of the signal chain, but do not modify the underlying data stream. Examples of sinks include graphical displays, network streams, or plugins that communicate with digital output hardware (such as Arduinos).
Utilities perform specialized functions such as merging or splitting signal chains, or starting/stopping recording based on digital inputs.
Record Nodes write incoming data to disk in a format specified by the currently loaded RecordEngine. It’s possible to use multiple Record Nodes in a single signal chain, in order to record data at different processing stages.
In addition to the user-defined signal chain, one module is created by default:
The AudioNode allows signals to be sent to the computer’s audio card, for purposes of audio monitoring. The last two channels of every Audio Monitor are automatically connected to the Audio Node.