diff --git a/Builds/MacOSX/Info.plist b/Builds/MacOSX/Info.plist
index 148cb10fe1c0290781bbdaa6473adfa3baf907c8..52711770d36a1a9273f881e6894f46fc86f3f798 100644
--- a/Builds/MacOSX/Info.plist
+++ b/Builds/MacOSX/Info.plist
@@ -16,9 +16,9 @@
     <key>CFBundleSignature</key>
     <string>????</string>
     <key>CFBundleShortVersionString</key>
-    <string>0.3.5</string>
+    <string>0.3.6</string>
     <key>CFBundleVersion</key>
-    <string>0.3.5</string>
+    <string>0.3.6</string>
     <key>NSHumanReadableCopyright</key>
     <string>Open Ephys</string>
     <key>NSHighResolutionCapable</key>
diff --git a/Builds/VisualStudio2012/resources.rc b/Builds/VisualStudio2012/resources.rc
index f8e9fb7b27f1fe31f745a58a2ab83b88b2b29263..2ec7e37977549fe56273d5725bb997199bd8b7b8 100644
--- a/Builds/VisualStudio2012/resources.rc
+++ b/Builds/VisualStudio2012/resources.rc
@@ -7,7 +7,7 @@
 #include <windows.h>
 
 VS_VERSION_INFO VERSIONINFO
-FILEVERSION  0,3,5,0
+FILEVERSION  0,3,6,0
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
@@ -15,9 +15,9 @@ BEGIN
     BEGIN
       VALUE "CompanyName",  "Open Ephys\0"
       VALUE "FileDescription",  "open-ephys\0"
-      VALUE "FileVersion",  "0.3.5\0"
+      VALUE "FileVersion",  "0.3.6\0"
       VALUE "ProductName",  "open-ephys\0"
-      VALUE "ProductVersion",  "0.3.5\0"
+      VALUE "ProductVersion",  "0.3.6\0"
     END
   END
 
diff --git a/Builds/VisualStudio2013/resources.rc b/Builds/VisualStudio2013/resources.rc
index f8e9fb7b27f1fe31f745a58a2ab83b88b2b29263..2ec7e37977549fe56273d5725bb997199bd8b7b8 100644
--- a/Builds/VisualStudio2013/resources.rc
+++ b/Builds/VisualStudio2013/resources.rc
@@ -7,7 +7,7 @@
 #include <windows.h>
 
 VS_VERSION_INFO VERSIONINFO
-FILEVERSION  0,3,5,0
+FILEVERSION  0,3,6,0
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
@@ -15,9 +15,9 @@ BEGIN
     BEGIN
       VALUE "CompanyName",  "Open Ephys\0"
       VALUE "FileDescription",  "open-ephys\0"
-      VALUE "FileVersion",  "0.3.5\0"
+      VALUE "FileVersion",  "0.3.6\0"
       VALUE "ProductName",  "open-ephys\0"
-      VALUE "ProductVersion",  "0.3.5\0"
+      VALUE "ProductVersion",  "0.3.6\0"
     END
   END
 
diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h
index 23beaaa63e64634b9d631992026c569f9529f71e..23180453ee27c07c99c7288b8ce1c817b8114347 100644
--- a/JuceLibraryCode/JuceHeader.h
+++ b/JuceLibraryCode/JuceHeader.h
@@ -39,8 +39,8 @@
 namespace ProjectInfo
 {
     const char* const  projectName    = "open-ephys";
-    const char* const  versionString  = "0.3.5";
-    const int          versionNumber  = 0x305;
+    const char* const  versionString  = "0.3.6";
+    const int          versionNumber  = 0x306;
 }
 
 #endif   // __APPHEADERFILE_YNSYIRR__
diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
index e51dd7c0a4656e429e4790df06e537718dcb394b..d70c3ff58f33c494e5a06f0842b46c16b9f4fe10 100755
--- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
+++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
@@ -29,6 +29,9 @@
 #endif
 
 //==============================================================================
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#pragma GCC diagnostic ignored "-Wnonnull"
 struct SystemVol
 {
     SystemVol (AudioObjectPropertySelector selector)
@@ -123,6 +126,7 @@ private:
                  && isSettable;
     }
 };
+#pragma GCC diagnostic pop
 
 #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
 float JUCE_CALLTYPE SystemAudioVolume::getGain()              { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).getGain(); }
diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm
index 0a00e9212450f7eb2d1bbaeb47a119cec8a02f72..45520d4981deb817c65504bb7a7580336da4fef1 100755
--- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm
+++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm
@@ -311,12 +311,15 @@ bool File::moveToTrash() const
     {
         NSString* p = juceStringToNS (getFullPathName());
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
         return [[NSWorkspace sharedWorkspace]
                     performFileOperation: NSWorkspaceRecycleOperation
                                   source: [p stringByDeletingLastPathComponent]
                              destination: nsEmptyString()
                                    files: [NSArray arrayWithObject: [p lastPathComponent]]
                                      tag: nil ];
+#pragma GCC diagnostic pop
     }
    #endif
 }
diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm
index 130759e6d67e4acbea60f278d6e478b2df5d54c4..c019d2c88e8a1b7d4402936ef7a4f600df1d4137 100755
--- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm
+++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm
@@ -243,8 +243,11 @@ public:
 
     void run() override
     {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
         connection = [[NSURLConnection alloc] initWithRequest: request
                                                      delegate: delegate];
+#pragma GCC diagnostic pop
         while (! threadShouldExit())
         {
             JUCE_AUTORELEASEPOOL
diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm
index 81a29a8047c455ad78a07e9eb420423cb2e05c37..f56683ecde6bf56d15e99613ec739e6d3d96be23 100755
--- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm
+++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm
@@ -272,16 +272,22 @@ namespace CoreTextTypeLayout
         }
 
         // Paragraph Attributes
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
         CTTextAlignment ctTextAlignment = kCTLeftTextAlignment;
+#pragma GCC diagnostic pop
         CTLineBreakMode ctLineBreakMode = kCTLineBreakByWordWrapping;
         const CGFloat ctLineSpacing = text.getLineSpacing();
 
         switch (text.getJustification().getOnlyHorizontalFlags())
         {
             case Justification::left:                   break;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
             case Justification::right:                  ctTextAlignment = kCTRightTextAlignment; break;
             case Justification::horizontallyCentred:    ctTextAlignment = kCTCenterTextAlignment; break;
             case Justification::horizontallyJustified:  ctTextAlignment = kCTJustifiedTextAlignment; break;
+#pragma GCC diagnostic pop
             default:                                    jassertfalse; break; // Illegal justification flags
         }
 
diff --git a/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm b/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm
index 6b863280f57c6d34f4e8137ef9842aec659d0228..cbf27f7a08e08aaa0c1ec9fa8e7bb24b97223144 100755
--- a/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm
+++ b/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm
@@ -160,9 +160,9 @@ public:
         static DownloadClickDetectorClass cls;
         clickListener = [cls.createInstance() init];
         DownloadClickDetectorClass::setOwner (clickListener, owner);
-        [webView setPolicyDelegate: clickListener];
-        [webView setFrameLoadDelegate: clickListener];
-        [webView setUIDelegate: clickListener];
+       // [webView setPolicyDelegate: (id <WebPolicyDelegate>)clickListener];
+       // [webView setFrameLoadDelegate: (id <WebFrameLoadDelegate>)clickListener];
+       // [webView setUIDelegate: (id <WebUIDelegate>)clickListener];
        #else
         webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
         setView (webView);
diff --git a/Resources/Python/event_listener.py b/Resources/Python/event_listener.py
new file mode 100644
index 0000000000000000000000000000000000000000..9304b266c4a00822c66845caad2da02bbcbaf68c
--- /dev/null
+++ b/Resources/Python/event_listener.py
@@ -0,0 +1,109 @@
+from __future__ import print_function, unicode_literals
+from collections import OrderedDict
+from itertools import chain, repeat
+try:
+    from itertools import izip
+except ImportError:
+    izip = zip  # Python 3
+import struct
+
+import zmq
+
+
+# Event types
+TTL = 3
+SPIKE = 4
+MESSAGE = 5
+BINARY_MSG = 6
+
+
+def unpacker(format, *fields):
+    s = struct.Struct(format)
+
+    def unpack(data):
+        values = s.unpack(data[:s.size])
+        if (len(values) == 1) and (not fields):
+            assert len(data) == s.size
+            return values[0], ''
+        assert len(values) <= len(fields)
+        return (OrderedDict(izip(fields, chain(values, repeat(None)))),
+                data[s.size:])
+
+    return unpack
+
+
+unpack_standard = unpacker('3BxB',
+                           'node_id',
+                           'event_id',
+                           'event_channel',
+                           'source_node_id',
+                           )
+
+
+unpack_ttl = unpacker('<Q')
+
+
+unpack_spike = unpacker('<2q2x5H3B2fH',
+                        'timestamp',
+                        'timestamp_software',
+                        'n_channels',
+                        'n_samples',
+                        'sorted_id',
+                        'electrode_id',
+                        'channel',
+                        'color_r', 'color_g', 'color_b',
+                        'pc_proj_x', 'pc_proj_y',
+                        'sampling_frequency_hz',
+                        'data',
+                        'gain',
+                        'threshold',
+                        )
+
+
+def run(hostname='localhost', port=5557):
+    with zmq.Context() as ctx:
+        with ctx.socket(zmq.SUB) as sock:
+            sock.connect('tcp://%s:%d' % (hostname, port))
+
+            for etype in (TTL, SPIKE, MESSAGE):
+                sock.setsockopt(zmq.SUBSCRIBE, chr(etype).encode('utf-8'))
+
+            while True:
+                try:
+                    parts = sock.recv_multipart()
+                    assert len(parts) == 3
+
+                    etype = ord(parts[0])
+                    timestamp_seconds = struct.unpack('d', parts[1])[0]
+                    body = parts[2]
+
+                    if etype == SPIKE:
+                        spike, body = unpack_spike(body)
+                        print('%g: Spike: %s' % (timestamp_seconds, spike))
+                        body = ''  # TODO: unpack other data
+
+                    else:
+                        header, body = unpack_standard(body)
+    
+                        if etype == TTL:
+                            word, body = unpack_ttl(body)
+                            print('%g: TTL: Channel %d: %s' %
+                                  (timestamp_seconds,
+                                   header['event_channel'] + 1,
+                                   'ON' if header['event_id'] else 'OFF'))
+    
+                        elif etype == MESSAGE:
+                            msg, body = body.decode('utf-8'), ''
+                            print('%g: Message: %s' % (timestamp_seconds, msg))
+
+
+                    # Check that all data was consumed
+                    assert len(body) == 0
+
+                except KeyboardInterrupt:
+                    print()  # Add final newline
+                    break
+
+
+if __name__ == '__main__':
+    run()
diff --git a/Resources/Python/record_control_example_client.py b/Resources/Python/record_control_example_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b4c0f899366ac26378629073787faf58c076883
--- /dev/null
+++ b/Resources/Python/record_control_example_client.py
@@ -0,0 +1,69 @@
+"""
+    A zmq client to test remote control of open-ephys GUI
+"""
+
+import zmq
+import os
+import time
+
+
+def run_client():
+
+    # Basic start/stop commands
+    start_cmd = 'StartRecord'
+    stop_cmd = 'StopRecord'
+
+    # Example settings
+    rec_dir = os.path.join(os.getcwd(), 'Output_RecordControl')
+    print "Saving data to:", rec_dir
+
+    # Some commands
+    commands = [start_cmd + ' RecDir=%s' % rec_dir,
+                start_cmd + ' PrependText=Session01 AppendText=Condition01',
+                start_cmd + ' PrependText=Session01 AppendText=Condition02',
+                start_cmd + ' PrependText=Session02 AppendText=Condition01',
+                start_cmd,
+                start_cmd + ' CreateNewDir=1']
+
+    # Connect network handler
+    ip = '127.0.0.1'
+    port = 5556
+    timeout = 1.
+
+    url = "tcp://%s:%d" % (ip, port)
+
+    with zmq.Context() as context:
+        with context.socket(zmq.REQ) as socket:
+            socket.RCVTIMEO = int(timeout * 1000)  # timeout in milliseconds
+            socket.connect(url)
+
+            # Finally, start data acquisition
+            socket.send('StartAcquisition')
+            answer = socket.recv()
+            print answer
+            time.sleep(5)
+
+            for start_cmd in commands:
+
+                for cmd in [start_cmd, stop_cmd]:
+                    socket.send(cmd)
+                    answer = socket.recv()
+                    print answer
+
+                    if 'StartRecord' in cmd:
+                        # Record data for 5 seconds
+                        time.sleep(5)
+                    else:
+                        # Stop for 1 second
+                        time.sleep(1)
+
+            # Finally, stop data acquisition; it might be a good idea to 
+            # wait a little bit until all data have been written to hard drive
+            time.sleep(0.5)
+            socket.send('StopAcquisition')
+            answer = socket.recv()
+            print answer
+
+
+if __name__ == '__main__':
+    run_client()
diff --git a/Source/CoreServices.cpp b/Source/CoreServices.cpp
index 11b541ddd7ab1e1975d30829b417a0db261cf03e..b58d8936136d47edf638dd8efe51d9853b68c5c0 100644
--- a/Source/CoreServices.cpp
+++ b/Source/CoreServices.cpp
@@ -52,6 +52,16 @@ void setRecordingStatus(bool enable)
     getControlPanel()->setRecordState(enable);
 }
 
