From 95d4c8b3f97163c675f7ec59dc18d61475406bf8 Mon Sep 17 00:00:00 2001
From: Aaron Cuevas Lopez <aacuelo@teleco.upv.es>
Date: Mon, 6 Mar 2017 17:12:39 +0100
Subject: [PATCH] Restore experimental plugins to development branch

---
 .../Plugins/SpikeRaster/SpikeRaster.vcxproj   |  132 ++
 .../SpikeRaster/SpikeRaster.vcxproj.filters   |   36 +
 Source/Plugins/JuliaProcessor/.Makefile       |   49 +
 Source/Plugins/JuliaProcessor/JuliaEditor.cpp |  151 +++
 Source/Plugins/JuliaProcessor/JuliaEditor.h   |   64 +
 .../Plugins/JuliaProcessor/JuliaProcessor.cpp |  169 +++
 .../Plugins/JuliaProcessor/JuliaProcessor.h   |   68 +
 .../Plugins/JuliaProcessor/OpenEphysLib.cpp   |   70 +
 .../JuliaProcessor/exampleProcessor.jl        |   21 +
 Source/Plugins/SpikeRaster/Makefile           |   45 +
 Source/Plugins/SpikeRaster/OpenEphysLib.cpp   |   97 ++
 Source/Plugins/SpikeRaster/SpikeRaster.cpp    |  220 +++
 Source/Plugins/SpikeRaster/SpikeRaster.h      |  120 ++
 .../Plugins/SpikeRaster/SpikeRasterEditor.cpp | 1192 +++++++++++++++++
 .../Plugins/SpikeRaster/SpikeRasterEditor.h   |  281 ++++
 15 files changed, 2715 insertions(+)
 create mode 100644 Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj
 create mode 100644 Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj.filters
 create mode 100644 Source/Plugins/JuliaProcessor/.Makefile
 create mode 100644 Source/Plugins/JuliaProcessor/JuliaEditor.cpp
 create mode 100644 Source/Plugins/JuliaProcessor/JuliaEditor.h
 create mode 100644 Source/Plugins/JuliaProcessor/JuliaProcessor.cpp
 create mode 100644 Source/Plugins/JuliaProcessor/JuliaProcessor.h
 create mode 100644 Source/Plugins/JuliaProcessor/OpenEphysLib.cpp
 create mode 100644 Source/Plugins/JuliaProcessor/exampleProcessor.jl
 create mode 100644 Source/Plugins/SpikeRaster/Makefile
 create mode 100644 Source/Plugins/SpikeRaster/OpenEphysLib.cpp
 create mode 100644 Source/Plugins/SpikeRaster/SpikeRaster.cpp
 create mode 100644 Source/Plugins/SpikeRaster/SpikeRaster.h
 create mode 100644 Source/Plugins/SpikeRaster/SpikeRasterEditor.cpp
 create mode 100644 Source/Plugins/SpikeRaster/SpikeRasterEditor.h

