diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile
index 409cb90c355514e7503a4be9333320cad96441be..2db986c5351a9a85601b27513d2968153817bd2b 100644
--- a/Builds/Linux/Makefile
+++ b/Builds/Linux/Makefile
@@ -102,6 +102,7 @@ OBJECTS := \
   $(OBJDIR)/SpikeDetector_300d85e7.o \
   $(OBJDIR)/AudioNode_94606ff3.o \
   $(OBJDIR)/EventNode_95c842b7.o \
+  $(OBJDIR)/EcubeEditor_50eace99.o \
   $(OBJDIR)/SpikeSorterEditor_30c3d470.o \
   $(OBJDIR)/LfpTriggeredAverageEditor_101d75b.o \
   $(OBJDIR)/SerialInputEditor_c00f31f5.o \
@@ -132,6 +133,7 @@ OBJECTS := \
   $(OBJDIR)/AudioEditor_fb2c6555.o \
   $(OBJDIR)/FilterEditor_dfe1f39d.o \
   $(OBJDIR)/GenericEditor_becb2ad6.o \
+  $(OBJDIR)/EcubeThread_d0477baf.o \
   $(OBJDIR)/okFrontPanelDLL_87687880.o \
   $(OBJDIR)/rhd2000datablock_722d8dae.o \
   $(OBJDIR)/rhd2000evalboard_e0b412d5.o \
@@ -148,6 +150,7 @@ OBJECTS := \
   $(OBJDIR)/SourceNode_c2d6336c.o \
   $(OBJDIR)/GenericProcessor_733760aa.o \
   $(OBJDIR)/ProcessorGraph_68b34a0b.o \
+  $(OBJDIR)/EcubeDialogComponent_2ec3bd57.o \
   $(OBJDIR)/CustomArrowButton_206e4278.o \
   $(OBJDIR)/GraphViewer_e43fd2ce.o \
   $(OBJDIR)/EditorViewportButtons_29af2a5c.o \
@@ -450,6 +453,11 @@ $(OBJDIR)/EventNode_95c842b7.o: ../../Source/Processors/EventNode.cpp
 	@echo "Compiling EventNode.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/EcubeEditor_50eace99.o: ../../Source/Processors/Editors/EcubeEditor.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling EcubeEditor.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/SpikeSorterEditor_30c3d470.o: ../../Source/Processors/Editors/SpikeSorterEditor.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling SpikeSorterEditor.cpp"
@@ -600,6 +608,11 @@ $(OBJDIR)/GenericEditor_becb2ad6.o: ../../Source/Processors/Editors/GenericEdito
 	@echo "Compiling GenericEditor.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/EcubeThread_d0477baf.o: ../../Source/Processors/DataThreads/EcubeThread.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling EcubeThread.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/okFrontPanelDLL_87687880.o: ../../Source/Processors/DataThreads/rhythm-api/okFrontPanelDLL.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling okFrontPanelDLL.cpp"
@@ -680,6 +693,11 @@ $(OBJDIR)/ProcessorGraph_68b34a0b.o: ../../Source/Processors/ProcessorGraph.cpp
 	@echo "Compiling ProcessorGraph.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/EcubeDialogComponent_2ec3bd57.o: ../../Source/UI/EcubeDialogComponent.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling EcubeDialogComponent.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/CustomArrowButton_206e4278.o: ../../Source/UI/CustomArrowButton.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling CustomArrowButton.cpp"
diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
index 4d18a78c470478c624d83d3434c52e7acc1a9bb2..53047b91eea1de9fbcac27d6176b81f53de1a5d2 100644
--- a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj
@@ -72,6 +72,7 @@
 		ED8CB527B27C67E9E4DA027C = {isa = PBXBuildFile; fileRef = BC3B7E4E25505D9044BFACC7; };
 		DE758AF46844DF951655966C = {isa = PBXBuildFile; fileRef = B27F558F42AC78F0E564B5AF; };
 		80E5365461A5A7A32C48C563 = {isa = PBXBuildFile; fileRef = F94DD42C7BBF81C101D3F605; };
+		A005D7947A9FA8D2E73982C8 = {isa = PBXBuildFile; fileRef = B477BCF5AD76FBA478E4FA30; };
 		96E000FFBED6B85F9DEB5494 = {isa = PBXBuildFile; fileRef = B8BDB9ED4D4E0D1882F94EE5; };
 		DD77A0AB68C932F294B753C2 = {isa = PBXBuildFile; fileRef = 7B7819A5759B54D91E334447; };
 		A3CF90DE56808A47519FC101 = {isa = PBXBuildFile; fileRef = 07BEF02C2B930DF7847C2921; };
@@ -102,6 +103,7 @@
 		BF3254F07C15D467D6DB3FEF = {isa = PBXBuildFile; fileRef = 10BE33089BA6F3468F36CD6C; };
 		6029B20DF2BD523AC0F78896 = {isa = PBXBuildFile; fileRef = D90290A0AA2C36CE757E46D5; };
 		6702EEA4E99D503C0EE933C4 = {isa = PBXBuildFile; fileRef = D3AE8303545E28D793312F46; };
+		5C597B1A42C8CB3940CBDDA9 = {isa = PBXBuildFile; fileRef = AFBAE04615D379A18B133090; };
 		89FCE8890946693CD5FC4A70 = {isa = PBXBuildFile; fileRef = 235A8987D99A191D07208D2F; };
 		C9AC286A46B3A1318F298DEF = {isa = PBXBuildFile; fileRef = ECB5A75A81B90327F58CBD9E; };
 		DA836EC803E4FF4EDEBE6386 = {isa = PBXBuildFile; fileRef = 2D2BAC4320470CF68743F58E; };
@@ -118,6 +120,7 @@
 		71111DE81104B1536ECB6DFB = {isa = PBXBuildFile; fileRef = ECA6FDB1366BE7EC30F1539B; };
 		85A60568B3DC342C76B4E679 = {isa = PBXBuildFile; fileRef = 3AE038CACE48AF85C4FB1ED5; };
 		8A5BACA019DA9B0EFAD5CE93 = {isa = PBXBuildFile; fileRef = 555D34D0CD8776EE5996CC3A; };
+		8F39AD3F7938EFE82D06E89F = {isa = PBXBuildFile; fileRef = AF28CAB9C7531EF7422602E1; };
 		BA608CEFC85F7AB9E30E0EB3 = {isa = PBXBuildFile; fileRef = F960CC94B136201BDA148EEA; };
 		D499273B65D901D0A101CAAA = {isa = PBXBuildFile; fileRef = E5C1D021C0FD6FAD082C5D75; };
 		95AE939ADE096394CCD2526F = {isa = PBXBuildFile; fileRef = 9F3B3184EC6D42CEA35D6ED8; };
@@ -886,7 +889,9 @@
 		A0D768F1B92568344DAC9F0B = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_Fonts.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp"; sourceTree = "SOURCE_ROOT"; };
 		A0E3B98412D88921BB0AA58E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AudioEditor.h; path = ../../Source/Processors/Editors/AudioEditor.h; sourceTree = "SOURCE_ROOT"; };
 		A15596CDCC27B86FC070D7FA = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Desktop.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp"; sourceTree = "SOURCE_ROOT"; };
+		A166A3013C7AF1BCCA050367 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EcubeThread.h; path = ../../Source/Processors/DataThreads/EcubeThread.h; sourceTree = "SOURCE_ROOT"; };
 		A17E8162EC7A0E513DDEB23C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_PluginDescription.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.cpp"; sourceTree = "SOURCE_ROOT"; };
+		A186E03EC7A6A7E657F38300 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EcubeDialogComponent.h; path = ../../Source/UI/EcubeDialogComponent.h; sourceTree = "SOURCE_ROOT"; };
 		A19C4BB4BD69D4351B344A17 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_MenuBarComponent.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp"; sourceTree = "SOURCE_ROOT"; };
 		A234B2D091071A1B710E884B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ChannelMappingNode.h; path = ../../Source/Processors/ChannelMappingNode.h; sourceTree = "SOURCE_ROOT"; };
 		A252FE4E6A360CBC4AF694B3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SpikeDetectorEditor.cpp; path = ../../Source/Processors/Editors/SpikeDetectorEditor.cpp; sourceTree = "SOURCE_ROOT"; };
@@ -943,11 +948,13 @@
 		AEC2DABFC0517B4BE0CD704C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_mac_AudioCDReader.mm"; path = "../../JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm"; sourceTree = "SOURCE_ROOT"; };
 		AEF53FD0FBBFF5242EDD7032 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Viewport.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp"; sourceTree = "SOURCE_ROOT"; };
 		AF1F3010721A6B29062E4838 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LowLevelGraphicsContext.h"; path = "../../JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h"; sourceTree = "SOURCE_ROOT"; };
+		AF28CAB9C7531EF7422602E1 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = EcubeDialogComponent.cpp; path = ../../Source/UI/EcubeDialogComponent.cpp; sourceTree = "SOURCE_ROOT"; };
 		AF3E3AE70160C3392B237316 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_mac_CoreAudio.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp"; sourceTree = "SOURCE_ROOT"; };
 		AF7106E30ED950436CCEC712 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_freetype_Fonts.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp"; sourceTree = "SOURCE_ROOT"; };
 		AF8ADA74003E96998A5E4404 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Typeface.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp"; sourceTree = "SOURCE_ROOT"; };
 		AF8B1228A9FDFA27E5F19011 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_StringRef.h"; path = "../../JuceLibraryCode/modules/juce_core/text/juce_StringRef.h"; sourceTree = "SOURCE_ROOT"; };
 		AFB684CE06F9256324EE0B4C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_FillType.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp"; sourceTree = "SOURCE_ROOT"; };
+		AFBAE04615D379A18B133090 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = EcubeThread.cpp; path = ../../Source/Processors/DataThreads/EcubeThread.cpp; sourceTree = "SOURCE_ROOT"; };
 		AFE835E175F7159E1E7C6CC7 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_CharacterFunctions.cpp"; path = "../../JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B00A9C0BAD3AF9F48E36A38F = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_MouseListener.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseListener.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B021D393D0E2625741512320 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_RenderingHelpers.h"; path = "../../JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h"; sourceTree = "SOURCE_ROOT"; };
@@ -979,6 +986,7 @@
 		B2FA9CC4754E136F22281176 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ImageEffectFilter.h"; path = "../../JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h"; sourceTree = "SOURCE_ROOT"; };
 		B3BAC48D01C49D8727D08097 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ListBox.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B43C27BEC3AB681389FC5FC5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_RelativeCoordinate.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.h"; sourceTree = "SOURCE_ROOT"; };