+bool getAcquisitionStatus()
+{
+	return getControlPanel()->getAcquisitionState();
+}
+
+void setAcquisitionStatus(bool enable)
+{
+    getControlPanel()->setAcquisitionState(enable);
+}
+
 void sendStatusMessage(const String& text)
 {
     getBroadcaster()->sendActionMessage(text);
@@ -77,6 +87,26 @@ int64 getSoftwareTimestamp()
 	return getMessageCenter()->getTimestamp(true);
 }
 
+void setRecordingDirectory(String dir)
+{
+    getControlPanel()->setRecordingDirectory(dir);
+}
+
+void createNewRecordingDir()
+{
+   getControlPanel()->labelTextChanged(NULL);
+}
+
+void setPrependTextToRecordingDir(String text)
+{
+    getControlPanel()->setPrependText(text);
+}
+
+void setAppendTextToRecordingDir(String text)
+{
+    getControlPanel()->setAppendText(text);
+}
+
 namespace RecordNode
 {
 void createNewrecordingDir()
diff --git a/Source/CoreServices.h b/Source/CoreServices.h
index 19fd631924cd76d13e8103260e137f6f8997ed85..1350f4ea6d77bb0b584b15678c5aa5e0fb9fe10f 100644
--- a/Source/CoreServices.h
+++ b/Source/CoreServices.h
@@ -43,6 +43,12 @@ PLUGIN_API bool getRecordingStatus();
 /** Activated or deactivates recording */
 PLUGIN_API void setRecordingStatus(bool enable);
 
+/** Returns true if the GUI is acquiring data */
+bool getAcquisitionStatus();
+
+/** Activates or deactivates data acquisition */
+void setAcquisitionStatus(bool enable);
+
 /** Sends a string to the message bar */
 PLUGIN_API void sendStatusMessage(const String& text);
 
@@ -60,6 +66,18 @@ PLUGIN_API int64 getGlobalTimestamp();
 /** Gets the software timestamp based on a high resolution timer aligned to the start of each processing block */
 PLUGIN_API int64 getSoftwareTimestamp();
 
+/** Set new recording directory */
+void setRecordingDirectory(String dir);
+
+/** Create new recording directory */
+void createNewRecordingDir();
+
+/** Manually set the text to be prepended to the recording directory */
+void setPrependTextToRecordingDir(String text);
+
+/** Manually set the text to be appended to the recording directory */
+void setAppendTextToRecordingDir(String text);
+
 namespace RecordNode
 {
 /** Forces creation of new directory on recording */
diff --git a/Source/Plugins/ArduinoOutput/ArduinoOutput.cpp b/Source/Plugins/ArduinoOutput/ArduinoOutput.cpp
index 4e175655330f2c7c2d853c04ba461976df17895a..0c2317d04e4418cecb8f6b737c46b535e7f5aedc 100644
--- a/Source/Plugins/ArduinoOutput/ArduinoOutput.cpp
+++ b/Source/Plugins/ArduinoOutput/ArduinoOutput.cpp
@@ -27,7 +27,7 @@
 #include <stdio.h>
 
 ArduinoOutput::ArduinoOutput()
-    : GenericProcessor("Arduino Output"), outputChannel(13), inputChannel(-1), state(true), deviceSelected(false)
+	: GenericProcessor("Arduino Output"), outputChannel(13), inputChannel(-1), state(true), deviceSelected(false), acquisitionIsActive(false)
 {
 }
 
diff --git a/Source/Plugins/EcubeSource/EcubeThread.h b/Source/Plugins/EcubeSource/EcubeThread.h
index 8b654620f463a179ad0efee1092b60ba602b81d3..13b43aac20619518374ea8228cac50c104f57458 100644
--- a/Source/Plugins/EcubeSource/EcubeThread.h
+++ b/Source/Plugins/EcubeSource/EcubeThread.h
@@ -32,8 +32,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <string.h>
 
-#define MAX_NUM_DATA_STREAMS 8
-
 class SourceNode;
 
 #if JUCE_WINDOWS
diff --git a/Source/Plugins/NetworkEvents/NetworkEvents.cpp b/Source/Plugins/NetworkEvents/NetworkEvents.cpp
index 7d53afb775e5bbfe82160a2e31d9b6640a26617c..1c2cc76dd1dfa90c83baa829f2bd237b08696257 100644
--- a/Source/Plugins/NetworkEvents/NetworkEvents.cpp
+++ b/Source/Plugins/NetworkEvents/NetworkEvents.cpp
@@ -403,7 +403,86 @@ String NetworkEvents::handleSpecialMessages(StringTS msg)
     }
 
     */
-    return String("NotHandled");
+
+	/** Start/stop data acquisition */
+	String s = msg.getString();
+
+	/** Command is first substring */
+	StringArray inputs = StringArray::fromTokens(s, " ");
+	String cmd = String(inputs[0]);
+
+	const MessageManagerLock mmLock;
+	if (cmd.compareIgnoreCase("StartAcquisition") == 0)
+	{
+		if (!CoreServices::getAcquisitionStatus())
+	    {
+	        CoreServices::setAcquisitionStatus(true);
+	    }
+		return String("StartedAcquisition");
+	}
+	else if (cmd.compareIgnoreCase("StopAcquisition") == 0)
+	{
+		if (CoreServices::getAcquisitionStatus())
+	    {
+	        CoreServices::setAcquisitionStatus(false);
+	    }
+		return String("StoppedAcquisition");
+	}
+	else if (String("StartRecord").compareIgnoreCase(cmd) == 0)
+	{
+		if (!CoreServices::getRecordingStatus() && CoreServices::getAcquisitionStatus())
+		{
+			/** First set optional parameters (name/value pairs)*/
+			if (s.contains("="))
+			{
+				String params = s.substring(cmd.length());
+				StringPairArray dict = parseNetworkMessage(params);
+
+				StringArray keys = dict.getAllKeys();
+				for (int i = 0; i<keys.size(); i++)
+				{
+					String key = keys[i];
+					String value = dict[key];
+
+					if (key.compareIgnoreCase("CreateNewDir") == 0)
+					{
+						if (value.compareIgnoreCase("1") == 0)
+						{
+							CoreServices::createNewRecordingDir();
+						}
+					}
+					else if (key.compareIgnoreCase("RecDir") == 0)
+					{
+						CoreServices::setRecordingDirectory(value);
+					}
+					else if (key.compareIgnoreCase("PrependText") == 0)
+					{
+						CoreServices::setPrependTextToRecordingDir(value);
+					}
+					else if (key.compareIgnoreCase("AppendText") == 0)
+					{
+						CoreServices::setAppendTextToRecordingDir(value);
+					}
+				}
+			}
+
+			/** Start recording */
+			CoreServices::setRecordingStatus(true);
+			return String("StartedRecording");
+		}
+	}
+	else if (String("StopRecord").compareIgnoreCase(cmd) == 0)
+	{
+		if (CoreServices::getRecordingStatus())
+		{
+			CoreServices::setRecordingStatus(false);
+			return String("StoppedRecording");
+		}
+	}
+	else
+	{
+	    return String("NotHandled");
+	}
 }
 
 void NetworkEvents::process(AudioSampleBuffer& buffer,
@@ -564,3 +643,51 @@ void NetworkEvents::createZmqContext()
         zmqcontext = zmq_ctx_new(); //<-- this is only available in version 3+
 #endif
 }
