annotate Source/TouchKeys/Osc.h @ 20:dfff66c07936

Lots of minor changes to support building on Visual Studio. A few MSVC-specific #ifdefs to eliminate things Visual Studio doesn't like. This version now compiles on Windows (provided liblo, Juce and pthread are present) but the TouchKeys device support is not yet enabled. Also, the code now needs to be re-checked on Mac and Linux.
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Sun, 09 Feb 2014 18:40:51 +0000
parents f943785252fc
children 48e3f67b5fe0
rev   line source
andrewm@0 1 /*
andrewm@0 2 TouchKeys: multi-touch musical keyboard control software
andrewm@0 3 Copyright (c) 2013 Andrew McPherson
andrewm@0 4
andrewm@0 5 This program is free software: you can redistribute it and/or modify
andrewm@0 6 it under the terms of the GNU General Public License as published by
andrewm@0 7 the Free Software Foundation, either version 3 of the License, or
andrewm@0 8 (at your option) any later version.
andrewm@0 9
andrewm@0 10 This program is distributed in the hope that it will be useful,
andrewm@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
andrewm@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andrewm@0 13 GNU General Public License for more details.
andrewm@0 14
andrewm@0 15 You should have received a copy of the GNU General Public License
andrewm@0 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
andrewm@0 17
andrewm@0 18 =====================================================================
andrewm@0 19
andrewm@0 20 Osc.h: classes for handling reception and transmission of OSC messages,
andrewm@0 21 using the liblo library.
andrewm@0 22 */
andrewm@0 23
andrewm@0 24 #ifndef OSC_H
andrewm@0 25 #define OSC_H
andrewm@0 26
andrewm@20 27 #include <cstdint>
andrewm@0 28 #include <iostream>
andrewm@0 29 #include <iomanip>
andrewm@0 30 #include <fstream>
andrewm@0 31 #include <set>
andrewm@0 32 #include <map>
andrewm@0 33 #include <string>
andrewm@0 34 #include <vector>
andrewm@0 35 #include <set>
andrewm@20 36 #include "lo/lo.h"
andrewm@0 37 #include "../JuceLibraryCode/JuceHeader.h"
andrewm@0 38
andrewm@0 39 using namespace std;
andrewm@0 40
andrewm@0 41 class OscMessageSource;
andrewm@0 42
andrewm@0 43 // This is an abstract base class implementing a single function oscHandlerMethod(). Objects that
andrewm@0 44 // want to register to receive OSC messages should inherit from OscHandler. Notice that all listener
andrewm@0 45 // add/remove methods are private or protected. The subclass of OscHandler should add any relevant
andrewm@0 46 // listeners, or optionally expose a public interface to add listeners. (Never call the methods in
andrewm@0 47 // OscMessageSource externally.)
andrewm@0 48
andrewm@0 49 class OscHandler
andrewm@0 50 {
andrewm@0 51 public:
andrewm@0 52 OscHandler() : oscController_(NULL) {}
andrewm@0 53
andrewm@0 54 // The OSC controller will call this method when it gets a matching message that's been registered
andrewm@0 55 virtual bool oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) = 0;
andrewm@0 56 void setOscController(OscMessageSource *c) { oscController_ = c; }
andrewm@0 57
andrewm@0 58 virtual ~OscHandler(); // In the destructor, remove all OSC listeners
andrewm@0 59 protected:
andrewm@0 60 bool addOscListener(const string& path);
andrewm@0 61 bool removeOscListener(const string& path);
andrewm@0 62 bool removeAllOscListeners();
andrewm@0 63
andrewm@0 64 OscMessageSource *oscController_;
andrewm@0 65 set<string> oscListenerPaths_;
andrewm@0 66 };
andrewm@0 67
andrewm@0 68 // Base class for anything that acts as a source of OSC messages. Could be
andrewm@0 69 // received externally or internally generated.
andrewm@0 70
andrewm@0 71 class OscMessageSource
andrewm@0 72 {
andrewm@0 73 friend class OscHandler;
andrewm@0 74
andrewm@0 75 public:
andrewm@0 76 OscMessageSource() {}
andrewm@0 77
andrewm@0 78 protected:
andrewm@9 79 bool addListener(const string& path, OscHandler *object,
andrewm@9 80 bool matchSubpath = false); // Add a listener object for a specific path
andrewm@0 81 bool removeListener(const string& path, OscHandler *object); // Remove a listener object from a specific path
andrewm@0 82 bool removeListener(OscHandler *object); // Remove a listener object from all paths
andrewm@0 83
andrewm@0 84 void updateListeners(); // Propagate changes to the listeners to the main object
andrewm@0 85
andrewm@0 86 //ReadWriteLock oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
andrewm@0 87 CriticalSection oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
andrewm@0 88 CriticalSection oscUpdaterMutex_; // This mutex controls the insertion of objects in add/removeListener
andrewm@0 89
andrewm@0 90 multimap<string, OscHandler*> noteListeners_; // Map from OSC path name to handler (possibly multiple handlers per object)
andrewm@0 91 multimap<string, OscHandler*> noteListenersToAdd_; // Collection of listeners to add on the next cycle
andrewm@0 92 multimap<string, OscHandler*> noteListenersToRemove_; // Collection of listeners to remove on the next cycle
andrewm@0 93 set<OscHandler*> noteListenersForBlanketRemoval_; // Collection of listeners to remove from all paths
andrewm@0 94 };
andrewm@0 95
andrewm@0 96 // This class specifically implements OSC messages coming from external sources
andrewm@0 97
andrewm@0 98 class OscReceiver : public OscMessageSource
andrewm@0 99 {
andrewm@0 100 public:
andrewm@6 101 OscReceiver(const int port, const char *prefix) {
andrewm@6 102 globalPrefix_.assign(prefix);
andrewm@0 103 useThru_ = false;
andrewm@6 104
andrewm@6 105 // Only start the server if the port is positive
andrewm@6 106 if(port > 0) {
andrewm@6 107 char portStr[16];
andrewm@20 108 #ifdef _MSC_VER
andrewm@20 109 _snprintf_s(portStr, 16, _TRUNCATE, "%d", port);
andrewm@20 110 #else
andrewm@6 111 snprintf(portStr, 16, "%d", port);
andrewm@20 112 #endif
andrewm@20 113
andrewm@6 114 oscServerThread_ = lo_server_thread_new(portStr, staticErrorHandler);
andrewm@6 115 if(oscServerThread_ != 0) {
andrewm@6 116 lo_server_thread_add_method(oscServerThread_, NULL, NULL, OscReceiver::staticHandler, (void *)this);
andrewm@6 117 lo_server_thread_start(oscServerThread_);
andrewm@6 118 }
andrewm@6 119 }
andrewm@6 120 else
andrewm@6 121 oscServerThread_ = 0;
andrewm@6 122 }
andrewm@0 123
andrewm@0 124 void setThruAddress(lo_address thruAddr, const char *prefix) {
andrewm@0 125 thruAddress_ = thruAddr;
andrewm@0 126 thruPrefix_.assign(prefix);
andrewm@0 127 useThru_ = true;
andrewm@0 128 }
andrewm@6 129
andrewm@6 130 // Check whether the server is operating
andrewm@6 131 bool running() { return (oscServerThread_ != 0); }
andrewm@6 132
andrewm@6 133 // Get or set the current port. Setting the port requires restarting the server.
andrewm@6 134 // setPort() returns true on success; false if an error occurred (which will leave the server not running).
andrewm@6 135 const int port() {
andrewm@6 136 if(oscServerThread_ == 0)
andrewm@6 137 return 0;
andrewm@6 138 return lo_server_get_port(oscServerThread_);
andrewm@6 139 }
andrewm@6 140 bool setPort(const int port);
andrewm@0 141
andrewm@0 142 // staticHandler() is called by liblo with new OSC messages. Its only function is to pass control
andrewm@0 143 // to the object-specific handler method, which has access to all internal variables.
andrewm@0 144
andrewm@0 145 int handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *data);
andrewm@0 146 static int staticHandler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *userData) {
andrewm@0 147 return ((OscReceiver *)userData)->handler(path, types, argv, argc, msg, userData);
andrewm@6 148 }
andrewm@6 149
andrewm@6 150 // staticErrorHandler() is called by liblo when an error occurs. For now, ignore errors.
andrewm@6 151
andrewm@6 152 static void staticErrorHandler(int num, const char *msg, const char *path) {}
andrewm@0 153
andrewm@0 154 ~OscReceiver() {
andrewm@6 155 if(oscServerThread_ != 0) {
andrewm@6 156 lo_server_thread_del_method(oscServerThread_, NULL, NULL);
andrewm@6 157 lo_server_thread_stop(oscServerThread_);
andrewm@6 158 lo_server_thread_free(oscServerThread_);
andrewm@6 159 }
andrewm@6 160 }
andrewm@0 161
andrewm@0 162 private:
andrewm@0 163 lo_server_thread oscServerThread_; // Thread that handles received OSC messages
andrewm@0 164
andrewm@0 165 // OSC thru
andrewm@0 166 bool useThru_; // Whether or not we retransmit any messages
andrewm@0 167 lo_address thruAddress_; // Address to which we retransmit
andrewm@0 168 string thruPrefix_; // Prefix that must be matched to be retransmitted
andrewm@0 169
andrewm@0 170 // State variables
andrewm@0 171 string globalPrefix_; // Prefix for all OSC paths
andrewm@0 172 };
andrewm@0 173
andrewm@0 174 class OscTransmitter
andrewm@0 175 {
andrewm@0 176 public:
andrewm@0 177 OscTransmitter() : enabled_(true), debugMessages_(false) {}
andrewm@0 178
andrewm@0 179 // Enable or disable transmission
andrewm@0 180 void setEnabled(bool enable) { enabled_ = enable; }
andrewm@0 181 bool enabled() { return enabled_; }
andrewm@0 182
andrewm@0 183 // Add and remove addresses to send to
andrewm@0 184 int addAddress(const char * host, const char * port, int proto = LO_UDP);
andrewm@0 185 void removeAddress(int index);
andrewm@0 186 void clearAddresses();
andrewm@0 187 vector<lo_address> addresses() { return addresses_; }
andrewm@0 188
andrewm@0 189 void sendMessage(const char * path, const char * type, ...);
andrewm@0 190 void sendMessage(const char * path, const char * type, const lo_message& message);
andrewm@0 191 void sendByteArray(const char * path, const unsigned char * data, int length);
andrewm@0 192
andrewm@0 193 void setDebugMessages(bool debug) { debugMessages_ = debug; }
andrewm@0 194
andrewm@0 195 ~OscTransmitter();
andrewm@0 196
andrewm@0 197 private:
andrewm@0 198 vector<lo_address> addresses_;
andrewm@0 199 bool enabled_;
andrewm@0 200 bool debugMessages_;
andrewm@0 201 };
andrewm@0 202
andrewm@0 203 #endif // OSC_H