+		B477BCF5AD76FBA478E4FA30 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = EcubeEditor.cpp; path = ../../Source/Processors/Editors/EcubeEditor.cpp; sourceTree = "SOURCE_ROOT"; };
 		B47B3368AA1A182B0CA1AB26 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Butterworth.cpp; path = ../../Source/Dsp/Butterworth.cpp; sourceTree = "SOURCE_ROOT"; };
 		B4C52FC94D6C680C33ED85C9 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_File.cpp"; path = "../../JuceLibraryCode/modules/juce_core/files/juce_File.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B4F0C0B262654C4782B5AC49 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FileChooserDialogBox.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h"; sourceTree = "SOURCE_ROOT"; };
@@ -997,6 +1005,7 @@
 		B7BEB7779860FE877E4D1BC8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_TextDiff.cpp"; path = "../../JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B7D848E4F85AE11FDE4D164D = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_linux_AudioCDReader.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_AudioCDReader.cpp"; sourceTree = "SOURCE_ROOT"; };
 		B83EBFAE6306941F79044523 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_DirectoryContentsDisplayComponent.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp"; sourceTree = "SOURCE_ROOT"; };
+		B86153418B2ECB863398897F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EcubeEditor.h; path = ../../Source/Processors/Editors/EcubeEditor.h; sourceTree = "SOURCE_ROOT"; };
 		B87864B2D6A2E741D4B426A3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_mac_Threads.mm"; path = "../../JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm"; sourceTree = "SOURCE_ROOT"; };
 		B87C1BD13762817BE27DC2F7 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FillType.h"; path = "../../JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h"; sourceTree = "SOURCE_ROOT"; };
 		B8A9063181FEE1920095F824 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ChangeBroadcaster.h"; path = "../../JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h"; sourceTree = "SOURCE_ROOT"; };
@@ -1253,7 +1262,7 @@
 		EE2C669B127D00C86B1B8CA8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_Registry.cpp"; path = "../../JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp"; sourceTree = "SOURCE_ROOT"; };
 		EE4DD055D31F7D9DC718DBD8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ComponentMovementWatcher.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentMovementWatcher.h"; sourceTree = "SOURCE_ROOT"; };
 		EEA51B7EF1CF19028C6672E0 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_DocumentWindow.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp"; sourceTree = "SOURCE_ROOT"; };
-		EEFC66D2DF5FD66B4D83B22F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Component.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h"; sourceTree = "SOURCE_ROOT"; };
+		EF059B26886B32000BCF8CFF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MouseInputSource.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h"; sourceTree = "SOURCE_ROOT"; };
 		EF3F9AA8D70E1D4D55F13182 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_AudioThumbnail.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_utils/gui/juce_AudioThumbnail.cpp"; sourceTree = "SOURCE_ROOT"; };
 		EF4A6E0E1232071252ACCD7B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_RelativeParallelogram.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/positioning/juce_RelativeParallelogram.h"; sourceTree = "SOURCE_ROOT"; };
 		EF610B2A17D9B1C0D24DCE67 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_android_JNIHelpers.h"; path = "../../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h"; sourceTree = "SOURCE_ROOT"; };
@@ -1264,14 +1273,13 @@
 		F1A3975235880CAC1D5757F4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_MP3AudioFormat.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp"; sourceTree = "SOURCE_ROOT"; };
 		F230A4C0186379F9EB0B0F74 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ReferenceNode.h; path = ../../Source/Processors/ReferenceNode.h; sourceTree = "SOURCE_ROOT"; };
 		F28414731D9EE1F75D7B7043 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AudioFormat.h"; path = "../../JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h"; sourceTree = "SOURCE_ROOT"; };
-		F2F11D7C596DAE5579610CCC = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_AudioCDReader.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp"; sourceTree = "SOURCE_ROOT"; };
 		F5A00ACFA3D76168F22F1205 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 		99E1BC08B886CFDD2CCFD462 = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "open-ephys.app"; sourceTree = "BUILT_PRODUCTS_DIR"; };
 		EBD8622EAEF10558809888B7 = {isa = PBXFileReference; lastKnownFileType = image.png; name = "RadioButtons_selected_over-01.png"; path = "../../Resources/Images/Icons/RadioButtons_selected_over-01.png"; sourceTree = "SOURCE_ROOT"; };
 		EC95A2CF4B33EA37DA5FC1AC = {isa = PBXFileReference; lastKnownFileType = file.ttf; name = nordic.ttf; path = ../../Resources/Fonts/nordic.ttf; sourceTree = "SOURCE_ROOT"; };
 		ECB5A75A81B90327F58CBD9E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = rhd2000datablock.cpp; path = "../../Source/Processors/DataThreads/rhythm-api/rhd2000datablock.cpp"; sourceTree = "SOURCE_ROOT"; };
 		ED887A521EEB8F3EBA7DDB31 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AudioIODeviceType.h"; path = "../../JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h"; sourceTree = "SOURCE_ROOT"; };
-		EF059B26886B32000BCF8CFF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MouseInputSource.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h"; sourceTree = "SOURCE_ROOT"; };
+		EEFC66D2DF5FD66B4D83B22F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Component.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h"; sourceTree = "SOURCE_ROOT"; };
 		EF8488936B3D3E9178C9099C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PulsePalOutput.h; path = ../../Source/Processors/PulsePalOutput.h; sourceTree = "SOURCE_ROOT"; };
 		EFC21F3CD0EB87D67E044E06 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MenuBarComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.h"; sourceTree = "SOURCE_ROOT"; };
 		F0CA3600E09054D7DB3B0067 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SmoothedFilter.h; path = ../../Source/Dsp/SmoothedFilter.h; sourceTree = "SOURCE_ROOT"; };
@@ -1281,6 +1289,7 @@
 		F1DBAE92084D9D90234AC436 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_AudioSourcePlayer.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp"; sourceTree = "SOURCE_ROOT"; };
 		F2A500BA3500C4A9D5792A54 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_DrawableImage.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h"; sourceTree = "SOURCE_ROOT"; };
 		F2EDB88302B8A9356F43B834 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Primes.h"; path = "../../JuceLibraryCode/modules/juce_cryptography/encryption/juce_Primes.h"; sourceTree = "SOURCE_ROOT"; };
+		F2F11D7C596DAE5579610CCC = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_AudioCDReader.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp"; sourceTree = "SOURCE_ROOT"; };
 		F3D0224E4247BCB06A9E4DDF = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_KeyPressMappingSet.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp"; sourceTree = "SOURCE_ROOT"; };
 		F3F48717927A4E24F7373C09 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_NamedValueSet.h"; path = "../../JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h"; sourceTree = "SOURCE_ROOT"; };
 		F463A19E6EFEB2837582B117 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_audio_processors.h"; path = "../../JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h"; sourceTree = "SOURCE_ROOT"; };