+
+StringPairArray NetworkEvents::parseNetworkMessage(String msg)
+{
+	StringArray splitted;
+	splitted.addTokens(msg, "=", "");
+
+	StringPairArray dict = StringPairArray();
+	String key = "";
+	String value = "";
+	for (int i = 0; i<splitted.size() - 1; i++)
+	{
+		String s1 = splitted[i];
+		String s2 = splitted[i + 1];
+
+		/** Get key */
+		if (!key.isEmpty())
+		{
+			if (s1.contains(" "))
+			{
+				int i1 = s1.lastIndexOf(" ");
+				key = s1.substring(i1 + 1);
+			}
+			else
+			{
+				key = s1;
+			}
+		}
+		else
+		{
+			key = s1.trim();
+		}
+
+		/** Get value */
+		if (i < splitted.size() - 2)
+		{
+			int i1 = s2.lastIndexOf(" ");
+			value = s2.substring(0, i1);
+		}
+		else
+		{
+			value = s2;
+		}
+
+		dict.set(key, value);
+	}
+
+	return dict;
+}
diff --git a/Source/Plugins/NetworkEvents/NetworkEvents.h b/Source/Plugins/NetworkEvents/NetworkEvents.h
index cb1d9db3ade119a561cb0edd746906841654f32f..b053a09f855456fdeee211cb8953e115cd087ca5 100644
--- a/Source/Plugins/NetworkEvents/NetworkEvents.h
+++ b/Source/Plugins/NetworkEvents/NetworkEvents.h
@@ -115,6 +115,9 @@ private:
     void handleEvent(int eventType, MidiMessage& event, int samplePos);
     void createZmqContext();
 
+	//* Split network message into name/value pairs (name1=val1 name2=val2 etc) */
+	StringPairArray parseNetworkMessage(String msg);
+
     StringTS createStringTS(String S, int64 t);
 
     static void* zmqcontext;
diff --git a/Source/Plugins/RecordControl/RecordControl.cpp b/Source/Plugins/RecordControl/RecordControl.cpp
index 110b89e84f8665475c7875fcc7f5c439569b65ef..612435be73fa7c7faf17a929b9752b183b0c72d0 100644
--- a/Source/Plugins/RecordControl/RecordControl.cpp
+++ b/Source/Plugins/RecordControl/RecordControl.cpp
@@ -109,8 +109,8 @@ void RecordControl::handleEvent(int eventType, MidiMessage& event, int)
         {
             CoreServices::setRecordingStatus(!CoreServices::getRecordingStatus());
         }
+    }
 
+}
 
-    }
 
-}
\ No newline at end of file
diff --git a/Source/Plugins/RecordControl/RecordControl.h b/Source/Plugins/RecordControl/RecordControl.h
index 9b6546a018e9e40d098b5d45326d4690fcd556dc..75601632b6c45f1df7a725c5785b657a2dae47a9 100644
--- a/Source/Plugins/RecordControl/RecordControl.h
+++ b/Source/Plugins/RecordControl/RecordControl.h
@@ -45,6 +45,7 @@ public:
     void setParameter(int, float);
     void updateTriggerChannel(int newChannel);
     void handleEvent(int eventType, MidiMessage& event, int);
+
     bool enable();
 
     bool isUtility()
