diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile
index f35c3a2e2e711a472cb5ea67be6bd003e0147d3a..2ed899ec5977a7eb734086eff1d1d0b700e3ae38 100644
--- a/Builds/Linux/Makefile
+++ b/Builds/Linux/Makefile
@@ -63,6 +63,7 @@ OBJECTS := \
   $(OBJDIR)/RootFinder_239a995f.o \
   $(OBJDIR)/State_22979684.o \
   $(OBJDIR)/AudioComponent_521bd9c9.o \
+  $(OBJDIR)/FileReader_18023b0e.o \
   $(OBJDIR)/ChannelMappingNode_d9219b9c.o \
   $(OBJDIR)/PulsePalOutput_9f4ef492.o \
   $(OBJDIR)/ReferenceNode_519d3b68.o \
@@ -280,6 +281,11 @@ $(OBJDIR)/AudioComponent_521bd9c9.o: ../../Source/Audio/AudioComponent.cpp
 	@echo "Compiling AudioComponent.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/FileReader_18023b0e.o: ../../Source/Processors/FileReader.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling FileReader.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/ChannelMappingNode_d9219b9c.o: ../../Source/Processors/ChannelMappingNode.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling ChannelMappingNode.cpp"
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
index cf3d1b7ca328fcb7d1c988fa42e5fe93acb2f552..191c0e73dc96441726d8a14a1cdc788d3b51b004 100644
--- a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
@@ -132,6 +132,7 @@
 		EDEE5E21F0C9BDB7DB796083 /* AudioResamplingNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76F569AE7B444D8F69EE0E86 /* AudioResamplingNode.cpp */; };
 		EE56A6BBBFA4A27A4BCF7279 /* SpikeDisplayCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7D4C9E3ED3763847C087F46 /* SpikeDisplayCanvas.cpp */; };
 		F0EC60AEFAFF3D289F8110BE /* ResamplingNodeEditor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C5ABE6BDCA91410BA92A7BD9 /* ResamplingNodeEditor.cpp */; };
+		F25EC78DCCC9CCEE805AE011 /* FileReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9215DC26F511C58DEE009209 /* FileReader.cpp */; };
 		F4397EAE00E0B9F96C8B6C07 /* InfoLabel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17E13CCDA0C82F92EAB05BE6 /* InfoLabel.cpp */; };
 		F505DF3C2BA492B5A2F28D05 /* Butterworth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B47B3368AA1A182B0CA1AB26 /* Butterworth.cpp */; };
 		FA2A052548AAD146F3F5AD83 /* juce_video.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A7695E93CE32F4E95042FCB /* juce_video.mm */; };
