From 167a58d7ee715bd01d64e17014d479ff149a03e6 Mon Sep 17 00:00:00 2001 From: Stuart Layton <slayton@mit.edu> Date: Thu, 15 Mar 2012 15:58:46 -0400 Subject: [PATCH] The spikeviewer shows up without segfaults! That was more painful than it should have been. The SpikeDisplayNode object isn't working properly. Queueing of spikes there appeared to be what was causing the problem. I need to consider an alternative way of handling the SpikeObjects. In the past I've used a circular buffer but that has problems with having a set size. I can use an stl::queue but queues are not thread safe, which is something that we definitely need. Anyway the plots show up and dance when the viewer is turned on. I still need to implement a Stereotrode plot and then the tetrode plot. I'll probably derive them from the ElectrodePlot base class instead of creating a base classs from which all three plot types are derived from. Finally I need to get a better handle on the setup of the OpenGLCanvas and how its sized. How the "Magical" scroll bars work and how to auto place the plots. Do we force all the plots to be of the same type? Or do we mix in Electrode Plots with Tetrode Plots? If so how do we orient them all? --- .../Processors/Editors/SpikeDisplayEditor.cpp | 18 +- Source/Processors/ProcessorGraph.cpp | 10 +- Source/Processors/SpikeDisplayNode.cpp | 112 ++----- Source/Processors/SpikeDisplayNode.h | 26 +- .../Visualization/SpikeDisplayCanvas.cpp | 315 ++++-------------- .../Visualization/SpikeDisplayCanvas.h | 51 +-- .../Processors/Visualization/SpikeObject.cpp | 55 +-- Source/Processors/Visualization/SpikeObject.h | 12 +- .../SpikePlotting/BaseUIElement.cpp | 13 +- .../SpikePlotting/BaseUIElement.h | 4 +- .../SpikePlotting/ElectrodePlot.cpp | 80 ++--- .../SpikePlotting/ElectrodePlot.h | 9 +- .../SpikePlotting/GenericAxes.cpp | 13 +- .../Visualization/SpikePlotting/GenericAxes.h | 3 +- .../Visualization/SpikePlotting/WaveAxes.cpp | 69 ++-- .../Visualization/SpikePlotting/WaveAxes.h | 6 +- 16 files changed, 299 insertions(+), 497 deletions(-) diff --git a/Source/Processors/Editors/SpikeDisplayEditor.cpp b/Source/Processors/Editors/SpikeDisplayEditor.cpp index 515126320..f73201e27 100644 --- a/Source/Processors/Editors/SpikeDisplayEditor.cpp +++ b/Source/Processors/Editors/SpikeDisplayEditor.cpp @@ -10,18 +10,18 @@ SpikeDisplayEditor::SpikeDisplayEditor (GenericProcessor* parentNode) desiredWidth = 250; StringArray timeBaseValues; - timeBaseValues.add("1"); - timeBaseValues.add("2"); - timeBaseValues.add("5"); - timeBaseValues.add("10"); + timeBaseValues.add("100"); + timeBaseValues.add("200"); + timeBaseValues.add("500"); + timeBaseValues.add("1000"); - createRadioButtons(35, 50, 160, timeBaseValues, "Display width (s)"); + createRadioButtons(35, 50, 160, timeBaseValues, "Thresholds (s)"); StringArray displayGainValues; - displayGainValues.add("1"); - displayGainValues.add("2"); - displayGainValues.add("4"); - displayGainValues.add("8"); + displayGainValues.add("100"); + displayGainValues.add("200"); + displayGainValues.add("400"); + displayGainValues.add("800"); createRadioButtons(35, 90, 160, displayGainValues, "Display Gain"); diff --git a/Source/Processors/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph.cpp index 219387137..25d8cdd56 100644 --- a/Source/Processors/ProcessorGraph.cpp +++ b/Source/Processors/ProcessorGraph.cpp @@ -27,6 +27,7 @@ #include "AudioNode.h" #include "LfpDisplayNode.h" +#include "SpikeDisplayNode.h" #include "EventNode.h" #include "FilterNode.h" #include "GenericProcessor.h" @@ -381,7 +382,13 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip // std::cout << "Graph data viewport: " << UI->getDataViewport() << std::endl; // processor->setDataViewport(getDataViewport()); //processor->setUIComponent(UI); - } else if (subProcessorType.equalsIgnoreCase("WiFi Output")) { + } + else if (subProcessorType.equalsIgnoreCase("Spike Viewer")) { + std::cout << "Creating an SpikeDisplayNode." << std::endl; + processor = new SpikeDisplayNode(); + + } + else if (subProcessorType.equalsIgnoreCase("WiFi Output")) { std::cout << "Creating a WiFi node." << std::endl; processor = new WiFiOutput(); } @@ -408,6 +415,7 @@ bool ProcessorGraph::processorWithSameNameExists(const String& name) } + void ProcessorGraph::removeProcessor(GenericProcessor* processor) { std::cout << "Removing processor with ID " << processor->getNodeId() << std::endl; diff --git a/Source/Processors/SpikeDisplayNode.cpp b/Source/Processors/SpikeDisplayNode.cpp index c44b6c12b..cc33126e3 100644 --- a/Source/Processors/SpikeDisplayNode.cpp +++ b/Source/Processors/SpikeDisplayNode.cpp @@ -26,11 +26,10 @@ SpikeDisplayNode::SpikeDisplayNode() : GenericProcessor("SpikeDisplay Viewer"), - bufferLength(200), displayGain(1), - displayBufferIndex(0), abstractFifo(100) + bufferSize(0), abstractFifo(100) { - displayBuffer = new AudioSampleBuffer(8, 100); +// displayBuffer = new AudioSampleBuffer(8, 100); eventBuffer = new MidiBuffer(); } @@ -54,37 +53,14 @@ void SpikeDisplayNode::updateSettings() std::cout << "Setting num inputs on SpikeDisplayNode to " << getNumInputs() << std::endl; } -bool SpikeDisplayNode::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, nSamples); - return true; - } else { - return false; - } - -} bool SpikeDisplayNode::enable() { - std::cout<<"SpikeDisplayNode enabled!"<<std::endl; - if (resizeBuffer()) - { - SpikeDisplayEditor* editor = (SpikeDisplayEditor*) getEditor(); - editor->enable(); - return true; - } else { - return false; - } - + std::cout<<"SpikeDisplayNode::enable()"<<std::endl; + SpikeDisplayEditor* editor = (SpikeDisplayEditor*) getEditor(); + editor->enable(); + return true; + } bool SpikeDisplayNode::disable() @@ -95,58 +71,44 @@ bool SpikeDisplayNode::disable() return true; } +int SpikeDisplayNode::getNumberOfChannelsForInput(int i){ + std::cout<<"SpikeDisplayNode::getNumberOfChannelsForInput()"<<std::endl; + return 1; +} + + void SpikeDisplayNode::setParameter (int parameterIndex, float newValue) { std::cout<<"SpikeDisplayNode setParameter!"<<std::endl; } + + void SpikeDisplayNode::process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples) { - std::cout<<"SpikeDisplayNode process!"<<std::endl; - // 1. place any new samples into the displayBuffer - - int samplesLeft = displayBuffer->getNumSamples() - displayBufferIndex; - - if (nSamples < samplesLeft) - { - - for (int chan = 0; chan < buffer.getNumChannels(); chan++) - { - displayBuffer->copyFrom(chan, // destChannel - displayBufferIndex, // destStartSample - buffer, // source - chan, // source channel - 0, // source start sample - nSamples); // numSamples - - } - displayBufferIndex += (nSamples); - - } else { - - int extraSamples = nSamples - samplesLeft; - - for (int chan = 0; chan < buffer.getNumChannels(); chan++) - { - displayBuffer->copyFrom(chan, // destChannel - displayBufferIndex, // destStartSample - buffer, // source - chan, // source channel - 0, // source start sample - samplesLeft); // numSamples - - displayBuffer->copyFrom(chan, - 0, - buffer, - chan, - samplesLeft, - extraSamples); - } - - displayBufferIndex = extraSamples; - } - + std::cout<<"SpikeDisplayNode::process"<<std::endl; + uint64_t ts = 00000; + int noise = 10; + SpikeObject newSpike; + generateSimulatedSpike(&newSpike, ts, noise); + spikebuffer.push(newSpike); + bufferSize++; + } +bool SpikeDisplayNode::getNextSpike(SpikeObject *spike){ + std::cout<<"SpikeDisplayNode::getNextSpike()"<<std::endl; + if (bufferSize<1 || spikebuffer.empty()) + return false; + else{ + SpikeObject s = spikebuffer.front(); + spikebuffer.pop(); + bufferSize--; + *spike = s; + return true; + } + + return false; +} \ No newline at end of file diff --git a/Source/Processors/SpikeDisplayNode.h b/Source/Processors/SpikeDisplayNode.h index edfce207c..950e9a84b 100644 --- a/Source/Processors/SpikeDisplayNode.h +++ b/Source/Processors/SpikeDisplayNode.h @@ -29,18 +29,18 @@ #include "Editors/VisualizerEditor.h" #include "GenericProcessor.h" #include "Visualization/SpikeObject.h" - +#include <queue> /** - Holds data in a displayBuffer to be used by the SpikeDisplayCanvas - for rendering individual spike events + Takes in MidiEvents and extracts SpikeObjects from the MidiEvent buffers. Those Events are then held in a queue until they are pulled by the spikeviewer @see GenericProcessor, SpikeDisplayEditor, SpikeDisplayCanvas */ + class DataViewport; class SpikeDisplayNode : public GenericProcessor @@ -63,22 +63,22 @@ public: bool enable(); bool disable(); - AudioSampleBuffer* getDisplayBufferAddress() {return displayBuffer;} - int getDisplayBufferIndex() {return displayBufferIndex;} + int getNumberOfChannelsForInput(int i); + bool getNextSpike(SpikeObject *spike); + private: - ScopedPointer<AudioSampleBuffer> displayBuffer; - ScopedPointer<MidiBuffer> eventBuffer; - - int displayBufferIndex; + int numberOfSources; + AbstractFifo abstractFifo; - float displayGain; // - int bufferLength; // s + //ScopedPointer<AudioSampleBuffer> displayBuffer; + ScopedPointer<MidiBuffer> eventBuffer; - AbstractFifo abstractFifo; + std::queue<SpikeObject> spikebuffer; - bool resizeBuffer(); + int bufferSize; + //bool resizeBuffer(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpikeDisplayNode); diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp index b2bf390b4..cd771d04b 100644 --- a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp +++ b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp @@ -24,23 +24,36 @@ #include "SpikeDisplayCanvas.h" SpikeDisplayCanvas::SpikeDisplayCanvas(SpikeDisplayNode* n) : processor(n), - xBuffer(0), yBuffer(0), - plotHeight(40), selectedChan(-1), screenBufferIndex(0), - timebase(1.0f), displayGain(5.0f), displayBufferIndex(0) + xBuffer(0), yBuffer(0), newSpike(false) { - nChans = processor->getNumInputs(); - sampleRate = processor->getSampleRate(); - std::cout << "Setting num inputs on SpikeDisplayCanvas to " << nChans << std::endl; + + + nSources = 0; //processor->getNumInputs(); + std::cout<<"SpikeDisplayNode has :"<<nSources<<" outputs!"<<std::endl; + + //memset(nChannels, 0, sizeof(nChannels[0]) * MAX_NUMBER_OF_SPIKE_SOURCES); + for (int i=0; i<nSources; i++) + nChannels[i] = processor->getNumberOfChannelsForInput(i); - displayBuffer = processor->getDisplayBufferAddress(); - displayBufferSize = displayBuffer->getNumSamples(); - std::cout << "Setting displayBufferSize on SpikeDisplayCanvas to " << displayBufferSize << std::endl; + // sampleRate = processor->getSampleRate(); + std::cout << "Setting num inputs on SpikeDisplayCanvas to " << nSources << std::endl; + + //generateEmptySpike(&spike, 1); + + for (int i=0; i<8; i++) + { + ElectrodePlot ep = ElectrodePlot(10 +i * 80, 10, 75, 75, ""); + plots.push_back(ep); + } + // displayBuffer = processor->getDisplayBufferAddress(); + // displayBufferSize = displayBuffer->getNumSamples(); + // std::cout << "Setting displayBufferSize on SpikeDisplayCanvas to " << displayBufferSize << std::endl; - totalHeight = (plotHeight+yBuffer)*nChans + yBuffer; + // totalHeight = (plotHeight+yBuffer)*nChans + yBuffer; - screenBuffer = new AudioSampleBuffer(nChans, 10000); + // screenBuffer = new AudioSampleBuffer(nChans, 10000); } @@ -52,13 +65,13 @@ SpikeDisplayCanvas::~SpikeDisplayCanvas() void SpikeDisplayCanvas::newOpenGLContextCreated() { + std::cout<<"SpikeDisplayCanvas::newOpenGLContextCreated()"<<std::endl; setUp2DCanvas(); - activateAntiAliasing(); + //activateAntiAliasing(); glClearColor (0.667, 0.698, 0.718, 1.0); resized(); - - + endAnimation(); //startTimer(50); } @@ -67,12 +80,12 @@ void SpikeDisplayCanvas::beginAnimation() { std::cout << "Beginning animation." << std::endl; - displayBufferSize = displayBuffer->getNumSamples(); + // displayBufferSize = displayBuffer->getNumSamples(); - screenBuffer->clear(); + // screenBuffer->clear(); //displayBufferIndex = 0; - screenBufferIndex = 0; +// screenBufferIndex = 0; startCallbacks(); } @@ -85,124 +98,41 @@ void SpikeDisplayCanvas::endAnimation() void SpikeDisplayCanvas::update() { - nChans = processor->getNumInputs(); - sampleRate = processor->getSampleRate(); + // nChans = processor->getNumInputs(); + // sampleRate = processor->getSampleRate(); - std::cout << "Setting num inputs on SpikeDisplayCanvas to " << nChans << std::endl; - if (nChans < 200 && nChans > 0) - screenBuffer->setSize(nChans, 10000); - //sampleRate = processor->getSampleRate(); + // std::cout << "Setting num inputs on SpikeDisplayCanvas to " << nChans << std::endl; + // if (nChans < 200 && nChans > 0) + // screenBuffer->setSize(nChans, 10000); + // //sampleRate = processor->getSampleRate(); - screenBuffer->clear(); + // screenBuffer->clear(); repaint(); - totalHeight = (plotHeight+yBuffer)*nChans + yBuffer; + // totalHeight = (plotHeight+yBuffer)*nChans + yBuffer; } void SpikeDisplayCanvas::setParameter(int param, float val) { - if (param == 0) - timebase = val; - else - displayGain = val; + // if (param == 0) + // timebase = val; + // else + // displayGain = val; } void SpikeDisplayCanvas::refreshState() { // called when the component's tab becomes visible again - displayBufferIndex = processor->getDisplayBufferIndex(); - screenBufferIndex = 0; + // displayBufferIndex = processor->getDisplayBufferIndex(); + // screenBufferIndex = 0; //resized(); } -void SpikeDisplayCanvas::updateScreenBuffer() -{ - // copy new samples from the displayBuffer into the screenBuffer - int maxSamples = getWidth(); - - int index = processor->getDisplayBufferIndex(); - - //std::cout << index << screenBufferIndex << std::endl; - - int nSamples = index - displayBufferIndex; - - if (nSamples < 0) - { - nSamples = (displayBufferSize - displayBufferIndex) + index; - } - - float ratio = sampleRate * timebase / float(getWidth()); - - // this number is crucial: - int valuesNeeded = (int) float(nSamples) / ratio; - - //lock->enterRead(); - float subSampleOffset = 0.0; - int nextPos = (displayBufferIndex + 1) % displayBufferSize; - - //int screenBufferPos; - - if (valuesNeeded > 0 && valuesNeeded < 1000) { - - int maxVal = screenBufferIndex + valuesNeeded; - int overflow = maxVal - maxSamples; - - screenBuffer->clear(screenBufferIndex, valuesNeeded); - - if (overflow > 0) - screenBuffer->clear(0, overflow); - - for (int i = 0; i < valuesNeeded; i++) - { - float gain = 1.0; - float alpha = (float) subSampleOffset; - float invAlpha = 1.0f - alpha; - - for (int channel = 0; channel < displayBuffer->getNumChannels(); channel++) { - - screenBuffer->addFrom(channel, - screenBufferIndex, - *displayBuffer, - channel, - displayBufferIndex, - 1, - invAlpha*gain*displayGain); - - screenBuffer->addFrom(channel, - screenBufferIndex, - *displayBuffer, - channel, - nextPos, - 1, - alpha*gain*displayGain); - } - - subSampleOffset += ratio; - - while (subSampleOffset >= 1.0) - { - if (++displayBufferIndex >= displayBufferSize) - displayBufferIndex = 0; - - nextPos = (displayBufferIndex + 1) % displayBufferSize; - subSampleOffset -= 1.0; - } - - screenBufferIndex++; - screenBufferIndex %= maxSamples; - - } - - } else { - //std::cout << "Skip." << std::endl; - } -} - void SpikeDisplayCanvas::canvasWasResized() { //std::cout << "Resized!" << std::endl; @@ -210,136 +140,35 @@ void SpikeDisplayCanvas::canvasWasResized() void SpikeDisplayCanvas::renderOpenGL() { - glClear(GL_COLOR_BUFFER_BIT); // clear buffers to preset values + std::cout<<"SpikeDisplayCanvas::renderOpenGL"<<std::endl; - //drawTicks(); - - updateScreenBuffer(); - - for (int i = 0; i < nChans; i++) - { - bool isSelected = false; - - if (selectedChan == i) - isSelected = true; - if (checkBounds(i)) { - setViewport(i); - //drawBorder(isSelected); - drawChannelInfo(i,isSelected); - drawWaveform(i,isSelected); - } - } - drawScrollBars(); - - //std::cout << "Render." << std::endl; -} - -void SpikeDisplayCanvas::drawWaveform(int chan, bool isSelected) -{ - // draw the screen buffer for a given channel - - float w = float(getWidth()); - - glBegin(GL_LINE_STRIP); - - for (float i = 0; i < float(getWidth()); i++) - { - glVertex2f(i/w,*screenBuffer->getSampleData(chan, int(i))+0.5); - } - - glEnd(); - - glColor4f(1.0, 1.0, 0.1, 1.0); - glBegin(GL_LINE_STRIP); - glVertex2f(float(screenBufferIndex)/w,0); - glVertex2f(float(screenBufferIndex)/w,1); - glEnd(); - -} - - -void SpikeDisplayCanvas::drawTicks() -{ + // Get Spikes from the processor + // Iterate through each spike, passing them individually to the appropriate plots and calling redraw before moving on to the next spike - glViewport(0,0,getWidth(),getHeight()); - - glColor4f(1.0f, 1.0f, 1.0f, 0.25f); - - for (int i = 0; i < 10; i++) - { - if (i == 5) - glLineWidth(3.0); - else if (i == 1 || i == 3 || i == 7 || i == 9) - glLineWidth(2.0); - else - glLineWidth(1.0); - - glBegin(GL_LINE_STRIP); - glVertex2f(0.1*i,0); - glVertex2f(0.1*i,1); - glEnd(); + //while(processor->getNextSpike(&spike)) + //{ + + // Identify which plot the spike should go to + + // Distribute those spike to the appropriate plot object + + SpikeObject tmpSpike; + for (int i=0; i<plots.size(); i++){ + generateSimulatedSpike(&tmpSpike, 0, 100); + plots[i].processSpikeObject(tmpSpike); + plots[i].redraw(); } -} - - -bool SpikeDisplayCanvas::checkBounds(int chan) -{ - bool isVisible; - - int lowerBound = (chan+1)*(plotHeight+yBuffer); - int upperBound = chan*(plotHeight+yBuffer); - - if (getScrollAmount() < lowerBound && getScrollAmount() + getHeight() > upperBound) - isVisible = true; - else - isVisible = false; - return isVisible; - -} - -void SpikeDisplayCanvas::setViewport(int chan) -{ - glViewport(xBuffer, - getHeight()-(chan+1)*(plotHeight+yBuffer)+getScrollAmount(), - getWidth()-2*xBuffer, - plotHeight); -} - -void SpikeDisplayCanvas::drawBorder(bool isSelected) -{ - float alpha = 0.5f; - - if (isSelected) - alpha = 1.0f; - - glColor4f(0.0f, 0.0f, 0.0f, alpha); - glBegin(GL_LINE_STRIP); - glVertex2f(0.0f, 0.0f); - glVertex2f(1.0f, 0.0f); - glVertex2f(1.0f, 1.0f); - glVertex2f(0.0f, 1.0f); - glVertex2f(0.0f, 0.0f); - glEnd(); - + //} + + drawScrollBars(); } -void SpikeDisplayCanvas::drawChannelInfo(int chan, bool isSelected) +void SpikeDisplayCanvas::drawTicks() { - float alpha = 0.5f; - - if (isSelected) - alpha = 1.0f; - - glColor4f(0.0f,0.0f,0.0f,alpha); - glRasterPos2f(5.0f/getWidth(),0.9); - String s = "";//String("Channel "); - s += (chan+1); - - getFont(String("cpmono-bold"))->FaceSize(35); - getFont(String("cpmono-bold"))->Render(s); + } int SpikeDisplayCanvas::getTotalHeight() @@ -351,17 +180,17 @@ int SpikeDisplayCanvas::getTotalHeight() void SpikeDisplayCanvas::mouseDownInCanvas(const MouseEvent& e) { - Point<int> pos = e.getPosition(); - int xcoord = pos.getX(); + // Point<int> pos = e.getPosition(); + // int xcoord = pos.getX(); - if (xcoord < getWidth()-getScrollBarWidth()) - { - int chan = (e.getMouseDownY() + getScrollAmount())/(yBuffer+plotHeight); + // if (xcoord < getWidth()-getScrollBarWidth()) + // { + // int chan = (e.getMouseDownY() + getScrollAmount())/(yBuffer+plotHeight); - selectedChan = chan; + // selectedChan = chan; - repaint(); - } + // repaint(); + // } } diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.h b/Source/Processors/Visualization/SpikeDisplayCanvas.h index 6313c3c3b..11647624f 100644 --- a/Source/Processors/Visualization/SpikeDisplayCanvas.h +++ b/Source/Processors/Visualization/SpikeDisplayCanvas.h @@ -24,8 +24,15 @@ #define SPIKEDISPLAYCANVAS_H_ #include "../../../JuceLibraryCode/JuceHeader.h" + #include "../SpikeDisplayNode.h" +#include "SpikePlotting/ElectrodePlot.h" +#include "SpikeObject.h" + #include "Visualizer.h" +#include <vector> + +#define MAX_NUMBER_OF_SPIKE_SOURCES = 128; class SpikeDisplayNode; @@ -51,36 +58,42 @@ private: int xBuffer, yBuffer; - float sampleRate; - float timebase; - float displayGain; + bool plotsInitialized; + bool newSpike; + SpikeObject spike; SpikeDisplayNode* processor; - AudioSampleBuffer* displayBuffer; - ScopedPointer<AudioSampleBuffer> screenBuffer; - MidiBuffer* eventBuffer; + std::vector<ElectrodePlot> plots; + // AudioSampleBuffer* displayBuffer; + // ScopedPointer<AudioSampleBuffer> screenBuffer; + // MidiBuffer* eventBuffer; - void setViewport(int chan); - void drawBorder(bool isSelected); - void drawChannelInfo(int chan, bool isSelected); - void drawWaveform(int chan, bool isSelected); + // void setViewport(int chan); + // void drawBorder(bool isSelected); + // void drawChannelInfo(int chan, bool isSelected); + // void drawWaveform(int chan, bool isSelected); void drawTicks(); - bool checkBounds(int chan); + // bool checkBounds(int chan); - void updateScreenBuffer(); - int screenBufferIndex; - int displayBufferIndex; - int displayBufferSize; + // void updateScreenBuffer(); + // int screenBufferIndex; + // int displayBufferIndex; + // int displayBufferSize; - int nChans, plotHeight, totalHeight; - int selectedChan; + int totalHeight; + // int selectedChan; int getTotalHeight(); - void canvasWasResized(); - void mouseDownInCanvas(const MouseEvent& e); + int nSources; + int nChannels[MAX_NUMBER_OF_SPIKE_CHANNELS]; + + void initializeSpikePlots(); + + void canvasWasResized(); + void mouseDownInCanvas(const MouseEvent& e); // void mouseDrag(const MouseEvent& e); // void mouseMove(const MouseEvent& e); // void mouseUp(const MouseEvent& e); diff --git a/Source/Processors/Visualization/SpikeObject.cpp b/Source/Processors/Visualization/SpikeObject.cpp index 7994ecf1e..9f4130b38 100644 --- a/Source/Processors/Visualization/SpikeObject.cpp +++ b/Source/Processors/Visualization/SpikeObject.cpp @@ -25,9 +25,8 @@ #include <iostream> #include "memory.h" #include <stdlib.h> - - - +#include "time.h" + // Simple method for serializing a SpikeObject into a string of bytes bool packSpike(SpikeObject *s, char* buffer, int bufferSize){ @@ -135,11 +134,21 @@ void makeBufferValid(char *buffer, int bufferSize){ void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise) { - uint16_t trace[32] = - { 1880, 1900, 1940, 2040, 2290, 2790, 3475, 3995, 4110, 3890, - 3505, 3090, 2720, 2410, 2155, 1945, 1775, 1635, 1520, 1420, - 1340, 1265, 1205, 1155, 1115, 1080, 1050, 1034, 1010, 1001, - 1000, 1000}; + std::cout<<"generateSimulatedSpike()"<<std::endl; + + uint16_t trace[][32] = + { + { 880, 900, 940, 1040, 1290, 1790, 2475, 2995, 3110, 2890, + 2505, 2090, 1720, 1410, 1155, 945, 775, 635, 520, 420, + 340, 265, 205, 155, 115, 80, 50, 34, 10, 34, 50, 80}, + { 1040, 1090, 1190, 1350, 1600, 1960, 2380, 2790, 3080, 3140, + 2910, 2430, 1810, 1180, 680, 380, 270, 320, 460, 630, + 770, 870, 940, 970, 990, 1000, 1000, 1000, 1000, 1000, 1000, 1000}, + { 1000, 1000, 1000, 1000, 1000, 1040, 1140, 1440, 2040, 2940, + 3800, 4140, 3880, 3680, 1640, 920, 520, 300, 140, 040, + 20, 20, 40, 100, 260, 500, 740, 900, 960, 1000, 1000, 1000} + }; + // uint16_t sineSpikeWave[32] = // { 78, 90, 101, 111, 120, 126, 129, 130, @@ -151,26 +160,20 @@ void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise) uint16_t gain = 5; - // if(sineWave){ - // memcpy(trace, sineSpikeWave, 64); - // gain = 100; - // } - // else{ - // memcpy(trace, realSpikeWave, 64); - // gain = 5; - // } - s->timestamp = timestamp; s->source = 0; s->nChannels = 4; s->nSamples = 32; int idx=0; + int waveType = rand()%3; // Pick one of the three predefined waveshapes to generate + double waveScaling = (double)(rand()%5 + 5) / 10.00; // Scale the wave between 50% and 150% + int shift = 1000; for (int i=0; i<4; i++) { s->gain[i] = gain; - s->threshold[i] = 12000; + s->threshold[i] = 8000; for (int j=0; j<32; j++){ @@ -179,10 +182,24 @@ void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise) n = rand() % noise - noise/2; } - s->data[idx] = (trace[j] + n) * gain; + s->data[idx] = (trace[waveType][j] + n) * gain * waveScaling + shift; idx = idx+1; } } + +} +void generateEmptySpike(SpikeObject *s, int nChannels){ +/* + std::cout<<"generateEmptySpike()"<<std::endl; + s->timestamp = 0; + s->source = 0; + s->nChannels = nChannels; + s->nSamples = 32; + + memset(&(s->gain), 0, 2*nChannels); + memset(&(s->threshold), 0, 2*nChannels); + memset(&(s->data), 0, 2*nChannels*32); +*/ } diff --git a/Source/Processors/Visualization/SpikeObject.h b/Source/Processors/Visualization/SpikeObject.h index dc9bd0f30..b838960df 100644 --- a/Source/Processors/Visualization/SpikeObject.h +++ b/Source/Processors/Visualization/SpikeObject.h @@ -21,8 +21,8 @@ */ -#ifndef SPIKEOBJECT_H_OPENEPHYS -#define SPIKEOBJECT_H_OPENEPHYS +#ifndef SPIKEOBJECT_H_ +#define SPIKEOBJECT_H_ #include <stdint.h> @@ -38,7 +38,8 @@ struct SpikeObject{ uint16_t nSamples; uint16_t data[MAX_NUMBER_OF_SPIKE_CHANNELS * MAX_NUMBER_OF_SPIKE_CHANNEL_SAMPLES]; uint16_t gain[MAX_NUMBER_OF_SPIKE_CHANNELS]; - uint16_t threshold[MAX_NUMBER_OF_SPIKE_CHANNELS]; + uint16_t threshold[MAX_NUMBER_OF_SPIKE_CHANNELS]; + }; /* @@ -76,5 +77,8 @@ void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise); // Define the << operator for the SpikeObject // std::ostream& operator<<(std::ostream &strm, const SpikeObject s); +// Helper function for zeroing out a spike object with a specified number of channels +void generateEmptySpike(SpikeObject *s, int nChannels); + -#endif //SPIKEOBJECT_H_OPENEPHYS \ No newline at end of file +#endif //SPIKEOBJECT_H_ \ No newline at end of file diff --git a/Source/Processors/Visualization/SpikePlotting/BaseUIElement.cpp b/Source/Processors/Visualization/SpikePlotting/BaseUIElement.cpp index b19e1a077..88e7a8e98 100644 --- a/Source/Processors/Visualization/SpikePlotting/BaseUIElement.cpp +++ b/Source/Processors/Visualization/SpikePlotting/BaseUIElement.cpp @@ -1,14 +1,14 @@ #include "BaseUIElement.h" BaseUIElement::BaseUIElement(): -xpos(0), ypos(0), width(100), height(100), enabled(true), padding(0) + xpos(0), ypos(0), width(100), height(100), enabled(true), padding(0) { elementName = (char*) "BaseUIElement"; clearNextDraw = false; } BaseUIElement::BaseUIElement(int x, int y, double w, double h): -enabled(true), padding(0) + enabled(true), padding(0) { xpos = x+padding; ypos = y+padding; @@ -18,7 +18,7 @@ enabled(true), padding(0) clearNextDraw = false; } BaseUIElement::BaseUIElement(int x, int y, double w, double h, int p): -enabled(true), padding(0) + enabled(true), padding(0) { xpos = x+padding; ypos = y+padding; @@ -32,13 +32,12 @@ void BaseUIElement::redraw(){ // std::cout<<"BaseUIElement::redraw(), Position:"<<xpos<<","<<ypos<<" : "<<width<<","<<height<<std::endl; setGlViewport(); - if (clearNextDraw){ + if (clearNextDraw || !clearNextDraw){ clearNextDraw = false; glColor3f(0.0, 0.0, 0.0); glRecti(-1,-1,1,1); - // glutSwapBuffers(); - glRecti(-1,-1,1,1); - //glutSwapBuffers(); + + } } void BaseUIElement::drawElementEdges(){ diff --git a/Source/Processors/Visualization/SpikePlotting/BaseUIElement.h b/Source/Processors/Visualization/SpikePlotting/BaseUIElement.h index 85b171b73..89536d8bf 100644 --- a/Source/Processors/Visualization/SpikePlotting/BaseUIElement.h +++ b/Source/Processors/Visualization/SpikePlotting/BaseUIElement.h @@ -1,5 +1,5 @@ -#ifndef BaseUIElement_H_ -#define BaseUIElement_H_ +#ifndef BASEUIELEMENT_H_ +#define BASEUIELEMENT_H_ #include "PlotUtils.h" diff --git a/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.cpp b/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.cpp index 0e4769797..e3d577604 100644 --- a/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.cpp +++ b/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.cpp @@ -1,19 +1,24 @@ #include "ElectrodePlot.h" +#include "../SpikeObject.h" ElectrodePlot::ElectrodePlot(): - BaseUIElement(), titleHeight(15), enableTitle(true), limitsChanged(true) + BaseUIElement(), titleHeight(0), enableTitle(true), limitsChanged(true) { plotTitle = (char*) "Electrode Plot"; - + } ElectrodePlot::ElectrodePlot(int x, int y, int w, int h, char *n): - BaseUIElement(x,y,w,h,1), titleHeight(15), enableTitle(true), limitsChanged(true) + BaseUIElement(x,y,w,h,1), titleHeight(0), enableTitle(true), limitsChanged(true) { plotTitle = n; titleBox = TitleBox(x, y+h-titleHeight-3, w, titleHeight+3, plotTitle); + + initAxes(); } +ElectrodePlot::~ElectrodePlot(){ +} // Each plot needs to update its children axes when its redraw gets called. // it also needs to call the parent plot when children axes get added it @@ -22,47 +27,18 @@ ElectrodePlot::ElectrodePlot(int x, int y, int w, int h, char *n): // the right direction void ElectrodePlot::redraw(){ - // std::cout<<"ElectrodePlot() starting drawing"<<std::endl; + std::cout<<"ElectrodePlot() starting drawing"<<std::endl;\ + BaseUIElement::clearNextDraw = true; BaseUIElement::redraw(); - - // SpikeObject tempSpike; - // std::list<GenericAxes>::iterator i; - // bool axesDrawnOnce = false; - // while(tetSource.getNextSpike(&tempSpike)){ - // axesDrawnOnce = true; - // for (i=axesList.begin(); i!= axesList.end(); ++i){ - // i->updateSpikeData(tempSpike); - - // if (limitsChanged){ - - // int n = i->getType(); - - // if (n>=WAVE1 && n<=WAVE4) - // i->setYLims(limits[n][0], limits[n][1]); - - // else if( n>=PROJ1x2 && n<=PROJ3x4){ - // int d1, d2; - // n2ProjIdx(i->getType(), &d1, &d2); - // i->setXLims(limits[d1][0], limits[d1][1]); - // i->setYLims(limits[d2][0], limits[d2][1]); - // } - // } - // i->redraw(); - - // } - // if (limitsChanged) - // limitsChanged = false; - // } - - // if (!axesDrawnOnce) - // for (i= axesList.begin(); i!=axesList.end(); ++i) - // i->redraw(); - - // titleBox.redraw(); - // BaseUIElement::drawElementEdges(); - // // std::cout<<"ElectrodePlot() Done drawing"<<std::endl; + + axes.redraw(); } +// This would normally happen for collection of axes but an electrode plot doesn't have a collection instead its a single axes +void ElectrodePlot::processSpikeObject(SpikeObject s){ + std::cout<<"ElectrdePlot::processSpikeObject()"<<std::endl; + axes.updateSpikeData(s); +} void ElectrodePlot::setTitle(char *n){ plotTitle = n; } @@ -70,7 +46,7 @@ void ElectrodePlot::setTitle(char *n){ void ElectrodePlot::setEnabled(bool e){ BaseUIElement::enabled = e; - axes->setEnabled(e); + axes.setEnabled(e); } bool ElectrodePlot::getEnabled(){ @@ -88,13 +64,11 @@ void ElectrodePlot::initAxes(){ double axesHeight = (BaseUIElement::height - titleHeight); - WaveAxes tempAx = WaveAxes(minX, minY, axesWidth, axesHeight, 0); + axes = WaveAxes(minX, minY, axesWidth, axesHeight, 0); - tempAx.setEnabled(false); - tempAx.setYLims(-1*pow(2,11), pow(2,14)); - tempAx.setWaveformColor(1.0, 1.0, 1.0); - - axes = &tempAx; + //axes.setEnabled(false); + axes.setYLims(-1*pow(2,11), pow(2,14)*1.6); + axes.setWaveformColor(1.0, 1.0, 1.0); } @@ -106,7 +80,7 @@ void ElectrodePlot::setPosition(int x, int y, double w, double h){ double axesWidth = BaseUIElement::width; double axesHeight = BaseUIElement::height - titleHeight; - axes->setPosition(minX, minY, axesWidth, axesHeight); + axes.setPosition(minX, minY, axesWidth, axesHeight); titleBox.setPosition(x, y+h-titleHeight-3, w, titleHeight+3); } @@ -142,6 +116,10 @@ void ElectrodePlot::initLimits(){ } +void ElectrodePlot::getPreferredDimensions(double *w, double *h){ + *w = 75; + *h = 75; +} // void ElectrodePlot::mouseDown(int x, int y){ // // selectedAxesN = -1; @@ -164,7 +142,7 @@ void ElectrodePlot::initLimits(){ // // if (!hit) // // selectedAxes = NULL; // // if (selectedAxes != NULL) -// // std::cout<<"ElectrodePlot::mouseDown() hit:"<<selectedAxes<<" AxesType:"<<selectedAxes->getType()<<std::endl; +// // std::cout<<"ElectrodePlot::mouseDown() hit:"<<selectedAxes<<" AxesType:"<<selectedaxes.getType()<<std::endl; // // else // // std::cout<<"ElectrodePlot::mouseDown() NO HIT!"<<std::endl; @@ -173,7 +151,7 @@ void ElectrodePlot::initLimits(){ // // if (selectedAxes == NULL || dx==0) // // return; -// // // zoomAxes(selectedAxes->getType(), true, dx>0); +// // // zoomAxes(selectedaxes.getType(), true, dx>0); // // if (shift) // // zoomAxes(selectedAxesN, true, dx); // // if (ctrl) diff --git a/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.h b/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.h index e92cd6942..5db97f504 100644 --- a/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.h +++ b/Source/Processors/Visualization/SpikePlotting/ElectrodePlot.h @@ -32,8 +32,8 @@ class ElectrodePlot : public BaseUIElement{ bool limitsChanged; double limits[1][2]; - WaveAxes *axes; - + WaveAxes axes; + // void zoomAxes(int n, bool xdim, int zoomval); // void zoomProjection (int n, bool xdim, int zoomval); @@ -48,6 +48,7 @@ class ElectrodePlot : public BaseUIElement{ public: ElectrodePlot(); ElectrodePlot(int x, int y,int w,int h, char *n); + ~ElectrodePlot(); void initAxes(); void redraw(); @@ -56,6 +57,8 @@ public: bool getEnabled(); void setPosition(int,int,double,double); + void getPreferredDimensions(double*, double*); + int getNumberOfAxes(); void clearOnNextDraw(bool c); void setTitleEnabled(bool e); @@ -66,6 +69,8 @@ public: void mouseDragY(int dy, bool shift, bool ctr); bool processKeyEvent(SimpleKeyEvent k); + + void processSpikeObject(SpikeObject s); }; diff --git a/Source/Processors/Visualization/SpikePlotting/GenericAxes.cpp b/Source/Processors/Visualization/SpikePlotting/GenericAxes.cpp index 236e81088..738974895 100644 --- a/Source/Processors/Visualization/SpikePlotting/GenericAxes.cpp +++ b/Source/Processors/Visualization/SpikePlotting/GenericAxes.cpp @@ -9,7 +9,7 @@ GenericAxes::GenericAxes(): ylims[0] = 0; ylims[1] = 1; - BaseUIElement::elementName = (char*) "GenericAxes"; + //BaseUIElement::elementName = (char*) "GenericAxes"; } GenericAxes::GenericAxes(int x, int y, double w, double h, int t): @@ -17,19 +17,24 @@ GenericAxes::GenericAxes(int x, int y, double w, double h, int t): gotFirstSpike(false), resizedFlag(false) { + std::cout<<"Generic Axes!!!!!"<<std::endl; + std::cout<<"gotFirstSpike:"<<gotFirstSpike<<std::endl; // if (t<WAVE1 || t>PROJ3x4) //("Invalid Axes type specified"); type = t; - BaseUIElement::elementName = (char*) "GenericAxes"; +// BaseUIElement::elementName = (char*) "GenericAxes"; } void GenericAxes::updateSpikeData(SpikeObject newSpike){ + + std::cout<<"GenericAxes::updateSpikeData() "; if (!gotFirstSpike){ - gotFirstSpike = true; - //std::cout<<"GenericAxes::updateSpikeData() got first spike"<<std::endl; + gotFirstSpike = true; } + s = newSpike; + std::cout<<"got spike with nSamples:"<<s.nSamples<<std::endl; } // void GenericAxes::redraw(){ diff --git a/Source/Processors/Visualization/SpikePlotting/GenericAxes.h b/Source/Processors/Visualization/SpikePlotting/GenericAxes.h index 3fb6b2d72..480a1a113 100644 --- a/Source/Processors/Visualization/SpikePlotting/GenericAxes.h +++ b/Source/Processors/Visualization/SpikePlotting/GenericAxes.h @@ -26,6 +26,8 @@ protected: bool resizedFlag; int type; + virtual void plot(){} + public: GenericAxes(); @@ -33,7 +35,6 @@ public: void updateSpikeData(SpikeObject s); - virtual void plot(){} virtual void redraw(){} diff --git a/Source/Processors/Visualization/SpikePlotting/WaveAxes.cpp b/Source/Processors/Visualization/SpikePlotting/WaveAxes.cpp index 3e2080f7c..c38fd955b 100644 --- a/Source/Processors/Visualization/SpikePlotting/WaveAxes.cpp +++ b/Source/Processors/Visualization/SpikePlotting/WaveAxes.cpp @@ -37,14 +37,19 @@ WaveAxes::WaveAxes(int x, int y, double w, double h, int t): BaseUIElement::elementName = (char*) "WaveAxes"; } + void WaveAxes::updateSpikeData(SpikeObject newSpike){ + std::cout<<"WaveAxes::updateSpikeData()"<<std::endl; GenericAxes::updateSpikeData(newSpike); + } + void WaveAxes::redraw(){ + BaseUIElement::redraw(); - if (BaseUIElement::enabled) - plot(); - + + plot(); + BaseUIElement::drawElementEdges(); } @@ -52,28 +57,18 @@ void WaveAxes::redraw(){ void WaveAxes::plot(){ int chan = 0; + // If no spikes have been received then don't plot anything if (!gotFirstSpike) { std::cout<<"\tWaiting for the first spike"<<std::endl; return; } - if (s.nSamples>1024) - return; - - // Set the plotting range for the current axes - // the xlims member is ignored as the xdims are 0->number of samples per waveform minus one + // Set the plotting range for the current axes the xlims member is ignored as the xdims are 0->number of samples per waveform minus one // so the line goes all the way to the edges ydims are specified by the ylims vector - setViewportRange(0, ylims[0], s.nSamples-1, ylims[1]); - // Are new waves getting overlayed on top of old waves? If not clear the display - if(!overlay){ - glColor3f(0.0,0.0,0.0); - glRectd(0, ylims[0], s.nSamples, ylims[1]); - } - - // Are we drawing the grid lines for the waveforms? + // draw the grid lines for the waveforms? if(drawGrid) drawWaveformGrid(s.threshold[chan], s.gain[chan]); @@ -86,37 +81,21 @@ void WaveAxes::plot(){ // if drawWaveformPoints is set to false then force the drawing of the line, _SOMETHING_ must be drawn glColor3fv(waveColor); - //if drawWaveformLine and drawWaveformPoints are both set - if(drawWaveformLine){ - glLineWidth(1); - glBegin( GL_LINE_STRIP ); - for (int i=0; i<s.nSamples; i++) - { - glVertex2f(x, s.data[sampIdx]); - sampIdx +=4; - x +=dx; - } - glEnd(); + glLineWidth(1); + glBegin( GL_LINE_STRIP ); + + int dSamples = 1; + for (int i=0; i<s.nSamples; i++) + { + //std::cout<<"\t"<<s.data[sampIdx]; + glVertex2f(x, s.data[sampIdx]); + sampIdx += dSamples; + x +=dx; } - -/* - //if drawWaveformLine and drawWaveformPoints are both set false then draw the points - //this ensures that something is always drawn - if(drawWaveformPoints || !drawWaveformLine){ - x = 0; - sampIdx = chan; - glColor3fv(pointColor); - glPointSize(1); - glBegin( GL_POINTS ); - for (int i=0; i<s.nSamples; i++) - { - glVertex2f(x, s.data[sampIdx]); - sampIdx +=4; - x +=dx; - } - glEnd(); - } */ + glEnd(); + // std::cout<<std::endl; + // Draw the threshold line and label diff --git a/Source/Processors/Visualization/SpikePlotting/WaveAxes.h b/Source/Processors/Visualization/SpikePlotting/WaveAxes.h index fb57d4360..56add88d8 100644 --- a/Source/Processors/Visualization/SpikePlotting/WaveAxes.h +++ b/Source/Processors/Visualization/SpikePlotting/WaveAxes.h @@ -15,7 +15,6 @@ class WaveAxes: public GenericAxes{ - SpikeObject s; GLfloat waveColor[3]; GLfloat thresholdColor[3]; GLfloat gridColor[3]; @@ -23,6 +22,10 @@ class WaveAxes: public GenericAxes{ void drawWaveformGrid(int thold, int gain); +protected: + void plot(); + + public: WaveAxes(); WaveAxes(int x, int y, double w, double h, int t); @@ -34,7 +37,6 @@ public: void setGridColor(GLfloat, GLfloat, GLfloat); void redraw(); - void plot(); bool drawWaveformLine; bool drawWaveformPoints; -- GitLab