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@0
|
78 bool addListener(const string& path, OscHandler *object); // Add a listener object for a specific path
|
andrewm@0
|
79 bool removeListener(const string& path, OscHandler *object); // Remove a listener object from a specific path
|
andrewm@0
|
80 bool removeListener(OscHandler *object); // Remove a listener object from all paths
|
andrewm@0
|
81
|
andrewm@0
|
82 void updateListeners(); // Propagate changes to the listeners to the main object
|
andrewm@0
|
83
|
andrewm@0
|
84 //ReadWriteLock oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
|
andrewm@0
|
85 CriticalSection oscListenerMutex_; // This mutex protects the OSC listener table from being modified mid-message
|
andrewm@0
|
86 CriticalSection oscUpdaterMutex_; // This mutex controls the insertion of objects in add/removeListener
|
andrewm@0
|
87
|
andrewm@0
|
88 multimap<string, OscHandler*> noteListeners_; // Map from OSC path name to handler (possibly multiple handlers per object)
|
andrewm@0
|
89 multimap<string, OscHandler*> noteListenersToAdd_; // Collection of listeners to add on the next cycle
|
andrewm@0
|
90 multimap<string, OscHandler*> noteListenersToRemove_; // Collection of listeners to remove on the next cycle
|
andrewm@0
|
91 set<OscHandler*> noteListenersForBlanketRemoval_; // Collection of listeners to remove from all paths
|
andrewm@0
|
92 };
|
andrewm@0
|
93
|
andrewm@0
|
94 // This class specifically implements OSC messages coming from external sources
|
andrewm@0
|
95
|
andrewm@0
|
96 class OscReceiver : public OscMessageSource
|
andrewm@0
|
97 {
|
andrewm@0
|
98 public:
|
andrewm@6
|
99 OscReceiver(const int port, const char *prefix) {
|
andrewm@6
|
100 globalPrefix_.assign(prefix);
|
andrewm@0
|
101 useThru_ = false;
|
andrewm@6
|
102
|
andrewm@6
|
103 // Only start the server if the port is positive
|
andrewm@6
|
104 if(port > 0) {
|
andrewm@6
|
105 char portStr[16];
|
andrewm@6
|
106 snprintf(portStr, 16, "%d", port);
|
andrewm@6
|
107
|
andrewm@6
|
108 oscServerThread_ = lo_server_thread_new(portStr, staticErrorHandler);
|
andrewm@6
|
109 if(oscServerThread_ != 0) {
|
andrewm@6
|
110 lo_server_thread_add_method(oscServerThread_, NULL, NULL, OscReceiver::staticHandler, (void *)this);
|
andrewm@6
|
111 lo_server_thread_start(oscServerThread_);
|
andrewm@6
|
112 }
|
andrewm@6
|
113 }
|
andrewm@6
|
114 else
|
andrewm@6
|
115 oscServerThread_ = 0;
|
andrewm@6
|
116 }
|
andrewm@0
|
117
|
andrewm@0
|
118 void setThruAddress(lo_address thruAddr, const char *prefix) {
|
andrewm@0
|
119 thruAddress_ = thruAddr;
|
andrewm@0
|
120 thruPrefix_.assign(prefix);
|
andrewm@0
|
121 useThru_ = true;
|
andrewm@0
|
122 }
|
andrewm@6
|
123
|
andrewm@6
|
124 // Check whether the server is operating
|
andrewm@6
|
125 bool running() { return (oscServerThread_ != 0); }
|
andrewm@6
|
126
|
andrewm@6
|
127 // Get or set the current port. Setting the port requires restarting the server.
|
andrewm@6
|
128 // setPort() returns true on success; false if an error occurred (which will leave the server not running).
|
andrewm@6
|
129 const int port() {
|
andrewm@6
|
130 if(oscServerThread_ == 0)
|
andrewm@6
|
131 return 0;
|
andrewm@6
|
132 return lo_server_get_port(oscServerThread_);
|
andrewm@6
|
133 }
|
andrewm@6
|
134 bool setPort(const int port);
|
andrewm@0
|
135
|
andrewm@0
|
136 // staticHandler() is called by liblo with new OSC messages. Its only function is to pass control
|
andrewm@0
|
137 // to the object-specific handler method, which has access to all internal variables.
|
andrewm@0
|
138
|
andrewm@0
|
139 int handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *data);
|
andrewm@0
|
140 static int staticHandler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *userData) {
|
andrewm@0
|
141 return ((OscReceiver *)userData)->handler(path, types, argv, argc, msg, userData);
|
andrewm@6
|
142 }
|
andrewm@6
|
143
|
andrewm@6
|
144 // staticErrorHandler() is called by liblo when an error occurs. For now, ignore errors.
|
andrewm@6
|
145
|
andrewm@6
|
146 static void staticErrorHandler(int num, const char *msg, const char *path) {}
|
andrewm@0
|
147
|
andrewm@0
|
148 ~OscReceiver() {
|
andrewm@6
|
149 if(oscServerThread_ != 0) {
|
andrewm@6
|
150 lo_server_thread_del_method(oscServerThread_, NULL, NULL);
|
andrewm@6
|
151 lo_server_thread_stop(oscServerThread_);
|
andrewm@6
|
152 lo_server_thread_free(oscServerThread_);
|
andrewm@6
|
153 }
|
andrewm@6
|
154 }
|
andrewm@0
|
155
|
andrewm@0
|
156 private:
|
andrewm@0
|
157 lo_server_thread oscServerThread_; // Thread that handles received OSC messages
|
andrewm@0
|
158
|
andrewm@0
|
159 // OSC thru
|
andrewm@0
|
160 bool useThru_; // Whether or not we retransmit any messages
|
andrewm@0
|
161 lo_address thruAddress_; // Address to which we retransmit
|
andrewm@0
|
162 string thruPrefix_; // Prefix that must be matched to be retransmitted
|
andrewm@0
|
163
|
andrewm@0
|
164 // State variables
|
andrewm@0
|
165 string globalPrefix_; // Prefix for all OSC paths
|
andrewm@0
|
166 };
|
andrewm@0
|
167
|
andrewm@0
|
168 class OscTransmitter
|
andrewm@0
|
169 {
|
andrewm@0
|
170 public:
|
andrewm@0
|
171 OscTransmitter() : enabled_(true), debugMessages_(false) {}
|
andrewm@0
|
172
|
andrewm@0
|
173 // Enable or disable transmission
|
andrewm@0
|
174 void setEnabled(bool enable) { enabled_ = enable; }
|
andrewm@0
|
175 bool enabled() { return enabled_; }
|
andrewm@0
|
176
|
andrewm@0
|
177 // Add and remove addresses to send to
|
andrewm@0
|
178 int addAddress(const char * host, const char * port, int proto = LO_UDP);
|
andrewm@0
|
179 void removeAddress(int index);
|
andrewm@0
|
180 void clearAddresses();
|
andrewm@0
|
181 vector<lo_address> addresses() { return addresses_; }
|
andrewm@0
|
182
|
andrewm@0
|
183 void sendMessage(const char * path, const char * type, ...);
|
andrewm@0
|
184 void sendMessage(const char * path, const char * type, const lo_message& message);
|
andrewm@0
|
185 void sendByteArray(const char * path, const unsigned char * data, int length);
|
andrewm@0
|
186
|
andrewm@0
|
187 void setDebugMessages(bool debug) { debugMessages_ = debug; }
|
andrewm@0
|
188
|
andrewm@0
|
189 ~OscTransmitter();
|
andrewm@0
|
190
|
andrewm@0
|
191 private:
|
andrewm@0
|
192 vector<lo_address> addresses_;
|
andrewm@0
|
193 bool enabled_;
|
andrewm@0
|
194 bool debugMessages_;
|
andrewm@0
|
195 };
|
andrewm@0
|
196
|
andrewm@0
|
197 #endif // OSC_H |