@@ -781,6 +782,7 @@
 		918837CC0447C50774036664 /* juce_StretchableLayoutResizerBar.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_StretchableLayoutResizerBar.cpp; path = ../../JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.cpp; sourceTree = SOURCE_ROOT; };
 		91D7B1F8B94AE9CFCC53771F /* EventDetector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EventDetector.h; path = ../../Source/Processors/EventDetector.h; sourceTree = SOURCE_ROOT; };
 		9200FC900D22733AE716C364 /* juce_CharPointer_UTF16.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF16.h; path = ../../JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h; sourceTree = SOURCE_ROOT; };
+		9215DC26F511C58DEE009209 /* FileReader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileReader.cpp; path = ../../Source/Processors/FileReader.cpp; sourceTree = SOURCE_ROOT; };
 		921F5D04122F324502DA4E75 /* juce_TextEditor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_TextEditor.cpp; path = ../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp; sourceTree = SOURCE_ROOT; };
 		92528D6653802FACF658D8EA /* FPGAOutputEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FPGAOutputEditor.h; path = ../../Source/Processors/Editors/FPGAOutputEditor.h; sourceTree = SOURCE_ROOT; };
 		92602D7166325C7232B85EDD /* DataThread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DataThread.cpp; path = ../../Source/Processors/DataThreads/DataThread.cpp; sourceTree = SOURCE_ROOT; };
@@ -1248,6 +1250,7 @@
 		FA23A1334E4CFA77BC18A153 /* FPGAThread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FPGAThread.cpp; path = ../../Source/Processors/DataThreads/FPGAThread.cpp; sourceTree = SOURCE_ROOT; };
 		FA2F04BA4E146ABF649BBE89 /* rhd2000evalboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = rhd2000evalboard.h; path = "../../Source/Processors/DataThreads/rhythm-api/rhd2000evalboard.h"; sourceTree = SOURCE_ROOT; };
 		FAC7E62CC15CA977A6FC72D1 /* juce_ChangeBroadcaster.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ChangeBroadcaster.cpp; path = ../../JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp; sourceTree = SOURCE_ROOT; };
+		FB071D0659E5F1CC630D765A /* FileReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FileReader.h; path = ../../Source/Processors/FileReader.h; sourceTree = SOURCE_ROOT; };
 		FB1B880F24F376D1AC52F2A6 /* juce_DrawableButton.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DrawableButton.cpp; path = ../../JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp; sourceTree = SOURCE_ROOT; };
 		FB1EA9CB3C695925627B0AC6 /* juce_HeapBlock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_HeapBlock.h; path = ../../JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h; sourceTree = SOURCE_ROOT; };
 		FB33617B5082CC0CDC189F2C /* juce_KeyboardFocusTraverser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_KeyboardFocusTraverser.h; path = ../../JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h; sourceTree = SOURCE_ROOT; };
@@ -2294,6 +2297,8 @@
 		83A3E005DDFCC55F277EEDA5 /* Processors */ = {
 			isa = PBXGroup;
 			children = (
+				9215DC26F511C58DEE009209 /* FileReader.cpp */,
+				FB071D0659E5F1CC630D765A /* FileReader.h */,
 				5654BDD4FBFF01AC3F17FA0D /* ChannelMappingNode.cpp */,
 				A234B2D091071A1B710E884B /* ChannelMappingNode.h */,
 				DBB295F412798131D3F04045 /* PulsePalOutput.cpp */,
@@ -3468,6 +3473,7 @@
 				AE06672D2CBF8F64465B2126 /* RootFinder.cpp in Sources */,
 				69630D3ECA4D6014EE3734CD /* State.cpp in Sources */,
 				0AE243437B40602D35435C32 /* AudioComponent.cpp in Sources */,
+				F25EC78DCCC9CCEE805AE011 /* FileReader.cpp in Sources */,
 				EA6A1BDDF81818D516B93DD6 /* ChannelMappingNode.cpp in Sources */,
 				7077270005BA819E3D5654B5 /* PulsePalOutput.cpp in Sources */,
 				FDCFDC9CC6D7A82131190FB0 /* ReferenceNode.cpp in Sources */,
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/contents.xcworkspacedata
old mode 100755
new mode 100644
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate
index 63924b6fb0fc499838b89ea8ac86904df7498056..e0f8ecf3b8d725434e6d6fc9d8bdbacf36ee218f 100755
Binary files a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate and b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/WorkspaceSettings.xcsettings b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/WorkspaceSettings.xcsettings
deleted file mode 100755
index 6ff33e6031ab04c443a8d439adb83f67eb5f4385..0000000000000000000000000000000000000000
--- a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IDEWorkspaceUserSettings_HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
-	<true/>
-	<key>IDEWorkspaceUserSettings_SnapshotAutomaticallyBeforeSignificantChanges</key>
-	<true/>
-</dict>
-</plist>
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/open-ephys.xcscheme b/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/open-ephys.xcscheme
deleted file mode 100755
index 5dc35bb337594d61e2c99cafb139eae70d13bd06..0000000000000000000000000000000000000000
--- a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/open-ephys.xcscheme
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C1E94289C8EA03969CA6896C"
-               BuildableName = "open-ephys.app"
-               BlueprintName = "open-ephys"
-               ReferencedContainer = "container:open-ephys.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      buildConfiguration = "Debug">
-      <Testables>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "C1E94289C8EA03969CA6896C"
-            BuildableName = "open-ephys.app"
-            BlueprintName = "open-ephys"
-            ReferencedContainer = "container:open-ephys.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </TestAction>
-   <LaunchAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
-      debugDocumentVersioning = "YES"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "C1E94289C8EA03969CA6896C"
-            BuildableName = "open-ephys.app"
-            BlueprintName = "open-ephys"
-            ReferencedContainer = "container:open-ephys.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Release"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "C1E94289C8EA03969CA6896C"
-            BuildableName = "open-ephys.app"
-            BlueprintName = "open-ephys"
-            ReferencedContainer = "container:open-ephys.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/xcschememanagement.plist b/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100755
index d832738999095c480a9817beba9247abbe599624..0000000000000000000000000000000000000000
--- a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/Josh.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>SchemeUserState</key>
-	<dict>
-		<key>open-ephys.xcscheme</key>
-		<dict>
-			<key>orderHint</key>
-			<integer>0</integer>
-		</dict>
-	</dict>
-	<key>SuppressBuildableAutocreation</key>
-	<dict>
-		<key>609761DEC9151D2CDD50270C</key>
-		<dict>
-			<key>primary</key>
-			<true/>
-		</dict>
-		<key>C1E94289C8EA03969CA6896C</key>
-		<dict>
-			<key>primary</key>
-			<true/>
-		</dict>
-	</dict>
-</dict>
-</plist>
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/ryan.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist b/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/ryan.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
deleted file mode 100644
index 647b6ca2b47334a4a3b309f80db229d8556c7630..0000000000000000000000000000000000000000
--- a/Builds/MacOSX/open-ephys.xcodeproj/xcuserdata/ryan.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Bucket
-   type = "1"
-   version = "1.0">
-   <FileBreakpoints>
-      <FileBreakpoint
-         shouldBeEnabled = "No"
-         ignoreCount = "0"
-         continueAfterRunningActions = "No"
-         filePath = "../../Source/Processors/Editors/ParameterEditor.cpp"
-         timestampString = "389810419.934004"
-         startingColumnNumber = "9223372036854775807"
-         endingColumnNumber = "9223372036854775807"
-         startingLineNumber = "527"
-         endingLineNumber = "527"
-         landmarkName = "ParameterEditor::channelSelectionUI()"
-         landmarkType = "5">
-      </FileBreakpoint>
-      <FileBreakpoint
-         shouldBeEnabled = "No"
-         ignoreCount = "0"
-         continueAfterRunningActions = "No"
-         filePath = "../../Source/Processors/Editors/ChannelSelector.h"
-         timestampString = "389831652.316183"
-         startingColumnNumber = "9223372036854775807"
-         endingColumnNumber = "9223372036854775807"
-         startingLineNumber = "113"
-         endingLineNumber = "113">
-      </FileBreakpoint>
-      <FileBreakpoint
-         shouldBeEnabled = "Yes"
-         ignoreCount = "0"
-         continueAfterRunningActions = "No"
-         filePath = "../../Source/Processors/Editors/ChannelSelector.h"
-         timestampString = "389833146.171327"
-         startingColumnNumber = "9223372036854775807"
-         endingColumnNumber = "9223372036854775807"
-         startingLineNumber = "112"
-         endingLineNumber = "112">
-      </FileBreakpoint>
-   </FileBreakpoints>
-</Bucket>
diff --git a/Builds/VisualStudio2010/open-ephys.vcxproj b/Builds/VisualStudio2010/open-ephys.vcxproj
index 42e91f6b4bbd47546759d676a8368b2a5629b0c0..eb4b9ededa47f8060e24daf256383641e64ad75f 100644
--- a/Builds/VisualStudio2010/open-ephys.vcxproj
+++ b/Builds/VisualStudio2010/open-ephys.vcxproj
@@ -147,6 +147,7 @@
     <ClCompile Include="..\..\Source\Dsp\RootFinder.cpp"/>
     <ClCompile Include="..\..\Source\Dsp\State.cpp"/>
     <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp"/>
+    <ClCompile Include="..\..\Source\Processors\FileReader.cpp"/>
     <ClCompile Include="..\..\Source\Processors\ChannelMappingNode.cpp"/>
     <ClCompile Include="..\..\Source\Processors\PulsePalOutput.cpp"/>
     <ClCompile Include="..\..\Source\Processors\ReferenceNode.cpp"/>
@@ -1328,6 +1329,7 @@
     <ClInclude Include="..\..\Source\Dsp\Types.h"/>
     <ClInclude Include="..\..\Source\Dsp\Utilities.h"/>
     <ClInclude Include="..\..\Source\Audio\AudioComponent.h"/>
+    <ClInclude Include="..\..\Source\Processors\FileReader.h"/>
     <ClInclude Include="..\..\Source\Processors\ChannelMappingNode.h"/>
     <ClInclude Include="..\..\Source\Processors\PulsePalOutput.h"/>
     <ClInclude Include="..\..\Source\Processors\ReferenceNode.h"/>
diff --git a/Builds/VisualStudio2010/open-ephys.vcxproj.filters b/Builds/VisualStudio2010/open-ephys.vcxproj.filters
index 6685c8880bae1ad8166b271d454a2ab3833fd9a1..d3b246777c040cb767e2472ca97ae6d1748bcf7b 100644
--- a/Builds/VisualStudio2010/open-ephys.vcxproj.filters
+++ b/Builds/VisualStudio2010/open-ephys.vcxproj.filters
@@ -415,6 +415,9 @@
     <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp">
       <Filter>open-ephys\Source\Audio</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\Source\Processors\FileReader.cpp">
+      <Filter>open-ephys\Source\Processors</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\Source\Processors\ChannelMappingNode.cpp">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClCompile>
@@ -1917,6 +1920,9 @@
     <ClInclude Include="..\..\Source\Audio\AudioComponent.h">
       <Filter>open-ephys\Source\Audio</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\Source\Processors\FileReader.h">
+      <Filter>open-ephys\Source\Processors</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\Source\Processors\ChannelMappingNode.h">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClInclude>
diff --git a/Builds/VisualStudio2012/open-ephys.sln b/Builds/VisualStudio2012/open-ephys.sln
index 7b3521089bd1056d8ba709ff0e694bcc2eaae886..3116206f2c14efe5d275395e6b114402fc27fd92 100644
--- a/Builds/VisualStudio2012/open-ephys.sln
+++ b/Builds/VisualStudio2012/open-ephys.sln
@@ -1,6 +1,6 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2012
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "open-ephys", "open-ephys.vcxproj", "{9C924D66-7DEC-1AEF-B375-DB8666BFB909}"
+Project("{5A05F353-1D63-394C-DFB0-981BB2309002}") = "open-ephys", "open-ephys.vcxproj", "{9C924D66-7DEC-1AEF-B375-DB8666BFB909}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Builds/VisualStudio2012/open-ephys.vcxproj b/Builds/VisualStudio2012/open-ephys.vcxproj
index 31a5aad6b2543b2f3282361ed3fff75da54a10fe..ecb81902960d1f599a4666f55efdbcdb16ad00e4 100644
--- a/Builds/VisualStudio2012/open-ephys.vcxproj
+++ b/Builds/VisualStudio2012/open-ephys.vcxproj
@@ -163,6 +163,7 @@
     <ClCompile Include="..\..\Source\Dsp\RootFinder.cpp"/>
     <ClCompile Include="..\..\Source\Dsp\State.cpp"/>
     <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp"/>
+    <ClCompile Include="..\..\Source\Processors\FileReader.cpp"/>
     <ClCompile Include="..\..\Source\Processors\ChannelMappingNode.cpp"/>
     <ClCompile Include="..\..\Source\Processors\PulsePalOutput.cpp"/>
     <ClCompile Include="..\..\Source\Processors\ReferenceNode.cpp"/>
@@ -1344,6 +1345,7 @@
     <ClInclude Include="..\..\Source\Dsp\Types.h"/>
     <ClInclude Include="..\..\Source\Dsp\Utilities.h"/>
     <ClInclude Include="..\..\Source\Audio\AudioComponent.h"/>
+    <ClInclude Include="..\..\Source\Processors\FileReader.h"/>
     <ClInclude Include="..\..\Source\Processors\ChannelMappingNode.h"/>
     <ClInclude Include="..\..\Source\Processors\PulsePalOutput.h"/>
     <ClInclude Include="..\..\Source\Processors\ReferenceNode.h"/>
diff --git a/Builds/VisualStudio2012/open-ephys.vcxproj.filters b/Builds/VisualStudio2012/open-ephys.vcxproj.filters
index 6685c8880bae1ad8166b271d454a2ab3833fd9a1..d3b246777c040cb767e2472ca97ae6d1748bcf7b 100644
--- a/Builds/VisualStudio2012/open-ephys.vcxproj.filters
+++ b/Builds/VisualStudio2012/open-ephys.vcxproj.filters
@@ -415,6 +415,9 @@
     <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp">
       <Filter>open-ephys\Source\Audio</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\Source\Processors\FileReader.cpp">
+      <Filter>open-ephys\Source\Processors</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\Source\Processors\ChannelMappingNode.cpp">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClCompile>
@@ -1917,6 +1920,9 @@
     <ClInclude Include="..\..\Source\Audio\AudioComponent.h">
       <Filter>open-ephys\Source\Audio</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\Source\Processors\FileReader.h">
+      <Filter>open-ephys\Source\Processors</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\Source\Processors\ChannelMappingNode.h">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClInclude>
diff --git a/README.rst b/README.rst
index 6980a103bbf0aaaf03418a291c3da0ab22d2b4fc..1c31169954b85bf45d9e58a233d41826a7ce4438 100644
--- a/README.rst
+++ b/README.rst
@@ -10,7 +10,7 @@ This repository contains all of the files (save for a few dependencies) you'll n
 
 We recommend reading through the GitHub wiki_ before attempting to make any changes.
 
-If you want to add files, you'll have to do that through "The Jucer," using the "open-ephys.jucer" file. The Jucer makefiles are located in the JuceLibraryCode/jucer/Builds folder, or as part of the Juce library package on SourceForge_.
+If you want to add files, you'll have to do that through the "Introjucer," using the "open-ephys.jucer" file. The Introjucer makefiles are located in the JuceLibraryCode/Introjucer/Builds folder, or as part of the Juce library package on SourceForge_.
 
 .. _SourceForge: http://sourceforge.net/projects/juce/files/juce/
 .. _JUCE: http://www.rawmaterialsoftware.com/juce.php
diff --git a/Source/Processors/DataThreads/DataThread.cpp b/Source/Processors/DataThreads/DataThread.cpp
index bdfebaaef6a735fafbbdd0260ecc233e11c700a9..450e27b93133f386125000aba159730b0aff4bf4 100755
--- a/Source/Processors/DataThreads/DataThread.cpp
+++ b/Source/Processors/DataThreads/DataThread.cpp
@@ -29,6 +29,11 @@ DataThread::DataThread(SourceNode* s) : Thread("Data Thread"), dataBuffer(0)
 {
     sn = s;
     setPriority(10);
+
+    timestamp = 0; // set default to zero, so that sources that
+                   // do not generate their own timestamps can simply increment
+                   // this value
+
 }
 
 DataThread::~DataThread()
diff --git a/Source/Processors/DataThreads/FileReaderThread.cpp b/Source/Processors/DataThreads/FileReaderThread.cpp
index 553725472d38c2656ecdedd1ce90d4e844726c16..64fb0928f1eed716c25699927f564ff361dcf2ac 100755
--- a/Source/Processors/DataThreads/FileReaderThread.cpp
+++ b/Source/Processors/DataThreads/FileReaderThread.cpp
@@ -137,11 +137,15 @@ bool FileReaderThread::updateBuffer()
 
         for (int n = 0; n < bufferSize; n++)
         {
-            thisSample[chan] = float(-readBuffer[n])*0.035;
+            thisSample[chan] = float(-readBuffer[n]) * 0.0305; // previously 0.035
 
             if (chan == 15)
             {
-                timestamp = timer.getHighResolutionTicks();
+
+                timestamp++; // = (0 << 0) + (0 << 8) + (0 << 16) + (0 << 24); // +
+                            //(4 << 32); // + (3 << 40) + (2 << 48) + (1 << 56);
+
+                //timestamp++; // = timer.getHighResolutionTicks();
                 dataBuffer->addToBuffer(thisSample, &timestamp, &eventCode, 1);
                 chan = 0;
             }
diff --git a/Source/Processors/DataThreads/RHD2000Thread.cpp b/Source/Processors/DataThreads/RHD2000Thread.cpp
index 76c9b5e9c365b09028469a4c03c6b7e0af6a00f0..e8fb5c1a9e8d41f2b81f02687ade3dc15f91968e 100644
--- a/Source/Processors/DataThreads/RHD2000Thread.cpp
+++ b/Source/Processors/DataThreads/RHD2000Thread.cpp
@@ -412,8 +412,7 @@ float RHD2000Thread::getSampleRate()
 
 float RHD2000Thread::getBitVolts()
 {
-    //return 0.1907;
-	return 0.000064; // empirical value determined by calibrating against reid's code - fix this
+	return 0.195f;
 }
 
 double RHD2000Thread::setUpperBandwidth(double desiredUpperBandwidth)
diff --git a/Source/Processors/Editors/ChannelSelector.cpp b/Source/Processors/Editors/ChannelSelector.cpp
index a33f807c4078495b7f2cfdb3fe499a81292f4d9f..2cc01b7c55827310e74a5b2565d075e1b0a9747d 100755
--- a/Source/Processors/Editors/ChannelSelector.cpp
+++ b/Source/Processors/Editors/ChannelSelector.cpp
@@ -525,13 +525,6 @@ void ChannelSelector::buttonClicked(Button* button)
                 audioButtons[i]->setToggleState(false, true);
             }
         }
-
-        if (radioStatus) // if radio buttons are active
-        {
-                // send a message to parent
-                GenericEditor* editor = (GenericEditor*) getParentComponent();
-                editor->channelChanged(-1);
-         }
     }
     else
     {
diff --git a/Source/Processors/Editors/FileReaderEditor.cpp b/Source/Processors/Editors/FileReaderEditor.cpp
index 902b5f9946eaef88f159e2da1ec78ff922cfe25f..64a855c3539027f3e10af4f48cfbc77de424a38c 100644
--- a/Source/Processors/Editors/FileReaderEditor.cpp
+++ b/Source/Processors/Editors/FileReaderEditor.cpp
@@ -23,14 +23,17 @@
 
 #include "FileReaderEditor.h"
 
-#include "../DataThreads/FileReaderThread.h"
+#include "../FileReader.h"
 
 #include <stdio.h>
 
-FileReaderEditor::FileReaderEditor(GenericProcessor* parentNode, FileReaderThread* thread_, bool useDefaultParameterEditors=true)
-   : GenericEditor(parentNode, useDefaultParameterEditors), thread(thread_)
+FileReaderEditor::FileReaderEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors=true)
+   : GenericEditor(parentNode, useDefaultParameterEditors)
 
 {
+
+	fileReader = (FileReader*) parentNode;
+
 	lastFilePath = File::getCurrentWorkingDirectory();
 
 	fileButton = new UtilityButton("Select file",Font("Small Text", 13, Font::plain));
@@ -44,6 +47,8 @@ FileReaderEditor::FileReaderEditor(GenericProcessor* parentNode, FileReaderThrea
 
     desiredWidth = 180;
 
+    setEnabledState(false);
+
 }
 
 FileReaderEditor::~FileReaderEditor()
@@ -56,8 +61,12 @@ void FileReaderEditor::setFile(String file)
 
 	File fileToRead(file);
 	lastFilePath = fileToRead.getParentDirectory();
-	thread->setFile(fileToRead.getFullPathName());
+	fileReader->setFile(fileToRead.getFullPathName());
 	fileNameLabel->setText(fileToRead.getFileName(),false);
+
+	setEnabledState(true);
+
+	repaint();
 }
 
 void FileReaderEditor::buttonEvent(Button* button)
