Skip to content

Commit

Permalink
Add/remove spike channels as undoable actions
Browse files Browse the repository at this point in the history
  • Loading branch information
medengineer committed Oct 28, 2023
1 parent e53fd43 commit 3a5a47c
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 22 deletions.
2 changes: 2 additions & 0 deletions Plugins/BasicSpikeDisplay/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ add_sources(${PLUGIN_NAME}
SpikeDetector/SpikeDetectorEditor.h
SpikeDetector/PopupConfigurationWindow.cpp
SpikeDetector/PopupConfigurationWindow.h
SpikeDetector/SpikeDetectorActions.cpp
SpikeDetector/SpikeDetectorActions.h
SpikeDisplayNode/SpikeDisplayCanvas.cpp
SpikeDisplayNode/SpikeDisplayCanvas.h
SpikeDisplayNode/SpikeDisplay.cpp
Expand Down
2 changes: 1 addition & 1 deletion Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ SpikeChannel* SpikeDetector::addSpikeChannel (SpikeChannel::Type type,
const Array<const ContinuousChannel*>& sourceChannels = spikeChannel->getSourceChannels();
std::string cacheKey = std::to_string(sourceChannels[0]->getSourceNodeId());
cacheKey += "|" + spikeChannel->getStreamName().toStdString();
cacheKey += "|" + String(this->getNodeId()).toStdString();
cacheKey += "|" + name.toStdString();

spikeChannel->setIdentifier(cacheKey);
Expand All @@ -568,7 +569,6 @@ SpikeChannel* SpikeDetector::addSpikeChannel (SpikeChannel::Type type,

}


void SpikeDetector::removeSpikeChannel (SpikeChannel* spikeChannel)
{

Expand Down
8 changes: 4 additions & 4 deletions Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ class SpikeDetector : public GenericProcessor
/** Get array of local SpikeChannel objects for a given dataStream*/
Array<SpikeChannel*> getSpikeChannelsForStream(uint16 streamId);

/** Checks whether a spike channel has been loaded, to prevent double-loading
when there is a Merger in the signal chain */
bool alreadyLoaded(String name, SpikeChannel::Type type, int stream_source, String stream_name);

private:

// INTERNAL BUFFERS
Expand All @@ -255,10 +259,6 @@ class SpikeDetector : public GenericProcessor
void addWaveformToSpikeBuffer (Spike::Buffer& s,
int sampleIndex,
AudioBuffer<float>& buffer);

/** Checks whether a spike channel has been loaded, to prevent double-loading
when there is a Merger in the signal chain */
bool alreadyLoaded(String name, SpikeChannel::Type type, int stream_source, String stream_name);

StreamSettings<SpikeDetectorSettings> settings;

Expand Down
159 changes: 159 additions & 0 deletions Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2021 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 <stdio.h>

#include "SpikeDetectorActions.h"

AddSpikeChannels::AddSpikeChannels(SpikeDetector* processor_,
DataStream* stream_,
SpikeChannel::Type type_,
int count_, //adds multiple channels atonce
Array<int> startChannels_) :
processor(processor_),
streamId(stream_->getStreamId()),
type(type_),
count(count_),
startChannels(startChannels_)
{
settings = nullptr;
}

AddSpikeChannels::~AddSpikeChannels()
{
if (settings != nullptr)
delete settings;
}

bool AddSpikeChannels::perform()
{
for (int i = 0; i < count; i++)
{
int startChannel = -1;

if (i < startChannels.size())
startChannel = startChannels[i];

processor->addSpikeChannel(type, streamId, startChannel);
}
return true;
}

bool AddSpikeChannels::undo()
{
for (int i = 0; i < count; i++)
processor->removeSpikeChannel(processor->getSpikeChannelsForStream(streamId).getLast());
return true;
}

RemoveSpikeChannels::RemoveSpikeChannels(SpikeDetector* processor_,
DataStream* stream_,
Array<SpikeChannel*> spikeChannelsToRemove_) :
processor(processor_),
spikeChannelsToRemove(spikeChannelsToRemove_),
streamId(stream_->getStreamId())
{
settings = std::make_unique<XmlElement>("SPIKE_CHANNELS");

for (auto spikeChannel : spikeChannelsToRemove)
{
if (spikeChannel->isLocal())
{

XmlElement* spikeParamsXml = settings->createNewChildElement("SPIKE_CHANNEL");

// general settings
spikeParamsXml->setAttribute("name", spikeChannel->getName());
spikeParamsXml->setAttribute("description", spikeChannel->getDescription());
spikeParamsXml->setAttribute("num_channels", (int)spikeChannel->getNumChannels());

// stream info
spikeParamsXml->setAttribute("sample_rate", spikeChannel->getSampleRate());
spikeParamsXml->setAttribute("stream_name", stream_->getName());
spikeParamsXml->setAttribute("stream_source", stream_->getSourceNodeId());

// parameters
spikeChannel->getParameter("local_channels")->toXml(spikeParamsXml);
spikeChannel->getParameter("thrshlder_type")->toXml(spikeParamsXml);

for (int ch = 0; ch < (int)spikeChannel->getNumChannels(); ch++)
{
spikeChannel->getParameter("abs_threshold" + String(ch+1))->toXml(spikeParamsXml);
spikeChannel->getParameter("std_threshold" + String(ch+1))->toXml(spikeParamsXml);
spikeChannel->getParameter("dyn_threshold" + String(ch+1))->toXml(spikeParamsXml);
}

spikeChannel->getParameter("waveform_type")->toXml(spikeParamsXml);
}
}
}

RemoveSpikeChannels::~RemoveSpikeChannels()
{
}

bool RemoveSpikeChannels::perform()
{
for (auto spikeChannel : spikeChannelsToRemove)
processor->removeSpikeChannel(spikeChannel);
return true;
}

bool RemoveSpikeChannels::undo()
{
for (auto* spikeParamsXml : settings->getChildIterator())
{
String name = spikeParamsXml->getStringAttribute("name", "");

//std::cout << "SPIKE CHANNEL NAME: " << name << std::endl;

double sample_rate = spikeParamsXml->getDoubleAttribute("sample_rate", 0.0f);
String stream_name = spikeParamsXml->getStringAttribute("stream_name", "");
int stream_source = spikeParamsXml->getIntAttribute("stream_source", 0);

SpikeChannel::Type type = SpikeChannel::typeFromNumChannels(spikeParamsXml->getIntAttribute("num_channels", 1));

if (!processor->alreadyLoaded(name, type, stream_source, stream_name))
{
uint16 streamId = processor->findSimilarStream(stream_source, stream_name, sample_rate, true);

SpikeChannel* spikeChannel = processor->addSpikeChannel(type, streamId, -1, name);

spikeChannel->getParameter("local_channels")->fromXml(spikeParamsXml);

SelectedChannelsParameter* param = (SelectedChannelsParameter*)spikeChannel->getParameter("local_channels");
((SpikeChannel*)param->getOwner())->localChannelIndexes = param->getArrayValue();

spikeChannel->getParameter("thrshlder_type")->fromXml(spikeParamsXml);

for (int ch = 0; ch < SpikeChannel::getNumChannels(type); ch++)
{
spikeChannel->getParameter("abs_threshold" + String(ch+1))->fromXml(spikeParamsXml);
spikeChannel->getParameter("std_threshold" + String(ch+1))->fromXml(spikeParamsXml);
spikeChannel->getParameter("dyn_threshold" + String(ch+1))->fromXml(spikeParamsXml);
}

spikeChannel->getParameter("waveform_type")->fromXml(spikeParamsXml);
}
}
}
102 changes: 102 additions & 0 deletions Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2023 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 SpikeDetectorActions_h
#define SpikeDetectorActions_h

#include <ProcessorHeaders.h>

#include "SpikeDetector.h"

/**
Adds a spike channel to the spike detector,
based on the description.
Undo: removes the spike channel from the
spike detector.
*/
class AddSpikeChannels : public UndoableAction
{

public:

/** Constructor*/
AddSpikeChannels(SpikeDetector* processor,
DataStream* stream,
SpikeChannel::Type type,
int count, //adds multiple channels atonce
Array<int> startChannels);

/** Destructor */
~AddSpikeChannels();

/** Perform the action*/
bool perform();

/** Undo the action*/
bool undo();

XmlElement* settings;

private:

SpikeDetector* processor;
uint16 streamId;
SpikeChannel::Type type;
Array<int> startChannels;

int count;

};

class RemoveSpikeChannels : public UndoableAction
{

public:

/** Constructor*/
RemoveSpikeChannels(SpikeDetector* processor,
DataStream* stream,
Array<SpikeChannel*> spikeChannelsToRemove);

/** Destructor */
~RemoveSpikeChannels();

/** Perform the action*/
bool perform();

/** Undo the action*/
bool undo();

std::unique_ptr<XmlElement> settings;

private:

SpikeDetector* processor;
uint16 streamId;
Array<SpikeChannel*> spikeChannelsToRemove;
Array<SpikeChannel*> removedSpikeChannels;

};

#endif /* SpikeDetectorActions_h */
30 changes: 16 additions & 14 deletions Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "SpikeDetector.h"
#include "PopupConfigurationWindow.h"

#include "SpikeDetectorActions.h"

#include <stdio.h>


Expand Down Expand Up @@ -80,17 +82,14 @@ void SpikeDetectorEditor::buttonClicked(Button* button)

void SpikeDetectorEditor::addSpikeChannels(PopupConfigurationWindow* window, SpikeChannel::Type type, int count, Array<int> startChannels)
{
SpikeDetector* processor = (SpikeDetector*) getProcessor();
SpikeDetector* processor = (SpikeDetector*)getProcessor();

for (int i = 0; i < count; i++)
{
int startChannel = -1;

if (i < startChannels.size())
startChannel = startChannels[i];
DataStream* stream = processor->getDataStream(getCurrentStream());

processor->addSpikeChannel(type, getCurrentStream(), startChannel);
}
AddSpikeChannels* action = new AddSpikeChannels(processor, stream, type, count, startChannels);

CoreServices::getUndoManager()->beginNewTransaction();
CoreServices::getUndoManager()->perform(action);

CoreServices::updateSignalChain(this);

Expand All @@ -104,16 +103,19 @@ void SpikeDetectorEditor::removeSpikeChannels(PopupConfigurationWindow* window,
{

SpikeDetector* processor = (SpikeDetector*)getProcessor();

for (auto spikeChannel : spikeChannelsToRemove)
processor->removeSpikeChannel(spikeChannel);

DataStream* stream = processor->getDataStream(getCurrentStream());

RemoveSpikeChannels* action = new RemoveSpikeChannels(processor, stream, spikeChannelsToRemove);

CoreServices::getUndoManager()->beginNewTransaction();
CoreServices::getUndoManager()->perform(action);

CoreServices::updateSignalChain(this);

if (window != nullptr)
window->update(processor->getSpikeChannelsForStream(getCurrentStream()));


}

int SpikeDetectorEditor::getNumChannelsForCurrentStream()
Expand All @@ -131,4 +133,4 @@ int SpikeDetectorEditor::getNumChannelsForCurrentStream()
return stream->getChannelCount();
else
return 0;
}
}
5 changes: 5 additions & 0 deletions Source/CoreServices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ namespace CoreServices
return STR_DEF(JUCE_APP_VERSION);
}

UndoManager* getUndoManager()
{
return getProcessorGraph()->getUndoManager();
}


namespace PluginInstaller
{
Expand Down
3 changes: 3 additions & 0 deletions Source/CoreServices.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ PLUGIN_API File getSavedStateDirectory();
/** Gets the GUI version */
PLUGIN_API String getGUIVersion();

/** Gets the GUI's undo manager */
PLUGIN_API UndoManager* getUndoManager();


namespace PluginInstaller
{
Expand Down
Loading

0 comments on commit 3a5a47c

Please sign in to comment.