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@22
|
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@49
|
174 // Simple class to hold a message alongw ith a path and a type
|
andrewm@49
|
175 class OscMessage
|
andrewm@49
|
176 {
|
andrewm@49
|
177 public:
|
andrewm@49
|
178 OscMessage(const char *path, const char *type, lo_message& message)
|
andrewm@49
|
179 : path_(path), type_(type), message_(message) {}
|
andrewm@49
|
180
|
andrewm@49
|
181 ~OscMessage() {
|
andrewm@49
|
182 lo_message_free(message_);
|
andrewm@49
|
183 }
|
andrewm@49
|
184
|
andrewm@49
|
185 const char *path() { return path_.c_str(); }
|
andrewm@49
|
186 const char *type() { return type_.c_str(); }
|
andrewm@49
|
187 lo_message message() { return message_; }
|
andrewm@49
|
188
|
andrewm@49
|
189 // Add a prefix to the message path
|
andrewm@49
|
190 void prependPath(const char *prefix) {
|
andrewm@49
|
191 path_.insert(0, prefix); // TODO: check that this is right
|
andrewm@49
|
192 }
|
andrewm@49
|
193
|
andrewm@49
|
194 private:
|
andrewm@49
|
195 std::string path_;
|
andrewm@49
|
196 std::string type_;
|
andrewm@49
|
197 lo_message message_;
|
andrewm@49
|
198 };
|
andrewm@49
|
199
|
andrewm@49
|
200
|
andrewm@0
|
201 class OscTransmitter
|
andrewm@0
|
202 {
|
andrewm@0
|
203 public:
|
andrewm@0
|
204 OscTransmitter() : enabled_(true), debugMessages_(false) {}
|
andrewm@0
|
205
|
andrewm@0
|
206 // Enable or disable transmission
|
andrewm@0
|
207 void setEnabled(bool enable) { enabled_ = enable; }
|
andrewm@0
|
208 bool enabled() { return enabled_; }
|
andrewm@0
|
209
|
andrewm@0
|
210 // Add and remove addresses to send to
|
andrewm@0
|
211 int addAddress(const char * host, const char * port, int proto = LO_UDP);
|
andrewm@0
|
212 void removeAddress(int index);
|
andrewm@0
|
213 void clearAddresses();
|
andrewm@0
|
214 vector<lo_address> addresses() { return addresses_; }
|
andrewm@0
|
215
|
andrewm@0
|
216 void sendMessage(const char * path, const char * type, ...);
|
andrewm@0
|
217 void sendMessage(const char * path, const char * type, const lo_message& message);
|
andrewm@0
|
218 void sendByteArray(const char * path, const unsigned char * data, int length);
|
andrewm@0
|
219
|
andrewm@0
|
220 void setDebugMessages(bool debug) { debugMessages_ = debug; }
|
andrewm@0
|
221
|
andrewm@0
|
222 ~OscTransmitter();
|
andrewm@49
|
223
|
andrewm@49
|
224 // Static methods
|
andrewm@49
|
225 static OscMessage* createMessage(const char * path, const char * type, ...);
|
andrewm@49
|
226 static OscMessage* createSuccessMessage() { return createMessage("/result", "i", 0, LO_ARGS_END); }
|
andrewm@49
|
227 static OscMessage* createFailureMessage() { return createMessage("/result", "i", 1, LO_ARGS_END); }
|
andrewm@0
|
228
|
andrewm@0
|
229 private:
|
andrewm@0
|
230 vector<lo_address> addresses_;
|
andrewm@0
|
231 bool enabled_;
|
andrewm@0
|
232 bool debugMessages_;
|
andrewm@0
|
233 };
|
andrewm@0
|
234
|
andrewm@0
|
235 #endif // OSC_H |