diff --git a/Source/Processors/Editors/FileReaderEditor.h b/Source/Processors/Editors/FileReaderEditor.h
index 7a7fdd4cb53d1c0dc30d682edfdb074e2fbece3f..fb82c4b6acd9274a3a2ef679a01555a13487c127 100644
--- a/Source/Processors/Editors/FileReaderEditor.h
+++ b/Source/Processors/Editors/FileReaderEditor.h
@@ -28,7 +28,7 @@
 #include "../../../JuceLibraryCode/JuceHeader.h"
 #include "GenericEditor.h"
 
-class FileReaderThread;
+class FileReader;
 
 
 /**
@@ -43,7 +43,7 @@ class FileReaderThread;
 
 {
 public:
-    FileReaderEditor(GenericProcessor* parentNode, FileReaderThread*, bool useDefaultParameterEditors);
+    FileReaderEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors);
     virtual ~FileReaderEditor();
 
     void buttonEvent(Button* button);
@@ -59,7 +59,7 @@ private:
 	ScopedPointer<UtilityButton> fileButton;
 	ScopedPointer<Label> fileNameLabel;
 
-	FileReaderThread* thread;
+	FileReader* fileReader;
 
 	File lastFilePath;
 
diff --git a/Source/Processors/Editors/GenericEditor.cpp b/Source/Processors/Editors/GenericEditor.cpp
index 28f5f8bcf739e8cc683d5cad6f2313ad55e36983..4738166c21f254c5f9556c07377a06735e200a76 100755
--- a/Source/Processors/Editors/GenericEditor.cpp
+++ b/Source/Processors/Editors/GenericEditor.cpp
@@ -90,7 +90,10 @@ void GenericEditor::constructorInitialize(GenericProcessor* owner, bool useDefau
         addChildComponent(channelSelector);
         channelSelector->setVisible(false);
 
-
+        isSplitOrMerge=false;
+    }
+    else{
+        isSplitOrMerge=true;
     }
 
     backgroundGradient = ColourGradient(Colour(190, 190, 190), 0.0f, 0.0f,
@@ -459,34 +462,62 @@ Channel* GenericEditor::getEventChannel(int chan)
 
 Array<int> GenericEditor::getActiveChannels()
 {
+    if (!isSplitOrMerge)
+    {
     Array<int> a = channelSelector->getActiveChannels();
     return a;
+    }
+    else{
+        Array<int> a;
+        return a;
+    }
 }
 
 bool GenericEditor::getRecordStatus(int chan)
 {
+    if (!isSplitOrMerge)
+    {
     return channelSelector->getRecordStatus(chan);
+    }
+    else{
+        return false;
+    }
 }
 
 bool GenericEditor::getAudioStatus(int chan)
 {
-    return channelSelector->getAudioStatus(chan);
+    if (!isSplitOrMerge)
+    {
+        return channelSelector->getAudioStatus(chan);
+    }
+    else{
+        return false;
+    }
 }
 
 void GenericEditor::getChannelSelectionState(int chan, bool* p, bool* r, bool* a)
 {
-
-    *p = channelSelector->getParamStatus(chan);
-    *r = channelSelector->getRecordStatus(chan);
-    *a = channelSelector->getAudioStatus(chan);
+    if (!isSplitOrMerge)
+    {
+        *p = channelSelector->getParamStatus(chan);
+        *r = channelSelector->getRecordStatus(chan);
+        *a = channelSelector->getAudioStatus(chan);
+    }
+    else{
+        *p = false;
+        *r = false;
+        *a = false;
+    }
 }
 
 void GenericEditor::setChannelSelectionState(int chan, bool p, bool r, bool a)
 {
-
-    channelSelector->setParamStatus(chan, p);
-    channelSelector->setRecordStatus(chan, r);
-    channelSelector->setAudioStatus(chan, a);
+    if (!isSplitOrMerge)
+    {
+        channelSelector->setParamStatus(chan, p);
+        channelSelector->setRecordStatus(chan, r);
+        channelSelector->setAudioStatus(chan, a);
+    }
 }
 
 void GenericEditor::saveEditorParameters(XmlElement* xml)
diff --git a/Source/Processors/Editors/GenericEditor.h b/Source/Processors/Editors/GenericEditor.h
index c8c59178b25dcf4e1c310ff5869d924bd6bdf9a1..762b9bc3f6593b28128c43f18c16d3516c1c609f 100755
--- a/Source/Processors/Editors/GenericEditor.h
+++ b/Source/Processors/Editors/GenericEditor.h
@@ -275,6 +275,8 @@ private:
     bool isSelected;
     bool isEnabled;
 
+    /**Used to determine if an editor is a splitter or Merger to avoid calling on CHannelSelector*/
+    bool isSplitOrMerge;
 
     int tNum;
 