@@ -1474,6 +1483,8 @@
 					4A94E809624F99387E600399,
 					12B5DDCB6E5ECD93A4C55BB5, ); name = Visualization; sourceTree = "<group>"; };
 		9F16043BF599BCE0C02A00A5 = {isa = PBXGroup; children = (
+					B477BCF5AD76FBA478E4FA30,
+					B86153418B2ECB863398897F,
 					B8BDB9ED4D4E0D1882F94EE5,
 					27963A329403E0882D24ECF0,
 					7B7819A5759B54D91E334447,
@@ -1544,6 +1555,8 @@
 					5DB3B3197F8C1E5EE159D6FC,
 					8A989F74B1957BCB3B9BA398, ); name = "rhythm-api"; sourceTree = "<group>"; };
 		DEA24DC5AC8325310FB40395 = {isa = PBXGroup; children = (
+					AFBAE04615D379A18B133090,
+					A166A3013C7AF1BCCA050367,
 					EBA825AF6FDB51EBA368CB8D,
 					A3FB0EA0264580F6B00D993B,
 					23A6BA852B71DAAF3F709428,
@@ -1616,6 +1629,8 @@
 					555D34D0CD8776EE5996CC3A,
 					0FDD7551AC98348D4A98ADC7, ); name = Processors; sourceTree = "<group>"; };
 		1D78FCCF430CD91FD1DBD95B = {isa = PBXGroup; children = (
+					AF28CAB9C7531EF7422602E1,
+					A186E03EC7A6A7E657F38300,
 					F960CC94B136201BDA148EEA,
 					C59B01C8DB5B3B4773032E12,
 					E5C1D021C0FD6FAD082C5D75,
@@ -2884,6 +2899,7 @@
 					ED8CB527B27C67E9E4DA027C,
 					DE758AF46844DF951655966C,
 					80E5365461A5A7A32C48C563,
+					A005D7947A9FA8D2E73982C8,
 					96E000FFBED6B85F9DEB5494,
 					DD77A0AB68C932F294B753C2,
 					A3CF90DE56808A47519FC101,
@@ -2914,6 +2930,7 @@
 					BF3254F07C15D467D6DB3FEF,
 					6029B20DF2BD523AC0F78896,
 					6702EEA4E99D503C0EE933C4,
+					5C597B1A42C8CB3940CBDDA9,
 					89FCE8890946693CD5FC4A70,
 					C9AC286A46B3A1318F298DEF,
 					DA836EC803E4FF4EDEBE6386,
@@ -2930,6 +2947,7 @@
 					71111DE81104B1536ECB6DFB,
 					85A60568B3DC342C76B4E679,
 					8A5BACA019DA9B0EFAD5CE93,
+					8F39AD3F7938EFE82D06E89F,
 					BA608CEFC85F7AB9E30E0EB3,
 					D499273B65D901D0A101CAAA,
 					95AE939ADE096394CCD2526F,
diff --git a/Builds/VisualStudio2012/open-ephys.vcxproj b/Builds/VisualStudio2012/open-ephys.vcxproj
index 8b03691616bcca793a13127956b2823b08b2c235..98e3de779bded57ccca310c876e2613b9d9dd01a 100644
--- a/Builds/VisualStudio2012/open-ephys.vcxproj
+++ b/Builds/VisualStudio2012/open-ephys.vcxproj
@@ -314,6 +314,7 @@
     <ClCompile Include="..\..\Source\Processors\SpikeDetector.cpp"/>
     <ClCompile Include="..\..\Source\Processors\AudioNode.cpp"/>
     <ClCompile Include="..\..\Source\Processors\EventNode.cpp"/>
+    <ClCompile Include="..\..\Source\Processors\Editors\EcubeEditor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\Editors\SpikeSorterEditor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\Editors\LfpTriggeredAverageEditor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\Editors\SerialInputEditor.cpp"/>
@@ -344,6 +345,7 @@
     <ClCompile Include="..\..\Source\Processors\Editors\AudioEditor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\Editors\FilterEditor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\Editors\GenericEditor.cpp"/>
+    <ClCompile Include="..\..\Source\Processors\DataThreads\EcubeThread.cpp"/>
     <ClCompile Include="..\..\Source\Processors\DataThreads\rhythm-api\okFrontPanelDLL.cpp"/>
     <ClCompile Include="..\..\Source\Processors\DataThreads\rhythm-api\rhd2000datablock.cpp"/>
     <ClCompile Include="..\..\Source\Processors\DataThreads\rhythm-api\rhd2000evalboard.cpp"/>
@@ -360,6 +362,7 @@
     <ClCompile Include="..\..\Source\Processors\SourceNode.cpp"/>
     <ClCompile Include="..\..\Source\Processors\GenericProcessor.cpp"/>
     <ClCompile Include="..\..\Source\Processors\ProcessorGraph.cpp"/>
+    <ClCompile Include="..\..\Source\UI\EcubeDialogComponent.cpp"/>
     <ClCompile Include="..\..\Source\UI\CustomArrowButton.cpp"/>
     <ClCompile Include="..\..\Source\UI\GraphViewer.cpp"/>
     <ClCompile Include="..\..\Source\UI\EditorViewportButtons.cpp"/>
@@ -1557,6 +1560,7 @@
     <ClInclude Include="..\..\Source\Processors\SpikeDetector.h"/>
     <ClInclude Include="..\..\Source\Processors\AudioNode.h"/>
     <ClInclude Include="..\..\Source\Processors\EventNode.h"/>
+    <ClInclude Include="..\..\Source\Processors\Editors\EcubeEditor.h"/>
     <ClInclude Include="..\..\Source\Processors\Editors\SpikeSorterEditor.h"/>
     <ClInclude Include="..\..\Source\Processors\Editors\LfpTriggeredAverageEditor.h"/>
     <ClInclude Include="..\..\Source\Processors\Editors\SerialInputEditor.h"/>
@@ -1587,6 +1591,7 @@
     <ClInclude Include="..\..\Source\Processors\Editors\AudioEditor.h"/>
     <ClInclude Include="..\..\Source\Processors\Editors\FilterEditor.h"/>
     <ClInclude Include="..\..\Source\Processors\Editors\GenericEditor.h"/>
+    <ClInclude Include="..\..\Source\Processors\DataThreads\EcubeThread.h"/>
     <ClInclude Include="..\..\Source\Processors\DataThreads\rhythm-api\okFrontPanelDLL.h"/>
     <ClInclude Include="..\..\Source\Processors\DataThreads\rhythm-api\rhd2000datablock.h"/>
     <ClInclude Include="..\..\Source\Processors\DataThreads\rhythm-api\rhd2000evalboard.h"/>
@@ -1603,6 +1608,7 @@
     <ClInclude Include="..\..\Source\Processors\SourceNode.h"/>
     <ClInclude Include="..\..\Source\Processors\GenericProcessor.h"/>
     <ClInclude Include="..\..\Source\Processors\ProcessorGraph.h"/>
+    <ClInclude Include="..\..\Source\UI\EcubeDialogComponent.h"/>
     <ClInclude Include="..\..\Source\UI\CustomArrowButton.h"/>
     <ClInclude Include="..\..\Source\UI\GraphViewer.h"/>
     <ClInclude Include="..\..\Source\UI\EditorViewportButtons.h"/>
diff --git a/Builds/VisualStudio2012/open-ephys.vcxproj.filters b/Builds/VisualStudio2012/open-ephys.vcxproj.filters
index 3f4d0a9f07504cdf83f78f141ac397833a99d864..edbdbb5c9f5f7238d025f1ee1e572d592336ba76 100644
--- a/Builds/VisualStudio2012/open-ephys.vcxproj.filters
+++ b/Builds/VisualStudio2012/open-ephys.vcxproj.filters
@@ -508,6 +508,9 @@
     <ClCompile Include="..\..\Source\Processors\EventNode.cpp">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\Source\Processors\Editors\EcubeEditor.cpp">
+      <Filter>open-ephys\Source\Processors\Editors</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\Source\Processors\Editors\SpikeSorterEditor.cpp">
       <Filter>open-ephys\Source\Processors\Editors</Filter>
     </ClCompile>
@@ -598,6 +601,9 @@
     <ClCompile Include="..\..\Source\Processors\Editors\GenericEditor.cpp">
       <Filter>open-ephys\Source\Processors\Editors</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\Source\Processors\DataThreads\EcubeThread.cpp">
+      <Filter>open-ephys\Source\Processors\DataThreads</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\Source\Processors\DataThreads\rhythm-api\okFrontPanelDLL.cpp">
       <Filter>open-ephys\Source\Processors\DataThreads\rhythm-api</Filter>
     </ClCompile>
@@ -646,6 +652,9 @@
     <ClCompile Include="..\..\Source\Processors\ProcessorGraph.cpp">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\Source\UI\EcubeDialogComponent.cpp">
+      <Filter>open-ephys\Source\UI</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\Source\UI\CustomArrowButton.cpp">
       <Filter>open-ephys\Source\UI</Filter>
     </ClCompile>
@@ -2094,6 +2103,9 @@
     <ClInclude Include="..\..\Source\Processors\EventNode.h">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\Source\Processors\Editors\EcubeEditor.h">
+      <Filter>open-ephys\Source\Processors\Editors</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\Source\Processors\Editors\SpikeSorterEditor.h">
       <Filter>open-ephys\Source\Processors\Editors</Filter>
     </ClInclude>
@@ -2184,6 +2196,9 @@
     <ClInclude Include="..\..\Source\Processors\Editors\GenericEditor.h">
       <Filter>open-ephys\Source\Processors\Editors</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\Source\Processors\DataThreads\EcubeThread.h">
+      <Filter>open-ephys\Source\Processors\DataThreads</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\Source\Processors\DataThreads\rhythm-api\okFrontPanelDLL.h">
       <Filter>open-ephys\Source\Processors\DataThreads\rhythm-api</Filter>
     </ClInclude>
@@ -2232,6 +2247,9 @@
     <ClInclude Include="..\..\Source\Processors\ProcessorGraph.h">
       <Filter>open-ephys\Source\Processors</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\Source\UI\EcubeDialogComponent.h">
+      <Filter>open-ephys\Source\UI</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\Source\UI\CustomArrowButton.h">
       <Filter>open-ephys\Source\UI</Filter>
     </ClInclude>
diff --git a/Resources/DLLs/InstallEcubeApi_0_10.exe b/Resources/DLLs/InstallEcubeApi_0_10.exe
new file mode 100644
index 0000000000000000000000000000000000000000..3d202816e3fa54790c463b3441ed0d3d7a2c46b0
Binary files /dev/null and b/Resources/DLLs/InstallEcubeApi_0_10.exe differ
diff --git a/Source/Processors/DataThreads/EcubeThread.cpp b/Source/Processors/DataThreads/EcubeThread.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0b0b956343fe46b3150253b763ee07ccd06da11
--- /dev/null
+++ b/Source/Processors/DataThreads/EcubeThread.cpp
@@ -0,0 +1,565 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Michael Borisov
+
+------------------------------------------------------------------
+
+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 "EcubeThread.h"
+#include "../SourceNode.h"
+#include "../../UI/EcubeDialogComponent.h"
+#include <stdint.h>
+
+#if JUCE_WINDOWS
+#import "libid:60C0AAC2-1E0B-4FE5-A921-AF9CEEAAA582"
+
+using namespace ecubeapiLib;
+
+class EcubeDevInt
+{
+public:
+
+    enum DataFormat
+    {
+        dfSeparateChannelsAnalog,
+        dfInterleavedChannelsAnalog,
+        dfDigital
+    };
+    IEcubePtr pEcube;
+    IEcubeDevicePtr pDevice;
+    IEcubeModulePtr pModule;
+    unsigned n_channel_objects;
+    std::map<int, int> chid_map;
+    IEcubeAnalogAcquisitionPtr pStrmA;
+    IEcubeDigitalInputStreamingPtr pStrmD;
+    HeapBlock<float, true> interleaving_buffer;
+    bool buf_timestamp_locked;
+    unsigned long buf_timestamp;
+    uint64 buf_timestamp64;
+    unsigned long int_buf_size;
+    DataFormat data_format;
+};
+
+static const char bits_port0[16] = { 23, 22, -1, 14, 11, -1, -1, 28, 12, 10, 27, 26, -1, -1, -1, -1 };
+static const char bits_port1[16] = { 20, 21, 19, 18, 13, 6, 4, 5, 3, 2, -1, -1, 29, -1, 24, 25 };
+static const char bits_port2[16] = { 16, 17, 15, 8, 9, 7, 0, 1, 31, 30, -1, -1, -1, -1, -1, -1 };
+
+
+
+static std::vector<std::wstring> SafeArrayToVecStr(SAFEARRAY* sa)
+{
+    HRESULT hr;
+    long lbound, ubound;
+
+    if (SafeArrayGetElemsize(sa) != sizeof(BSTR*))
+        _com_raise_error(E_FAIL);
+    if(SafeArrayGetDim(sa)!=1)
+        _com_raise_error(E_FAIL);
+    if (FAILED(hr = SafeArrayGetLBound(sa, 1, &lbound)))
+        _com_raise_error(hr);
+    if (FAILED(hr = SafeArrayGetUBound(sa, 1, &ubound)))
+        _com_raise_error(hr);
+
+    std::vector<std::wstring> result;
+    for (long index = lbound; index <= ubound; index++)
+    {
+        BSTR raw_bstr;
+        if (FAILED(hr = SafeArrayGetElement(sa, &index, &raw_bstr)))
+            _com_raise_error(hr);
+        _bstr_t bstr(raw_bstr, false);
+        result.push_back(raw_bstr);
+    }
+    return result;
+}
+
+static StringArray SafeArrayToStringArray(SAFEARRAY* sa)
+{
+    HRESULT hr;
+    long lbound, ubound;
+    VARTYPE vt;
+
+    hr = SafeArrayGetVartype(sa, &vt);
+    if (FAILED(hr))
+        _com_raise_error(hr);
+    if (vt!=VT_BSTR)
+        _com_raise_error(E_FAIL);
+    if (SafeArrayGetElemsize(sa) != sizeof(BSTR*))
+        _com_raise_error(E_FAIL);
+    if (SafeArrayGetDim(sa) != 1)
+        _com_raise_error(E_FAIL);
+    if (FAILED(hr = SafeArrayGetLBound(sa, 1, &lbound)))
+        _com_raise_error(hr);
+    if (FAILED(hr = SafeArrayGetUBound(sa, 1, &ubound)))
+        _com_raise_error(hr);
+
+    StringArray result;
+    for (long index = lbound; index <= ubound; index++)
+    {
+        BSTR raw_bstr;
+        if (FAILED(hr = SafeArrayGetElement(sa, &index, &raw_bstr)))
+            _com_raise_error(hr);
+        _bstr_t bstr(raw_bstr, false);
+        result.add(raw_bstr);
+    }
+    return result;
+}
+
+std::vector<std::wstring> GetEcubeModuleChannels(IEcubeModulePtr& mp)
+{
+    std::vector<std::wstring> chnames;
+    {
+        SAFEARRAY *sa = mp->GetChannels();
+        chnames = SafeArrayToVecStr(sa);
+        SafeArrayDestroy(sa); // ARTEM - Leaks a safearray if an exception is thrown here
+    }
+    return chnames;
+}
+
+EcubeThread::EcubeThread(SourceNode* sn) : DataThread(sn), numberingScheme(1), acquisition_running(false)
+{
+    try
+    {
+        EcubeDialogComponent component;
+        DialogWindow::LaunchOptions opt;
+        opt.dialogTitle = "eCube parameters";
+        opt.content = OptionalScopedPointer<Component>(&component, false);
+        opt.escapeKeyTriggersCloseButton = true;
+
+        pDevInt = new EcubeDevInt;
+        pDevInt->pEcube.CreateInstance(__uuidof(Ecube));
+        {
+            SAFEARRAY *sa = pDevInt->pEcube->DetectNetworkDevices();
+            StringArray a(SafeArrayToStringArray(sa)); // ARTEM - Leaks a safearray if an exception is thrown here
+            SafeArrayDestroy(sa);
+            component.SetDeviceNames(a);
+        }
+        int dres = opt.runModal();
+        if (dres != 1)
+        {
+            throw std::runtime_error("Operation cancelled");
+        }
+
+        pDevInt->pDevice = pDevInt->pEcube->OpenNetworkDevice(_bstr_t(component.GetAddressValue().toUTF16()));
+        pDevInt->n_channel_objects = 0;
+        {
+            String selmod = component.GetModuleName();
+            if (selmod == "Headstage(s)")
+            {
+                m_samplerate = 25000.0f;
+                pDevInt->data_format = EcubeDevInt::dfSeparateChannelsAnalog;
+                // Get status of headstage selection
+                bool selhs[10];
+                component.GetHeadstageSelection(selhs);
+                bool acq_created = false;
+                for (int i = 0; i < 10; i++)
+                {
+                    if (selhs[i])
+                    {
+                        String modname = "Headstage" + String(i + 1);
+                        pDevInt->pModule = pDevInt->pDevice->OpenModule(_bstr_t(modname.toUTF16()));
+                        std::vector<std::wstring> chnames = GetEcubeModuleChannels(pDevInt->pModule);
+                        for (int j = 0; j < chnames.size(); j++)
+                        {
+                            IEcubeChannelPtr pch = pDevInt->pModule->OpenChannel(chnames[j].c_str());
+                            if (!acq_created)
+                            {
+                                pDevInt->pStrmA = pDevInt->pEcube->CreateAnalogAcquisition(pch);
+                                acq_created = true;
+                            }
+                            else
+                                pDevInt->pStrmA->AddChannel(pch);
+                            pDevInt->chid_map[pch->GetID()] = pDevInt->n_channel_objects;
+                            pDevInt->n_channel_objects++;
+                        }
+                    }
+                }
+                dataBuffer = new DataBuffer(pDevInt->n_channel_objects, 10000);
+                // Create the interleaving buffer based on the number of channels
+                pDevInt->interleaving_buffer.malloc(sizeof(float)* 1500 * pDevInt->n_channel_objects);
+            }
+            else if (selmod == "Panel Analog Input")
+            {
+                pDevInt->pModule = pDevInt->pDevice->OpenModule(_bstr_t(L"PanelAnalogInput"));
+                m_samplerate = 25000.0f;// 40.0e6 / 572 original samplerate;
+                pDevInt->data_format = EcubeDevInt::dfInterleavedChannelsAnalog;
+                bool acq_created = false;
+                std::vector<std::wstring> chnames = GetEcubeModuleChannels(pDevInt->pModule);
+                for (int j = 0; j < chnames.size(); j++)
+                {
+                    IEcubeChannelPtr pch = pDevInt->pModule->OpenChannel(chnames[j].c_str());
+                    if (!acq_created)
+                    {
+                        pDevInt->pStrmA = pDevInt->pEcube->CreateAnalogAcquisition(pch);
+                        acq_created = true;
+                    }
+                    else
+                        pDevInt->pStrmA->AddChannel(pch);
+                    pDevInt->chid_map[pch->GetID()] = pDevInt->n_channel_objects;
+                    pDevInt->n_channel_objects++;
+                }
+                dataBuffer = new DataBuffer(32, 10000);
+                // The interleaving buffer is there just for short->float conversion
+                pDevInt->interleaving_buffer.malloc(sizeof(float)* 1500);
+            }
+            else if (selmod == "Panel Digital Input")
+            {
+                pDevInt->pModule = pDevInt->pDevice->OpenModule(_bstr_t(L"PanelDigitalIO"));
+                m_samplerate = 25000.0f;
+                pDevInt->data_format = EcubeDevInt::dfDigital;
+
+                bool acq_created = false;
+                std::vector<std::wstring> chnames = GetEcubeModuleChannels(pDevInt->pModule);
+                for (int j = 0; j < chnames.size(); j++)
+                {
+                    IEcubeChannelPtr pch = pDevInt->pModule->OpenChannel(chnames[j].c_str());
+                    if (!acq_created)
+                    {
+                        pDevInt->pStrmD = pDevInt->pEcube->CreateDigitalInputStreaming(pch);
+                        acq_created = true;
+                    }
+                    else
+                        pDevInt->pStrmD->AddChannel(pch);
+                    pDevInt->chid_map[pch->GetID()] = pDevInt->n_channel_objects;
+                    pDevInt->n_channel_objects++;
+                }
+
+                dataBuffer = new DataBuffer(64, 10000);
+                // Create the interleaving buffer based on the number of digital ports
+                pDevInt->interleaving_buffer.malloc(sizeof(float)* 1500 * 64);
+            }
+            else
+                throw std::runtime_error("Invlid module selection");
+        }
+
+        pDevInt->buf_timestamp_locked = false;
+
+        setDefaultChannelNamesAndType();
+
+    }
+    catch (_com_error& e)
+    {
+        // Convert COM errors to std::runtime_errors, to avoid making other files Windows-dependent
+        throw std::runtime_error(std::string(e.Description()));
+    }
+}
+void EcubeThread::getChannelsInfo(StringArray &Names_, Array<channelType> &type_, Array<int> &stream_, Array<int> &originalChannelNumber_, Array<float> &gains_)
+{
+    Names_ = Names;
+    type_ = type;
+    stream_ = stream;
+    originalChannelNumber_ = originalChannelNumber;
+    gains_ = gains;
+}
+
+/* This will give default names & gains to channels, unless they were manually modified by the user
+In that case, the query channelModified, will return the values that need to be put */
+void EcubeThread::setDefaultChannelNamesAndType()
+{
+    Names.clear();
+    type.clear();
+    stream.clear();
+    gains.clear();
+    originalChannelNumber.clear();
+    String prefix;
+    channelType common_type;
+
+    int numch = getNumChannels();
+
+    if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog)
+    {
+        prefix = "HS10_CH";
+        common_type = DATA_CHANNEL;
+    }
+    else if (pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+    {
+        prefix = "PAI";
+        common_type = AUX_CHANNEL;
+    }
+    else //if (pDevInt->data_format == EcubeDevInt::dfDigital)
+    {
+        prefix = "PDI";
+        common_type = AUX_CHANNEL;
+    }
+
+    if (numberingScheme != 1)
+        prefix += "_";
+
+    for (int i = 0; i < numch; i++)
+    {
+        Names.add(prefix + String(i));
+        gains.add(getBitVolts());
+        type.add(common_type);
+        originalChannelNumber.add(i);
+    }
+
+    stream.add(0);
+}
+
+void EcubeThread::setDefaultNamingScheme(int scheme)
+{
+    numberingScheme = scheme;
+    setDefaultChannelNamesAndType();
+}
+
+
+EcubeThread::~EcubeThread()
+{
+    if (acquisition_running)
+        stopAcquisition();
+    if (isThreadRunning())
+    {
+        signalThreadShouldExit();
+    }
+    waitForThreadToExit(-1);
+}
+
+int EcubeThread::getNumChannels()
+{
+    if (pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+        return 32;
+    else if (pDevInt->data_format == EcubeDevInt::dfDigital)
+        return 64;
+    else
+        return pDevInt->n_channel_objects;
+}
+
+float EcubeThread::getSampleRate()
+{
+    return m_samplerate;
+}
+
+float EcubeThread::getBitVolts()
+{
+    return 10e3/32768; // For some reason the data is supposed to be in millivolts
+}
+
+bool EcubeThread::foundInputSource()
+{
+    return true;
+}
+
+bool EcubeThread::updateBuffer()
+{
+    unsigned long ba;
+    int16 eventcode = 0;
+    int nchan = pDevInt->n_channel_objects;
+
+    if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog || pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+        ba = pDevInt->pStrmA->WaitForData(100);
+    else
+        ba = pDevInt->pStrmD->WaitForData(100);
+    while (ba)
+    {
+        for (unsigned long i = 0; i < ba; i++)
+        {
+            IEcubeDataBufferPtr ab;
+            if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog || pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+                ab = pDevInt->pStrmA->FetchNextBuffer();
+            else
+                ab = pDevInt->pStrmD->FetchNextBuffer(); 
+            unsigned long chid = ab->GetStreamID();
+            std::map<int, int>::const_iterator chit = pDevInt->chid_map.find(chid);
+            if (chit != pDevInt->chid_map.end())
+            {
+                unsigned long bts = ab->GetTimestamp();
+                unsigned long datasize = ab->GetDataSize() / 2; // Data size is returned in bytes, not in samples
+                if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog)
+                {
+                    if (!pDevInt->buf_timestamp_locked || bts != pDevInt->buf_timestamp || datasize != pDevInt->int_buf_size)
+                    {
+                        // The new buffer does not match interleaving buffer length, or has a different timestamp,
+                        // or interleaving buffer is empty
+                        if (pDevInt->buf_timestamp_locked)
+                        {
+                            // Interleaving buffer is not empty.
+                            // Send its contents out to the application
+                            int64 cts = pDevInt->buf_timestamp64 / 3200; // Convert eCube 80MHz timestamp into a 25kHz timestamp
+                            for (unsigned long j = 0; j < pDevInt->int_buf_size; j++)
+                            {
+                                dataBuffer->addToBuffer(pDevInt->interleaving_buffer + j*nchan, &cts, &eventCode, 1);
+                                cts++;
+                            }
+                            // Update the 64-bit timestamp, take account of its wrap-around
+                            unsigned tsdif = bts - pDevInt->buf_timestamp;
+                            pDevInt->buf_timestamp64 += tsdif;
+                        }
+                        else
+                        {
+                            // The interleaving buffer is empty
+                            pDevInt->buf_timestamp64 = bts;
+                        }
+                        pDevInt->int_buf_size = datasize;
+                        pDevInt->buf_timestamp = bts;
+                        pDevInt->buf_timestamp_locked = true;
+                        // Clear the interleaving buffer within the new packet's size
+                        memset(pDevInt->interleaving_buffer, 0, sizeof(float)*datasize*nchan);
+                    }
+                    chid = chit->second; // Adjust the channel id to become the channel index
+                    unsigned char* dp = ab->GetDataPointer();
+                    const short* pData = (const short*)dp;
+                    for (unsigned long j = 0; j < datasize; j++)
+                    {
+                        pDevInt->interleaving_buffer[chid + nchan*j] = pData[j] * 10.0e3 / 32768; // OpenEphys uses 10e3 instead of just 10
+                    }
+                }
+                else if (pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+                {
+                    if (pDevInt->buf_timestamp_locked)
+                    {
+                        // Update the 64-bit timestamp, take care of its wrap-around
+                        unsigned tsdif = pDevInt->buf_timestamp - bts;
+                        pDevInt->buf_timestamp64 += tsdif;
+                    }
+                    else
+                    {
+                        pDevInt->buf_timestamp64 = bts;
+                    }
+                    pDevInt->buf_timestamp = bts;
+                    pDevInt->buf_timestamp_locked = true;
+                    unsigned char* dp = ab->GetDataPointer();
+                    const short* pData = (const short*)dp;
+                    for (unsigned j = 0; j < datasize; j++)
+                    {
+                        pDevInt->interleaving_buffer[j] = pData[j] * 10.0e3 / 32768;
+                    }
+                    unsigned long datasam = datasize / 32;
+                    int64 cts = pDevInt->buf_timestamp64 / 3200; // Convert eCube's 80MHz timestamps into number of samples on the Panel Analog input (orig sample rate 1144)
+                    for (unsigned long j = 0; j < datasam; j++)
+                    {
+                        dataBuffer->addToBuffer(pDevInt->interleaving_buffer+j*32, &cts, &eventCode, 1);
+                        cts++;
+                    }
+                }
+                else // Digital data
+                {
+                    unsigned tsdif = bts - pDevInt->buf_timestamp;
+                    if (!pDevInt->buf_timestamp_locked || (bts != pDevInt->buf_timestamp && tsdif!=5 && tsdif!=10 && tsdif!=0xFFFFFFFB && tsdif!=0xFFFFFFFA) || datasize != pDevInt->int_buf_size)
+                    {
+                        // The new buffer does not match interleaving buffer length, or has a different timestamp,
+                        // or interleaving buffer is empty
+                        if (pDevInt->buf_timestamp_locked)
+                        {
+                            // Interleaving buffer is not empty.
+                            // Send its contents out to the application
+                            int64 cts = pDevInt->buf_timestamp64 / 3200; // Convert eCube 80MHz timestamp into a 25kHz timestamp
+                            for (unsigned long j = 0; j < pDevInt->int_buf_size; j++)
+                            {
+                                dataBuffer->addToBuffer(pDevInt->interleaving_buffer + j*64, &cts, &eventCode, 1);
+                                cts++;
+                            }
+                            // Update the 64-bit timestamp, take account of its wrap-around
+                            pDevInt->buf_timestamp64 += tsdif;
+                        }
+                        else
+                        {
+                            // The interleaving buffer is empty
+                            pDevInt->buf_timestamp64 = bts;
+                        }
+                        pDevInt->buf_timestamp = bts;
+                        pDevInt->int_buf_size = datasize;
+                        pDevInt->buf_timestamp_locked = true;
+                        // Clear the interleaving buffer within the new packet's size
+                        memset(pDevInt->interleaving_buffer, 0, sizeof(float)*datasize*64);
+                    }
+                    // Convert data from ecube buffer into the interleaving buffer format
+                    chid = chit->second; // Adjust the channel id to become the channel index
+                    unsigned char* dp = ab->GetDataPointer();
+                    const uint16_t* pData = (const uint16_t*)dp;
+                    const char* pbits;
+                    switch (chid)
+                    {
+                    case 0:
+                    case 3:
+                        pbits = bits_port0;
+                        break;
+                    case 1:
+                    case 4:
+                        pbits = bits_port1;
+                        break;
+                    case 2:
+                    case 5:
+                    default:
+                        pbits = bits_port2;
+                        break;
+                    }
+                    int bitchn_offset = chid >= 3 ? 32 : 0;
+                    for (unsigned long j = 0; j < datasize; j++)
+                    {
+                        uint16_t wrd = pData[j];
+                        uint16_t msk = 1;
+                        for (unsigned long k = 0; k < 16; k++)
+                        {
+                            int bitchn = pbits[k];
+                            if (bitchn>=0)
+                            {
+                                float val = wrd&msk ? 1.0e3f : 0.0f;
+                                pDevInt->interleaving_buffer[bitchn + bitchn_offset + 64*j] = val;
+                            }
+                            msk <<= 1;
+                        }
+                    }
+                }
+            }
+        }
+        if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog || pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+            ba = pDevInt->pStrmA->GetBuffersAcquired();
+        else
+            ba = pDevInt->pStrmD->GetBuffersAcquired();
+    }
+    return true;
+}
+
+bool EcubeThread::startAcquisition()
+{
+    pDevInt->buf_timestamp_locked = false;
+    if (!isThreadRunning())
+        startThread();
+
+    if (!acquisition_running)
+    {
+        if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog || pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+            pDevInt->pStrmA->Start();
+        else
+            pDevInt->pStrmD->Start();
+        acquisition_running = true;
+    }
+
+    return true;
+}
+
+bool EcubeThread::stopAcquisition()
+{
+    if (acquisition_running)
+    {
+        if (pDevInt->data_format == EcubeDevInt::dfSeparateChannelsAnalog || pDevInt->data_format == EcubeDevInt::dfInterleavedChannelsAnalog)
+            pDevInt->pStrmA->Stop();
+        else
+            pDevInt->pStrmD->Stop();
+    }
+    acquisition_running = false;
+    return true;
+}
+
+void EcubeThread::run()
+{
+    // Call the base class's run
+    DataThread::run();
+}
+
+
+#endif
\ No newline at end of file
diff --git a/Source/Processors/DataThreads/EcubeThread.h b/Source/Processors/DataThreads/EcubeThread.h
new file mode 100644
index 0000000000000000000000000000000000000000..858c81db68ef58e94ace5cb88920d3ccf318045a
--- /dev/null
+++ b/Source/Processors/DataThreads/EcubeThread.h
@@ -0,0 +1,116 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+Copyright (C) 2013 Michael Borisov
+
+------------------------------------------------------------------
+
+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 __ECUBETHREAD_H_EC4CAD67__
+#define __ECUBETHREAD_H_EC4CAD67__
+
+
+#include "../../../JuceLibraryCode/JuceHeader.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "DataThread.h"
+#include "../GenericProcessor.h"
+
+#define MAX_NUM_DATA_STREAMS 8
+
+class SourceNode;
+
+#if JUCE_WINDOWS
+class EcubeDevInt;
+#endif
+
+class EcubeThread : public DataThread
+
+{
+public:
+
+#if JUCE_WINDOWS
+    EcubeThread(SourceNode* sn);
+    ~EcubeThread();
+
+    void run(); // Overridden
+
+    /** Fills the DataBuffer with incoming data. This is the most important
+    method for each DataThread.*/
+    virtual bool updateBuffer();
+
+    /** Returns true if the data source is connected, false otherwise.*/
+    virtual bool foundInputSource();
+
+    /** Initializes data transfer.*/
+    virtual bool startAcquisition();
+
+    /** Stops data transfer.*/
+    virtual bool stopAcquisition();
+
+    /** Returns the number of continuous channels the data source can provide.*/
+    virtual int getNumChannels();
+
+    /** Returns the sample rate of the data source.*/
+    virtual float getSampleRate();
+
+    /** Returns the volts per bit of the data source.*/
+    virtual float getBitVolts();
+
+    virtual void getChannelsInfo(StringArray &Names, Array<channelType> &type, Array<int> &stream, Array<int> &originalChannelNumber, Array<float> &gains);
+    void setDefaultNamingScheme(int scheme);
+
+
+private:
+    void setDefaultChannelNamesAndType();
+
+    // used for data stream names...
+    int numberingScheme;
+    StringArray Names;
+    Array<channelType> type;
+    Array<float> gains;
+    Array<int> stream;
+    Array<int> originalChannelNumber;
+
+    ScopedPointer<EcubeDevInt> pDevInt;
+
+    float m_samplerate;
+    bool acquisition_running;
+
+#else
+/** Empty methods for non-Windows platforms **/
+bool updateBuffer() {}
+bool foundInputSource() {}
+bool startAcquisition() {}
+bool stopAcquisition() {}
+int getNumChannels() {}
+float getSampleRate() {}
+float getBitVolts() {}
+
+#endif
+
+
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EcubeThread);
+};
+
+#endif  // __RHD2000THREAD_H_2C4CBD67__
diff --git a/Source/Processors/Editors/EcubeEditor.cpp b/Source/Processors/Editors/EcubeEditor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0573ad09910d9d5c8d26420198c2d0bba1b9d6c3
--- /dev/null
+++ b/Source/Processors/Editors/EcubeEditor.cpp
@@ -0,0 +1,173 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+Copyright (C) 2013 Michael Borisov
+
+------------------------------------------------------------------
+
+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 "EcubeEditor.h"
+
+#include "../DataThreads/EcubeThread.h"
+
+#include <stdio.h>
+
+EcubeEditor::EcubeEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors = true)
+: GenericEditor(parentNode, useDefaultParameterEditors)
+
+{
+    desiredWidth = 180;
+
+    ipLabel = new Label("IP address label", "Address");
+    ipLabel->setBounds(35, 80, 180, 20);
+    ipLabel->setFont(Font("Small Text", 12, Font::plain));
+    addAndMakeVisible(ipLabel);
+
+    ipValue = new Label("IP address value", "127.0.0.1");
+    ipValue->setBounds(40, 50, 60, 20);
+    ipValue->setFont(Font("Default", 15, Font::plain));
+    ipValue->setColour(Label::textColourId, Colours::white);
+    ipValue->setColour(Label::backgroundColourId, Colours::grey);
+    ipValue->setEditable(true);
+    ipValue->addListener(this);
+    addAndMakeVisible(ipValue);
+
+}
+
+EcubeEditor::~EcubeEditor()
+{
+
+}
+
+void EcubeEditor::labelTextChanged(Label* label)
+{/*
+    FilterNode* fn = (FilterNode*)getProcessor();
+
+    Value val = label->getTextValue();
+    double requestedValue = double(val.getValue());
+
+    if (requestedValue < 0.01 || requestedValue > 10000)
+    {
+        sendActionMessage("Value out of range.");
+
+        if (label == highCutValue)
+        {
+            label->setText(lastHighCutString, dontSendNotification);
+            lastHighCutString = label->getText();
+        }
+        else
+        {
+            label->setText(lastLowCutString, dontSendNotification);
+            lastLowCutString = label->getText();
+        }
+
+        return;
+    }
+
+    Array<int> chans = getActiveChannels();
+
+    // This needs to change, since there's not enough feedback about whether
+    // or not individual channel settings were altered:
+
+    for (int n = 0; n < chans.size(); n++)
+    {
+
+        if (label == highCutValue)
+        {
+            double minVal = fn->getLowCutValueForChannel(n);
+
+            if (requestedValue > minVal)
+            {
+                fn->setCurrentChannel(n);
+                fn->setParameter(1, requestedValue);
+            }
+
+            lastHighCutString = label->getText();
+
+        }
+        else
+        {
+            double maxVal = fn->getHighCutValueForChannel(n);
+
+            if (requestedValue < maxVal)
+            {
+                fn->setCurrentChannel(n);
+                fn->setParameter(0, requestedValue);
+            }
+
+            lastLowCutString = label->getText();
+        }
+
+    }
+*/
+}
+
+
+void EcubeEditor::buttonEvent(Button* button)
+{
+
+    if (!acquisitionIsActive)
+    {
+
+/*        if (button == fileButton)
+        {
+            //std::cout << "Button clicked." << std::endl;
+            FileChooser chooseFileReaderFile("Please select the file you want to load...",
+                lastFilePath,
+                "*");
+
+            if (chooseFileReaderFile.browseForFileToOpen())
+            {
+                // Use the selected file
+                setFile(chooseFileReaderFile.getResult().getFullPathName());
+
+                // lastFilePath = fileToRead.getParentDirectory();
+
+                // thread->setFile(fileToRead.getFullPathName());
+
+                // fileNameLabel->setText(fileToRead.getFileName(),false);
+            }
+        }
+        */
+    }
+}
+
+void EcubeEditor::saveEditorParameters(XmlElement* xml)
+{
+
+    // XmlElement* fileName = xml->createNewChildElement("FILENAME");
+    // fileName->addTextElement(lastFilePath.getFullPathName());
+
+}
+
+void EcubeEditor::loadEditorParameters(XmlElement* xml)
+{
+
+    // forEachXmlChildElement(*xml, xmlNode)
+    //    {
+    //       if (xmlNode->hasTagName("FILENAME"))
+    //       {
+
+    //           lastFilePath = File(xmlNode->getText());
+    //           thread->setFile(lastFilePath.getFullPathName());
+    //           fileNameLabel->setText(lastFilePath.getFullPathName(),false);
+    //       }
+    //   }
+
+}
diff --git a/Source/Processors/Editors/EcubeEditor.h b/Source/Processors/Editors/EcubeEditor.h
new file mode 100644
index 0000000000000000000000000000000000000000..4cef4eff132fe8465dfabbba83efd5b5113b9e9f
--- /dev/null
+++ b/Source/Processors/Editors/EcubeEditor.h
@@ -0,0 +1,65 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+Copyright (C) 2013 Michael Borisov
+
+------------------------------------------------------------------
+
+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 __ECUBEEDITOR_H_D3EC8BA8__
+#define __ECUBEEDITOR_H_D3EC8BA8__
+
+#include "../../../JuceLibraryCode/JuceHeader.h"
+#include "GenericEditor.h"
+
+
+
+/**
+
+User interface for the "eCube" source node.
+
+@see SourceNode, eCube
+
+*/
+
+class EcubeEditor : public GenericEditor,
+    public Label::Listener
+{
+public:
+    EcubeEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors);
+    virtual ~EcubeEditor();
+
+    void buttonEvent(Button* button);
+    void labelTextChanged(Label* label);
+
+    void saveEditorParameters(XmlElement*);
+    void loadEditorParameters(XmlElement*);
+
+private:
+    ScopedPointer<Label> ipLabel;
+    ScopedPointer<Label> ipValue;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EcubeEditor);
+
+};
+
+
+
+#endif  // __ECUBEEDITOR_H_D3EC8BA8__
diff --git a/Source/Processors/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph.cpp
index 41243fe9b8595e19484b328aea1cdd937358cbb2..b57ee2d7946530ef29ec103f91ecdcd7875a59c2 100644
--- a/Source/Processors/ProcessorGraph.cpp
+++ b/Source/Processors/ProcessorGraph.cpp
@@ -492,6 +492,7 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip
         if (subProcessorType.equalsIgnoreCase("RHA2000-EVAL") ||
             // subProcessorType.equalsIgnoreCase("File Reader") ||
             subProcessorType.equalsIgnoreCase("Custom FPGA") ||
+            subProcessorType.equalsIgnoreCase("eCube") || // Added by Michael Borisov
             subProcessorType.equalsIgnoreCase("Rhythm FPGA"))
         {
 
diff --git a/Source/Processors/SourceNode.cpp b/Source/Processors/SourceNode.cpp
index 43d61844cbdaf262a4532d304e907f1ce086693a..6716b423e7a1deb08b68d6e648359db794a899d3 100755
--- a/Source/Processors/SourceNode.cpp
+++ b/Source/Processors/SourceNode.cpp
@@ -27,9 +27,11 @@
 #include "DataThreads/FPGAThread.h"
 #include "DataThreads/FileReaderThread.h"
 #include "DataThreads/RHD2000Thread.h"
+#include "DataThreads/EcubeThread.h" // Added by Michael Borisov
 #include "Editors/SourceNodeEditor.h"
 #include "Editors/FileReaderEditor.h"
 #include "Editors/RHD2000Editor.h"
+#include "Editors/EcubeEditor.h" // Added by Michael Borisov
 #include "Channel.h"
 #include <stdio.h>
 
@@ -57,6 +59,12 @@ SourceNode::SourceNode(const String& name_)
     {
         dataThread = new RHD2000Thread(this);
     }
+#if JUCE_WINDOWS
+    else if (getName().equalsIgnoreCase("eCube"))
+    {
+        dataThread = new EcubeThread(this);
+    }
+#endif
 
     if (dataThread != 0)
     {
diff --git a/Source/UI/EcubeDialogComponent.cpp b/Source/UI/EcubeDialogComponent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8db0f41bcd338314cd6a8cfea3b2e4c2e9884d84
--- /dev/null
+++ b/Source/UI/EcubeDialogComponent.cpp
@@ -0,0 +1,419 @@
+/*
+  ==============================================================================
+
+  This is an automatically generated GUI class created by the Introjucer!
+
+  Be careful when adding custom code to these files, as only the code within
+  the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
+  and re-saved.
+
+  Created with Introjucer version: 3.1.0
+
+  ------------------------------------------------------------------------------
+
+  The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
+  Copyright 2004-13 by Raw Material Software Ltd.
+
+  ==============================================================================
+*/
+
+//[Headers] You can add your own extra header files here...
+//[/Headers]
+
+#include "EcubeDialogComponent.h"
+
+
+//[MiscUserDefs] You can add your own user definitions and misc code here...
+//[/MiscUserDefs]
+
+//==============================================================================
+EcubeDialogComponent::EcubeDialogComponent ()
+{
+    addAndMakeVisible (laAddressLabel = new Label ("Address Label",
+                                                   TRANS("Network Address:")));
+    laAddressLabel->setFont (Font (15.00f, Font::plain));
+    laAddressLabel->setJustificationType (Justification::centredLeft);
+    laAddressLabel->setEditable (false, false, false);
+    laAddressLabel->setColour (TextEditor::textColourId, Colours::black);
+    laAddressLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
+
+    addAndMakeVisible (bnOK = new TextButton ("OK button"));
+    bnOK->setButtonText (TRANS("Connect"));
+    bnOK->addListener (this);
+
+    addAndMakeVisible (bnCancel = new TextButton ("Cancel Button"));
+    bnCancel->setButtonText (TRANS("Cancel"));
+    bnCancel->addListener (this);
+
+    addAndMakeVisible (comboModule = new ComboBox ("Module selection combobox"));
+    comboModule->setTooltip (TRANS("Choose eCube module"));
+    comboModule->setEditableText (false);
+    comboModule->setJustificationType (Justification::centredLeft);
+    comboModule->setTextWhenNothingSelected (String::empty);
+    comboModule->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
+    comboModule->addItem (TRANS("Headstage(s)"), 1);
+    comboModule->addItem (TRANS("Panel Analog Input"), 2);
+    comboModule->addItem (TRANS("Panel Digital Input"), 3);
+    comboModule->addListener (this);
+
+    addAndMakeVisible (labelModule = new Label ("Module selection label",
+                                                TRANS("Module")));
+    labelModule->setFont (Font (15.00f, Font::plain));
+    labelModule->setJustificationType (Justification::centredLeft);
+    labelModule->setEditable (false, false, false);
+    labelModule->setColour (TextEditor::textColourId, Colours::black);
+    labelModule->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
+
+    addAndMakeVisible (cbNetworkAddress = new ComboBox ("Network Address ComboBox"));
+    cbNetworkAddress->setEditableText (true);
+    cbNetworkAddress->setJustificationType (Justification::centredLeft);
+    cbNetworkAddress->setTextWhenNothingSelected (String::empty);
+    cbNetworkAddress->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
+    cbNetworkAddress->addListener (this);
+
+    addAndMakeVisible (groupComponent = new GroupComponent ("headstage group",
+                                                            TRANS("Headstage selection")));
+
+    addAndMakeVisible (toggleHeadstage1 = new ToggleButton ("Headstage1 button"));
+    toggleHeadstage1->setButtonText (TRANS("Headstage 1"));
+    toggleHeadstage1->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage2 = new ToggleButton ("Headstage2 button"));
+    toggleHeadstage2->setButtonText (TRANS("Headstage 2"));
+    toggleHeadstage2->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage3 = new ToggleButton ("Headstage3 button"));
+    toggleHeadstage3->setButtonText (TRANS("Headstage 3"));
+    toggleHeadstage3->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage4 = new ToggleButton ("Headstage4 button"));
+    toggleHeadstage4->setButtonText (TRANS("Headstage 4"));
+    toggleHeadstage4->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage5 = new ToggleButton ("Headstage5 button"));
+    toggleHeadstage5->setButtonText (TRANS("Headstage 5"));
+    toggleHeadstage5->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage6 = new ToggleButton ("Headstage6 button"));
+    toggleHeadstage6->setButtonText (TRANS("Headstage 6"));
+    toggleHeadstage6->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage7 = new ToggleButton ("Headstage7 button"));
+    toggleHeadstage7->setButtonText (TRANS("Headstage 7"));
+    toggleHeadstage7->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage8 = new ToggleButton ("Headstage8 button"));
+    toggleHeadstage8->setButtonText (TRANS("Headstage 8"));
+    toggleHeadstage8->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage9 = new ToggleButton ("Headstage9 button"));
+    toggleHeadstage9->setButtonText (TRANS("Headstage 9"));
+    toggleHeadstage9->addListener (this);
+
+    addAndMakeVisible (toggleHeadstage10 = new ToggleButton ("Headstage10 button"));
+    toggleHeadstage10->setButtonText (TRANS("Headstage 10"));
+    toggleHeadstage10->addListener (this);
+    toggleHeadstage10->setToggleState (true, dontSendNotification);
+
+
+    //[UserPreSize]
+    //[/UserPreSize]
+
+    setSize (310, 320);
+
+
+    //[Constructor] You can add your own custom stuff here..
+    //[/Constructor]
+}
+
+EcubeDialogComponent::~EcubeDialogComponent()
+{
+    //[Destructor_pre]. You can add your own custom destruction code here..
+    //[/Destructor_pre]
+
+    laAddressLabel = nullptr;
+    bnOK = nullptr;
+    bnCancel = nullptr;
+    comboModule = nullptr;
+    labelModule = nullptr;
+    cbNetworkAddress = nullptr;
+    groupComponent = nullptr;
+    toggleHeadstage1 = nullptr;
+    toggleHeadstage2 = nullptr;
+    toggleHeadstage3 = nullptr;
+    toggleHeadstage4 = nullptr;
+    toggleHeadstage5 = nullptr;
+    toggleHeadstage6 = nullptr;
+    toggleHeadstage7 = nullptr;
+    toggleHeadstage8 = nullptr;
+    toggleHeadstage9 = nullptr;
+    toggleHeadstage10 = nullptr;
+
+
+    //[Destructor]. You can add your own custom destruction code here..
+    //[/Destructor]
+}
+
+//==============================================================================
+void EcubeDialogComponent::paint (Graphics& g)
+{
+    //[UserPrePaint] Add your own custom painting code here..
+    //[/UserPrePaint]
+
+    g.fillAll (Colours::white);
+
+    //[UserPaint] Add your own custom painting code here..
+    //[/UserPaint]
+}
+
+void EcubeDialogComponent::resized()
+{
+    laAddressLabel->setBounds (16, 8, 150, 24);
+    bnOK->setBounds (24, 280, 112, 24);
+    bnCancel->setBounds (160, 280, 87, 24);
+    comboModule->setBounds (80, 72, 192, 24);
+    labelModule->setBounds (16, 72, 72, 24);
+    cbNetworkAddress->setBounds (24, 40, 248, 24);
+    groupComponent->setBounds (8, 104, 288, 160);
+    toggleHeadstage1->setBounds (24, 128, 112, 24);
+    toggleHeadstage2->setBounds (24, 152, 112, 24);
+    toggleHeadstage3->setBounds (24, 176, 112, 24);
+    toggleHeadstage4->setBounds (24, 200, 112, 24);
+    toggleHeadstage5->setBounds (24, 224, 112, 24);
+    toggleHeadstage6->setBounds (160, 128, 112, 24);
+    toggleHeadstage7->setBounds (160, 152, 112, 24);
+    toggleHeadstage8->setBounds (160, 176, 112, 24);
+    toggleHeadstage9->setBounds (160, 200, 112, 24);
+    toggleHeadstage10->setBounds (160, 224, 112, 24);
+    //[UserResized] Add your own custom resize handling here..
+    //[/UserResized]
+}
+
+void EcubeDialogComponent::buttonClicked (Button* buttonThatWasClicked)
+{
+    //[UserbuttonClicked_Pre]
+    //[/UserbuttonClicked_Pre]
+
+    if (buttonThatWasClicked == bnOK)
+    {
+        //[UserButtonCode_bnOK] -- add your button handler code here..
+        DialogWindow* dw = findParentComponentOfClass<DialogWindow>();
+        if (dw != nullptr)
+        {
+            dw->exitModalState(1);
+        }
+        //[/UserButtonCode_bnOK]
+    }
+    else if (buttonThatWasClicked == bnCancel)
+    {
+        //[UserButtonCode_bnCancel] -- add your button handler code here..
+        DialogWindow* dw = findParentComponentOfClass<DialogWindow>();
+        if (dw != nullptr)
+        {
+            dw->exitModalState(0);
+        }
+        //[/UserButtonCode_bnCancel]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage1)
+    {
+        //[UserButtonCode_toggleHeadstage1] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage1]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage2)
+    {
+        //[UserButtonCode_toggleHeadstage2] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage2]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage3)
+    {
+        //[UserButtonCode_toggleHeadstage3] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage3]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage4)
+    {
+        //[UserButtonCode_toggleHeadstage4] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage4]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage5)
+    {
+        //[UserButtonCode_toggleHeadstage5] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage5]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage6)
+    {
+        //[UserButtonCode_toggleHeadstage6] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage6]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage7)
+    {
+        //[UserButtonCode_toggleHeadstage7] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage7]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage8)
+    {
+        //[UserButtonCode_toggleHeadstage8] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage8]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage9)
+    {
+        //[UserButtonCode_toggleHeadstage9] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage9]
+    }
+    else if (buttonThatWasClicked == toggleHeadstage10)
+    {
+        //[UserButtonCode_toggleHeadstage10] -- add your button handler code here..
+        //[/UserButtonCode_toggleHeadstage10]
+    }
+
+    //[UserbuttonClicked_Post]
+    //[/UserbuttonClicked_Post]
+}
+
+void EcubeDialogComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
+{
+    //[UsercomboBoxChanged_Pre]
+    //[/UsercomboBoxChanged_Pre]
+
+    if (comboBoxThatHasChanged == comboModule)
+    {
+        //[UserComboBoxCode_comboModule] -- add your combo box handling code here..
+        //[/UserComboBoxCode_comboModule]
+    }
+    else if (comboBoxThatHasChanged == cbNetworkAddress)
+    {
+        //[UserComboBoxCode_cbNetworkAddress] -- add your combo box handling code here..
+        //[/UserComboBoxCode_cbNetworkAddress]
+    }
+
+    //[UsercomboBoxChanged_Post]
+    //[/UsercomboBoxChanged_Post]
+}
+
+
+
+//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
+String EcubeDialogComponent::GetAddressValue()
+{
+    return cbNetworkAddress->getText();
+}
+
+String EcubeDialogComponent::GetModuleName()
+{
+    return comboModule->getText();
+}
+
+void EcubeDialogComponent::SetDeviceNames(const StringArray& names)
+{
+    for (int i = 0; i < names.size(); i++)
+    {
+        cbNetworkAddress->addItem(names[i], i+1);
+    }
+}
+
+void EcubeDialogComponent::GetHeadstageSelection(bool hs[10])
+{
+    for (int i = 0; i < 10; i++)
+        hs[i] = false;
+    if (toggleHeadstage1->getToggleState())
+        hs[0] = true;
+    if (toggleHeadstage2->getToggleState())
+        hs[1] = true;
+    if (toggleHeadstage3->getToggleState())
+        hs[2] = true;
+    if (toggleHeadstage4->getToggleState())
+        hs[3] = true;
+    if (toggleHeadstage5->getToggleState())
+        hs[4] = true;
+    if (toggleHeadstage6->getToggleState())
+        hs[5] = true;
+    if (toggleHeadstage7->getToggleState())
+        hs[6] = true;
+    if (toggleHeadstage8->getToggleState())
+        hs[7] = true;
+    if (toggleHeadstage9->getToggleState())
+        hs[8] = true;
+    if (toggleHeadstage10->getToggleState())
+        hs[9] = true;
+}
+
+//[/MiscUserCode]
+
+
+//==============================================================================
+#if 0
+/*  -- Introjucer information section --
+
+    This is where the Introjucer stores the metadata that describe this GUI layout, so
+    make changes in here at your peril!
+
+BEGIN_JUCER_METADATA
+
+<JUCER_COMPONENT documentType="Component" className="EcubeDialogComponent" componentName=""
+                 parentClasses="public Component" constructorParams="" variableInitialisers=""
+                 snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330"
+                 fixedSize="0" initialWidth="310" initialHeight="320">
+  <BACKGROUND backgroundColour="ffffffff"/>
+  <LABEL name="Address Label" id="598e4f972ee19e11" memberName="laAddressLabel"
+         virtualName="" explicitFocusOrder="0" pos="16 8 150 24" edTextCol="ff000000"
+         edBkgCol="0" labelText="Network Address:" editableSingleClick="0"
+         editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font"
+         fontsize="15" bold="0" italic="0" justification="33"/>
+  <TEXTBUTTON name="OK button" id="d46371ace0e2731f" memberName="bnOK" virtualName=""
+              explicitFocusOrder="0" pos="24 280 112 24" buttonText="Connect"
+              connectedEdges="0" needsCallback="1" radioGroupId="0"/>
+  <TEXTBUTTON name="Cancel Button" id="e57f56c355a79a42" memberName="bnCancel"
+              virtualName="" explicitFocusOrder="0" pos="160 280 87 24" buttonText="Cancel"
+              connectedEdges="0" needsCallback="1" radioGroupId="0"/>
+  <COMBOBOX name="Module selection combobox" id="937bd3c2684c1901" memberName="comboModule"
+            virtualName="" explicitFocusOrder="0" pos="80 72 192 24" tooltip="Choose eCube module"
+            editable="0" layout="33" items="Headstage(s)&#10;Panel Analog Input&#10;Panel Digital Input"
+            textWhenNonSelected="" textWhenNoItems="(no choices)"/>
+  <LABEL name="Module selection label" id="8db8f9471da84061" memberName="labelModule"
+         virtualName="" explicitFocusOrder="0" pos="16 72 72 24" edTextCol="ff000000"
+         edBkgCol="0" labelText="Module" editableSingleClick="0" editableDoubleClick="0"
+         focusDiscardsChanges="0" fontname="Default font" fontsize="15"
+         bold="0" italic="0" justification="33"/>
+  <COMBOBOX name="Network Address ComboBox" id="40e24a76fea3653c" memberName="cbNetworkAddress"
+            virtualName="" explicitFocusOrder="0" pos="24 40 248 24" editable="1"
+            layout="33" items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
+  <GROUPCOMPONENT name="headstage group" id="8dee2bbe073f0f63" memberName="groupComponent"
+                  virtualName="" explicitFocusOrder="0" pos="8 104 288 160" title="Headstage selection"/>
+  <TOGGLEBUTTON name="Headstage1 button" id="c735dad1514586" memberName="toggleHeadstage1"
+                virtualName="" explicitFocusOrder="0" pos="24 128 112 24" buttonText="Headstage 1"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage2 button" id="24f215ce96874781" memberName="toggleHeadstage2"
+                virtualName="" explicitFocusOrder="0" pos="24 152 112 24" buttonText="Headstage 2"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage3 button" id="f035e57b5cc87d27" memberName="toggleHeadstage3"
+                virtualName="" explicitFocusOrder="0" pos="24 176 112 24" buttonText="Headstage 3"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage4 button" id="6abc75f398861c5f" memberName="toggleHeadstage4"
+                virtualName="" explicitFocusOrder="0" pos="24 200 112 24" buttonText="Headstage 4"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage5 button" id="3c5bb9c9db62f1bb" memberName="toggleHeadstage5"
+                virtualName="" explicitFocusOrder="0" pos="24 224 112 24" buttonText="Headstage 5"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage6 button" id="306515c3b3ea7522" memberName="toggleHeadstage6"
+                virtualName="" explicitFocusOrder="0" pos="160 128 112 24" buttonText="Headstage 6"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage7 button" id="704c88cd419204b0" memberName="toggleHeadstage7"
+                virtualName="" explicitFocusOrder="0" pos="160 152 112 24" buttonText="Headstage 7"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage8 button" id="ba85df2f98d869d9" memberName="toggleHeadstage8"
+                virtualName="" explicitFocusOrder="0" pos="160 176 112 24" buttonText="Headstage 8"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage9 button" id="16256502b9f60cd4" memberName="toggleHeadstage9"
+                virtualName="" explicitFocusOrder="0" pos="160 200 112 24" buttonText="Headstage 9"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+  <TOGGLEBUTTON name="Headstage10 button" id="fab4cbe0a6d27a36" memberName="toggleHeadstage10"
+                virtualName="" explicitFocusOrder="0" pos="160 224 112 24" buttonText="Headstage 10"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="1"/>
+</JUCER_COMPONENT>
+
+END_JUCER_METADATA
+*/
+#endif
+
+
+//[EndFile] You can add extra defines here...
+//[/EndFile]
diff --git a/Source/UI/EcubeDialogComponent.h b/Source/UI/EcubeDialogComponent.h
new file mode 100644
index 0000000000000000000000000000000000000000..2f2da1addbd43d822d87bebfcc8a4201d7cdb40f
--- /dev/null
+++ b/Source/UI/EcubeDialogComponent.h
@@ -0,0 +1,92 @@
+/*
+  ==============================================================================
+
+  This is an automatically generated GUI class created by the Introjucer!
+
+  Be careful when adding custom code to these files, as only the code within
+  the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
+  and re-saved.
+
+  Created with Introjucer version: 3.1.0
+
+  ------------------------------------------------------------------------------
+
+  The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
+  Copyright 2004-13 by Raw Material Software Ltd.
+
+  ==============================================================================
+*/
+
+#ifndef __JUCE_HEADER_CDBC0626D3EB7016__
+#define __JUCE_HEADER_CDBC0626D3EB7016__
+
+//[Headers]     -- You can add your own extra header files here --
+#include "JuceHeader.h"
+//[/Headers]
+
+
+
+//==============================================================================
+/**
+                                                                    //[Comments]
+    An auto-generated component, created by the Introjucer.
+
+    Describe your class and how it works here!
+                                                                    //[/Comments]
+*/
+class EcubeDialogComponent  : public Component,
+                              public ButtonListener,
+                              public ComboBoxListener
+{
+public:
+    //==============================================================================
+    EcubeDialogComponent ();
+    ~EcubeDialogComponent();
+
+    //==============================================================================
+    //[UserMethods]     -- You can add your own custom methods in this section.
+    String GetAddressValue();
+    String GetModuleName();
+    void SetDeviceNames(const StringArray& names);
+    void GetHeadstageSelection(bool hs[10]);
+    //[/UserMethods]
+
+    void paint (Graphics& g);
+    void resized();
+    void buttonClicked (Button* buttonThatWasClicked);
+    void comboBoxChanged (ComboBox* comboBoxThatHasChanged);
+
+
+
+private:
+    //[UserVariables]   -- You can add your own custom variables in this section.
+    //[/UserVariables]
+
+    //==============================================================================
+    ScopedPointer<Label> laAddressLabel;
+    ScopedPointer<TextButton> bnOK;
+    ScopedPointer<TextButton> bnCancel;
+    ScopedPointer<ComboBox> comboModule;
+    ScopedPointer<Label> labelModule;
+    ScopedPointer<ComboBox> cbNetworkAddress;
+    ScopedPointer<GroupComponent> groupComponent;
+    ScopedPointer<ToggleButton> toggleHeadstage1;
+    ScopedPointer<ToggleButton> toggleHeadstage2;
+    ScopedPointer<ToggleButton> toggleHeadstage3;
+    ScopedPointer<ToggleButton> toggleHeadstage4;
+    ScopedPointer<ToggleButton> toggleHeadstage5;
+    ScopedPointer<ToggleButton> toggleHeadstage6;
+    ScopedPointer<ToggleButton> toggleHeadstage7;
+    ScopedPointer<ToggleButton> toggleHeadstage8;
+    ScopedPointer<ToggleButton> toggleHeadstage9;
+    ScopedPointer<ToggleButton> toggleHeadstage10;
+
+
+    //==============================================================================
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EcubeDialogComponent)
+};
+
+//[EndFile] You can add extra defines here...
+//[/EndFile]
+
+#endif   // __JUCE_HEADER_CDBC0626D3EB7016__
diff --git a/Source/UI/ProcessorList.cpp b/Source/UI/ProcessorList.cpp
index 18a6cf0cbd9b68fd856c0cdb1101a71f92fac2c4..28c2817e235fa997b8e503cf55bbe3fc23b91eff 100755
--- a/Source/UI/ProcessorList.cpp
+++ b/Source/UI/ProcessorList.cpp
@@ -55,6 +55,9 @@ ProcessorList::ProcessorList()
     //sources->addSubItem(new ProcessorListItem("Signal Generator"));
     //sources->addSubItem(new ProcessorListItem("Custom FPGA"));
     sources->addSubItem(new ProcessorListItem("Rhythm FPGA"));
+#if JUCE_WINDOWS // eCube module currently only available for Windows
+    sources->addSubItem(new ProcessorListItem("eCube")); // Added by Michael Borisov
+#endif
     sources->addSubItem(new ProcessorListItem("File Reader"));
     //sources->addSubItem(new ProcessorListItem("Network Events"));
     sources->addSubItem(new ProcessorListItem("Serial Port"));
diff --git a/open-ephys.jucer b/open-ephys.jucer
index f3791847eae5b954987a0f92cc1e7d042035f0e7..ce7c632944877ae23e88f6370ddc31a107157b3f 100644
--- a/open-ephys.jucer
+++ b/open-ephys.jucer
@@ -381,6 +381,8 @@
         <FILE id="hGnGAjh" name="EventNode.cpp" compile="1" resource="0" file="Source/Processors/EventNode.cpp"/>
         <FILE id="dUtRN6" name="EventNode.h" compile="0" resource="0" file="Source/Processors/EventNode.h"/>
         <GROUP id="AqvwO6w" name="Editors">
+          <FILE id="K24HnQ" name="EcubeEditor.cpp" compile="1" resource="0" file="Source/Processors/Editors/EcubeEditor.cpp"/>
+          <FILE id="lLjRJj" name="EcubeEditor.h" compile="0" resource="0" file="Source/Processors/Editors/EcubeEditor.h"/>
           <FILE id="Fs9xVR" name="SpikeSorterEditor.cpp" compile="1" resource="0"
                 file="Source/Processors/Editors/SpikeSorterEditor.cpp"/>
           <FILE id="YnUVDV" name="SpikeSorterEditor.h" compile="0" resource="0"
@@ -497,6 +499,8 @@
                 file="Source/Processors/Editors/GenericEditor.h"/>
         </GROUP>
         <GROUP id="ZgsuWxi" name="DataThreads">
+          <FILE id="mbMbly" name="EcubeThread.cpp" compile="1" resource="0" file="Source/Processors/DataThreads/EcubeThread.cpp"/>
+          <FILE id="lKsc0T" name="EcubeThread.h" compile="0" resource="0" file="Source/Processors/DataThreads/EcubeThread.h"/>
           <GROUP id="LcWQtrg" name="rhythm-api">
             <FILE id="hyM1EYD" name="okFrontPanelDLL.cpp" compile="1" resource="0"
                   file="Source/Processors/DataThreads/rhythm-api/okFrontPanelDLL.cpp"/>
@@ -554,6 +558,10 @@
               file="Source/Processors/ProcessorGraph.h"/>
       </GROUP>
       <GROUP id="RNGb1yR" name="UI">
+        <FILE id="PBkkUW" name="EcubeDialogComponent.cpp" compile="1" resource="0"
+              file="Source/UI/EcubeDialogComponent.cpp"/>
+        <FILE id="MFmxar" name="EcubeDialogComponent.h" compile="0" resource="0"
+              file="Source/UI/EcubeDialogComponent.h"/>
         <FILE id="gXswXJ" name="CustomArrowButton.cpp" compile="1" resource="0"
               file="Source/UI/CustomArrowButton.cpp"/>
         <FILE id="ECOEoc" name="CustomArrowButton.h" compile="0" resource="0"