@@ -65,4 +66,4 @@ private:
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp
index 81edfd38d6a67a58647031e9b9e81fb3e63f2d6e..767a377cbd01c8e4ba7ff244a218d68553022fb4 100644
--- a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp
+++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp
@@ -318,7 +318,9 @@ bool RHD2000Thread::uploadBitfile(String bitfilename)
 
         bool response = AlertWindow::showOkCancelBox(AlertWindow::NoIcon,
                                                      "FPGA bitfile not found.",
-                                                     "The rhd2000.bit file was not found in the directory of the executable. Would you like to browse for it?",
+                                                     (evalBoard->isUSB3() ? 
+													 "The rhd2000_usb3.bit file was not found in the directory of the executable. Would you like to browse for it?" :
+													 "The rhd2000.bit file was not found in the directory of the executable. Would you like to browse for it?"),
                                                      "Yes", "No", 0, 0);
         if (response)
         {
@@ -1495,7 +1497,7 @@ bool RHD2000Thread::stopAcquisition()
 
 bool RHD2000Thread::updateBuffer()
 {
-	int chOffset;
+	//int chOffset;
 	unsigned char* bufferPtr;
     //cout << "Number of 16-bit words in FIFO: " << evalBoard->numWordsInFifo() << endl;
     //cout << "Block size: " << blockSize << endl;
diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h
index fbeb3302ac26d9e8f28c1d5230bfae6b547bb005..63e0df6fce1d5309ed891507b1fdfb0d6248a9bb 100644
--- a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h
+++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h
@@ -43,7 +43,6 @@
 #define MAX_NUM_DATA_STREAMS_USB3 16
 #define MAX_NUM_HEADSTAGES 8
 
-#define MAX_NUM_DATA_STREAMS(u3) (u3 ? MAX_NUM_DATA_STREAMS_USB3 : MAX_NUM_DATA_STREAMS_USB2)
 #define MAX_NUM_CHANNELS MAX_NUM_DATA_STREAMS_USB3*35
 
 class SourceNode;
diff --git a/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.cpp b/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.cpp
index 45c1c3a1f6bcaab22e80ad3a6cf2dab56ffa137e..832b80e26fa3798efb0d7ffbabefd325f47fbd63 100644
--- a/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.cpp
+++ b/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.cpp
@@ -1389,15 +1389,15 @@ void Rhd2000EvalBoard::flush()
 	{
 		dev->SetWireInValue(WireInResetRun, 1 << 16, 1 << 16); //Override pipeout block throttle
 		dev->UpdateWireIns();
-		cout << "Pre-Flush: " << numWordsInFifo() << endl;
+		//cout << "Pre-Flush: " << numWordsInFifo() << endl;
 		while (numWordsInFifo() >= USB_BUFFER_SIZE / 2) {
 			dev->ReadFromBlockPipeOut(PipeOutData, USB3_BLOCK_SIZE, USB_BUFFER_SIZE, usbBuffer);
-			cout << "Flush phase A: " << numWordsInFifo() << endl;
+		//	cout << "Flush phase A: " << numWordsInFifo() << endl;
 		}
 		while (numWordsInFifo() > 0) {
 			dev->ReadFromBlockPipeOut(PipeOutData, USB3_BLOCK_SIZE, USB3_BLOCK_SIZE *max(2 * numWordsInFifo() / USB3_BLOCK_SIZE, (unsigned int)1), usbBuffer);
-			cout << "Flush phase B: " << numWordsInFifo() << endl;
-			printFIFOmetrics();
+		//	cout << "Flush phase B: " << numWordsInFifo() << endl;
+		//	printFIFOmetrics();
 		}
 		dev->SetWireInValue(WireInResetRun, 0, 1 << 16);
 		dev->UpdateWireIns();
@@ -1488,6 +1488,7 @@ bool Rhd2000EvalBoard::readDataBlocks(int numBlocks, queue<Rhd2000DataBlock> &da
     unsigned int numWordsToRead, numBytesToRead;
     int i;
     Rhd2000DataBlock *dataBlock;
+	long res;
 
     numWordsToRead = numBlocks * dataBlock->calculateDataBlockSizeInWords(numDataStreams, usb3);
 
@@ -1502,7 +1503,18 @@ bool Rhd2000EvalBoard::readDataBlocks(int numBlocks, queue<Rhd2000DataBlock> &da
         return false;
     }
 
-    dev->ReadFromPipeOut(PipeOutData, numBytesToRead, usbBuffer);
+	if (usb3)
+	{
+		res = dev->ReadFromBlockPipeOut(PipeOutData, USB3_BLOCK_SIZE, numBytesToRead, usbBuffer);
+	}
+	else
+	{
+		res = dev->ReadFromPipeOut(PipeOutData, numBytesToRead, usbBuffer);
+	}
+	if (res == ok_Timeout)
+	{
+		cerr << "CRITICAL: Timeout on pipe read. Check block and buffer sizes." << endl;
+	}
 
     dataBlock = new Rhd2000DataBlock(numDataStreams, usb3);
     for (i = 0; i < numBlocks; ++i) {
diff --git a/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.h b/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.h
index fffb8404c1e5bcc2b6504de03ef54eb9dcf7704a..440cee2251188f27a3496991726e3133fc85b795 100644
--- a/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.h
+++ b/Source/Processors/DataThreads/RhythmNode/rhythm-api/rhd2000evalboard.h
@@ -30,7 +30,7 @@
 
 #define MAX_NUM_DATA_STREAMS(u3) ( u3 ? MAX_NUM_DATA_STREAMS_USB3 : MAX_NUM_DATA_STREAMS_USB2 )
 
-#define USB3_BLOCK_SIZE	2048
+#define USB3_BLOCK_SIZE	1024
 #define DDR_BLOCK_SIZE 32
 
 #include <queue>
diff --git a/Source/Processors/EventBroadcaster/EventBroadcaster.cpp b/Source/Processors/EventBroadcaster/EventBroadcaster.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0527dcff471db91b1e0963b72bcb7ff2340fc34e
--- /dev/null
+++ b/Source/Processors/EventBroadcaster/EventBroadcaster.cpp
@@ -0,0 +1,153 @@
+/*
+  ==============================================================================
+
+    EventBroadcaster.cpp
+    Created: 22 May 2015 3:31:50pm
+    Author:  Christopher Stawarz
+
+  ==============================================================================
+*/
+
+#include "EventBroadcaster.h"
+#include "EventBroadcasterEditor.h"
+
+
+std::shared_ptr<void> EventBroadcaster::getZMQContext() {
+    // Note: C++11 guarantees that initialization of static local variables occurs exactly once, even
+    // if multiple threads attempt to initialize the same static local variable concurrently.
+#ifdef ZEROMQ
+    static const std::shared_ptr<void> ctx(zmq_ctx_new(), zmq_ctx_destroy);
+#else
+    static const std::shared_ptr<void> ctx;
+#endif
+    return ctx;
+}
+
+
+void EventBroadcaster::closeZMQSocket(void* socket)
+{
+#ifdef ZEROMQ
+    zmq_close(socket);
+#endif
+}
+
+
+EventBroadcaster::EventBroadcaster()
+    : GenericProcessor("Event Broadcaster"),
+      zmqContext(getZMQContext()),
+      zmqSocket(nullptr, &closeZMQSocket),
+      listeningPort(0),
+      currentSampleRate(0)
+{
+    setListeningPort(5557);
+}
+
+
+AudioProcessorEditor* EventBroadcaster::createEditor()
+{
+    editor = new EventBroadcasterEditor(this, true);
+    return editor;
+}
+
+
+int EventBroadcaster::getListeningPort() const
+{
+    return listeningPort;
+}
+
+
+void EventBroadcaster::setListeningPort(int port, bool forceRestart)
+{
+    if ((listeningPort != port) || forceRestart)
+    {
+#ifdef ZEROMQ
+        zmqSocket.reset(zmq_socket(zmqContext.get(), ZMQ_PUB));
+        if (!zmqSocket)
+        {
+            std::cout << "Failed to create socket: " << zmq_strerror(zmq_errno()) << std::endl;
+            return;
+        }
+
+        String url = String("tcp://*:") + String(port);
+        if (0 != zmq_bind(zmqSocket.get(), url.toRawUTF8()))
+        {
+            std::cout << "Failed to open socket: " << zmq_strerror(zmq_errno()) << std::endl;
+            return;
+        }
+#endif
+
+        listeningPort = port;
+    }
+}
+
+
+void EventBroadcaster::process(AudioSampleBuffer& continuousBuffer, MidiBuffer& eventBuffer)
+{
+    currentSampleRate = getSampleRate();
+    checkForEvents(eventBuffer);
+}
+
+
+bool EventBroadcaster::isSink()
+{
+    return true;
+}
+
+
+void EventBroadcaster::handleEvent(int eventType, MidiMessage& event, int samplePosition)
+{
+    const uint8_t* buffer = event.getRawData();
+    uint8_t type = buffer[0];
+    int64_t timestamp;
+    
+    switch (type) {
+        case TTL:
+        case MESSAGE:
+        case BINARY_MSG: {
+            uint8_t nodeID = buffer[1];
+            timestamp = timestamps.at(nodeID) + samplePosition;
+            break;
+        }
+            
+        case SPIKE:
+            std::copy_n(buffer + 1, sizeof(timestamp), reinterpret_cast<uint8_t *>(&timestamp));
+            break;
+            
+        default:
+            // Don't broadcast other event types
+            return;
+    }
+    
+    double timestampSeconds = double(timestamp) / currentSampleRate;
+
+#ifdef ZEROMQ
+    if (-1 == zmq_send(zmqSocket.get(), &type, sizeof(type), ZMQ_SNDMORE) ||
+        -1 == zmq_send(zmqSocket.get(), &timestampSeconds, sizeof(timestampSeconds), ZMQ_SNDMORE) ||
+        -1 == zmq_send(zmqSocket.get(), buffer + 1, event.getRawDataSize() - 1, 0) /* Omit event type */)
+    {
+        std::cout << "Failed to send message: " << zmq_strerror(zmq_errno()) << std::endl;
+    }
+#endif
+}
+
+
+void EventBroadcaster::saveCustomParametersToXml(XmlElement* parentElement)
+{
+    XmlElement* mainNode = parentElement->createNewChildElement("EVENTBROADCASTER");
+    mainNode->setAttribute("port", listeningPort);
+}
+
+
+void EventBroadcaster::loadCustomParametersFromXml()
+{
+    if (parametersAsXml)
+    {
+        forEachXmlChildElement(*parametersAsXml, mainNode)
+        {
+            if (mainNode->hasTagName("EVENTBROADCASTER"))
+            {
+                setListeningPort(mainNode->getIntAttribute("port"));
+            }
+        }
+    }
+}
diff --git a/Source/Processors/EventBroadcaster/EventBroadcaster.h b/Source/Processors/EventBroadcaster/EventBroadcaster.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e7424a472d41149c5e32dc1179ddb82f478f455
--- /dev/null
+++ b/Source/Processors/EventBroadcaster/EventBroadcaster.h
@@ -0,0 +1,58 @@
+/*
+  ==============================================================================
+
+    EventBroadcaster.h
+    Created: 22 May 2015 3:31:50pm
+    Author:  Christopher Stawarz
+
+  ==============================================================================
+*/
+
+#ifndef EVENTBROADCASTER_H_INCLUDED
+#define EVENTBROADCASTER_H_INCLUDED
+
+#include "../GenericProcessor/GenericProcessor.h"
+
+#ifdef ZEROMQ
+
+#ifdef WIN32
+#include "../../../Resources/windows-libs/ZeroMQ/include/zmq.h"
+#include "../../../Resources/windows-libs/ZeroMQ/include/zmq_utils.h"
+#else
+#include <zmq.h>
+#endif
+
+#endif
+#include <memory>
+
+class EventBroadcaster : public GenericProcessor
+{
+public:
+    EventBroadcaster();
+
+    AudioProcessorEditor* createEditor() override;
+
+    int getListeningPort() const;
+    void setListeningPort(int port, bool forceRestart = false);
+
+    void process(AudioSampleBuffer& continuousBuffer, MidiBuffer& eventBuffer) override;
+    bool isSink() override;
+    void handleEvent(int eventType, MidiMessage& event, int samplePosition = 0) override;
+
+    void saveCustomParametersToXml(XmlElement* parentElement) override;
+    void loadCustomParametersFromXml() override;
+
+private:
+    static std::shared_ptr<void> getZMQContext();
+    static void closeZMQSocket(void* socket);
+    
+    const std::shared_ptr<void> zmqContext;
+    std::unique_ptr<void, decltype(&closeZMQSocket)> zmqSocket;
+    int listeningPort;
+    
+    float currentSampleRate;
+
+};
+
+
+#endif  // EVENTBROADCASTER_H_INCLUDED
diff --git a/Source/Processors/EventBroadcaster/EventBroadcasterEditor.cpp b/Source/Processors/EventBroadcaster/EventBroadcasterEditor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9519f7e1b4f4db0a6ac017ccb0b497f7fa35267f
--- /dev/null
+++ b/Source/Processors/EventBroadcaster/EventBroadcasterEditor.cpp
@@ -0,0 +1,63 @@
+/*
+  ==============================================================================
+
+    EventBroadcasterEditor.cpp
+    Created: 22 May 2015 3:34:30pm
+    Author:  Christopher Stawarz
+
+  ==============================================================================
+*/
+
+#include "EventBroadcasterEditor.h"
+#include "EventBroadcaster.h"
+
+
+EventBroadcasterEditor::EventBroadcasterEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors)
+    : GenericEditor(parentNode, useDefaultParameterEditors)
+
+{
+    desiredWidth = 180;
+
+    urlLabel = new Label("Port", "Port:");
+    urlLabel->setBounds(20,80,140,25);
+    addAndMakeVisible(urlLabel);
+    EventBroadcaster* p = (EventBroadcaster*)getProcessor();
+
+    restartConnection = new UtilityButton("Restart Connection",Font("Default", 15, Font::plain));
+    restartConnection->setBounds(20,45,150,18);
+    restartConnection->addListener(this);
+    addAndMakeVisible(restartConnection);
+
+    portLabel = new Label("Port", String(p->getListeningPort()));
+    portLabel->setBounds(70,85,80,18);
+    portLabel->setFont(Font("Default", 15, Font::plain));
+    portLabel->setColour(Label::textColourId, Colours::white);
+    portLabel->setColour(Label::backgroundColourId, Colours::grey);
+    portLabel->setEditable(true);
+    portLabel->addListener(this);
+    addAndMakeVisible(portLabel);
+
+    setEnabledState(false);
+}
+
+
+void EventBroadcasterEditor::buttonEvent(Button* button)
+{
+    if (button == restartConnection)
+    {
+        EventBroadcaster* p = (EventBroadcaster*)getProcessor();
+        p->setListeningPort(p->getListeningPort(), true);
+    }
+}
+
+
+void EventBroadcasterEditor::labelTextChanged(juce::Label* label)
+{
+    if (label == portLabel)
+    {
+        Value val = label->getTextValue();
+
+        EventBroadcaster* p = (EventBroadcaster*)getProcessor();
+        p->setListeningPort(val.getValue());
+    }
+}
diff --git a/Source/Processors/EventBroadcaster/EventBroadcasterEditor.h b/Source/Processors/EventBroadcaster/EventBroadcasterEditor.h
new file mode 100644
index 0000000000000000000000000000000000000000..59533658e6a48527286cecd2c585a6c49d57de69
--- /dev/null
+++ b/Source/Processors/EventBroadcaster/EventBroadcasterEditor.h
@@ -0,0 +1,43 @@
+/*
+  ==============================================================================
+
+    EventBroadcasterEditor.h
+    Created: 22 May 2015 3:34:30pm
+    Author:  Christopher Stawarz
+
+  ==============================================================================
+*/
+
+#ifndef EVENTBROADCASTEREDITOR_H_INCLUDED
+#define EVENTBROADCASTEREDITOR_H_INCLUDED
+
+#include "../Editors/GenericEditor.h"
+
+
+/**
+
+ User interface for the "EventBroadcaster" source node.
+
+ @see EventBroadcaster
+
+ */
+
+class EventBroadcasterEditor : public GenericEditor, public Label::Listener
+{
+public:
+    EventBroadcasterEditor(GenericProcessor* parentNode, bool useDefaultParameterEditors);
+
+    void buttonEvent(Button* button) override;
+    void labelTextChanged(juce::Label* label) override;
+
+private:
+    ScopedPointer<UtilityButton> restartConnection;
+    ScopedPointer<Label> urlLabel;
+    ScopedPointer<Label> portLabel;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EventBroadcasterEditor);
+
+};
+
+
+#endif  // EVENTBROADCASTEREDITOR_H_INCLUDED
diff --git a/Source/Processors/GenericProcessor/GenericProcessor.cpp b/Source/Processors/GenericProcessor/GenericProcessor.cpp
index 4717d0fd919ded44746d6f5091308a5b3fe5b79b..ffc990e41ca02b85cdca1eb19bb4686462a081d4 100755
--- a/Source/Processors/GenericProcessor/GenericProcessor.cpp
+++ b/Source/Processors/GenericProcessor/GenericProcessor.cpp
@@ -769,7 +769,8 @@ void GenericProcessor::addEvent(MidiBuffer& eventBuffer,
     if (!isTimestamp && !timestampSet && !isSource() && !generatesTimestamps())
         setTimestamp(eventBuffer, getTimestamp(0));
 
-    uint8* data = new uint8[6+numBytes];
+	HeapBlock<uint8> data(static_cast<const size_t>(6 + numBytes));
+    //uint8* data = new uint8[6+numBytes];
 
     data[0] = type;    // event type
     data[1] = nodeId;  // processor ID automatically added
diff --git a/Source/Processors/SourceNode/SourceNode.cpp b/Source/Processors/SourceNode/SourceNode.cpp
index f1a855e6d3f06b30fb727f0ffdd6081974e9547f..359c60bf08681ef805c34e2bd2a7a45215817b06 100755
--- a/Source/Processors/SourceNode/SourceNode.cpp
+++ b/Source/Processors/SourceNode/SourceNode.cpp
@@ -43,7 +43,8 @@ SourceNode::SourceNode(const String& name_, DataThreadCreator dt)
         }
 
         numEventChannels = dataThread->getNumEventChannels();
-        eventChannelState = new int[numEventChannels];
+        //eventChannelState = new int[numEventChannels];
+		eventChannelState.malloc(numEventChannels);
         for (int i = 0; i < numEventChannels; i++)
         {
             eventChannelState[i] = 0;
@@ -53,7 +54,7 @@ SourceNode::SourceNode(const String& name_, DataThreadCreator dt)
     else
     {
         enabledState(false);
-        eventChannelState = 0;
+     //   eventChannelState = 0;
         numEventChannels = 0;
     }
 
@@ -61,7 +62,8 @@ SourceNode::SourceNode(const String& name_, DataThreadCreator dt)
     startTimer(sourceCheckInterval);
 
     timestamp = 0;
-    eventCodeBuffer = new uint64[10000]; //10000 samples per buffer max?
+    //eventCodeBuffer = new uint64[10000]; //10000 samples per buffer max?
+	eventCodeBuffer.malloc(10000);
 
 
 }
@@ -76,8 +78,8 @@ SourceNode::~SourceNode()
     }
 
 
-    if (eventChannelState)
-        delete[] eventChannelState;
+    //if (eventChannelState)
+    //    delete[] eventChannelState;
 }
 
 DataThread* SourceNode::getThread()
@@ -379,7 +381,9 @@ void SourceNode::process(AudioSampleBuffer& buffer,
                              TTL,    // eventType
                              i,      // sampleNum
                              0,	     // eventID
-                             c		 // eventChannel
+                             c,		 // eventChannel
+							 8,
+							 (uint8*)(&eventCodeBuffer[i])
                             );
                 }
                 else
@@ -393,7 +397,9 @@ void SourceNode::process(AudioSampleBuffer& buffer,
                              TTL,    // eventType
                              i,      // sampleNum
                              1,		 // eventID
-                             c		 // eventChannel
+                             c,		 // eventChannel
+							 8,
+							 (uint8*)(&eventCodeBuffer[i])
                             );
 
 
diff --git a/Source/Processors/SourceNode/SourceNode.h b/Source/Processors/SourceNode/SourceNode.h
index 0c4781a6e28ebf7e6104caa62f7e893f40be0471..88febeba150e1d7a2a16bdfd2b163e58e2bb9e9a 100755
--- a/Source/Processors/SourceNode/SourceNode.h
+++ b/Source/Processors/SourceNode/SourceNode.h
@@ -118,8 +118,10 @@ private:
     DataBuffer* inputBuffer;
 
     uint64 timestamp;
-    uint64* eventCodeBuffer;
-    int* eventChannelState;
+    //uint64* eventCodeBuffer;
+    //int* eventChannelState;
+	HeapBlock<uint64> eventCodeBuffer;
+	HeapBlock<int> eventChannelState;
 
     int ttlState;
 
diff --git a/Source/UI/ControlPanel.cpp b/Source/UI/ControlPanel.cpp
index 2bca3db57e9e3384dc16c35d372d76027c77bd48..a41c544bb51cc809b6b93c33c5e937aff1e48d15 100755
--- a/Source/UI/ControlPanel.cpp
+++ b/Source/UI/ControlPanel.cpp
@@ -485,6 +485,33 @@ void ControlPanel::setRecordState(bool t)
 
 }
 