diff --git a/Source/Processors/Editors/RHD2000Editor.cpp b/Source/Processors/Editors/RHD2000Editor.cpp
index 2ea0127f684931ae5ea45af2c8824914a036d4d0..bea8a7d372c6cb7cfa3d767da10398f7458f1bbb 100644
--- a/Source/Processors/Editors/RHD2000Editor.cpp
+++ b/Source/Processors/Editors/RHD2000Editor.cpp
@@ -191,7 +191,8 @@ void SampleRateInterface::comboBoxChanged(ComboBox* cb)
             //cb->setText(cb->getItemText(cb->getSelectedId()),true);
             std::cout << "Setting sample rate to index " << cb->getSelectedId() << std::endl;
 
-            repaint();
+            editor->getEditorViewport()->makeEditorVisible(editor, false, true);
+            //repaint();
         }
     }
 }
diff --git a/Source/Processors/Editors/SpikeDetectorEditor.cpp b/Source/Processors/Editors/SpikeDetectorEditor.cpp
index a8de474db5957720d6697497c9af27cc6cb80c25..19b31041c723bebad9550da6d171d1cb43f0c377 100755
--- a/Source/Processors/Editors/SpikeDetectorEditor.cpp
+++ b/Source/Processors/Editors/SpikeDetectorEditor.cpp
@@ -530,9 +530,7 @@ void ElectrodeButton::paintButton(Graphics& g, bool isMouseOver, bool isButtonDo
 
     g.drawRect(0,0,getWidth(),getHeight(),1.0);
 
-    if (chan >= 0)
-        g.drawText(String(chan),0,0,getWidth(),getHeight(),Justification::centred,true);
-
+    g.drawText(String(chan),0,0,getWidth(),getHeight(),Justification::centred,true);
 }
 
 
diff --git a/Source/Processors/Editors/SpikeDetectorEditor.h b/Source/Processors/Editors/SpikeDetectorEditor.h
index 81e772c3e23b40a67acb64a6c6abdd2808ccb1b3..2fae5e948af40923ac7d1ad435706afef1dd6930 100755
--- a/Source/Processors/Editors/SpikeDetectorEditor.h
+++ b/Source/Processors/Editors/SpikeDetectorEditor.h
@@ -80,11 +80,7 @@ public:
     ElectrodeEditorButton(const String& name_, Font font_) : Button("Electrode Editor"),
         name(name_), font(font_)
     {
-        if (name.equalsIgnoreCase("edit") || 
-            name.equalsIgnoreCase("monitor") ||
-            name.equalsIgnoreCase("mapping") ||
-            name.equalsIgnoreCase("ref")
-            )
+        if (name.equalsIgnoreCase("edit") || name.equalsIgnoreCase("monitor"))
             setClickingTogglesState(true);
     }
     ~ElectrodeEditorButton() {}
