Skip to content
Snippets Groups Projects
Commit 97f1e2e3 authored by kmichaelfox's avatar kmichaelfox
Browse files

add channel height zoom, channel display order reversing, and channel display skipping

parent b0c1f4f5
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 open-ephys. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
This diff is collapsed.
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2013 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LFPDISPLAYCANVAS_H_Alpha__
#define __LFPDISPLAYCANVAS_H_Alpha__
#include <VisualizerWindowHeaders.h>
#include "LfpDisplayNode.h"
#include <vector>
#include <array>
#define CHANNEL_TYPES 3
#define MAX_N_CHAN 2048
#define MAX_N_SAMP 5000
#define MAX_N_SAMP_PER_PIXEL 100
namespace LfpDisplayNodeAlpha {
class LfpDisplayNode;
class LfpTimescale;
class LfpDisplay;
class LfpChannelDisplay;
class LfpChannelDisplayInfo;
class EventDisplayInterface;
class LfpViewport;
class LfpDisplayOptions;
//==============================================================================
/**
Displays multiple channels of continuous data.
@see LfpDisplayNode, LfpDisplayEditor
*/
class LfpDisplayCanvas : public Visualizer,
public KeyListener
{
public:
LfpDisplayCanvas(LfpDisplayNode* n);
~LfpDisplayCanvas();
void beginAnimation();
void endAnimation();
void refreshState();
void update();
void setParameter(int, float);
void setParameter(int, int, int, float) {}
void paint(Graphics& g);
void refresh();
void resized();
/** Resizes the LfpDisplay to the size required to fit all channels
@param respectViewportPosition (optional) if true, viewport automatically
scrolls to maintain view prior to resizing
*/
void resizeToChannels(bool respectViewportPosition = false);
void toggleOptionsDrawer(bool);
int getChannelHeight();
float channelOverlapFactor;
float histogramParameterA;
float histogramParameterB;
int getNumChannels();
/** Returns the number of channels NOT hidden for display */
int getNumChannelsVisible();
bool getInputInvertedState();
bool getDrawMethodState();
const float getXCoord(int chan, int samp);
const float getYCoord(int chan, int samp);
std::array<float, MAX_N_SAMP_PER_PIXEL> getSamplesPerPixel(int chan, int px);
const int getSampleCountPerPixel(int px);
const float getYCoordMin(int chan, int samp);
const float getYCoordMean(int chan, int samp);
const float getYCoordMax(int chan, int samp);
float getMean(int chan);
float getStd(int chan);
Array<int> screenBufferIndex;
Array<int> lastScreenBufferIndex;
void saveVisualizerParameters(XmlElement* xml);
void loadVisualizerParameters(XmlElement* xml);
bool keyPressed(const KeyPress& key);
bool keyPressed(const KeyPress& key, Component* orig);
//void scrollBarMoved(ScrollBar *scrollBarThatHasMoved, double newRangeStart);
bool fullredraw; // used to indicate that a full redraw is required. is set false after each full redraw, there is a similar switch for each display;
static const int leftmargin=50; // left margin for lfp plots (so the ch number text doesnt overlap)
Array<bool> isChannelEnabled;
bool drawClipWarning; // optinally draw (subtle) warning if data is clipped in display
bool drawSaturationWarning; // optionally raise hell if the actual data is saturating
int nChans;
int nChansVisible; // the number of channels NOT hidden for display
float timebase;
void redraw();
DataChannel::DataChannelTypes selectedChannelType;
ScopedPointer<LfpViewport> viewport;
private:
Array<float> sampleRate;
bool optionsDrawerIsOpen;
float displayGain;
float timeOffset;
//int spread ; // vertical spacing between channels
//float waves[MAX_N_CHAN][MAX_N_SAMP*2]; // we need an x and y point for each sample
LfpDisplayNode* processor;
AudioSampleBuffer* displayBuffer; // sample wise data buffer for display
ScopedPointer<AudioSampleBuffer> screenBuffer; // subsampled buffer- one int per pixel
//'define 3 buffers for min mean and max for better plotting of spikes
// not pretty, but 'AudioSampleBuffer works only for channels X samples
ScopedPointer<AudioSampleBuffer> screenBufferMin; // like screenBuffer but holds min/mean/max values per pixel
ScopedPointer<AudioSampleBuffer> screenBufferMean; // like screenBuffer but holds min/mean/max values per pixel
ScopedPointer<AudioSampleBuffer> screenBufferMax; // like screenBuffer but holds min/mean/max values per pixel
MidiBuffer* eventBuffer;
ScopedPointer<LfpTimescale> timescale;
ScopedPointer<LfpDisplay> lfpDisplay;
ScopedPointer<LfpDisplayOptions> options;
void refreshScreenBuffer();
void updateScreenBuffer();
Array<int> displayBufferIndex;
int displayBufferSize;
int scrollBarThickness;
//float samplesPerPixel[MAX_N_SAMP][MAX_N_SAMP_PER_PIXEL];
//float*** samplesPerPixel;
void resizeSamplesPerPixelBuffer(int numChannels);
std::vector<std::array<std::array<float, MAX_N_SAMP_PER_PIXEL>, MAX_N_SAMP>> samplesPerPixel;
int sampleCountPerPixel[MAX_N_SAMP];
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LfpDisplayCanvas);
};
//==============================================================================
/**
Toggles view options drawer for LfpDisplayCanvas.
*/
class ShowHideOptionsButton : public Button
{
public:
ShowHideOptionsButton(LfpDisplayOptions*);
virtual ~ShowHideOptionsButton();
void paintButton(Graphics& g, bool, bool);
LfpDisplayOptions* options;
};
class LfpDisplayOptions : public Component,
public Slider::Listener,
public ComboBox::Listener,
public Button::Listener
{
public:
LfpDisplayOptions(LfpDisplayCanvas*, LfpTimescale*, LfpDisplay*, LfpDisplayNode*);
~LfpDisplayOptions();
void paint(Graphics& g);
void resized();
void setRangeSelection(float range, bool canvasMustUpdate = false); // set range selection combo box to correct value if it has been changed by scolling etc.
void setSpreadSelection(int spread, bool canvasMustUpdate = false); // set spread selection combo box to correct value if it has been changed by scolling etc.
void comboBoxChanged(ComboBox* cb);
void buttonClicked(Button* button);
/** Handles slider events for all editors. */
void sliderValueChanged(Slider* sl);
/** Called by sliderValueChanged(). Deals with clicks on custom sliders. Subclasses
of GenericEditor should modify this method only.*/
void sliderEvent(Slider* sl);
int getChannelHeight();
bool getDrawMethodState();
bool getInputInvertedState();
//void setRangeSelection(float range, bool canvasMustUpdate);
void setSpreadSelection();
void togglePauseButton();
void saveParameters(XmlElement* xml);
void loadParameters(XmlElement* xml);
DataChannel::DataChannelTypes getChannelType(int n);
DataChannel::DataChannelTypes getSelectedType();
String getTypeName(DataChannel::DataChannelTypes type);
int getRangeStep(DataChannel::DataChannelTypes type);
void setSelectedType(DataChannel::DataChannelTypes type, bool toggleButton = true);
int selectedSpread;
String selectedSpreadValue;
int selectedTimebase;
String selectedTimebaseValue;
int selectedOverlap;
String selectedOverlapValue;
int selectedChannelDisplaySkip;
String selectedChannelDisplaySkipValue;
enum ChannelDisplaySkipValue {
None = 0,
One,
Two,
Four,
Eight,
Sixteen,
ThirtyTwo
} enum_selectedChannelDisplaySkipValue = None;
int selectedSaturation; // for saturation warning
String selectedSaturationValue;
float selectedSaturationValueFloat; // TODO: this is way ugly - we should refactor all these parameters soon and get them into a nicer format- probably when we do the general plugin parameter overhaul.
private:
LfpDisplayCanvas* canvas;
LfpDisplay* lfpDisplay;
LfpTimescale* timescale;
LfpDisplayNode* processor;
Font labelFont;
Colour labelColour;
ScopedPointer<ComboBox> timebaseSelection;
ScopedPointer<ComboBox> rangeSelection;
ScopedPointer<ComboBox> spreadSelection;
ScopedPointer<ComboBox> overlapSelection;
ScopedPointer<UtilityButton> drawClipWarningButton; // optinally draw (subtle) warning if data is clipped in display
ScopedPointer<ComboBox> saturationWarningSelection;
ScopedPointer<UtilityButton> drawSaturateWarningButton; // optionally raise hell if the actual data is saturating
ScopedPointer<ComboBox> colorGroupingSelection;
ScopedPointer<UtilityButton> invertInputButton;
ScopedPointer<UtilityButton> drawMethodButton;
ScopedPointer<UtilityButton> pauseButton;
OwnedArray<UtilityButton> typeButtons;
// label and slider for channel vertical zoom
ScopedPointer<Label> channelZoomSliderLabel;
ScopedPointer<Slider> channelZoomSlider;
// label and button for reversing the order of displayed channels
ScopedPointer<Label> reverseChannelsDisplayLabel;
ScopedPointer<UtilityButton> reverseChannelsDisplayButton;
// label and combobox for channel skipping in display
ScopedPointer<Label> channelDisplaySkipLabel;
ScopedPointer<ComboBox> channelDisplaySkipSelection;
StringArray channelDisplaySkipOptions;
ScopedPointer<Slider> brightnessSliderA;
ScopedPointer<Slider> brightnessSliderB;
ScopedPointer<Label> sliderALabel;
ScopedPointer<Label> sliderBLabel;
ScopedPointer<ShowHideOptionsButton> showHideOptionsButton;
StringArray voltageRanges[CHANNEL_TYPES];
StringArray timebases;
StringArray spreads; // option for vertical spacing between channels
StringArray colorGroupings; // option for coloring every N channels the same
StringArray overlaps; //
StringArray saturationThresholds; //default values for when different amplifiers saturate
DataChannel::DataChannelTypes selectedChannelType;
int selectedVoltageRange[CHANNEL_TYPES];
String selectedVoltageRangeValues[CHANNEL_TYPES];
float rangeGain[CHANNEL_TYPES];
StringArray rangeUnits;
StringArray typeNames;
int rangeSteps[CHANNEL_TYPES];
OwnedArray<EventDisplayInterface> eventDisplayInterfaces;
};
//==============================================================================
/**
Displays the timescale of the LfpDisplayCanvas in the viewport.
*/
class LfpTimescale : public Component
{
public:
LfpTimescale(LfpDisplayCanvas*);
~LfpTimescale();
void paint(Graphics& g);
void setTimebase(float t);
private:
LfpDisplayCanvas* canvas;
float timebase;
Font font;
StringArray labels;
};
//==============================================================================
/**
Holds and draws all of the LfpDisplayChannel and lfpDisplayChannelInfo
instances.
All of the channels and channelInfos are drawn here to a "master" bitmap
lfpChannelBitmap with height equal to the sum of all channel heights. This
bitmap is drawn by the LfpViewport using Viewport::setViewedComponent.
*/
class LfpDisplay : public Component
{
public:
LfpDisplay(LfpDisplayCanvas*, Viewport*);
~LfpDisplay();
Image lfpChannelBitmap; // plot as bitmap instead of separately setting pixels
// this is done purely for the preformance improvement
void setNumChannels(int numChannels);
int getNumChannels();
int getTotalHeight();
void paint(Graphics& g);
void refresh();
void resized();
void reactivateChannels();
void mouseDown(const MouseEvent& event);
void mouseWheelMove(const MouseEvent& event, const MouseWheelDetails& wheel) ;
void setRange(float range, DataChannel::DataChannelTypes type);
//Withouth parameters returns selected type
int getRange();
int getRange(DataChannel::DataChannelTypes type);
void setChannelHeight(int r, bool resetSingle = true);
int getChannelHeight();
void setInputInverted(bool);
void setDrawMethod(bool);
/** Returns a bool indicating if the channels are displayed in reverse order (true) */
bool getChannelsReversed();
/** Reorders the displayed channels, reversed if state == true and normal if false */
void setChannelsReversed(bool state);
void setColors();
bool setEventDisplayState(int ch, bool state);
bool getEventDisplayState(int ch);
int getColorGrouping();
void setColorGrouping(int i);
void setEnabledState(bool state, int chan, bool updateSavedChans = true);
bool getEnabledState(int);
bool getSingleChannelState();
Colour backgroundColour;
Array<Colour> channelColours;
Array<LfpChannelDisplay*> channels;
Array<LfpChannelDisplayInfo*> channelInfo;
struct LfpChannel
{
LfpChannelDisplay * channel;
LfpChannelDisplayInfo * channelInfo;
};
Array<LfpChannel> drawableChannels;
bool eventDisplayEnabled[8];
bool isPaused; // simple pause function, skips screen bufer updates
void toggleSingleChannel(int chan = -2);
LfpDisplayOptions* options;
private:
int singleChan;
Array<bool> savedChannelState;
int numChans;
int totalHeight;
int colorGrouping;
bool channelsReversed;
LfpDisplayCanvas* canvas;
Viewport* viewport;
float range[3];
};
//==============================================================================
/**
Displays the information pertaining to a single data channel.
*/
class LfpChannelDisplay : public Component
{
public:
LfpChannelDisplay(LfpDisplayCanvas*, LfpDisplay*, LfpDisplayOptions*, int channelNumber);
~LfpChannelDisplay();
void resized();
void paint(Graphics& g);
void pxPaint(); // like paint, but just populate lfpChannelBitmap
// needs to avoid a paint(Graphics& g) mechanism here becauswe we need to clear the screen in the lfpDisplay repaint(),
// because otherwise we cant deal with the channel overlap (need to clear a vertical section first, _then_ all channels are dawn, so cant do it per channel)
void select();
void deselect();
bool getSelected();
void setName(String);
void setColour(Colour c);
void setChannelHeight(int);
int getChannelHeight();
void setChannelOverlap(int);
int getChannelOverlap();
/** Return the assigned channel number for this display */
int getChannelNumber();
void setRange(float range);
int getRange();
void setInputInverted(bool);
void setCanBeInverted(bool);
void setDrawMethod(bool);
PopupMenu getOptions();
void changeParameter(const int id);
void setEnabledState(bool);
bool getEnabledState()
{
return isEnabled;
}
/** Set the isHidden flag, indicates whether this channel display
should render to screen or not */
void setHidden(bool);
/** Return a bool flag describing whether this channel display is
hidden from the canvas */
bool getHidden() {
return isHidden;
}
DataChannel::DataChannelTypes getType();
void updateType();
bool fullredraw; // used to indicate that a full redraw is required. is set false after each full redraw
protected:
LfpDisplayCanvas* canvas;
LfpDisplay* display;
LfpDisplayOptions* options;
bool isSelected;
bool isHidden;
int chan;
String name;
Font channelFont;
Colour lineColour;
int channelOverlap;
int channelHeight;
float channelHeightFloat;
float range;
bool isEnabled;
bool inputInverted;
bool canBeInverted;
bool drawMethod;
DataChannel::DataChannelTypes type;
String typeStr;
};
//==============================================================================
/**
Displays meta data pertaining to an associated channel, such as channel number.
The enableButton displays the channel number and toggles the drawing of the
associated LfpChannelDisplay waveform on or off.
*/
class LfpChannelDisplayInfo : public LfpChannelDisplay,
public Button::Listener
{
public:
LfpChannelDisplayInfo(LfpDisplayCanvas*, LfpDisplay*, LfpDisplayOptions*, int channelNumber);
void paint(Graphics& g);
void buttonClicked(Button* button);
void resized();
void setEnabledState(bool);
void updateType();
void updateXY(float, float);
void setSingleChannelState(bool);
private:
bool isSingleChannel;
float x, y;
ScopedPointer<UtilityButton> enableButton;
};
//==============================================================================
/**
Interface class for Event Display channels.
*/
class EventDisplayInterface : public Component,
public Button::Listener
{
public:
EventDisplayInterface(LfpDisplay*, LfpDisplayCanvas*, int chNum);
~EventDisplayInterface();
void paint(Graphics& g);
void buttonClicked(Button* button);
void checkEnabledState();
bool isEnabled;
private:
int channelNumber;
LfpDisplay* display;
LfpDisplayCanvas* canvas;
ScopedPointer<UtilityButton> chButton;
};
//==============================================================================
/**
Encapsulates the logic for the LfpDisplayCanvas's viewable area and user inter-
action (scrolling) when drawn in the environment.
Not much is overridden here, it uses mostly JUCE's Viewport functionality
by inheriting Viewport but stores a reference to the LfpDisplayCanvas.
@see Viewport, LfpDisplayCanvas
*/
class LfpViewport : public Viewport
{
public:
LfpViewport(LfpDisplayCanvas* canvas);
void visibleAreaChanged(const Rectangle<int>& newVisibleArea);
private:
LfpDisplayCanvas* canvas;
};
};
#endif // __LFPDISPLAYCANVAS_H_Alpha__
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2013 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LfpDisplayEditor.h"
using namespace LfpDisplayNodeAlpha;
LfpDisplayEditor::LfpDisplayEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors=true)
: VisualizerEditor(parentNode, useDefaultParameterEditors)
{
tabText = "LFP";
desiredWidth = 180;
}
LfpDisplayEditor::~LfpDisplayEditor()
{
}
Visualizer* LfpDisplayEditor::createNewCanvas()
{
LfpDisplayNode* processor = (LfpDisplayNode*) getProcessor();
return new LfpDisplayCanvas(processor);
}
// not really being used (yet)...
void LfpDisplayEditor::buttonEvent(Button* button)
{
}
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2013 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LFPDISPLAYEDITOR_H_Alpha__
#define __LFPDISPLAYEDITOR_H_Alpha__
#include <VisualizerEditorHeaders.h>
#include "LfpDisplayNode.h"
#include "LfpDisplayCanvas.h"
class Visualizer;
namespace LfpDisplayNodeAlpha {
/**
User interface for the LfpDisplayNode sink.
@see LfpDisplayNode, LfpDisplayCanvas
*/
class LfpDisplayEditor : public VisualizerEditor
{
public:
LfpDisplayEditor(GenericProcessor*, bool useDefaultParameterEditors);
~LfpDisplayEditor();
// not really being used (yet) ...
void buttonEvent(Button* button);
Visualizer* createNewCanvas();
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LfpDisplayEditor);
};
};
#endif // __LFPDISPLAYEDITOR_H_Alpha__
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2016 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LfpDisplayNode.h"
#include "LfpDisplayCanvas.h"
#include <stdio.h>
using namespace LfpDisplayNodeAlpha;
LfpDisplayNode::LfpDisplayNode()
: GenericProcessor ("LFP Viewer Alpha")
, displayGain (1)
, bufferLength (20.0f)
, abstractFifo (100)
{
setProcessorType (PROCESSOR_TYPE_SINK);
displayBuffer = new AudioSampleBuffer (8, 100);
const int heapSize = 5000;
arrayOfOnes = new float[heapSize];
for (int n = 0; n < heapSize; ++n)
{
arrayOfOnes[n] = 1;
}
}
LfpDisplayNode::~LfpDisplayNode()
{
delete[] arrayOfOnes;
}
AudioProcessorEditor* LfpDisplayNode::createEditor()
{
editor = new LfpDisplayEditor (this, true);
return editor;
}
void LfpDisplayNode::updateSettings()
{
std::cout << "Setting num inputs on LfpDisplayNode to " << getNumInputs() << std::endl;
channelForEventSource.clear();
eventSourceNodes.clear();
ttlState.clear();
for (int i = 0; i < eventChannelArray.size(); ++i)
{
uint32 sourceID = getChannelSourceID(eventChannelArray[i]);
if (!eventSourceNodes.contains(sourceID))
{
eventSourceNodes.add(sourceID);
}
}
numEventChannels = eventSourceNodes.size();
std::cout << "Found " << numEventChannels << " event channels." << std::endl;
for (int i = 0; i < eventSourceNodes.size(); ++i)
{
std::cout << "Adding channel " << getNumInputs() + i << " for event source node " << eventSourceNodes[i] << std::endl;
channelForEventSource[eventSourceNodes[i]] = getNumInputs() + i;
ttlState[eventSourceNodes[i]] = 0;
}
displayBufferIndex.clear();
displayBufferIndex.insertMultiple (0, 0, getNumInputs() + numEventChannels);
}
uint32 LfpDisplayNode::getChannelSourceID(const EventChannel* event) const
{
int metaDataIndex = event->findMetaData(MetaDataDescriptor::UINT16, 3, "source.channel.identifier.full");
if (metaDataIndex < 0)
{
return getProcessorFullId(event->getSourceNodeID(), event->getSubProcessorIdx());
}
uint16 values[3];
event->getMetaDataValue(metaDataIndex)->getValue(static_cast<uint16*>(values));
return getProcessorFullId(values[1], values[2]);
}
bool LfpDisplayNode::resizeBuffer()
{
int nSamples = (int) getSampleRate() * bufferLength;
int nInputs = getNumInputs();
std::cout << "Resizing buffer. Samples: " << nSamples << ", Inputs: " << nInputs << std::endl;
if (nSamples > 0 && nInputs > 0)
{
abstractFifo.setTotalSize (nSamples);
displayBuffer->setSize (nInputs + numEventChannels, nSamples); // add extra channels for TTLs
return true;
}
else
{
return false;
}
}
bool LfpDisplayNode::enable()
{
if (resizeBuffer())
{
LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor();
editor->enable();
return true;
}
else
{
return false;
}
}
bool LfpDisplayNode::disable()
{
LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor();
editor->disable();
return true;
}
void LfpDisplayNode::setParameter (int parameterIndex, float newValue)
{
editor->updateParameterButtons (parameterIndex);
//
//Sets Parameter in parameters array for processor
parameters[parameterIndex]->setValue (newValue, currentChannel);
//std::cout << "Saving Parameter from " << currentChannel << ", channel ";
LfpDisplayEditor* ed = (LfpDisplayEditor*) getEditor();
if (ed->canvas != 0)
ed->canvas->setParameter (parameterIndex, newValue);
}
void LfpDisplayNode::handleEvent(const EventChannel* eventInfo, const MidiMessage& event, int samplePosition)
{
if (Event::getEventType(event) == EventChannel::TTL)
{
TTLEventPtr ttl = TTLEvent::deserializeFromMessage(event, eventInfo);
//int eventNodeId = *(dataptr+1);
const int eventId = ttl->getState() ? 1 : 0;
const int eventChannel = ttl->getChannel();
const int eventTime = samplePosition;
const uint32 eventSourceNodeId = getChannelSourceID(eventInfo);
const int nSamples = getNumSourceSamples(eventSourceNodeId);
int samplesToFill = nSamples - eventTime;
if (samplesToFill < 0) samplesToFill = 0;
// std::cout << "Received event from " << eventSourceNode << ", channel "
// << eventChannel << ", with ID " << eventId << ", copying to "
// << channelForEventSource[eventSourceNode] << std::endl;
////
int bufferIndex = (displayBufferIndex[channelForEventSource[eventSourceNodeId]] + eventTime - nSamples)
% displayBuffer->getNumSamples();
bufferIndex = bufferIndex >= 0
? bufferIndex
: displayBuffer->getNumSamples() + bufferIndex;
if (eventId == 1)
{
ttlState[eventSourceNodeId] |= (1L << eventChannel);
}
else
{
ttlState[eventSourceNodeId] &= ~(1L << eventChannel);
}
if (samplesToFill + bufferIndex < displayBuffer->getNumSamples())
{
//std::cout << bufferIndex << " " << samplesToFill << " " << ttlState[eventSourceNode] << std::endl;
displayBuffer->copyFrom (channelForEventSource[eventSourceNodeId], // destChannel
bufferIndex, // destStartSample
arrayOfOnes, // source
samplesToFill, // numSamples
float (ttlState[eventSourceNodeId])); // gain
}
else
{
const int block2Size = (samplesToFill + bufferIndex) % displayBuffer->getNumSamples();
const int block1Size = samplesToFill - block2Size;
displayBuffer->copyFrom (channelForEventSource[eventSourceNodeId], // destChannel
bufferIndex, // destStartSample
arrayOfOnes, // source
block1Size, // numSamples
float (ttlState[eventSourceNodeId])); // gain
displayBuffer->copyFrom (channelForEventSource[eventSourceNodeId], // destChannel
0, // destStartSample
arrayOfOnes, // source
block2Size, // numSamples
float (ttlState[eventSourceNodeId])); // gain
}
// std::cout << "Received event from " << eventNodeId
// << " on channel " << eventChannel
// << " with value " << eventId
// << " at timestamp " << event.getTimeStamp() << std::endl;
}
}
void LfpDisplayNode::initializeEventChannels()
{
for (int i = 0; i < eventSourceNodes.size(); ++i)
{
const int chan = channelForEventSource[eventSourceNodes[i]];
const int index = displayBufferIndex[chan];
const int samplesLeft = displayBuffer->getNumSamples() - index;
const int nSamples = getNumSourceSamples(eventSourceNodes[i]);
//std::cout << "Event source node " << i << ", channel " << chan << std::endl;
if (nSamples < samplesLeft)
{
// std::cout << getNumInputs()+1 << " " << displayBufferIndex << " " << totalSamples << " " << ttlState << std::endl;
displayBuffer->copyFrom (chan, // destChannel
index, // destStartSample
arrayOfOnes, // source
nSamples, // numSamples
float (ttlState[eventSourceNodes[i]])); // gain
displayBufferIndex.set (chan, index + nSamples);
}
else
{
int extraSamples = nSamples - samplesLeft;
displayBuffer->copyFrom (chan, // destChannel
index, // destStartSample
arrayOfOnes, // source
samplesLeft, // numSamples
float (ttlState[eventSourceNodes[i]])); // gain
displayBuffer->copyFrom (chan, // destChannel
0, // destStartSample
arrayOfOnes, // source
extraSamples, // numSamples
float (ttlState[eventSourceNodes[i]])); // gain
displayBufferIndex.set (chan, extraSamples);
}
}
}
void LfpDisplayNode::process (AudioSampleBuffer& buffer)
{
// 1. place any new samples into the displayBuffer
//std::cout << "Display node sample count: " << nSamples << std::endl; ///buffer.getNumSamples() << std::endl;
initializeEventChannels();
checkForEvents (); // see if we got any TTL events
ScopedLock displayLock (displayMutex);
for (int chan = 0; chan < buffer.getNumChannels(); ++chan)
{
const int samplesLeft = displayBuffer->getNumSamples() - displayBufferIndex[chan];
const int nSamples = getNumSamples (chan);
if (nSamples < samplesLeft)
{
displayBuffer->copyFrom (chan, // destChannel
displayBufferIndex[chan], // destStartSample
buffer, // source
chan, // source channel
0, // source start sample
nSamples); // numSamples
displayBufferIndex.set (chan, displayBufferIndex[chan] + nSamples);
}
else
{
const int extraSamples = nSamples - samplesLeft;
displayBuffer->copyFrom (chan, // destChannel
displayBufferIndex[chan], // destStartSample
buffer, // source
chan, // source channel
0, // source start sample
samplesLeft); // numSamples
displayBuffer->copyFrom (chan, // destChannel
0, // destStartSample
buffer, // source
chan, // source channel
samplesLeft, // source start sample
extraSamples); // numSamples
displayBufferIndex.set (chan, extraSamples);
}
}
}
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2016 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LFPDISPLAYNODE_H_Alpha__
#define __LFPDISPLAYNODE_H_Alpha__
#include <ProcessorHeaders.h>
#include "LfpDisplayEditor.h"
class DataViewport;
namespace LfpDisplayNodeAlpha
{
/**
Holds data in a displayBuffer to be used by the LfpDisplayCanvas
for rendering continuous data streams.
@see GenericProcessor, LfpDisplayEditor, LfpDisplayCanvas
*/
class LfpDisplayNode : public GenericProcessor
{
public:
LfpDisplayNode();
~LfpDisplayNode();
AudioProcessorEditor* createEditor() override;
void process (AudioSampleBuffer& buffer) override;
void setParameter (int parameterIndex, float newValue) override;
void updateSettings() override;
bool enable() override;
bool disable() override;
void handleEvent (const EventChannel* eventInfo, const MidiMessage& event, int samplePosition = 0) override;
AudioSampleBuffer* getDisplayBufferAddress() const { return displayBuffer; }
int getDisplayBufferIndex (int chan) const { return displayBufferIndex[chan]; }
CriticalSection* getMutex() { return &displayMutex; }
private:
void initializeEventChannels();
ScopedPointer<AudioSampleBuffer> displayBuffer;
Array<int> displayBufferIndex;
Array<uint32> eventSourceNodes;
std::map<uint32, int> channelForEventSource;
int numEventChannels;
float displayGain; //
float bufferLength; // s
AbstractFifo abstractFifo;
int64 bufferTimestamp;
std::map<uint32, uint64> ttlState;
float* arrayOfOnes;
int totalSamples;
bool resizeBuffer();
CriticalSection displayMutex;
uint32 getChannelSourceID(const EventChannel* event) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LfpDisplayNode);
};
};
#endif // __LFPDISPLAYNODE_H_Alpha__
LIBNAME := $(notdir $(CURDIR))
OBJDIR := $(OBJDIR)/$(LIBNAME)
TARGET := $(LIBNAME).so
SRC_DIR := ${shell find ./ -type d -print}
VPATH := $(SOURCE_DIRS)
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp))
OBJ := $(addprefix $(OBJDIR)/,$(notdir $(SRC:.cpp=.o)))
BLDCMD := $(CXX) -shared -o $(OUTDIR)/$(TARGET) $(OBJ) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)
VPATH = $(SRC_DIR)
.PHONY: objdir
$(OUTDIR)/$(TARGET): objdir $(OBJ)
-@mkdir -p $(BINDIR)
-@mkdir -p $(LIBDIR)
-@mkdir -p $(OUTDIR)
@echo "Building $(TARGET)"
@$(BLDCMD)
$(OBJDIR)/%.o : %.cpp
@echo "Compiling $<"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
objdir:
-@mkdir -p $(OBJDIR)
clean:
@echo "Cleaning $(LIBNAME)"
-@rm -rf $(OBJDIR)
-@rm -f $(OUTDIR)/$(TARGET)
-include $(OBJ:%.o=%.d)
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2013 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../../Processors/PluginManager/OpenEphysPlugin.h"
#include "LfpDisplayNode.h"
#include <string>
#ifdef WIN32
#include <Windows.h>
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
using namespace Plugin;
using namespace LfpDisplayNodeAlpha;
#define NUM_PLUGINS 1
extern "C" EXPORT void getLibInfo(Plugin::LibraryInfo* info)
{
info->apiVersion = PLUGIN_API_VER;
info->name = "LFP viewer Alpha";
info->libVersion = 1;
info->numPlugins = NUM_PLUGINS;
}
extern "C" EXPORT int getPluginInfo(int index, Plugin::PluginInfo* info)
{
switch (index)
{
case 0:
info->type = Plugin::PLUGIN_TYPE_PROCESSOR;
info->processor.name = "LFP Viewer Alpha";
info->processor.type = Plugin::SinkProcessor;
info->processor.creator = &(Plugin::createProcessor<LfpDisplayNodeAlpha::LfpDisplayNode>);
break;
default:
return -1;
break;
}
return 0;
}
#ifdef WIN32
BOOL WINAPI DllMain(IN HINSTANCE hDllHandle,
IN DWORD nReason,
IN LPVOID Reserved)
{
return TRUE;
}
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment