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@0
|
27 #include <iostream>
|
andrewm@0
|
28 #include <iomanip>
|
andrewm@0
|
29 #include <fstream>
|
andrewm@0
|
30 #include <set>
|
andrewm@0
|
31 #include <map>
|
andrewm@0
|
32 #include <string>
|
andrewm@0
|
33 #include <vector>
|
andrewm@0
|
34 #include <set>
|
andrewm@0
|
35 #include "../JuceLibraryCode/JuceHeader.h"
|
andrewm@0
|
36 #include "lo/lo.h"
|
andrewm@0
|
37
|
andrewm@0
|
38 using namespace std;
|
andrewm@0
|
39
|
andrewm@0
|
40 class OscMessageSource;
|
andrewm@0
|
41
|
andrewm@0
|
42 // This is an abstract base class implementing a single function oscHandlerMethod(). Objects that
|
andrewm@0
|
43 // want to register to receive OSC messages should inherit from OscHandler. Notice that all listener
|
andrewm@0
|
44 // add/remove methods are private or protected. The subclass of OscHandler should add any relevant
|
andrewm@0
|
45 // listeners, or optionally expose a public interface to add listeners. (Never call the methods in
|
andrewm@0
|
46 // OscMessageSource externally.)
|
andrewm@0
|
47
|
andrewm@0
|
48 class OscHandler
|
andrewm@0
|
49 {
|
andrewm@0
|
50 public:
|
andrewm@0
|
51 OscHandler() : oscController_(NULL) {}
|
andrewm@0
|
52
|
andrewm@0
|
53 // The OSC controller will call this method when it gets a matching message that's been registered
|
andrewm@0
|
54 virtual bool oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) = 0;
|
andrewm@0
|
55 void setOscController(OscMessageSource *c) { oscController_ = c; }
|
andrewm@0
|
56
|
andrewm@0
|
57 virtual ~OscHandler(); // In the destructor, remove all OSC listeners
|
andrewm@0
|
58 protected:
|
andrewm@0
|
59 bool addOscListener(const string& path);
|
andrewm@0
|
60 bool removeOscListener(const string& path);
|
andrewm@0
|
61 bool removeAllOscListeners();
|
andrewm@0
|
62
|
andrewm@0
|
63 OscMessageSource *oscController_;
|
andrewm@0
|
64 set<string> oscListenerPaths_;
|
andrewm@0
|
65 };
|
andrewm@0
|
66
|
andrewm@0
|
67 // Base class for anything that acts as a source of OSC messages. Could be
|
andrewm@0
|
68 // received externally or internally generated.
|
andrewm@0
|
69
|
andrewm@0
|
70 class OscMessageSource
|
andrewm@0
|
71 {
|
andrewm@0
|
72 friend class OscHandler;
|
andrewm@0
|
73
|
andrewm@0
|
74 public:
|
andrewm@0
|
75 OscMessageSource() {}
|
andrewm@0
|
76
|
andrewm@0
|
77 protected:
|
andrewm@9
|
78 bool addListener(const string& path, OscHandler *object,
|
andrewm@9
|
79 bool matchSubpath = false); // Add a listener object for a specific path
|
andrewm@0
|
80 bool removeListener(const string& path, OscHandler *object); // Remove a listener object from a specific path
|
andrewm@0
|
81 bool removeListener(OscHandler *object); // Remove a listener object from all paths
|
andrewm@0
|
82
|
andrewm@0
|
83 void updateListeners(); // Propagate changes to the listeners to the main object
|
andrewm@0
|
84
|
andrewm@0
|
85 //ReadWriteLock oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
|
andrewm@0
|
86 CriticalSection oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
|
andrewm@0
|
87 CriticalSection oscUpdaterMutex_; // This mutex controls the insertion of objects in add/removeListener
|
andrewm@0
|
88
|
andrewm@0
|
89 multimap<string, OscHandler*> noteListeners_; // Map from OSC path name to handler (possibly multiple handlers per object)
|
andrewm@0
|
90 multimap<string, OscHandler*> noteListenersToAdd_; // Collection of listeners to add on the next cycle
|
andrewm@0
|
91 multimap<string, OscHandler*> noteListenersToRemove_; // Collection of listeners to remove on the next cycle
|
andrewm@0
|
92 set<OscHandler*> noteListenersForBlanketRemoval_; // Collection of listeners to remove from all paths
|
andrewm@0
|
93 };
|
andrewm@0
|
94
|
andrewm@0
|
95 // This class specifically implements OSC messages coming from external sources
|
andrewm@0
|
96
|
andrewm@0
|
97 class OscReceiver : public OscMessageSource
|
andrewm@0
|
98 {
|
andrewm@0
|
99 public:
|
andrewm@6
|
100 OscReceiver(const int port, const char *prefix) {
|
andrewm@6
|
101 globalPrefix_.assign(prefix);
|
andrewm@0
|
102 useThru_ = false;
|
andrewm@6
|
103
|
andrewm@6
|
104 // Only start the server if the port is positive
|
andrewm@6
|
105 if(port > 0) {
|
andrewm@6
|
106 char portStr[16];
|
andrewm@6
|
107 snprintf(portStr, 16, "%d", port);
|
andrewm@6
|
108
|
andrewm@6
|
109 oscServerThread_ = lo_server_thread_new(portStr, staticErrorHandler);
|
andrewm@6
|
110 if(oscServerThread_ != 0) {
|
andrewm@6
|
111 lo_server_thread_add_method(oscServerThread_, NULL, NULL, OscReceiver::staticHandler, (void *)this);
|
andrewm@6
|
112 lo_server_thread_start(oscServerThread_);
|
andrewm@6
|
113 }
|
andrewm@6
|
114 }
|
andrewm@6
|
115 else
|
andrewm@6
|
116 oscServerThread_ = 0;
|
andrewm@6
|
117 }
|
andrewm@0
|
118
|
andrewm@0
|
119 void setThruAddress(lo_address thruAddr, const char *prefix) {
|
andrewm@0
|
120 thruAddress_ = thruAddr;
|
andrewm@0
|
121 thruPrefix_.assign(prefix);
|
andrewm@0
|
122 useThru_ = true;
|
andrewm@0
|
123 }
|
andrewm@6
|
124
|
andrewm@6
|
125 // Check whether the server is operating
|
andrewm@6
|
126 bool running() { return (oscServerThread_ != 0); }
|
andrewm@6
|
127
|
andrewm@6
|
128 // Get or set the current port. Setting the port requires restarting the server.
|
andrewm@6
|
129 // setPort() returns true on success; false if an error occurred (which will leave the server not running).
|
andrewm@6
|
130 const int port() {
|
andrewm@6
|
131 if(oscServerThread_ == 0)
|
andrewm@6
|
132 return 0;
|
andrewm@6
|
133 return lo_server_get_port(oscServerThread_);
|
andrewm@6
|
134 }
|
andrewm@6
|
135 bool setPort(const int port);
|
andrewm@0
|
136
|
andrewm@0
|
137 // staticHandler() is called by liblo with new OSC messages. Its only function is to pass control
|
andrewm@0
|
138 // to the object-specific handler method, which has access to all internal variables.
|
andrewm@0
|
139
|
andrewm@0
|
140 int handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *data);
|
andrewm@0
|
141 static int staticHandler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *userData) {
|
andrewm@0
|
142 return ((OscReceiver *)userData)->handler(path, types, argv, argc, msg, userData);
|
andrewm@6
|
143 }
|
andrewm@6
|
144
|
andrewm@6
|
145 // staticErrorHandler() is called by liblo when an error occurs. For now, ignore errors.
|
andrewm@6
|
146
|
andrewm@6
|
147 static void staticErrorHandler(int num, const char *msg, const char *path) {}
|
andrewm@0
|
148
|
andrewm@0
|
149 ~OscReceiver() {
|
andrewm@6
|
150 if(oscServerThread_ != 0) {
|
andrewm@6
|
151 lo_server_thread_del_method(oscServerThread_, NULL, NULL);
|
andrewm@6
|
152 lo_server_thread_stop(oscServerThread_);
|
andrewm@6
|
153 lo_server_thread_free(oscServerThread_);
|
andrewm@6
|
154 }
|
andrewm@6
|
155 }
|
andrewm@0
|
156
|
andrewm@0
|
157 private:
|
andrewm@0
|
158 lo_server_thread oscServerThread_; // Thread that handles received OSC messages
|
andrewm@0
|
159
|
andrewm@0
|
160 // OSC thru
|
andrewm@0
|
161 bool useThru_; // Whether or not we retransmit any messages
|
andrewm@0
|
162 lo_address thruAddress_; // Address to which we retransmit
|
andrewm@0
|
163 string thruPrefix_; // Prefix that must be matched to be retransmitted
|
andrewm@0
|
164
|
andrewm@0
|
165 // State variables
|
andrewm@0
|
166 string globalPrefix_; // Prefix for all OSC paths
|
andrewm@0
|
167 };
|
andrewm@0
|
168
|
andrewm@0
|
169 class OscTransmitter
|
andrewm@0
|
170 {
|
andrewm@0
|
171 public:
|
andrewm@0
|
172 OscTransmitter() : enabled_(true), debugMessages_(false) {}
|
andrewm@0
|
173
|
andrewm@0
|
174 // Enable or disable transmission
|
andrewm@0
|
175 void setEnabled(bool enable) { enabled_ = enable; }
|
andrewm@0
|
176 bool enabled() { return enabled_; }
|
andrewm@0
|
177
|
andrewm@0
|
178 // Add and remove addresses to send to
|
andrewm@0
|
179 int addAddress(const char * host, const char * port, int proto = LO_UDP);
|
andrewm@0
|
180 void removeAddress(int index);
|
andrewm@0
|
181 void clearAddresses();
|
andrewm@0
|
182 vector<lo_address> addresses() { return addresses_; }
|
andrewm@0
|
183
|
andrewm@0
|
184 void sendMessage(const char * path, const char * type, ...);
|
andrewm@0
|
185 void sendMessage(const char * path, const char * type, const lo_message& message);
|
andrewm@0
|
186 void sendByteArray(const char * path, const unsigned char * data, int length);
|
andrewm@0
|
187
|
andrewm@0
|
188 void setDebugMessages(bool debug) { debugMessages_ = debug; }
|
andrewm@0
|
189
|
andrewm@0
|
190 ~OscTransmitter();
|
andrewm@0
|
191
|
andrewm@0
|
192 private:
|
andrewm@0
|
193 vector<lo_address> addresses_;
|
andrewm@0
|
194 bool enabled_;
|
andrewm@0
|
195 bool debugMessages_;
|
andrewm@0
|
196 };
|
andrewm@0
|
197
|
andrewm@0
|
198 #endif // OSC_H |