diff --git a/Source/Processors/FileReader.cpp b/Source/Processors/FileReader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..26dd11906045e6c03e1986d64d562c6233a6b07f
--- /dev/null
+++ b/Source/Processors/FileReader.cpp
@@ -0,0 +1,200 @@
+/*
+    ------------------------------------------------------------------
+
+    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 "FileReader.h"
+#include "Editors/FileReaderEditor.h"
+#include <stdio.h>
+
+FileReader::FileReader()
+    : GenericProcessor("File Reader")
+{
+
+	input = 0;
+	timestamp = 0; 
+
+	enabledState(false);
+
+}
+
+FileReader::~FileReader()
+{
+	if (input)
+        fclose(input);
+}
+
+AudioProcessorEditor* FileReader::createEditor()
+{
+	editor = new FileReaderEditor(this, true);
+
+	return editor;
+
+}
+
+
+float FileReader::getDefaultSampleRate()
+{
+    return 40000.0f;
+}
+
+int FileReader::getDefaultNumOutputs()
+{
+    return 16;
+}
+
+float FileReader::getDefaultBitVolts()
+{
+    return 0.05f;
+}
+
+void FileReader::enabledState(bool t)
+{
+
+	isEnabled = t;
+
+}
+
+
+void FileReader::setFile(String fullpath)
+{
+
+    filePath = fullpath;
+
+    const char* path = filePath.getCharPointer();
+
+    if (input)
+        fclose(input);
+
+    input = fopen(path, "r");
+
+    // Avoid a segfault if file isn't found
+    if (!input)
+    {
+        std::cout << "Can't find data file "
+                  << '"' << path << "\""
+                  << std::endl;
+        return;
+    }
+
+    fseek(input, 0, SEEK_END);
+    lengthOfInputFile = ftell(input);
+    rewind(input);
+
+}
+
+
+String FileReader::getFile()
+{
+    return filePath;
+}
+
+void FileReader::updateSettings()
+{
+
+}
+
+
+
+void FileReader::process(AudioSampleBuffer& buffer, MidiBuffer& events, int& nSamples)
+{
+
+	uint8 data[8];
+    memcpy(data, &timestamp, 8);
+
+    // generate timestamp
+    addEvent(events,    // MidiBuffer
+             TIMESTAMP, // eventType
+             0,         // sampleNum
+             nodeId,    // eventID
+             0,		 // eventChannel
+             8,         // numBytes
+             data   // data
+            );
+
+    // FIXME: needs to account for the fact that the ratio might not be an exact
+    //        integer value
+	int samplesNeeded = (int) float(buffer.getNumSamples()) * (getDefaultSampleRate()/44100.0f); 
+
+	 if (ftell(input) >= lengthOfInputFile - samplesNeeded)
+     {
+        rewind(input);
+     }
+
+     size_t numRead = fread(readBuffer, 2, samplesNeeded*buffer.getNumChannels(), input);
+
+    int chan = 0;
+    int samp = 0;
+
+    for (size_t n = 0; n < numRead; n++)
+    {
+        
+        if (chan == buffer.getNumChannels())
+        {
+        	samp++;
+            timestamp++;
+            chan = 0;
+        }
+
+        *buffer.getSampleData(chan++, samp) = float(-readBuffer[n]) * getDefaultBitVolts(); // previously 0.035
+
+    }
+
+    nSamples = samplesNeeded;
+
+}
+
+
+void FileReader::setParameter(int parameterIndex, float newValue)
+{
+
+}
+
+
+
+void FileReader::saveCustomParametersToXml(XmlElement* parentElement)
+{
+
+    XmlElement* childNode = parentElement->createNewChildElement("FILENAME");
+    childNode->setAttribute("path", getFile());
+
+}
+
+void FileReader::loadCustomParametersFromXml()
+{
+
+    if (parametersAsXml != nullptr)
+    {
+        // use parametersAsXml to restore state 
+
+        forEachXmlChildElement(*parametersAsXml, xmlNode)
+        {
+           if (xmlNode->hasTagName("FILENAME"))
+            {
+                String filepath = xmlNode->getStringAttribute("path");
+               FileReaderEditor* fre = (FileReaderEditor*) getEditor();
+                fre->setFile(filepath);
+
+            }
+        }   
+    }
+
+}
\ No newline at end of file
diff --git a/Source/Processors/FileReader.h b/Source/Processors/FileReader.h
new file mode 100644
index 0000000000000000000000000000000000000000..38e5c697412298296cd82f05d88115f65c2f2d58
--- /dev/null
+++ b/Source/Processors/FileReader.h
@@ -0,0 +1,98 @@
+/*
+    ------------------------------------------------------------------
+
+    This file is part of the Open Ephys GUI
+    Copyright (C) 2013 Open Ephys
+
+    ------------------------------------------------------------------
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef __FILEREADER_H_B327D3D2__
+#define __FILEREADER_H_B327D3D2__
+
+
+#include "../../JuceLibraryCode/JuceHeader.h"
+
+#include "GenericProcessor.h"
+
+#define BUFFER_SIZE 102400
+
+/**
+
+  Reads data from a file.
+
+  @see GenericProcessor
+
+*/
+
+class FileReader : public GenericProcessor
+
+{
+public:
+
+    FileReader();
+    ~FileReader();
+
+    void process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages, int& nSamples);
+    void setParameter(int parameterIndex, float newValue);
+
+    AudioProcessorEditor* createEditor();
+
+    bool hasEditor() const
+    {
+        return true;
+    }
+
+    void updateSettings();
+
+    bool isSource()
+    {
+    	return true;
+    }
+
+    void enabledState(bool t);
+
+    float getDefaultSampleRate();
+    int getDefaultNumOutputs();
+    float getDefaultBitVolts();
+
+    void setFile(String fullpath);
+    String getFile();
+
+    void saveCustomParametersToXml(XmlElement* parentElement);
+    void loadCustomParametersFromXml();
+
+private:
+
+	uint64 timestamp;
+
+	int lengthOfInputFile;
+    FILE* input;
+
+    int16 readBuffer[BUFFER_SIZE];
+
+    int bufferSize;
+
+    String filePath;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FileReader);
+
+};
+
+
+#endif  // __FILEREADER_H_B327D3D2__
diff --git a/Source/Processors/GenericProcessor.cpp b/Source/Processors/GenericProcessor.cpp
index 435f4f8d5b5e3e5ce1240698dc721c7e287438de..1c9306941f922b2a7e0b9b191eef3fa0078f3b26 100755
--- a/Source/Processors/GenericProcessor.cpp
+++ b/Source/Processors/GenericProcessor.cpp
@@ -469,16 +469,18 @@ void GenericProcessor::addEvent(MidiBuffer& eventBuffer,
     data[1] = nodeId;  // processor ID automatically added
     data[2] = eventId; // event ID
     data[3] = eventChannel; // event channel
-    memcpy(&data[4], eventData, numBytes);
+    memcpy(data + 4, eventData, numBytes);
 
-    eventBuffer.addEvent(data, 		// spike data
-                         sizeof(data), // total bytes
+
+    //std::cout << 4 + numBytes << std::endl;
+
+    eventBuffer.addEvent(data, 		// raw data
+                         4 + numBytes, // total bytes
                          sampleNum);     // sample index
 
     //if (type == TTL)
     //	std::cout << "Adding event for channel " << (int) eventChannel << " with ID " << (int) eventId << std::endl;
 
-    delete data;
 }
 
 // void GenericProcessor::unpackEvent(int type,
diff --git a/Source/Processors/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph.cpp
index 078e28f862e962ec47eb8fa808a936c431a51dff..2c0a3707d99c7c6f97b1566a9214e20ea624bf3d 100644
--- a/Source/Processors/ProcessorGraph.cpp
+++ b/Source/Processors/ProcessorGraph.cpp
@@ -41,10 +41,10 @@
 #include "SpikeDetector.h"
 #include "PhaseDetector.h"
 #include "WiFiOutput.h"
+#include "FileReader.h"
 #include "ArduinoOutput.h"
 #include "FPGAOutput.h"
 #include "PulsePalOutput.h"
-#include "ChannelMappingNode.h"
 #include "Utilities/RecordControl.h"
 #include "Utilities/Splitter.h"
 #include "Utilities/Merger.h"
@@ -430,7 +430,7 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip
     {
 
         if (subProcessorType.equalsIgnoreCase("RHA2000-EVAL") ||
-            subProcessorType.equalsIgnoreCase("File Reader") ||
+           // subProcessorType.equalsIgnoreCase("File Reader") ||
             subProcessorType.equalsIgnoreCase("Custom FPGA") ||
             subProcessorType.equalsIgnoreCase("Rhythm FPGA"))
         {
@@ -456,6 +456,11 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip
             processor = new EventNode();
             std::cout << "Creating a new event node." << std::endl;
         }
+        else if (subProcessorType.equalsIgnoreCase("File Reader"))
+        {
+            processor = new FileReader();
+            std::cout << "Creating a new file reader." << std::endl;
+        }
 
 
         sendActionMessage("New source node created.");
@@ -498,11 +503,6 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip
             std::cout << "Creating a new digital reference." << std::endl;
             processor = new ReferenceNode();
         }
-        else if (subProcessorType.equalsIgnoreCase("Channel Mapping"))
-        {
-            std::cout << "Creating a new channel mapping node." << std::endl;
-            processor = new ChannelMappingNode();
-        }
 
         sendActionMessage("New filter node created.");
 
diff --git a/Source/Processors/RecordNode.cpp b/Source/Processors/RecordNode.cpp
index 69aa7c771eb505cafd6e4dfb85126f6f73d7c812..bfcc0e1eb80064d4409c0917cbb6c280eab37430 100755
--- a/Source/Processors/RecordNode.cpp
+++ b/Source/Processors/RecordNode.cpp
@@ -31,7 +31,7 @@
 RecordNode::RecordNode()
     : GenericProcessor("Record Node"),
       isRecording(false), isProcessing(false), signalFilesShouldClose(false),
-      timestamp(0), newDirectoryNeeded(true)
+      timestamp(0), newDirectoryNeeded(true), sampleCount(0), zeroBuffer(1, 50000)
 {
 
 
@@ -48,13 +48,15 @@ RecordNode::RecordNode()
     recordMarker = new char[10];
     for (int i = 0; i < 9; i++)
     {
-        recordMarker[i] = 0;
+        recordMarker[i] = i;
     }
     recordMarker[9] = 255;
 
     // 128 inputs, 0 outputs
     setPlayConfigDetails(getNumInputs(),getNumOutputs(),44100.0,128);
 
+    zeroBuffer.clear();
+
 }
 
 
@@ -310,6 +312,8 @@ void RecordNode::setParameter(int parameterIndex, float newValue)
 
         openFile(eventChannel);
 
+         sampleCount = 0; // reset sample count
+
         // create / open necessary files
         for (int i = 0; i < channelPointers.size(); i++)
         {
@@ -352,6 +356,13 @@ void RecordNode::setParameter(int parameterIndex, float newValue)
 
                 if (isRecording)
                 {
+
+                    if (sampleCount < BLOCK_LENGTH)
+                    {
+                        // fill out the rest of the current buffer
+                        writeContinuousBuffer(zeroBuffer.getSampleData(0), BLOCK_LENGTH - sampleCount, currentChannel);
+                    }
+
                     closeFile(channelPointers[currentChannel]);
                 }
 
@@ -365,6 +376,13 @@ void RecordNode::setParameter(int parameterIndex, float newValue)
 
                     openFile(channelPointers[currentChannel]);
 
+                    if (sampleCount > 0)
+                    {
+                        writeTimestampAndSampleCount(channelPointers[currentChannel]->file);
+                        // fill up the first data block up to sample count
+                        writeContinuousBuffer(zeroBuffer.getSampleData(0), sampleCount, currentChannel);
+                    }
+
                 }
             }
         }
@@ -399,9 +417,14 @@ void RecordNode::openFile(Channel* ch)
     {
         std::cout << "File already exists, just opening." << std::endl;
     }
+
+    
+
     //To avoid a race condition resulting on data written before the header,
     //do not assign the channel pointer until the header has been written
     ch->file = chFile;
+
+
 }
 
 void RecordNode::closeFile(Channel* ch)
@@ -453,6 +476,9 @@ String RecordNode::generateHeader(Channel* ch)
         header += "header.sampleRate = ";
         header += String(ch->sampleRate);
         header += ";\n";
+        header += "header.blockLength = '";
+        header += BLOCK_LENGTH;
+        header += "';\n";
     }
 
     header += "header.bitVolts = ";
@@ -474,6 +500,13 @@ void RecordNode::closeAllFiles()
     {
         if (channelPointers[i]->isRecording)
         {
+
+            if (sampleCount < BLOCK_LENGTH)
+            {
+                // fill out the rest of the current buffer
+                writeContinuousBuffer(zeroBuffer.getSampleData(0), BLOCK_LENGTH - sampleCount, i);
+            }
+
             closeFile(channelPointers[i]);
         }
     }
@@ -508,55 +541,67 @@ float RecordNode::getFreeSpace()
 
 void RecordNode::writeContinuousBuffer(float* data, int nSamples, int channel)
 {
+    
+    // check to see if the file exists
     if (channelPointers[channel]->file == NULL)
         return;
 
-    float scaleFactor = float(0x7fff) * channelPointers[channel]->bitVolts;
-    // scale the data appropriately -- currently just getting it into the right
-    // range; actually need to take into account the gain of each channel
+    // scale the data back into the range of int16
+    float scaleFactor =  float(0x7fff) * channelPointers[channel]->bitVolts;
     for (int n = 0; n < nSamples; n++)
     {
-        *(continuousDataFloatBuffer+n) = *(data+n) / 10000.0f; // / scaleFactor;
+        *(continuousDataFloatBuffer+n) = *(data+n) / scaleFactor;
     }
+    AudioDataConverters::convertFloatToInt16BE(continuousDataFloatBuffer, continuousDataIntegerBuffer, nSamples);
 
-    // find file and write samples to disk
+    // 
+    //int16 samps = (int16) nSamples;
 
-    //if (nSamples < 1000) // this is temporary, but there seems to be an error reading in the data if too many samples are written
-    // in the first few blocks
-    //{
+    if (sampleCount == 0)
+    {
+        writeTimestampAndSampleCount(channelPointers[channel]->file);
+    }
 
-    AudioDataConverters::convertFloatToInt16BE(continuousDataFloatBuffer, continuousDataIntegerBuffer, nSamples);
+    // FIXME: ensure fwrite returns equal "count"; otherwise,
+    // there was an error.
+    fwrite(continuousDataIntegerBuffer,     // ptr
+           2,                               // size of each element
+           nSamples,                        // count
+           channelPointers[channel]->file); // ptr to FILE object
 
-    int16 samps = (int16) nSamples;
+    if (sampleCount + nSamples == BLOCK_LENGTH)
+    {
+        writeRecordMarker(channelPointers[channel]->file);
+    }
 
-    //std::cout << samps << std::endl;
+}
 
-    fwrite(&timestamp,							// ptr
-           8,   							// size of each element
-           1, 		  						// count
-           channelPointers[channel]->file);   // ptr to FILE object
+void RecordNode::writeTimestampAndSampleCount(FILE* file)
+{
 
-    fwrite(&samps,								// ptr
-           2,   							// size of each element
-           1, 		  						// count
-           channelPointers[channel]->file);   // ptr to FILE object
+    int16 samps = BLOCK_LENGTH;
 
-    fwrite(continuousDataIntegerBuffer,		// ptr
-           2,			     					// size of each element
-           nSamples, 		  					// count
-           channelPointers[channel]->file);   // ptr to FILE object
-    // FIXME: ensure fwrite returns equal "count"; otherwise,
-    // there was an error.
+    fwrite(&timestamp,                       // ptr
+            8,                               // size of each element
+            1,                               // count
+            file); // ptr to FILE object
 
-    // write a 10-byte marker indicating the end of a record
-    fwrite(recordMarker,		// ptr
-           1,			     					// size of each element
-           10, 		  					// count
-           channelPointers[channel]->file);   // ptr to FILE object
+    fwrite(&samps,                           // ptr
+            2,                               // size of each element
+            1,                               // count
+            file); // ptr to FILE object
 
+}
 
+void RecordNode::writeRecordMarker(FILE* file)
+{
+    // write a 10-byte marker indicating the end of a record
+
+    fwrite(recordMarker,        // ptr
+           1,                   // size of each element
+           10,                  // count
+           file);               // ptr to FILE object
 
-    //}
 }
 
 void RecordNode::writeEventBuffer(MidiMessage& event, int samplePosition) //, int node, int channel)
@@ -565,10 +610,12 @@ void RecordNode::writeEventBuffer(MidiMessage& event, int samplePosition) //, in
     //std::cout << "Received event!" << std::endl;
 
     const uint8* dataptr = event.getRawData();
-    int16 samplePos = (int16) samplePosition;
+    uint64 samplePos = (uint64) samplePosition;
+
+    uint64 eventTimestamp = timestamp + samplePos;
 
     // write timestamp (for buffer only, not the actual event timestamp!!!!!)
-    fwrite(&timestamp,							// ptr
+    fwrite(&eventTimestamp,							// ptr
            8,   							// size of each element
            1, 		  						// count
            eventChannel->file);   			// ptr to FILE object
@@ -592,7 +639,18 @@ void RecordNode::handleEvent(int eventType, MidiMessage& event, int samplePositi
     else if (eventType == TIMESTAMP)
     {
     	const uint8* dataptr = event.getRawData();
-    	memcpy(&timestamp, dataptr, 8);
+
+        // std::cout << (int) *(dataptr + 11) << " " <<
+        //             (int) *(dataptr + 10) << " " <<
+        //             (int) *(dataptr + 9) << " " <<
+        //             (int) *(dataptr + 8) << " " <<
+        //             (int) *(dataptr + 7) << " " <<
+        //             (int) *(dataptr + 6) << " " <<
+        //             (int) *(dataptr + 5) << " " <<
+        //             (int) *(dataptr + 4) << std::endl;
+
+
+    	memcpy(&timestamp, dataptr + 4, 8); // remember to skip first four bytes
     }
 
 }
@@ -619,28 +677,63 @@ void RecordNode::process(AudioSampleBuffer& buffer,
 
         // cycle through buffer channels
 
+        int samplesWritten = 0;
+
         if (channelPointers.size() > 0)
         {
 
-            for (int i = 0; i < buffer.getNumChannels(); i++)
-            {
+            while (samplesWritten < nSamples)
+            { 
 
+                int numSamplesToWrite = nSamples - samplesWritten;
 
-                if (channelPointers[i]->isRecording)
+                if (sampleCount + numSamplesToWrite < BLOCK_LENGTH)
                 {
-                    // write buffer to disk!
-                    writeContinuousBuffer(buffer.getSampleData(i),
-                                          nSamples,
-                                          i);
 
-                    //std::cout << "Record channel " << i << std::endl;
-                }
+                    for (int i = 0; i < buffer.getNumChannels(); i++)
+                    {
+
+                        if (channelPointers[i]->isRecording)
+                        {
+                            // write buffer to disk!
+                            writeContinuousBuffer(buffer.getSampleData(i,samplesWritten),
+                                                  numSamplesToWrite,
+                                                  i);
+
+                            //std::cout << "Record channel " << i << std::endl;
+                        }
+                    }
+
+                    samplesWritten += numSamplesToWrite;
+                    sampleCount += numSamplesToWrite;
 
+                } else {
 
+                    numSamplesToWrite = BLOCK_LENGTH - sampleCount;
+
+                    for (int i = 0; i < buffer.getNumChannels(); i++)
+                    {
+
+                        if (channelPointers[i]->isRecording)
+                        {
+                            // write buffer to disk!
+                            writeContinuousBuffer(buffer.getSampleData(i,samplesWritten),
+                                                  numSamplesToWrite,
+                                                  i);
+
+                            //std::cout << "Record channel " << i << std::endl;
+                        }
+                    }
+
+                    timestamp += numSamplesToWrite;
+                    samplesWritten += numSamplesToWrite;
+                    sampleCount = 0;
+
+                }
             }
         }
 
-        
+      //  std::cout << nSamples << " " << samplesWritten << " " << sampleCount << std::endl;
 
         return;
 
diff --git a/Source/Processors/RecordNode.h b/Source/Processors/RecordNode.h
index 97b78461f291e427dcc442f482ec4bd2f9e552a6..baadf10e0cb4acb6faae76b8355c7f86024c6409 100755
--- a/Source/Processors/RecordNode.h
+++ b/Source/Processors/RecordNode.h
@@ -34,6 +34,7 @@
 #include "Channel.h"
 
 #define HEADER_SIZE 1024
+#define BLOCK_LENGTH 1024
 
 /**
 
@@ -129,10 +130,15 @@ private:
     */
     float* continuousDataFloatBuffer;
 
+    AudioSampleBuffer zeroBuffer;
+
     /** Integer timestamp saved for each buffer.
     */
     uint64 timestamp;
 
+    /** Integer to keep track of the number samples written in each buffer */
+    int sampleCount;
+
     /** Used to generate timestamps if none are given.
     */
     Time timer;
@@ -179,6 +185,9 @@ private:
     */
     void writeEventBuffer(MidiMessage& event, int samplePos);
 
+    void writeRecordMarker(FILE*);
+    void writeTimestampAndSampleCount(FILE*);
+
     /** Used to indicate the end of each record */
     char* recordMarker;
 
diff --git a/Source/Processors/SourceNode.cpp b/Source/Processors/SourceNode.cpp
index c69fb102eb27f4400255286c6ac3e9bf9cd1f52c..c04d71b034df54e1db588595056161abf709345f 100755
--- a/Source/Processors/SourceNode.cpp
+++ b/Source/Processors/SourceNode.cpp
@@ -210,10 +210,10 @@ AudioProcessorEditor* SourceNode::createEditor()
     {
         editor = new RHD2000Editor(this, (RHD2000Thread*) dataThread.get(), true);
     }
-    else if (getName().equalsIgnoreCase("File Reader"))
-    {
-        editor = new FileReaderEditor(this, (FileReaderThread*) dataThread.get(), true);
-    }
+  //  else if (getName().equalsIgnoreCase("File Reader"))
+  //  {
+   //     editor = new FileReaderEditor(this, (FileReaderThread*) dataThread.get(), true);
+   // }
     else {
         editor = new SourceNodeEditor(this, true);
     }
@@ -325,7 +325,9 @@ void SourceNode::process(AudioSampleBuffer& buffer,
 
     nSamples = inputBuffer->readAllFromBuffer(buffer, &timestamp, eventCodeBuffer, buffer.getNumSamples());
 
-    //std::cout << "TIMESTAMP: " << timestamp << std::endl;
+    //std::cout << *buffer.getSampleData(0) << std::endl; 
+
+    //std::cout << "Source node timestamp: " << timestamp << std::endl;
 
     //std::cout << "Samples per buffer: " << nSamples << std::endl;
 
@@ -342,6 +344,16 @@ void SourceNode::process(AudioSampleBuffer& buffer,
              data   // data
             );
 
+    // std::cout << (int) *(data + 7) << " " <<
+    //                 (int) *(data + 6) << " " <<
+    //                 (int) *(data + 5) << " " <<
+    //                 (int) *(data + 4) << " " <<
+    //                 (int) *(data + 3) << " " <<
+    //                 (int) *(data + 2) << " " <<
+    //                 (int) *(data + 1) << " " <<
+    //                 (int) *(data + 0) << std::endl;
+
+
     // fill event buffer
     for (int i = 0; i < nSamples; i++)
     {
diff --git a/Source/Processors/Visualization/LfpDisplayCanvas.cpp b/Source/Processors/Visualization/LfpDisplayCanvas.cpp
index dce90aaf12f1ebff28e471bde7a92c1be0c4f08c..9cf1e5e94e27fd5efcfdd91e1e1c269c2e109de5 100755
--- a/Source/Processors/Visualization/LfpDisplayCanvas.cpp
+++ b/Source/Processors/Visualization/LfpDisplayCanvas.cpp
@@ -115,12 +115,15 @@ void LfpDisplayCanvas::resized()
     timescale->setBounds(0,0,getWidth()-scrollBarThickness,30);
     viewport->setBounds(0,30,getWidth(),getHeight()-90);
 
-    lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness, lfpDisplay->getTotalHeight());
+    lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness, getChannelHeight()*nChans);
 
     rangeSelection->setBounds(5,getHeight()-30,100,25);
     timebaseSelection->setBounds(175,getHeight()-30,100,25);
     spreadSelection->setBounds(345,getHeight()-30,100,25);
 
+   // std::cout << "Canvas thinks LfpDisplay should be this high: " 
+    //	<< lfpDisplay->getTotalHeight() << std::endl;
+
 }
 
 void LfpDisplayCanvas::beginAnimation()
@@ -173,14 +176,21 @@ void LfpDisplayCanvas::comboBoxChanged(ComboBox* cb)
     else if (cb == spreadSelection)
     {
          //spread = spreads[cb->getSelectedId()-1].getFloatValue();
-         lfpDisplay->setChannelHeight(spreads[cb->getSelectedId()-1].getFloatValue());
-         lfpDisplay->resized();
-         std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
+         lfpDisplay->setChannelHeight(spreads[cb->getSelectedId()-1].getIntValue());
+         //lfpDisplay->resized();
+         resized();
+         //std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
     }
 
     timescale->setTimebase(timebase);
 }
 
+int LfpDisplayCanvas::getChannelHeight()
+{
+	return spreads[spreadSelection->getSelectedId()-1].getIntValue();
+
+}
+
 
 void LfpDisplayCanvas::setParameter(int param, float val)
 {
@@ -267,8 +277,6 @@ void LfpDisplayCanvas::updateScreenBuffer()
             for (int channel = 0; channel < nChans; channel++)
             {
 
-                gain = 1.0f / (processor->channels[channel]->bitVolts * float(0x7fff));
-
                 screenBuffer->addFrom(channel, // destChannel
                                       screenBufferIndex, // destStartSample
                                       displayBuffer->getSampleData(channel, displayBufferIndex), // source
@@ -513,6 +521,7 @@ void LfpDisplay::setNumChannels(int numChannels)
 
         lfpChan->setColour(channelColours[i % channelColours.size()]);
         lfpChan->setRange(range);
+        lfpChan->setChannelHeight(canvas->getChannelHeight());
 
         addAndMakeVisible(lfpChan);
 
@@ -524,6 +533,11 @@ void LfpDisplay::setNumChannels(int numChannels)
 
 }
 
+int LfpDisplay::getTotalHeight()
+{
+	return totalHeight;
+}
+
 void LfpDisplay::resized()
 {
 
@@ -543,6 +557,8 @@ void LfpDisplay::resized()
 
     }
 
+   // std::cout << "Total height: " << totalHeight << std::endl;
+
 }
 
 void LfpDisplay::paint(Graphics& g)
@@ -589,16 +605,16 @@ void LfpDisplay::setRange(float r)
 
 }
 
-void LfpDisplay::setChannelHeight(float r)
+void LfpDisplay::setChannelHeight(int r)
 {
 
     for (int i = 0; i < numChans; i++)
     {
-
         channels[i]->setChannelHeight(r);
-
     }
 
+    resized();
+
 }
 
 void LfpDisplay::mouseDown(const MouseEvent& event)
@@ -628,7 +644,7 @@ LfpChannelDisplay::LfpChannelDisplay(LfpDisplayCanvas* c, int channelNumber) :
     canvas(c), isSelected(false), chan(channelNumber), channelHeight(40), channelOverlap(60), range(1000.0f)
 {
 
-    ch = (float) channelHeight;
+    channelHeightFloat = (float) channelHeight;
 
     channelFont = Font("Default", channelHeight*0.6, Font::plain);
 
@@ -655,13 +671,13 @@ void LfpChannelDisplay::paint(Graphics& g)
     if (isSelected)
     {
         g.setColour(Colours::lightgrey);
-        g.fillRect(0,center-50,10,100);
-        g.drawLine(0,center+50,getWidth(),center+50);
-        g.drawLine(0,center-50,getWidth(),center-50);
+        g.fillRect(0,center-channelHeight/2,10,channelHeight);
+        g.drawLine(0,center+channelHeight/2,getWidth(),center+channelHeight/2);
+        g.drawLine(0,center-channelHeight/2,getWidth(),center-channelHeight/2);
 
         g.setColour(Colour(25,25,25));
-        g.drawLine(0,center+25,10,center+25);
-        g.drawLine(0,center-25,10,center-25);
+        g.drawLine(0,center+channelHeight/4,10,center+channelHeight/4);
+        g.drawLine(0,center-channelHeight/4,10,center-channelHeight/4);
 
     }
 
@@ -670,23 +686,24 @@ void LfpChannelDisplay::paint(Graphics& g)
     g.drawLine(0, getHeight()/2, getWidth(), getHeight()/2);
 
     int stepSize = 1;
-	int from=0;
-	int to=0;
+	int from = 0;
+	int to = 0;
     g.setColour(lineColour);
 
     for (int i = 0; i < getWidth()-stepSize; i += stepSize)
     {
 
 	   // drawLine makes for nice anti-aliased plots, but is pretty slow
-       //g.drawLine(i,
-       //          (canvas->getYCoord(chan, i)/range*ch)+getHeight()/2,
-       //           i+stepSize,
-       //            (canvas->getYCoord(chan, i+stepSize)/range*ch)+getHeight()/2);
+       // g.drawLine(i,
+       //           (canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2,
+       //            i+stepSize,
+       //             (canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2);
+
 
+		// // pixel wise line plot has no anti-aliasing, but runs much faster
+		double a = (canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2;
+		double b = (canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2;
 
-		// pixel wise line plot has no anti-aliasing, but runs much faster
-		double a = (canvas->getYCoord(chan, i)/range*ch)+getHeight()/2;
-		double b = (canvas->getYCoord(chan, i+stepSize)/range*ch)+getHeight()/2;
 		if (a<b){
 			 from = (a);
 			 to = (b);
@@ -695,17 +712,17 @@ void LfpChannelDisplay::paint(Graphics& g)
 			 to = (a);
 		}
 		
-		if ((to-from)<40){ // if there is too much vertical range in one pixel, dont draw the full line for speed reasons 
+		if ((to-from) < 40){ // if there is too much vertical range in one pixel, don't draw the full line for speed reasons 
 			for (int j = from; j <= to; j += 1)
 			{
 				g.setPixel(i,j);
 			}
-		}else if ((to-from)<100){
+		} else if ((to-from) < 100){
 			for (int j = from; j <= to; j += 2)
 			{
 				g.setPixel(i,j);
 			}
-		}else{
+		} else {
 			g.setPixel(i,to);
 			g.setPixel(i,from);
 		}
@@ -714,10 +731,11 @@ void LfpChannelDisplay::paint(Graphics& g)
 		
     }
 
-  //  g.setColour(lineColour.withAlpha(0.7f)); // alpha on seems to decrease draw speed
+ // g.setColour(lineColour.withAlpha(0.7f)); // alpha on seems to decrease draw speed
     g.setFont(channelFont);
+    g.setFont(channelHeightFloat);
 
-    g.drawText(String(chan+1), 20, channelHeight/2, 200, 50, Justification::left, false);
+    g.drawText(String(chan+1), 15, center-channelHeight/2, 200, channelHeight, Justification::left, false);
 
 
 }
@@ -726,6 +744,8 @@ void LfpChannelDisplay::paint(Graphics& g)
 void LfpChannelDisplay::setRange(float r)
 {
     range = r;
+
+    //std::cout << "Range: " << r << std::endl;
 }
 
 void LfpChannelDisplay::select()
@@ -747,7 +767,8 @@ void LfpChannelDisplay::setColour(Colour c)
 void LfpChannelDisplay::setChannelHeight(int c)
 {
     channelHeight = c;
-    ch = (float) channelHeight;
+    channelHeightFloat = (float) channelHeight;
+    channelOverlap = channelHeight / 2;
 }
 
 int LfpChannelDisplay::getChannelHeight()
diff --git a/Source/Processors/Visualization/LfpDisplayCanvas.h b/Source/Processors/Visualization/LfpDisplayCanvas.h
index 89cca530a457fcf80f417b7a8fd4ff5811b68109..ebe5250480859a471bdd013048050ded9b39afd9 100755
--- a/Source/Processors/Visualization/LfpDisplayCanvas.h
+++ b/Source/Processors/Visualization/LfpDisplayCanvas.h
@@ -65,6 +65,8 @@ public:
 
     void resized();
 
+    int getChannelHeight();
+
     float getXCoord(int chan, int samp);
     float getYCoord(int chan, int samp);
 
@@ -85,7 +87,7 @@ private:
     float timebase;
     float displayGain;
     float timeOffset;
-    float spread ; // vertical spacing between channels
+    //int spread ; // vertical spacing between channels
 
     static const int MAX_N_CHAN = 256;  // maximum number of channels
     static const int MAX_N_SAMP = 5000; // maximum display size in pixels
@@ -151,11 +153,9 @@ public:
     ~LfpDisplay();
 
     void setNumChannels(int numChannels);
-    int getTotalHeight()
-    {
-        return totalHeight;
-    }
-
+    
+    int getTotalHeight();
+    
     void paint(Graphics& g);
 
     void refresh();
@@ -165,7 +165,7 @@ public:
     void mouseDown(const MouseEvent& event);
 
     void setRange(float range);
-    void setChannelHeight(float r);
+    void setChannelHeight(int r);
 
 private:
     int numChans;
@@ -217,7 +217,7 @@ private:
 
     int channelOverlap;
     int channelHeight;
-    float ch;
+    float channelHeightFloat;
 
     float range;
 
diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp
index 6267bc67ef541043a04a85727d3a8e4ffe088147..b36307fc645d395003a7dfbe2fea9ec35c51cc61 100755
--- a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp
+++ b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp
@@ -39,6 +39,8 @@ SpikeDisplayCanvas::SpikeDisplayCanvas(SpikeDisplayNode* n) :
 
     addAndMakeVisible(viewport);
 
+    setWantsKeyboardFocus(true);
+
     update();
 
 }
@@ -69,7 +71,6 @@ void SpikeDisplayCanvas::update()
 
     int nPlots = processor->getNumElectrodes();
     spikeDisplay->clear();
-    //numChannelsPerPlot.clear();
 
     for (int i = 0; i < nPlots; i++)
     {
@@ -77,7 +78,6 @@ void SpikeDisplayCanvas::update()
     }
 
     //initializeSpikePlots();
-
     spikeDisplay->resized();
     spikeDisplay->repaint();
 }
@@ -158,6 +158,20 @@ void SpikeDisplayCanvas::processSpikeEvents()
 
 }
 