+bool ControlPanel::getRecordingState()
+{
+
+	return recordButton->getToggleState();
+
+}
+
+void ControlPanel::setRecordingDirectory(String path)
+{
+    File newFile(path);
+    filenameComponent->setCurrentFile(newFile, true, sendNotificationSync);
+
+    graph->getRecordNode()->newDirectoryNeeded = true;
+    masterClock->resetRecordTime();
+}
+
+bool ControlPanel::getAcquisitionState()
+{
+	return playButton->getToggleState();
+}
+
+void ControlPanel::setAcquisitionState(bool state)
+{
+	playButton->setToggleState(state, sendNotification);
+}
+
+
 void ControlPanel::updateChildComponents()
 {
 
@@ -994,6 +1021,16 @@ String ControlPanel::getTextToPrepend()
     }
 }
 
+void ControlPanel::setPrependText(String t)
+{
+    prependText->setText(t, sendNotificationSync);
+}
+
+void ControlPanel::setAppendText(String t)
+{
+    appendText->setText(t, sendNotificationSync);
+}
+
 void ControlPanel::setDateText(String t)
 {
     dateText->setText(t, dontSendNotification);
@@ -1077,4 +1114,4 @@ StringArray ControlPanel::getRecentlyUsedFilenames()
 void ControlPanel::setRecentlyUsedFilenames(const StringArray& filenames)
 {
     filenameComponent->setRecentlyUsedFilenames(filenames);
-}
\ No newline at end of file
+}
diff --git a/Source/UI/ControlPanel.h b/Source/UI/ControlPanel.h
index 8d81f9d9f0f710d97bf7d17716d664b7f259b426..8b733c6dd26a9e0988d2255dde47c9b1fa890e87 100755
--- a/Source/UI/ControlPanel.h
+++ b/Source/UI/ControlPanel.h
@@ -301,6 +301,19 @@ public:
 
     /** Used to manually turn recording on and off.*/
     void setRecordState(bool isRecording);
+
+    /** Return current recording state.*/
+    bool getRecordingState();
+
+    /** Set recording directory and update FilenameComponent */
+    void setRecordingDirectory(String path);
+
+    /** Return current acquisition state.*/
+    bool getAcquisitionState();
+
+    /** Used to manually turn recording on and off.*/
+    void setAcquisitionState(bool state);
+
     /** Returns a boolean that indicates whether or not the FilenameComponet
         is visible. */
     bool isOpen()
@@ -317,6 +330,12 @@ public:
     /** Used by RecordNode to set the filename. */
     String getTextToAppend();
 
+    /** Manually set the text to be prepended to the recording directory */
+    void setPrependText(String text);
+
+    /** Manually set the text to be appended to the recording directory */
+    void setAppendText(String text);
+
     /** Set date text. */
     void setDateText(String);