diff --git a/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj b/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj
new file mode 100644
index 000000000..898fba3e7
--- /dev/null
+++ b/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D6A842A4-AC32-4AD0-B6AF-01AF1FBC8661}</ProjectGuid>
+    <RootNamespace>SpikeRaster</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\Plugin_Debug32.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\Plugin_Debug64.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\Plugin_Release32.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\Plugin_Release64.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\OpenEphysLib.cpp" />
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRaster.cpp" />
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRasterEditor.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRaster.h" />
+    <ClInclude Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRasterEditor.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj.filters b/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj.filters
new file mode 100644
index 000000000..3123d5593
--- /dev/null
+++ b/Builds/VisualStudio2013/Plugins/SpikeRaster/SpikeRaster.vcxproj.filters
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\OpenEphysLib.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRaster.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRasterEditor.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRaster.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\Source\Plugins\SpikeRaster\SpikeRasterEditor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Source/Plugins/JuliaProcessor/.Makefile b/Source/Plugins/JuliaProcessor/.Makefile
new file mode 100644
index 000000000..664cabad4
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/.Makefile
@@ -0,0 +1,49 @@
+
+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)))
+
+
+JL_SHARE =  $(shell julia -e 'print(joinpath(JULIA_HOME,Base.DATAROOTDIR,"julia"))')
+
+# flags to compile "JuliaProcessor". $(JL_SHARE): toplevel of julia package
+#CXXFLAGS += -I $(JL_SHARE)/src -I $(JULIA)/src/support -I $(JULIA)/usr/include
+#LDFLAGS += -L $(JL_SHARE)/usr/lib -Wl,-R $(JULIA)/usr/lib -ljulia
+CFLAGS   += $(shell $(JL_SHARE)/julia-config.jl --cflags)
+CXXFLAGS += $(shell $(JL_SHARE)/julia-config.jl --cflags)
+LDFLAGS  += $(shell $(JL_SHARE)/julia-config.jl --ldflags)
+LDLIBS   += $(shell $(JL_SHARE)/julia-config.jl --ldlibs)
+
+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)
diff --git a/Source/Plugins/JuliaProcessor/JuliaEditor.cpp b/Source/Plugins/JuliaProcessor/JuliaEditor.cpp
new file mode 100644
index 000000000..929d13499
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/JuliaEditor.cpp
@@ -0,0 +1,151 @@
+/*
+    ------------------------------------------------------------------
+
+    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 "JuliaEditor.h"
+#include "JuliaProcessor.h"
+#include <stdio.h>
+ 
+JuliaEditor::JuliaEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors=true)
+    : GenericEditor(parentNode, useDefaultParameterEditors)
+{
+    juliaProcessor = (JuliaProcessor*) parentNode;
+
+    lastFilePath = File::getCurrentWorkingDirectory();
+
+    fileButton = new UtilityButton("Select file",Font("Small Text", 13, Font::plain));
+    fileButton->addListener(this);
+    fileButton->setBounds(10,85,100,25);
+    addAndMakeVisible(fileButton);
+
+    reloadFileButton = new UtilityButton("refresh",Font("Small Text", 13, Font::plain));
+    reloadFileButton->addListener(this);
+    reloadFileButton->setBounds(100+10,85,60,25);
+    addAndMakeVisible(reloadFileButton);
+
+    fileNameLabel = new Label("FileNameLabel", "No file selected.");
+    fileNameLabel->setBounds(10,85+20,140,25);
+    addAndMakeVisible(fileNameLabel);
+
+    bufferSizeSelection = new Label("Buffer Size","30000"); // this is currently set in RHD2000Thread, the cleaner would be to set it here again
+    bufferSizeSelection->setEditable(true,false,false);
+    bufferSizeSelection->addListener(this);
+    bufferSizeSelection->setBounds(120,60,60,20);
+    bufferSizeSelection->setColour(Label::textColourId, Colours::darkgrey);
+    addAndMakeVisible(bufferSizeSelection);
+
+    bufferSizeSelectionLabel = new Label("","NBuf.:");
+    bufferSizeSelectionLabel->attachToComponent (bufferSizeSelection,true);
+    addAndMakeVisible(bufferSizeSelectionLabel);
+
+    // Image im;
+    // im = ImageCache::getFromMemory(BinaryData::JuliaIconActive_png,
+    //                                BinaryData::JuliaIconActive_pngSize);
+
+    // icon = new ImageIcon(im);
+    // addAndMakeVisible(icon);
+    // icon->setBounds(15,25,61,54);
+    // icon->setOpacity(0.3f);
+
+    desiredWidth = 200;
+
+    setEnabledState(false);
+}
+
+JuliaEditor::~JuliaEditor()
+{
+
+}
+
+void JuliaEditor::setFile(String file)
+{
+    File fileToRead(file);
+    lastFilePath = fileToRead.getParentDirectory();
+    juliaProcessor->setFile(fileToRead.getFullPathName());
+    fileNameLabel->setText(fileToRead.getFileName(), dontSendNotification);
+
+
+    // setEnabledState(true);
+    // icon->setOpacity(1.0f); // tie this to hasJuliaInstance instead of just setting it!
+    // repaint();
+}
+
+void JuliaEditor::buttonEvent(Button* button)
+{
+    if (!acquisitionIsActive)
+    {
+        if (button == fileButton)
+        {
+            //std::cout << "Button clicked." << std::endl;
+            //FileChooser chooseJuliaProcessorFile("Please select the file you want to load...",  lastFilePath, "*");
+
+
+                // file dialogs are screwed up in current xubuntu, so we'll do this for now.
+                setFile("/home/jvoigts/Documents/Github/plugin-GUI/Source/Plugins/JuliaProcessor/exampleProcessor.jl");
+
+
+          //  if (chooseJuliaProcessorFile.browseForFileToOpen())
+            {
+                // Use the selected file
+                //setFile(chooseJuliaProcessorFile.getResult().getFullPathName());
+
+                // lastFilePath = fileToRead.getParentDirectory();
+                // thread->setFile(fileToRead.getFullPathName());
+                // fileNameLabel->setText(fileToRead.getFileName(),false);
+            }
+        } 
+        if (button == reloadFileButton)
+        {
+            juliaProcessor->reloadFile();
+        }
+    }
+}
+
+void JuliaEditor::labelTextChanged(Label* label)
+{
+    if (!acquisitionIsActive)
+    {
+        if (label == bufferSizeSelection)
+        {
+            Value val = label->getTextValue();
+            juliaProcessor->setBuffersize(int(val.getValue()));
+        }
+    }
+}
+
+void JuliaEditor::saveEditorParameters(XmlElement* xml)
+{
+    // XmlElement* fileName = xml->createNewChildElement("FILENAME");
+    // fileName->addTextElement(lastFilePath.getFullPathName());
+}
+
+void JuliaEditor::loadEditorParameters(XmlElement* xml)
+{
+    // forEachXmlChildElement(*xml, xmlNode)
+    //    {
+    //       if (xmlNode->hasTagName("FILENAME"))
+    //       {
+    //           lastFilePath = File(xmlNode->getText());
+    //           thread->setFile(lastFilePath.getFullPathName());
+    //           fileNameLabel->setText(lastFilePath.getFullPathName(),false);
+    //       }
+    //   }
+}
\ No newline at end of file
diff --git a/Source/Plugins/JuliaProcessor/JuliaEditor.h b/Source/Plugins/JuliaProcessor/JuliaEditor.h
new file mode 100644
index 000000000..6aec26625
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/JuliaEditor.h
@@ -0,0 +1,64 @@
+/*
+    ------------------------------------------------------------------
+
+    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 JULIAEDITOR_H_INCLUDED
+#define JULIAEDITOR_H_INCLUDED
+
+#include <EditorHeaders.h>
+
+//class ImageIcon;
+class JuliaProcessor;
+
+/**
+
+  User interface for the Julia processor.
+
+  @see JuliaProcessor
+
+*/
+
+class JuliaEditor : public GenericEditor, public Label::Listener
+
+{
+public:
+    JuliaEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors);
+    virtual ~JuliaEditor();
+    void buttonEvent(Button* button);
+    void labelTextChanged(Label* te);
+    void setFile(String file);
+    void saveEditorParameters(XmlElement*);
+    void loadEditorParameters(XmlElement*);
+    ImageIcon* icon;
+
+private:
+    ScopedPointer<UtilityButton> fileButton;
+    ScopedPointer<UtilityButton> reloadFileButton;
+    ScopedPointer<Label> fileNameLabel;
+    ScopedPointer<Label> bufferSizeSelection;
+    ScopedPointer<Label> bufferSizeSelectionLabel;
+    JuliaProcessor* juliaProcessor;
+    File lastFilePath;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JuliaEditor);
+};
+
+#endif  // JULIAEDITOR_H_INCLUDED
\ No newline at end of file
diff --git a/Source/Plugins/JuliaProcessor/JuliaProcessor.cpp b/Source/Plugins/JuliaProcessor/JuliaProcessor.cpp
new file mode 100644
index 000000000..561369399
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/JuliaProcessor.cpp
@@ -0,0 +1,169 @@
+/*
+    ------------------------------------------------------------------
+
+    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 "JuliaProcessor.h"
+#include "JuliaEditor.h"
+#include <stdio.h>
+#include <julia.h>
+
+JuliaProcessor::JuliaProcessor()
+    : GenericProcessor("Julia Processor")
+{
+	hasJuliaInstance = false;
+    dataHistoryBufferNumChannels = 256;
+    dataHistoryBuffer = new AudioSampleBuffer(dataHistoryBufferNumChannels, 60000);
+    dataHistoryBuffer->clear();
+}
+
+JuliaProcessor::~JuliaProcessor()
+{
+	jl_atexit_hook(0);
+	deleteAndZero(dataHistoryBuffer);
+}
+
+AudioProcessorEditor* JuliaProcessor::createEditor()
+{
+    editor = new JuliaEditor(this, true);
+    std::cout << "Creating Julia editor." << std::endl;
+    return editor;
+}
+
+void JuliaProcessor::setFile(String fullpath)
+{
+	hasJuliaInstance = true;
+	filePath = fullpath;
+
+	FILE* fp = popen("echo $JULIA", "r");
+	char input[255];
+	fgets(input, sizeof(input), fp);
+	
+	String julia_bin_dir = input;
+	julia_bin_dir = julia_bin_dir.trimEnd();
+	julia_bin_dir += "/home/jvoigts/Documents/Github/julia/usr/bin";
+	String julia_sys_dir = input;
+	julia_sys_dir = julia_sys_dir.trimEnd();
+	julia_sys_dir += "/home/jvoigts/Documents/Github/julia/usr/lib/julia/sys.so";
+	
+	const char* jbin = julia_bin_dir.toRawUTF8();
+	const char* jsys = julia_sys_dir.toRawUTF8();
+	
+	jl_init_with_image(jbin, jsys);
+
+	String juliaString = "include(\"" + filePath + "\")";
+	run_julia_string(juliaString);
+}
+
+void JuliaProcessor::reloadFile()
+{
+    if (hasJuliaInstance) 
+    {
+        String juliaString = "reload(\"" + filePath + "\")";
+        run_julia_string(juliaString);
+    }
+    else
+    {
+        std::cout << "No julia instance running - cant refresh" << std::endl;
+    }
+}
+
+
+void JuliaProcessor::setParameter(int parameterIndex, float newValue)
+{
+    editor->updateParameterButtons(parameterIndex);
+}
+
+void JuliaProcessor::setBuffersize(int bufferSize)
+{
+    if (bufferSize > 1)
+    {
+        dataHistoryBufferSize=bufferSize;
+        printf("Setting history buffer size to %d samples \n", dataHistoryBufferSize);
+        dataHistoryBuffer->setSize(dataHistoryBufferNumChannels, dataHistoryBufferSize, false, true, false);
+    }
+    else 
+    {
+        printf("History buffer size has to be at least 1");
+    }
+}
+
+void JuliaProcessor::run_julia_string(String juliaString)
+{
+    // need to convert from juce String to char array
+    const char* jstr = juliaString.toRawUTF8();
+
+    printf("executing julia cmd: %s\n", jstr);
+
+    jl_eval_string(jstr);
+
+    if (jl_exception_occurred())
+    	printf("%s \n", jl_typeof_str(jl_exception_occurred()));
+}
+
+
+
+String JuliaProcessor::getFile()
+{
+    return filePath;
+}
+
+void JuliaProcessor::process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
+{
+	if (hasJuliaInstance)
+	{
+		jl_function_t *func = jl_get_function(jl_main_module, "oe_process!");    
+	
+		// create 2D array of float64 type
+		jl_value_t *array_type = jl_apply_array_type(jl_float32_type, 1); // last arg is nDims
+
+		for (int n = 0; n < getNumOutputs(); n++)
+		{
+			float* ptr = buffer.getWritePointer(n); // to perform in-place edits to the buffer
+			jl_array_t *x = jl_ptr_to_array_1d(array_type, ptr , buffer.getNumSamples(), 0);
+			JL_GC_PUSH1(&x);
+			jl_call1(func, (jl_value_t*)x);
+			JL_GC_POP();
+		}
+	}
+}
+
+void JuliaProcessor::saveCustomParametersToXml(XmlElement* parentElement)
+{
+    XmlElement* childNode = parentElement->createNewChildElement("FILENAME");
+    childNode->setAttribute("path", getFile());
+}
+
+void JuliaProcessor::loadCustomParametersFromXml()
+{
+    if (parametersAsXml != nullptr)
+    {
+        // use parametersAsXml to restore state
+        forEachXmlChildElement(*parametersAsXml, xmlNode)
+        {
+            if (xmlNode->hasTagName("FILENAME"))
+            {
+                String filepath = xmlNode->getStringAttribute("path");
+                JuliaEditor* fre = (JuliaEditor*) getEditor();
+                fre->setFile(filepath);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Source/Plugins/JuliaProcessor/JuliaProcessor.h b/Source/Plugins/JuliaProcessor/JuliaProcessor.h
new file mode 100644
index 000000000..eed4a215d
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/JuliaProcessor.h
@@ -0,0 +1,68 @@
+/*
+    ------------------------------------------------------------------
+
+    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 JULIAPROCESSOR_H_INCLUDED
+#define JULIAPROCESSOR_H_INCLUDED
+
+#include <ProcessorHeaders.h>
+
+/**
+  Julia Processor.
+
+  Allows the user to select a Julia Programming Language file to use as filter
+
+  @see GenericProcessor, JuliaEditor
+*/
+
+class JuliaProcessor : public GenericProcessor
+
+{
+public:
+    JuliaProcessor();
+    ~JuliaProcessor();
+    void setFile(String fullpath);
+    String getFile();
+    void reloadFile();
+    void process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
+    void setParameter(int parameterIndex, float newValue);
+    void setBuffersize(int bufferSize);
+    AudioProcessorEditor* createEditor();
+    bool hasEditor() const
+    {
+        return true;
+    }
+    void saveCustomParametersToXml(XmlElement* parentElement);
+    void loadCustomParametersFromXml();
+
+private:
+    bool hasJuliaInstance;
+    String filePath;
+    int dataHistoryBufferSize;
+    int dataHistoryBufferNumChannels; 
+    AudioSampleBuffer* dataHistoryBuffer;
+    void run_julia_string(String juliaString);
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JuliaProcessor);
+};
+
+
+#endif  // JULIAPROCESSOR_H_INCLUDED
diff --git a/Source/Plugins/JuliaProcessor/OpenEphysLib.cpp b/Source/Plugins/JuliaProcessor/OpenEphysLib.cpp
new file mode 100644
index 000000000..189d524e9
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/OpenEphysLib.cpp
@@ -0,0 +1,70 @@
+/*
+------------------------------------------------------------------
+
+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 <PluginInfo.h>
+#include "JuliaProcessor.h"
+#include <string>
+#ifdef WIN32
+#include <Windows.h>
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT __attribute__((visibility("default")))
+#endif
+
+using namespace Plugin;
+#define NUM_PLUGINS 1
+
+extern "C" EXPORT void getLibInfo(Plugin::LibraryInfo* info)
+{
+	info->apiVersion = PLUGIN_API_VER;
+	info->name = "Julia Processor";
+	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 = "Julia Processor";
+		info->processor.type = Plugin::FilterProcessor;
+		info->processor.creator = &(Plugin::createProcessor<JuliaProcessor>);
+		break;
+	default:
+		return -1;
+		break;
+	}
+	return 0;
+}
+
+#ifdef WIN32
+BOOL WINAPI DllMain(IN HINSTANCE hDllHandle,
+	IN DWORD     nReason,
+	IN LPVOID    Reserved)
+{
+	return TRUE;
+}
+
+#endif
diff --git a/Source/Plugins/JuliaProcessor/exampleProcessor.jl b/Source/Plugins/JuliaProcessor/exampleProcessor.jl
new file mode 100644
index 000000000..2efab302f
--- /dev/null
+++ b/Source/Plugins/JuliaProcessor/exampleProcessor.jl
@@ -0,0 +1,21 @@
+# set things up here
+#global last=100
+
+# this function is called once per buffer upddate and is passed
+# the current buffer in data
+
+#data[:]=sin(data/tscale)*200;
+
+
+function oe_process!(data)
+	
+	global last;	
+
+	f=0.05;
+	for i in eachindex(data)
+		
+		data[i]=f*data[i] + (1-f)*last;
+		last = data[i];
+	end
+
+end
\ No newline at end of file
diff --git a/Source/Plugins/SpikeRaster/Makefile b/Source/Plugins/SpikeRaster/Makefile
new file mode 100644
index 000000000..973d9274e
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/Makefile
@@ -0,0 +1,45 @@
+
+LIBNAME := $(notdir $(CURDIR))
+OBJDIR := $(OBJDIR)/$(LIBNAME)
+TARGET := $(LIBNAME).so
+OS := $(shell uname)
+
+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)))
+
+#Extra macros and libraries needed by the plugin
+#CXXFLAGS := $(CXXFLAGS) -D EXAMPLE_MACRO
+#LDFLAGS := $(LDFLAGS) -lExampleLib
+
+
+
+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)
diff --git a/Source/Plugins/SpikeRaster/OpenEphysLib.cpp b/Source/Plugins/SpikeRaster/OpenEphysLib.cpp
new file mode 100644
index 000000000..abc7fd079
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/OpenEphysLib.cpp
@@ -0,0 +1,97 @@
+/*
+------------------------------------------------------------------
+
+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 <PluginInfo.h>
+#include "SpikeRaster.h"
+#include <string>
+#ifdef WIN32
+#include <Windows.h>
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT __attribute__((visibility("default")))
+#endif
+
+using namespace Plugin;
+//Number of plugins defined on the library. Can be of different types (Processors, RecordEngines, etc...)
+#define NUM_PLUGINS 1
+
+extern "C" EXPORT void getLibInfo(Plugin::LibraryInfo* info)
+{
+	info->apiVersion = PLUGIN_API_VER; /*API version, defined by the GUI source. 
+	Should not be changed to ensure it is always equal to the one used in the latest codebase. The GUI refueses to load plugins with mismatched API versions */
+	info->name = "Example library"; //Name of the Library, used only for information
+	info->libVersion = 1; //Version of the library, used only for information
+	info->numPlugins = NUM_PLUGINS;
+}
+
+extern "C" EXPORT int getPluginInfo(int index, Plugin::PluginInfo* info)
+{
+	switch (index)
+	{
+	//one case per plugin. This example is for a processor which connects directly to the signal chain
+	case 0:
+		info->type = Plugin::PLUGIN_TYPE_PROCESSOR; //Type of plugin. See "Source/Processors/PluginManager/OpenEphysPlugin.h" for complete info about the different type structures
+		//For processor
+		info->processor.name = "Spike Raster"; //Processor name shown in the GUI
+		info->processor.type = Plugin::FilterProcessor; //Type of processor. Can be FilterProcessor, SourceProcessor, SinkProcessor or UtilityProcessor. Specifies where on the processor list will appear
+		info->processor.creator = &(Plugin::createProcessor<SpikeRaster>); //Class factory pointer. Replace "ExampleProcessor" with the name of your class.
+		break;
+/**
+Examples for other plugin types
+
+For a RecordEngine, which provides formats for recording data
+	case x:
+		info->type = Plugin::RecordEnginePlugin;
+		info->recordEngine.name = "Record Engine Name";
+		info->recordEngine.creator = &(Plugin::createRecordEngine<RecordEngineClassName>);
+		break;
+
+For a DataThread, which allows to use the existing SourceNode to connect to an asynchronous data source, such as acquisition hardware
+	case x:
+		info->type = Plugin::DatathreadPlugin;
+		info->dataThread.name = "Source name"; //Name that will appear on the processor list
+		info->dataThread.creator = &createDataThread<DataThreadClassName>;
+
+For a FileSource, which allows importing data formats into the FileReader
+	case x:
+		info->type = Plugin::FileSourcePlugin;
+		info->fileSource.name = "File Source Name";
+		info->fileSource.extensions = "xxx;xxx;xxx"; //Semicolon separated list of supported extensions. Eg: "txt;dat;info;kwd"
+		info->fileSource.creator = &(Plugin::createFileSource<FileSourceClassName>);
+**/
+	default:
+		return -1;
+		break;
+	}
+	return 0;
+}
+
+#ifdef WIN32
+BOOL WINAPI DllMain(IN HINSTANCE hDllHandle,
+	IN DWORD     nReason,
+	IN LPVOID    Reserved)
+{
+	return TRUE;
+}
+
+#endif
diff --git a/Source/Plugins/SpikeRaster/SpikeRaster.cpp b/Source/Plugins/SpikeRaster/SpikeRaster.cpp
new file mode 100644
index 000000000..b3ef325c2
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/SpikeRaster.cpp
@@ -0,0 +1,220 @@
+
+/*
+    ------------------------------------------------------------------
+
+    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 <stdio.h>
+#include "SpikeRaster.h"
+
+#include "SpikeRasterEditor.h"
+
+SpikeRaster::SpikeRaster()
+    : GenericProcessor("Spike Raster"), displayBufferSize(100), redrawRequested(false)
+
+{
+	//Without a custom editor, generic parameter controls can be added
+    //parameters.add(Parameter("thresh", 0.0, 500.0, 200.0, 0));
+
+    electrodes.clear();
+
+}
+
+SpikeRaster::~SpikeRaster()
+{
+
+}
+
+/**
+	If the processor uses a custom editor, this method must be present.
+*/
+
+AudioProcessorEditor* SpikeRaster::createEditor()
+{
+	editor = new SpikeRasterEditor(this, true);
+
+	//std::cout << "Creating editor." << std::endl;
+
+	return editor;
+}
+
+void SpikeRaster::updateSettings()
+{
+    //std::cout << "Setting num inputs on SpikeDisplayNode to " << getNumInputs() << std::endl;
+
+    electrodes.clear();
+
+    for (int i = 0; i < eventChannels.size(); i++)
+    {
+        ChannelType type = eventChannels[i]->getType();
+
+        if (type == ELECTRODE_CHANNEL)
+        {
+
+            Electrode elec;
+			elec.numChannels = static_cast<SpikeChannel*>(eventChannels[i]->extraData.get())->numChannels;
+
+            elec.name = eventChannels[i]->getName();
+            elec.currentSpikeIndex = 0;
+            elec.mostRecentSpikes.ensureStorageAllocated(displayBufferSize);
+
+            for (int j = 0; j < elec.numChannels; j++)
+            {
+                elec.displayThresholds.add(0);
+                elec.detectorThresholds.add(0);
+            }
+
+            electrodes.add(elec);
+
+        }
+    }
+
+}
+
+
+void SpikeRaster::setParameter(int param, float newValue)
+{
+
+
+    if (param == 2)   // redraw
+    {
+        redrawRequested = true;
+
+    }
+}
+
+bool SpikeRaster::enable()
+{
+    std::cout << "SpikeRaster::enable()" << std::endl;
+    SpikeRasterEditor* editor = (SpikeRasterEditor*) getEditor();
+
+    editor->enable();
+    return true;
+
+}
+
+bool SpikeRaster::disable()
+{
+    std::cout << "SpikeRaster disabled!" << std::endl;
+    SpikeRasterEditor* editor = (SpikeRasterEditor*) getEditor();
+    editor->disable();
+    return true;
+}
+
+int SpikeRaster::getNumElectrodes()
+{
+	return electrodes.size();
+}
+
+void SpikeRaster::setRasterPlot(RasterPlot* r)
+{
+	canvas = r;
+	r->setSampleRate(settings.sampleRate);
+}
+
+void SpikeRaster::process(AudioSampleBuffer& buffer, MidiBuffer& events)
+{
+
+    checkForEvents(events); // automatically calls 'handleEvent
+
+    if (redrawRequested)
+    {
+    	canvas->setTimestamp(getTimestamp(0));
+
+    	//std::cout << "redraw" << std::endl;
+        for (int i = 0; i < getNumElectrodes(); i++)
+        {
+
+            Electrode& e = electrodes.getReference(i);
+
+            // transfer buffered spikes to spike plot
+            for (int j = 0; j < e.currentSpikeIndex; j++)
+            {
+                //std::cout << "Transferring spikes." << std::endl;
+                canvas->processSpikeObject(e.mostRecentSpikes[j]);
+                e.currentSpikeIndex = 0;
+            }
+
+        }
+
+        //redrawRequested = true;
+    }
+
+
+
+}
+
+void SpikeRaster::handleEvent(int eventType, MidiMessage& event, int samplePosition)
+{
+
+    //std::cout << "Received event of type " << eventType << std::endl;
+
+    if (eventType == SPIKE)
+    {
+
+        const uint8_t* dataptr = event.getRawData();
+        int bufferSize = event.getRawDataSize();
+
+        if (bufferSize > 0)
+        {
+
+            SpikeObject newSpike;
+
+            bool isValid = unpackSpike(&newSpike, dataptr, bufferSize);
+
+            if (isValid)
+            {
+                int electrodeNum = newSpike.source;
+
+                Electrode& e = electrodes.getReference(electrodeNum);
+                // std::cout << electrodeNum << std::endl;
+
+                // add to buffer
+                if (e.currentSpikeIndex < displayBufferSize)
+                {
+                    //  std::cout << "Adding spike " << e.currentSpikeIndex + 1 << std::endl;
+                    e.mostRecentSpikes.set(e.currentSpikeIndex, newSpike);
+                    e.currentSpikeIndex++;
+                }
+
+            }
+
+        }
+
+    } else if (eventType == TTL)
+    {
+    	const uint8* dataptr = event.getRawData();
+
+        //int eventNodeId = *(dataptr+1);
+        int eventId = *(dataptr+2);
+        int eventChannel = *(dataptr+3);
+        uint8 sourceNodeId = event.getNoteNumber();
+
+		int64 timestamp = timestamps[sourceNodeId] + samplePosition;
+
+        if (eventId == 1)
+        {
+        	canvas->processEvent(eventChannel, timestamp);
+        }
+    }
+
+}
diff --git a/Source/Plugins/SpikeRaster/SpikeRaster.h b/Source/Plugins/SpikeRaster/SpikeRaster.h
new file mode 100644
index 000000000..41758a92c
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/SpikeRaster.h
@@ -0,0 +1,120 @@
+/*
+    ------------------------------------------------------------------
+
+    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 SPIKERASTER_H_INCLUDED
+#define SPIKERASTER_H_INCLUDED
+
+#ifdef _WIN32
+#include <Windows.h>
+#endif
+
+#include <ProcessorHeaders.h>
+#include <SpikeLib.h>
+
+class RasterPlot;
+
+/**
+
+  Displays a raster and peri-stimulus time histogram for incoming spikes.
+
+  @see GenericProcessor
+
+*/
+
+class SpikeRaster : public GenericProcessor
+
+{
+public:
+
+    /** The class constructor, used to initialize any members. */
+    SpikeRaster();
+
+    /** The class destructor, used to deallocate memory */
+    ~SpikeRaster();
+
+    /** Determines whether the processor is treated as a source. */
+    bool isSource()
+    {
+        return false;
+    }
+
+    /** Determines whether the processor is treated as a sink. */
+    bool isSink()
+    {
+        return false;
+    }
+
+    /** Indicates if the processor has a custom editor. Defaults to false */
+    bool hasEditor() const
+    {
+        return true;
+    }
+
+    void updateSettings();
+
+    int getNumElectrodes();
+
+    AudioProcessorEditor* createEditor();
+
+    void process(AudioSampleBuffer& buffer, MidiBuffer& events);
+
+    void handleEvent(int, MidiMessage&, int);
+
+    void setParameter(int parameterIndex, float newValue);
+
+    bool enable();
+    bool disable();
+
+    void setRasterPlot(RasterPlot*);
+
+private:
+
+    struct Electrode
+    {
+        String name;
+
+        int numChannels;
+
+        Array<float> displayThresholds;
+        Array<float> detectorThresholds;
+
+        Array<SpikeObject> mostRecentSpikes;
+        int currentSpikeIndex;
+
+        int recordIndex;
+
+    };
+
+    RasterPlot* canvas;
+
+
+    Array<Electrode> electrodes;
+
+    int displayBufferSize;
+    bool redrawRequested;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SpikeRaster);
+
+};
+
+#endif  // SPIKERASTER_H_INCLUDED
diff --git a/Source/Plugins/SpikeRaster/SpikeRasterEditor.cpp b/Source/Plugins/SpikeRaster/SpikeRasterEditor.cpp
new file mode 100644
index 000000000..04a9c0340
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/SpikeRasterEditor.cpp
@@ -0,0 +1,1192 @@
+/*
+------------------------------------------------------------------
+
+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 "SpikeRasterEditor.h"
+
+SpikeRasterEditor::SpikeRasterEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors=true)
+: VisualizerEditor(parentNode, useDefaultParameterEditors)
+
+{
+    
+    tabText = "Raster";
+    
+    desiredWidth = 180;
+    
+}
+
+SpikeRasterEditor::~SpikeRasterEditor()
+{
+}
+
+
+Visualizer* SpikeRasterEditor::createNewCanvas()
+{
+    
+    SpikeRaster* processor = (SpikeRaster*) getProcessor();
+    canvas = new SpikeRasterCanvas(processor);
+    return canvas;
+    
+}
+
+void SpikeRasterEditor::updateSettings()
+{
+    updateVisualizer();
+}
+
+// ==============================================================
+
+
+SpikeRasterCanvas::SpikeRasterCanvas(SpikeRaster* sr) : processor(sr), currentMap(0)
+{
+
+    rasterPlot = new RasterPlot(this);
+    addAndMakeVisible(rasterPlot);
+
+    ratePlot = new RatePlot(rasterPlot);
+    addAndMakeVisible(ratePlot);
+
+    psth = new PSTH(rasterPlot);
+    addAndMakeVisible(psth);
+
+    timescale = new Timescale(rasterPlot);
+    addAndMakeVisible(timescale);
+
+    for (int i = 0; i < 8; i++)
+    {
+        EventChannelButton* ecb = new EventChannelButton(rasterPlot, i, rasterPlot->getColourForChannel(i));
+        eventChannelButtons.add(ecb);
+        addAndMakeVisible(ecb);
+    }
+
+    triggerLabel = new Label("Triggers:","Triggers:");
+    triggerLabel->setFont(Font("Small Text", 11, Font::plain));
+    triggerLabel->setColour(Label::textColourId, Colour(200,200,200));
+    addAndMakeVisible(triggerLabel);
+
+    viewLabel = new Label("View:","View:");
+    viewLabel->setFont(Font("Small Text", 11, Font::plain));
+    viewLabel->setColour(Label::textColourId, Colour(200,200,200));
+    addAndMakeVisible(viewLabel);
+
+    viewButton = new UtilityButton("Continuous", Font("Small Text", 13, Font::plain));
+    viewButton->setRadius(5.0f);
+    viewButton->setEnabledState(true);
+    viewButton->setCorners(true, true, true, true);
+    viewButton->addListener(this);
+    viewButton->setToggleState(false, dontSendNotification);
+    addAndMakeVisible(viewButton);
+
+    clearButton = new UtilityButton("Clear", Font("Small Text", 13, Font::plain));
+    clearButton->setRadius(5.0f);
+    clearButton->setEnabledState(true);
+    clearButton->setCorners(true, true, true, true);
+    clearButton->addListener(this);
+    clearButton->setToggleState(false, dontSendNotification);
+    addAndMakeVisible(clearButton);
+
+    preSecsLabel = new Label("Pre:","Pre:");
+    preSecsLabel->setFont(Font("Small Text", 13, Font::plain));
+    preSecsLabel->setColour(Label::textColourId, Colour(200, 200, 200));
+    addAndMakeVisible(preSecsLabel);
+
+    postSecsLabel = new Label("Post:","Post:");
+    postSecsLabel->setFont(Font("Small Text", 13, Font::plain));
+    postSecsLabel->setColour(Label::textColourId, Colour(200, 200, 200));
+    addAndMakeVisible(postSecsLabel);
+
+    preSecsInput = new Label("0.5","0.5");
+    preSecsInput->setFont(Font("Small Text", 13, Font::plain));
+    preSecsInput->setColour(Label::textColourId, Colour(250, 250, 250));
+    preSecsInput->setEditable(true);
+    preSecsInput->addListener(this);
+    addAndMakeVisible(preSecsInput);
+
+    postSecsInput = new Label("1.5","1.5");
+    postSecsInput->setFont(Font("Small Text", 13, Font::plain));
+    postSecsInput->setColour(Label::textColourId, Colour(250, 250, 250));
+    postSecsInput->setEditable(true);
+    postSecsInput->addListener(this);
+    addAndMakeVisible(postSecsInput);
+
+    electrodeLayoutLabel = new Label("Layout:","Layout:");
+    electrodeLayoutLabel->setFont(Font("Small Text", 11, Font::plain));
+    electrodeLayoutLabel->setColour(Label::textColourId, Colour(200,200,200));
+    addAndMakeVisible(electrodeLayoutLabel);
+
+    electrodeLayoutSelector = new UtilityButton("Default", Font("Small Text", 13, Font::plain));
+    electrodeLayoutSelector->setRadius(5.0f);
+    electrodeLayoutSelector->setEnabledState(true);
+    electrodeLayoutSelector->setCorners(true, true, true, true);
+    electrodeLayoutSelector->addListener(this);
+    electrodeLayoutSelector->setToggleState(false, dontSendNotification);
+    addAndMakeVisible(electrodeLayoutSelector);
+
+    resized();
+    repaint();
+    
+    processor->setRasterPlot(rasterPlot);
+
+    viewType = 0;
+    
+}
+
+
+SpikeRasterCanvas::~SpikeRasterCanvas(){
+    
+}
+    
+void SpikeRasterCanvas::beginAnimation()
+{
+    startCallbacks();
+    std::cout << "Spike Raster beginning animation." << std::endl;
+    
+	rasterPlot->reset();
+
+}
+
+void SpikeRasterCanvas::endAnimation()
+{
+    stopCallbacks();
+    rasterPlot->resetTimestamps();
+}
+    
+void SpikeRasterCanvas::refreshState()
+{
+}
+
+void SpikeRasterCanvas::update()
+{
+    rasterPlot->setNumberOfElectrodes(processor->getNumElectrodes());
+    ratePlot->setNumberOfElectrodes(processor->getNumElectrodes());
+
+}
+    
+void SpikeRasterCanvas::setParameter(int, float) {}
+
+void SpikeRasterCanvas::paint(Graphics& g)
+{
+    g.fillAll(Colours::darkgrey);
+}
+    
+void SpikeRasterCanvas::refresh()
+{
+    processor->setParameter(2, 0.0f); // request redraw
+    
+    repaint();
+}
+    
+void SpikeRasterCanvas::resized()
+{
+    rasterPlot->setBounds(140, 10, getWidth()-300, getHeight()-120);
+
+    ratePlot->setBounds(10, 10, 120, getHeight()-120);
+
+    psth->setBounds(140, getHeight()-100, getWidth()-300, 70);
+
+    timescale->setBounds(125, getHeight()-25, getWidth()-270, 20);
+
+    for (int i = 0; i < eventChannelButtons.size(); i++)
+    {
+         eventChannelButtons[i]->setBounds(getWidth()-140 + (i % 4) * 20, 40 + (i/4) * 20, 35, 35);
+    }
+
+    triggerLabel->setBounds(getWidth()-140, 15, 140, 30);
+    viewLabel->setBounds(getWidth()-140, 100, 140, 20);
+    viewButton->setBounds(getWidth()-135, 120, 105, 20);
+    clearButton->setBounds(getWidth()-135, 250, 74, 20);
+    preSecsLabel->setBounds(getWidth()-140, 160, 105, 15);
+    preSecsInput->setBounds(getWidth()-90, 160, 45, 15);
+    postSecsLabel->setBounds(getWidth()-140, 180, 105, 15);
+    postSecsInput->setBounds(getWidth()-90, 180, 45, 15);
+    electrodeLayoutLabel->setBounds(10, getHeight()-90, 140, 20);
+    electrodeLayoutSelector->setBounds(10, getHeight()-70, 105, 20);
+
+}
+
+void SpikeRasterCanvas::labelTextChanged(Label* l)
+{
+    String text = l->getText();
+    float value = text.getFloatValue();
+
+    if (value < 0.05)
+        value = 0.05;
+    else if (value > 5)
+        value = 5;
+
+    if (l == preSecsInput)
+        rasterPlot->setPreSecs(value);
+    else if (l == postSecsInput)
+        rasterPlot->setPostSecs(value);
+
+    l->setText(String(value), dontSendNotification);
+
+    float min = preSecsInput->getText().getFloatValue()*-1;
+    float max = postSecsInput->getText().getFloatValue();
+
+    timescale->setRange(min, max);
+
+}
+
+void SpikeRasterCanvas::buttonClicked(Button* b)
+{
+    if (b == viewButton)
+    {
+        if (viewType == 0)
+        {
+            viewType = 1;
+            viewButton->setLabel("All");
+        } else if (viewType == 1)
+        {
+            viewType = 2;
+            viewButton->setLabel("Single");
+        } else {
+            viewType = 0;
+            viewButton->setLabel("Continuous");
+        }
+
+        rasterPlot->setViewType(viewType);
+    } 
+    else if (b == clearButton)
+    {
+        rasterPlot->clear();
+    } else if (b == electrodeLayoutSelector)
+    {
+        if (ratePlot->layout == 0)
+        {
+            ratePlot->setLayout(1);
+            electrodeLayoutSelector->setLabel("Neuropix");
+        } else if (ratePlot->layout == 1)
+        {
+            ratePlot->setLayout(2);
+            electrodeLayoutSelector->setLabel("Neuroseeker");
+        } else {
+            ratePlot->setLayout(0);
+            electrodeLayoutSelector->setLabel("Default");
+        }
+
+    }
+
+    repaint();
+}
+    
+// =====================================================
+
+
+
+RasterPlot::RasterPlot(SpikeRasterCanvas*)
+{
+
+    rasterWidth = 500;
+    rasterTimebase = 2.0f;
+    preStimSecs = 0.5f;
+    rasterStartTimestamp = 0;
+    triggerTimestamp = -1;
+	currentTimestamp = 0;
+
+    viewType = 0;
+    trialIndex2 = 0;
+    totalTrials = 0;
+
+    electrodeChannels.add(0);
+
+    spikeBuffer = AudioSampleBuffer(100,rasterWidth);
+    trialBuffer1 = AudioSampleBuffer(100,rasterWidth);
+    trialBuffer2 = AudioSampleBuffer(100,rasterWidth);
+
+    spikeBuffer.clear();
+    trialBuffer1.clear();
+    trialBuffer2.clear();
+
+    random = Random();
+
+    reset();
+    
+}
+
+RasterPlot::~RasterPlot()
+{
+
+}
+
+void RasterPlot::setNumberOfElectrodes(int n)
+{
+    numElectrodes = n;
+
+    lastBufferPos.clear();
+
+    for (int i = 0; i < numElectrodes; i++)
+    {
+        lastBufferPos.add(0); // 0.0 to 1.0
+    }
+
+}
+
+void RasterPlot::reset()
+{
+    spikeBuffer.clear();
+    rasterStartTimestamp = 0;
+	currentTimestamp = 0;
+
+    repaint();
+}
+
+void RasterPlot::clear()
+{
+    trialBuffer1.clear();
+    trialBuffer2.clear();
+    trialIndex1 = 0;
+    trialIndex2 = 0;
+
+    setNumberOfElectrodes(numElectrodes); // clear last sample
+}
+
+void RasterPlot::setViewType(int v)
+{
+    viewType = v;
+}
+
+void RasterPlot::paint(Graphics& g)
+{
+
+    //std::cout << "Drawing raster" << std::endl;
+
+    AudioSampleBuffer* buffer;
+
+    switch (viewType)
+    {
+        case 0:
+            buffer = &spikeBuffer;
+            break;
+        case 1:
+            buffer = &trialBuffer1;
+            break;
+        case 2:
+            buffer = &trialBuffer2;
+            break;
+        default:
+			buffer = &spikeBuffer;
+            break;
+    }
+
+    g.fillAll(Colours::grey);
+
+    float numYPixels = buffer->getNumChannels();
+    float numXPixels = buffer->getNumSamples();
+
+    float xHeight = getWidth()/numXPixels;
+    float yHeight = getHeight()/numYPixels;
+
+    for (int n = 0; n < numXPixels; n++)
+    {
+        for (int m = 0; m < numYPixels; m++)
+        {
+            float colourIndex = buffer->getSample(m,n);
+
+            if (viewType == 1)
+            {
+                colourIndex /= (totalTrials);
+            }
+
+            if (colourIndex > 0)
+            {
+                g.setColour(Colour(colourIndex*128+127, colourIndex*128+127, colourIndex*128+127));
+                g.fillRect(n*xHeight, getHeight() - m*yHeight - yHeight, xHeight, yHeight);
+            }
+        }
+    }
+
+    //std::cout << lastBufferPos[0] << std::endl;
+
+    g.setColour(Colours::black);
+    g.fillRect(getMaxBufferPos() * getWidth(), 0.0f, xHeight*2, float(getHeight()));
+}
+
+void RasterPlot::resized()
+{
+
+}
+
+void RasterPlot::setPreSecs(float value)
+{
+    float postStimSecs = rasterTimebase - preStimSecs;
+
+    preStimSecs = value;
+    rasterTimebase = postStimSecs + preStimSecs;
+
+    clear();
+    getParentComponent()->repaint();
+}
+
+void RasterPlot::setPostSecs(float value)
+{
+
+    rasterTimebase = value + preStimSecs;
+
+    clear();
+
+    getParentComponent()->repaint();
+
+}
+
+void RasterPlot::setTimestamp(int64 ts)
+{
+
+    //std::cout << "Setting timestamp to " << ts << std::endl;
+
+	if (ts > 10000000000000 || ts < currentTimestamp)
+		return;
+
+    currentTimestamp = ts;
+
+    float bufferStart = (currentTimestamp - rasterStartTimestamp) / (sampleRate * rasterTimebase); // (0 to 1)
+
+	//std::cout << "Buffer start:  " << bufferStart <<  std::endl;
+	//std::cout << "Raster start:  " << rasterStartTimestamp << std::endl;
+	//std::cout << "Sample rate:  " << sampleRate << std::endl;
+	//std::cout << "Raster timebase:  " << rasterTimebase << std::endl;
+
+    if (bufferStart < 1.0) // no overflow
+    {
+        for (int i = 0; i < numElectrodes; i++)
+        {
+            spikeBuffer.clear(i, lastBufferPos[i] * rasterWidth + 1,
+                                 (bufferStart - lastBufferPos[i]) * rasterWidth);
+
+            lastBufferPos.set(i, bufferStart);
+        }
+
+    } else {
+        
+        float bufferRemaining = bufferStart - 1.0;
+
+        //std::cout << "Buffer remaining: " << bufferRemaining * rasterWidth << std::endl;
+
+        for (int i = 0; i < numElectrodes; i++)
+        {
+            int startSample = lastBufferPos[i] * rasterWidth + 1;
+            int endSample = (1.0 - lastBufferPos[i]) * rasterWidth;
+
+            if (startSample + endSample > spikeBuffer.getNumSamples())
+                endSample = spikeBuffer.getNumSamples() - startSample;
+
+           // std::cout << "Clearing " << startSample << " to " << endSample << std::endl;
+
+            spikeBuffer.clear(i, startSample, endSample);
+
+            startSample = 0;
+            endSample = bufferRemaining * rasterWidth;
+
+            if (startSample + endSample > spikeBuffer.getNumSamples())
+                endSample = spikeBuffer.getNumSamples() - startSample;
+
+        //    std::cout << "Clearing " << startSample << " to " << endSample << std::endl;
+            spikeBuffer.clear(i, startSample, endSample);
+
+            lastBufferPos.set(i, bufferRemaining);
+        }
+
+        rasterStartTimestamp = currentTimestamp - int( bufferRemaining * sampleRate * rasterTimebase );
+    }
+
+    if (triggerTimestamp > 0)
+    {
+        // copy data
+        int offset = int((rasterTimebase - preStimSecs) * sampleRate);
+
+        if (currentTimestamp > triggerTimestamp + offset)
+        {
+            
+            std::cout << "Trigger time: " <<  triggerTimestamp << 
+                        ", Current time: " <<  currentTimestamp << 
+                        ", Offset: " << offset << std::endl;
+
+            // copy data to all channels buffer
+
+            for (int n = 0; n < numElectrodes; n++)
+            {
+                trialBuffer1.addFrom(n, 0, spikeBuffer, n, 0, spikeBuffer.getNumSamples());
+            }
+
+            trialBuffer2.clear(trialIndex2, 0, trialBuffer2.getNumSamples());
+
+            for (int n = 0; n < electrodeChannels.size(); n++)
+            {
+                trialBuffer2.addFrom(trialIndex2, 0, spikeBuffer, electrodeChannels[n], 0, spikeBuffer.getNumSamples());
+            }
+
+            trialIndex2++;
+            totalTrials++;
+
+            if (trialIndex2 == spikeBuffer.getNumChannels())
+                trialIndex2 = 0;
+
+            triggerTimestamp = -1;
+        } 
+    }
+
+}
+
+void RasterPlot::resetTimestamps()
+{
+
+    //rasterStartTimestamp = 0;
+    //setNumberOfElectrodes(numElectrodes);
+}
+
+void RasterPlot::setSampleRate(float sr)
+{
+    sampleRate = sr;
+}
+
+void RasterPlot::processSpikeObject(const SpikeObject& s)
+{
+    int electrode = s.source;
+    //int unit = s.sortedId;
+    int timestamp = s.timestamp; // absolute time
+
+    float bufferPos = float(timestamp - rasterStartTimestamp) / (sampleRate * rasterTimebase);
+
+    //std::cout << "Spike time: " << bufferPos << std::endl;
+
+	//std::cout << spikeBuffer.getNumSamples() << " " << spikeBuffer.getNumChannels() << " " << (int)(timestamp * 1000) << " " << electrode << std::endl;
+
+    if (bufferPos > 0. && bufferPos < 1.)
+    {
+        //std::cout << relativeTimestamp << " " << displayStartTimestamp << std::endl;
+
+        int startSample = lastBufferPos[electrode] * rasterWidth + 1;
+        int numSamples = (bufferPos - lastBufferPos[electrode]) * rasterWidth;
+
+        if (numSamples > 0)
+        {
+            std::cout << electrode << " " << startSample << " " << numSamples << std::endl;
+            spikeBuffer.clear(electrode, startSample, numSamples);
+        } else {
+            spikeBuffer.clear(electrode, 0, bufferPos);
+        }
+
+        spikeBuffer.setSample(electrode, bufferPos * rasterWidth, 1);
+
+        lastBufferPos.set(electrode, bufferPos);
+    }
+}
+
+void RasterPlot::processEvent(int chan, int64 timestamp)
+{
+    //std::cout << "Event chan: " << chan << ", ts: " << timestamp << std::endl;
+
+    std::cout << std::endl;
+
+    if (triggerChannels.contains(chan))
+        triggerTimestamp = timestamp;
+}
+
+Array<float> RasterPlot::getPSTH(int numBins)
+{
+    int samplesPerBin = rasterWidth / numBins;
+
+    AudioSampleBuffer* buffer;
+
+    Array<float> psth;
+
+    switch (viewType)
+    {
+        case 0:
+        {
+            buffer = &spikeBuffer;
+            break;
+        }
+        case 1:
+        {
+            buffer = &trialBuffer1;
+            break;
+        }   
+        case 2:
+        {
+            buffer = &trialBuffer2;
+            break;
+        }   
+        default:
+            buffer = &spikeBuffer;
+            break;
+    }
+
+    int startBin; //= samplesPerBin * i;
+
+    float maxBin = 0;
+
+    for (int i = 0; i < numBins; i++)
+    {
+        startBin = samplesPerBin * i;
+
+        float totalSpikes = 0;
+
+        for (int m = startBin; m < startBin + samplesPerBin; m++)
+        {
+            for (int n = 0; n < numElectrodes; n++)
+            {
+                if (m < spikeBuffer.getNumSamples())
+                    totalSpikes += buffer->getSample(n,m);
+            }
+        }
+
+        float spikeRate = totalSpikes;
+
+        // normalize it!
+
+        switch (viewType)
+        {
+            case 0:
+            {
+                spikeRate /= float(numElectrodes);
+                
+                break;
+            }
+            case 1:
+            {
+                if (totalTrials > 0)
+                {
+                    spikeRate /= float(numElectrodes);
+                    spikeRate /= float(totalTrials);
+                }
+                    
+                break;
+            }   
+            case 2:
+            {
+                if (trialIndex2 > 0)
+                {
+                    
+                    spikeRate /= float(jmin(trialIndex2, trialBuffer2.getNumChannels()));
+
+                }
+                    
+                break;
+            }   
+            default:
+                break;
+        }
+
+        spikeRate /= (rasterTimebase / numBins);
+
+        psth.add(spikeRate);
+
+        maxBin = jmax(maxBin, spikeRate);
+
+        //std::cout << spikeRate << std::endl;
+    }
+
+    float upperBound = int(maxBin) / int(50) * int(50) + 40; 
+
+    for (int i = 0; i < numBins; i++)
+    {
+        psth.set(i, psth[i]/upperBound);
+    }
+
+    psth.add(upperBound);
+
+    return psth;
+}
+
+void RasterPlot::setEventTrigger(int ch, bool trigger)
+{
+
+    if (trigger)
+    {
+        triggerChannels.add(ch);
+    }
+    else
+    {
+        triggerChannels.remove(triggerChannels.indexOf(ch));
+    }
+}
+
+void RasterPlot::toggleElectrodeState(int ch)
+{
+    if (electrodeChannels.contains(ch))
+    {
+        electrodeChannels.remove(electrodeChannels.indexOf(ch));
+    } else {
+        electrodeChannels.add(ch);
+    }
+
+    trialBuffer2.clear();
+    trialIndex2 = 0;
+}
+
+
+float RasterPlot::getMaxBufferPos()
+{
+    float maxBufferPos = 0.0f;
+
+
+
+    switch (viewType)
+    {
+        case 0:
+        {
+            for (int i = 0; i < numElectrodes; i++)
+                maxBufferPos = jmax(lastBufferPos[i], maxBufferPos);
+            break;
+        }
+        
+        case 1:
+        {
+            maxBufferPos = preStimSecs / rasterTimebase;
+            break;
+        }
+            
+        case 2:
+        {
+            maxBufferPos = preStimSecs / rasterTimebase;
+            break;
+        }
+            
+        default:
+            break;
+    }
+
+    
+
+    return maxBufferPos; 
+}
+
+Array<float> RasterPlot::getFiringRates()
+{
+    Array<float> rates;
+
+    for (int n = 0; n < numElectrodes; n++)
+    {
+        float totalSpikes = 0;
+
+        for (int m = 0; m < spikeBuffer.getNumSamples(); m++)
+        {
+            totalSpikes += spikeBuffer.getSample(n,m);
+        }
+
+        float spikeRate = totalSpikes / rasterTimebase;
+
+        rates.add(spikeRate);
+    }
+
+    return rates;
+}
+
+Colour RasterPlot::getColourForChannel(int ch)
+{
+
+    if (ch == 0)
+        return Colour(224,185,36);
+    else if (ch == 1)
+        return Colour(243,119,33);
+    else if (ch == 2)
+        return Colour(237,37,36);
+    else if (ch == 3)
+        return Colour(217,46,171);
+    else if (ch == 4)
+        return Colour(101,31,255);
+    else if (ch == 5)
+        return Colour(48,117,255);
+    else if (ch == 6)
+        return Colour(116,227,156);
+    else
+        return Colour(82,173,0);
+ 
+}
+
+
+
+//===========================================================
+
+PSTH::PSTH(RasterPlot* r) : raster(r)
+{
+    numBins = 50;
+}
+
+PSTH::~PSTH()
+{
+
+}
+
+void PSTH::paint(Graphics& g)
+{
+    g.fillAll(Colours::lightgrey);
+
+    Array<float> psth = raster->getPSTH(numBins);
+
+    g.setColour(Colours::grey);
+
+    //if (psth[numBins] > 0)
+    //{
+        //std::cout << psth[numBins] << std::endl;
+    float barHeight = 10.0f;
+
+    while (barHeight < psth[numBins])
+    {
+         float h = getHeight() - (barHeight/psth[numBins])*getHeight();
+         g.drawLine(0.0f, h, getWidth(), h);
+         barHeight += 10.0f;
+     }
+    //}
+
+    g.setColour(Colour(225, 54, 125));
+
+    float maxBufferPos = raster->getMaxBufferPos();
+
+    float barWidth = float(getWidth())/float(numBins);
+
+    for (int i = 0; i < numBins; i++)
+    {
+
+        g.fillRect(barWidth*i, float(getHeight()*(1-psth[i])), barWidth+0.5f, psth[i]*getHeight());
+ 
+    }
+
+    g.setColour(Colours::black);
+    g.fillRect(maxBufferPos * getWidth(), 0.0f, 2.0, float(getHeight()));
+
+    float textHeight = 9.0f;
+    g.setFont(7);
+    while (textHeight < psth[numBins])
+    {
+         float h = getHeight() - (textHeight/psth[numBins])*getHeight();
+         g.drawText(String(textHeight + 1.0f), 2, (int) h, 8, 7, Justification::left, false);
+         textHeight += 10.0f;
+     }
+}
+
+void PSTH::resized()
+{
+
+}
+
+void PSTH::reset()
+{
+
+}
+
+// ==========================================================
+
+
+ElectrodeRateButton::ElectrodeRateButton(RatePlot* r, int chan_) : Button(String(chan_)), ratePlot(r)
+{
+    std::cout << "created button for channel " << chan_ << std::endl;
+    chan = chan_;
+    rate = 0.0f;
+
+    setClickingTogglesState(true);
+}
+
+
+ElectrodeRateButton::~ElectrodeRateButton()
+{
+
+}
+
+void ElectrodeRateButton::paintButton(Graphics& g, bool isMouseOver, bool isButtonDown)
+{
+    if (getToggleState())
+    {
+        //std::cout << "hi" << std::endl;
+        g.fillAll(Colours::white);
+    }
+
+    Colour fillColour = Colour(jmin(rate/20.0f, 1.0f)*225, 54.0f, jmin(rate/20.0f, 1.0f)*125);
+    g.setColour(fillColour);
+    g.fillRect(1,1,getHeight()-2,getWidth()-2);
+}
+
+void ElectrodeRateButton::resized()
+{
+
+}
+
+//===========================================================
+
+RatePlot::RatePlot(RasterPlot* r) : raster(r)
+{
+    layout = 0;
+}
+
+RatePlot::~RatePlot()
+{
+
+}
+
+void RatePlot::paint(Graphics& g)
+{
+    g.fillAll(Colours::lightgrey);
+
+    Array<float> rates = raster->getFiringRates();
+
+    for (int i = 0; i < electrodeButtons.size(); i++)
+    {
+        electrodeButtons[i]->rate = rates[i];
+    }
+
+    g.setFont(Font("Small Text", 12, Font::plain));
+
+    float xDist;
+    if (layout == 0)
+        xDist = 20;
+    else
+        xDist = 38;
+
+    g.drawText("0", 0., getHeight() - 20, xDist, 15., Justification::right, false);
+
+    for (int i = 9; i < electrodeButtons.size(); i += 10)
+    {
+        g.drawText(String(i+1), 0., getHeight() - (float(i)/4.*10. + 20.), xDist, 15., Justification::right, false);
+    }
+
+}
+
+void RatePlot::resized()
+{
+    float electrodeSize = 10.0f;
+    float xLoc = 0;
+    float yLoc = 0;
+    float xDist;
+
+    if (layout == 0)
+        xDist = 25;
+    else
+        xDist = 40;
+
+    float yDist = 20;
+
+    for (int i = 0; i < electrodeButtons.size(); i++)
+    {
+        switch (layout)
+        {
+            case 0:
+            {
+                if (i % 5 == 0){
+                    xLoc = 0;
+                    yLoc = (i / 5) * electrodeSize;
+                }
+                else if (i % 5 == 1){
+                    xLoc = electrodeSize * 3;
+                    yLoc = (i / 5)*electrodeSize;
+                }
+                else if (i % 5 == 2){
+                    xLoc = electrodeSize * 6;
+                    yLoc = (i / 5) * electrodeSize;
+                }
+                else if (i % 5 == 3){
+                    xLoc = electrodeSize*1.5;
+                    yLoc = (i / 5)*electrodeSize + electrodeSize/2;
+                }
+                else{
+                    xLoc = electrodeSize * 4.5;
+                    yLoc = (i / 5)*electrodeSize + electrodeSize/2;
+                }
+                break;
+            }
+            case 1:
+            {
+                if (i % 4 == 0){
+                    xLoc = 0;
+                    yLoc = (i / 4)*electrodeSize;
+                }
+                else if (i % 4 == 1){
+                    xLoc = electrodeSize * 2;
+                    yLoc = (i / 4)*electrodeSize;
+                }
+                else if (i % 4 == 2){
+                    xLoc = electrodeSize;
+                    yLoc = (i / 4)*electrodeSize + electrodeSize/2;
+                }
+                else{
+                    xLoc = electrodeSize * 3;
+                    yLoc = (i / 4)*electrodeSize + electrodeSize/2;
+                }
+                break;
+            }
+            case 2:
+            {
+                xLoc = (i % 4) * electrodeSize;
+                yLoc = (i / 4) * electrodeSize;
+                break;
+            }
+        }
+
+        electrodeButtons[i]->setBounds(xDist + xLoc, getHeight() - yDist - yLoc, electrodeSize, electrodeSize);
+        
+    }
+}
+
+void RatePlot::reset()
+{
+    
+}
+
+void RatePlot::buttonClicked(Button* button)
+{
+    ElectrodeRateButton* b = (ElectrodeRateButton*) button;
+
+    std::cout << b->chan << " was clicked." << std::endl;
+    b->repaint();
+
+    raster->toggleElectrodeState(b->chan);
+
+}
+
+void RatePlot::setLayout(int layout_)
+{
+    layout = layout_;
+
+    resized();
+
+}
+
+void RatePlot::setNumberOfElectrodes(int n)
+{
+    electrodeButtons.clear();
+
+    for (int i = 0; i < n; i++)
+    {
+        ElectrodeRateButton* button = new ElectrodeRateButton(this, i);
+        electrodeButtons.add(button);
+        addAndMakeVisible(button);
+        button->addListener(this);
+    }
+
+    if (electrodeButtons.size() > 0)
+        electrodeButtons[0]->setToggleState(true, dontSendNotification);
+
+    resized();
+}
+
+// =========================================================
+
+EventChannelButton::EventChannelButton(RasterPlot* rp, int chNum, Colour col):
+    isEnabled(false), colour(col), rasterPlot(rp)
+{
+
+    channelNumber = chNum;
+
+    chButton = new UtilityButton(String(channelNumber+1), Font("Small Text", 13, Font::plain));
+    chButton->setRadius(5.0f);
+    chButton->setBounds(4,4,14,14);
+    chButton->setEnabledState(true);
+    chButton->setCorners(true, false, true, false);
+
+    chButton->addListener(this);
+    addAndMakeVisible(chButton);
+
+}
+
+EventChannelButton::~EventChannelButton()
+{
+
+}
+
+void EventChannelButton::buttonClicked(Button* button)
+{
+
+    isEnabled = !isEnabled;
+
+    rasterPlot->setEventTrigger(channelNumber, isEnabled);
+
+    repaint();
+
+}
+
+
+void EventChannelButton::paint(Graphics& g)
+{
+
+    if (isEnabled)
+    {
+        g.setColour(colour);
+        g.fillRoundedRectangle(2,2,18,18,6.0f);
+    }
+
+}
+
+// ============================================================================
+
+
+
+Timescale::Timescale(RasterPlot* r) : raster(r)
+{
+    min = -0.5f;
+    max = 1.5f;
+
+    resolution = 0.5f;
+
+}
+
+Timescale::~Timescale()
+{
+
+}
+
+void Timescale::paint(Graphics& g)
+{
+    g.fillAll(Colour(55,55,55));
+    g.setColour(Colours::grey);
+    g.fillRect(2,2,getWidth()-4,getHeight()-4);
+    g.setColour(Colours::lightgrey);
+    g.setFont(Font("Small Text", 12, Font::plain));
+
+    float pt = min;
+
+    //g.drawText("0", 2, (int) h, 8, 7, Justification::left, false);
+
+    //std::cout << "max: " << max << ", min: " << min << std::endl;
+
+    while (pt <= max + resolution)
+    {
+        float xLoc = (pt - min)/(max-min);
+         //std::cout << " x: " << xLoc << " , val: " << pt << std::endl;
+         g.drawText(String(pt), xLoc*(getWidth()-30)-4, 4, 40, 13, Justification::centred, false);
+         pt += resolution;
+     }
+}
+
+void Timescale::resized()
+{
+
+}
+
+void Timescale::setRange(float mn, float mx)
+{
+    min = mn;
+    max = mx;
+
+    if (max - min > 4)
+    {
+        resolution = 1;
+    } else if (max - min < 4 && max - min > 1)
+    {
+        resolution = 0.5f;
+    } else if (max - min < 1)
+    {
+        resolution = 0.25f;
+    }
+
+    repaint();
+}
+
diff --git a/Source/Plugins/SpikeRaster/SpikeRasterEditor.h b/Source/Plugins/SpikeRaster/SpikeRasterEditor.h
new file mode 100644
index 000000000..050f6672b
--- /dev/null
+++ b/Source/Plugins/SpikeRaster/SpikeRasterEditor.h
@@ -0,0 +1,281 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2014 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 SPIKERASTEREDITOR_H_INCLUDED
+#define SPIKERASTEREDITOR_H_INCLUDED
+
+#include <VisualizerEditorHeaders.h>
+#include <VisualizerWindowHeaders.h>
+#include <SpikeLib.h>
+
+#include "SpikeRaster.h"
+
+class Visualizer;
+class ElectrodeRateButton;
+
+class PSTH : public Component
+{
+public:
+    PSTH(RasterPlot*);
+    virtual ~PSTH();
+
+    void paint(Graphics& g);
+    void resized();
+    void reset();
+
+    int numBins;
+    RasterPlot* raster;
+
+};
+
+
+
+class RatePlot : public Component, public Button::Listener
+{
+public:
+    RatePlot(RasterPlot*);
+    virtual ~RatePlot();
+
+    void paint(Graphics& g);
+    void resized();
+    void reset();
+
+    void buttonClicked(Button* button);
+
+    void setNumberOfElectrodes(int);
+
+    void setLayout(int);
+
+    OwnedArray<ElectrodeRateButton> electrodeButtons;
+
+    int layout;
+    int numElectrodes;
+    RasterPlot* raster;
+
+};
+
+class ElectrodeRateButton : public Button
+{
+public:
+    ElectrodeRateButton(RatePlot*, int chan);
+    virtual ~ElectrodeRateButton();
+
+    void paintButton(Graphics& g, bool, bool);
+    void resized();
+    int chan;
+    float rate;
+    bool isSelected;
+
+    RatePlot* ratePlot;
+
+};
+
+class Timescale : public Component
+{
+public:
+    Timescale(RasterPlot*);
+    virtual ~Timescale();
+
+    void paint(Graphics& g);
+    void resized();
+
+    void setRange(float, float);
+
+    RasterPlot* raster;
+
+    float min, max;
+    float resolution;
+
+};
+
+class EventChannelButton : public Component,
+    public Button::Listener
+{
+public:
+    EventChannelButton(RasterPlot*, int chNum, Colour col);
+    ~EventChannelButton();
+
+    void paint(Graphics& g);
+
+    void buttonClicked(Button* button);
+
+    bool isEnabled;
+
+private:
+
+    int channelNumber;
+    Colour colour;
+    RasterPlot* rasterPlot;
+    ScopedPointer<UtilityButton> chButton;
+
+};
+
+/**
+ 
+ User interface for the SpikeRaster module.
+ 
+ @see SpikeRaster
+ 
+ */
+
+class SpikeRasterEditor : public VisualizerEditor
+{
+public:
+    SpikeRasterEditor(GenericProcessor*, bool useDefaultParameterEditors);
+    ~SpikeRasterEditor();
+    
+    void updateSettings();
+    
+    Visualizer* createNewCanvas();
+    
+private:
+
+    RasterPlot* rasterPlot;
+    
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SpikeRasterEditor);
+};
+
+
+class SpikeRasterCanvas : public Visualizer, public Button::Listener, public Label::Listener
+{
+public:
+    SpikeRasterCanvas(SpikeRaster* n);
+    ~SpikeRasterCanvas();
+    
+    void beginAnimation();
+    void endAnimation();
+    
+    void refreshState();
+    void update();
+    
+    void setParameter(int, float);
+    void setParameter(int, int, int, float) {}
+
+    void buttonClicked(Button*);
+    void labelTextChanged(Label*);
+    
+    void paint(Graphics& g);
+
+    void refresh();
+    
+    void resized();
+    
+    RasterPlot* getRasterPlot() {return rasterPlot.get();}
+
+private:
+    SpikeRaster* processor;
+    ScopedPointer<RasterPlot> rasterPlot;
+    ScopedPointer<PSTH> psth;
+    ScopedPointer<RatePlot> ratePlot;
+    ScopedPointer<Timescale> timescale;
+
+    ScopedPointer<Label> triggerLabel;
+    OwnedArray<EventChannelButton> eventChannelButtons;
+
+    ScopedPointer<Label> viewLabel;
+    ScopedPointer<UtilityButton> viewButton;
+
+    ScopedPointer<Label> electrodeLayoutLabel;
+    ScopedPointer<UtilityButton> electrodeLayoutSelector;
+
+    ScopedPointer<UtilityButton> clearButton;
+
+    ScopedPointer<Label> preSecsInput;
+    ScopedPointer<Label> postSecsInput;
+    ScopedPointer<Label> preSecsLabel;
+    ScopedPointer<Label> postSecsLabel;
+
+    int viewType;
+
+    int currentMap;
+
+    float psthHeight;
+
+};
+
+class RasterPlot : public Component
+{
+public:
+    RasterPlot(SpikeRasterCanvas*);
+    virtual ~RasterPlot();
+
+    AudioSampleBuffer spikeBuffer;
+    AudioSampleBuffer trialBuffer1;
+    AudioSampleBuffer trialBuffer2;
+
+    void paint(Graphics& g);
+    void resized();
+    void reset();
+
+    void processSpikeObject(const SpikeObject& s);
+    void processEvent(int eventChan, int64 ts);
+
+    Random random;
+
+    void setNumberOfElectrodes(int);
+    void setSampleRate(float);
+    void toggleElectrodeState(int);
+
+    void setTimestamp(int64);
+    void resetTimestamps();
+
+    void setPreSecs(float);
+    void setPostSecs(float);
+
+    void setViewType(int);
+
+    void clear();
+
+    void setEventTrigger(int, bool);
+
+    Array<float> getPSTH(int numBins);
+    Array<float> getFiringRates();
+    Array<int> triggerChannels;
+    Array<int> electrodeChannels;
+    float getMaxBufferPos();
+    
+    int rasterWidth; // number of pixels across raster
+    float rasterTimebase; // timebase in s
+    float preStimSecs; // pre-stimulus time
+
+    int64 currentTimestamp;  // start time of data buffer (samples)
+    int64 rasterStartTimestamp;  // start time of raster (samples)
+    int64 triggerTimestamp;  // last trigger time
+
+    int numElectrodes;
+    int viewType;
+    int trialIndex1;
+    int trialIndex2;
+    int totalTrials;
+
+    Array<float> lastBufferPos;
+    float sampleRate;
+
+    Colour getColourForChannel(int ch);
+
+};
+
+
+
+
+#endif
\ No newline at end of file
-- 
GitLab