+bool SpikeDisplayCanvas::keyPressed(const KeyPress& key)
+{
+    if (key.getKeyCode() == 67) // C
+    {
+        spikeDisplay->clear();
+        
+        std::cout << "Clearing display" << std::endl;
+        return true;
+    }
+
+    return false;
+
+}
+
 // ----------------------------------------------------------------
 
 SpikeDisplay::SpikeDisplay(SpikeDisplayCanvas* sdc, Viewport* v) :
@@ -175,8 +189,14 @@ SpikeDisplay::~SpikeDisplay()
 
 void SpikeDisplay::clear()
 {
-    if (spikePlots.size() > 0)
-        spikePlots.clear();
+   if (spikePlots.size() > 0)
+   {
+        for (int i = 0; i < spikePlots.size(); i++)
+        {
+            spikePlots[i]->clear();
+        }
+   }
+        
 }
 
 void SpikeDisplay::addSpikePlot(int numChannels, int electrodeNum)
@@ -763,7 +783,13 @@ void WaveAxes::updateSpikeData(const SpikeObject& s)
 
 void WaveAxes::clear()
 {
-
+    for (int n = 0; n < bufferSize; n++)
+    {
+        SpikeObject so;
+        generateEmptySpike(&so, 4);
+        
+        spikeBuffer.add(so);
+    }
 }
 
 void WaveAxes::mouseMove(const MouseEvent& event)
@@ -820,12 +846,12 @@ void WaveAxes::mouseDrag(const MouseEvent& event)
     }
 }
 
-MouseCursor WaveAxes::getMouseCursor()
-{
-    MouseCursor c = MouseCursor(cursorType);
+// MouseCursor WaveAxes::getMouseCursor()
+// {
+//     MouseCursor c = MouseCursor(cursorType);
 
-    return c;
-}
+//     return c;
+// }
 
 void WaveAxes::mouseExit(const MouseEvent& event)
 {
diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.h b/Source/Processors/Visualization/SpikeDisplayCanvas.h
index 83c26deded262bdf38d4b99a32c5822ae3142581..8edb6ecf2dba2788406250814f54582eb58cb143 100755
--- a/Source/Processors/Visualization/SpikeDisplayCanvas.h
+++ b/Source/Processors/Visualization/SpikeDisplayCanvas.h
@@ -91,6 +91,8 @@ public:
 
     void resized();
 
+    bool keyPressed(const KeyPress& key);
+
 private:
 
     SpikeDisplayNode* processor;
@@ -286,7 +288,7 @@ public:
     void mouseDown(const MouseEvent& event);
     void mouseDrag(const MouseEvent& event);
 
-    MouseCursor getMouseCursor();
+    //MouseCursor getMouseCursor();
 
 private:
 
diff --git a/Source/Processors/Visualization/SpikeObject.cpp b/Source/Processors/Visualization/SpikeObject.cpp
index 3f20f647d5c68753348e993889ff85af6ce31cce..7522d0b08182a45d7500010a0d4a5d7ab262c80f 100755
--- a/Source/Processors/Visualization/SpikeObject.cpp
+++ b/Source/Processors/Visualization/SpikeObject.cpp
@@ -77,8 +77,8 @@ int packSpike(SpikeObject* s, uint8_t* buffer, int bufferSize)
 // Simple method for deserializing a string of bytes into a Spike object
 bool unpackSpike(SpikeObject* s, const uint8_t* buffer, int bufferSize)
 {
-    // if !(isBufferValid(buffer, bufferSize));
-    // 	return false;
+    // if (!isBufferValid(buffer, bufferSize))
+   //  	return false;
 
     int idx = 0;
 
@@ -106,8 +106,8 @@ bool unpackSpike(SpikeObject* s, const uint8_t* buffer, int bufferSize)
     memcpy(&(s->threshold), buffer+idx, s->nChannels *2);
     idx += s->nChannels * 2;
 
-    //    if (idx >= bufferSize)
-    //		std::cout<<"Buffer Overrun! More data extracted than was given!"<<std::endl;
+   // if (idx >= bufferSize)
+   // 		std::cout<<"Buffer Overrun! More data extracted than was given!"<<std::endl;
 
     return true;
 
@@ -135,7 +135,7 @@ bool isBufferValid(uint8_t* buffer, int bufferSize)
     uint16_t integrityCheck = 0;
     memcpy(buffer + idx, &integrityCheck, 2);
 
-    std::cout<<integrityCheck<<" == "<< runningSum <<std::endl;
+    std::cout << integrityCheck<< " == " << runningSum <<std::endl;
 
     return (integrityCheck == runningSum);
 }
diff --git a/Source/UI/ProcessorList.cpp b/Source/UI/ProcessorList.cpp
index 4ec6b1a5235e7ff2a135bf08027192fe4b0ffcf8..d5998e9343ea85e7c93a92d3cd3b74f6b1e5e51f 100755
--- a/Source/UI/ProcessorList.cpp
+++ b/Source/UI/ProcessorList.cpp
@@ -79,7 +79,6 @@ ProcessorList::ProcessorList()
     //filters->addSubItem(new ProcessorListItem("Resampler"));
     filters->addSubItem(new ProcessorListItem("Phase Detector"));
     filters->addSubItem(new ProcessorListItem("Digital Ref"));
-    filters->addSubItem(new ProcessorListItem("Channel Mapping"));
 
     ProcessorListItem* sinks = new ProcessorListItem("Sinks");
     sinks->addSubItem(new ProcessorListItem("LFP Viewer"));
diff --git a/open-ephys.jucer b/open-ephys.jucer
index a4433bc216cec817fcf878990c5560e342b2a178..fd93835c60aa5d598a83af886f18a286325ffe30 100644
--- a/open-ephys.jucer
+++ b/open-ephys.jucer
@@ -230,6 +230,8 @@
               file="Source/Audio/AudioComponent.h"/>
       </GROUP>
       <GROUP id="yQmqZWk" name="Processors">
+        <FILE id="M6nCIs" name="FileReader.cpp" compile="1" resource="0" file="Source/Processors/FileReader.cpp"/>
+        <FILE id="VU1bQ0" name="FileReader.h" compile="0" resource="0" file="Source/Processors/FileReader.h"/>
         <FILE id="e7QoyI" name="ChannelMappingNode.cpp" compile="1" resource="0"
               file="Source/Processors/ChannelMappingNode.cpp"/>
         <FILE id="RzEj1s" name="ChannelMappingNode.h" compile="0" resource="0"