Mercurial > hg > touchkeys
view Source/TouchKeys/Osc.cpp @ 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 | 90ce403d0dc5 |
line wrap: on
line source
/* TouchKeys: multi-touch musical keyboard control software Copyright (c) 2013 Andrew McPherson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ===================================================================== Osc.cpp: classes for handling reception and transmission of OSC messages, using the liblo library. */ #include "Osc.h" #undef DEBUG_OSC #pragma mark OscHandler OscHandler::~OscHandler() { if(oscController_ != NULL) // Remove (individually) each listener { set<string>::iterator it; for(it = oscListenerPaths_.begin(); it != oscListenerPaths_.end(); ++it) { #ifdef DEBUG_OSC cout << "Deleting path " << *it << endl; #endif string pathToRemove = *it; oscController_->removeListener(pathToRemove, this); } } } #pragma mark -- Private Methods // Call this internal method to add a listener to the OSC controller. Returns true on success. bool OscHandler::addOscListener(const string& path) { if(oscController_ == NULL) return false; if(oscListenerPaths_.count(path) > 0) return false; oscListenerPaths_.insert(path); oscController_->addListener(path, this); return true; } bool OscHandler::removeOscListener(const string& path) { if(oscController_ == NULL) return false; if(oscListenerPaths_.count(path) == 0) return false; oscController_->removeListener(path, this); oscListenerPaths_.erase(path); return true; } bool OscHandler::removeAllOscListeners() { if(oscController_ == NULL) return false; set<string>::iterator it = oscListenerPaths_.begin(); while(it != oscListenerPaths_.end()) { removeOscListener(*it++); } return true; } #pragma mark OscMessageSource // Adds a specific object listening for a specific OSC message. The object will be // added to the internal map from strings to objects. All messages are preceded by // a global prefix (typically "/mrp"). Returns true on success. bool OscMessageSource::addListener(const string& path, OscHandler *object, bool matchSubpath) { if(object == NULL) return false; #ifdef OLD_OSC_MESSAGE_SOURCE double before = Time::getMillisecondCounterHiRes(); oscListenerMutex_.enterWrite(); cout << "addListener(): took " << Time::getMillisecondCounterHiRes() - before << "ms to acquire mutex\n"; noteListeners_.insert(pair<string, OscHandler*>(path, object)); oscListenerMutex_.exitWrite(); #else ScopedLock sl(oscUpdaterMutex_); // Add this object to the insertion list noteListenersToAdd_.insert(std::pair<string, OscHandler*>(path, object)); #endif #ifdef DEBUG_OSC cout << "Added OSC listener to path '" << path << "'\n"; #endif return true; } // Removes a specific object from listening to a specific OSC message. // Returns true if at least one path was removed. bool OscMessageSource::removeListener(const string& path, OscHandler *object) { if(object == NULL) return false; bool removedAny = false; #ifdef OLD_OSC_MESSAGE_SOURCE oscListenerMutex_.enterWrite(); // Lock the mutex so no incoming messages happen in the middle multimap<string, OscHandler*>::iterator it; pair<multimap<string, OscHandler*>::iterator,multimap<string, OscHandler*>::iterator> ret; // Every time we remove an element from the multimap, the iterator is potentially corrupted. Realistically // there should never be more than one entry with the same object and same path (we check this on insertion). ret = noteListeners_.equal_range(path); it = ret.first; while(it != ret.second) { if(it->second == object) { noteListeners_.erase(it++); removedAny = true; break; } else ++it; } oscListenerMutex_.exitWrite(); #else ScopedLock sl(oscUpdaterMutex_); // Add this object to the removal list noteListenersToRemove_.insert(std::pair<string, OscHandler*>(path, object)); // Also remove this object from anything on the add list, so it doesn't // get put back in by a previous add call. pair<multimap<string, OscHandler*>::iterator,multimap<string, OscHandler*>::iterator> ret; multimap<string, OscHandler*>::iterator it; ret = noteListenersToAdd_.equal_range(path); it = ret.first; while(it != ret.second) { if(it->second == object) { noteListenersToAdd_.erase(it++); //break; } else ++it; } removedAny = true; // FIXME: do we still need this? #endif #ifdef DEBUG_OSC if(removedAny) cout << "Removed OSC listener from path '" << path << "'\n"; else cout << "Removal failed to find OSC listener on path '" << path << "'\n"; #endif return removedAny; } // Removes an object from all OSC messages it was listening to. Returns true if object // was found and removed. bool OscMessageSource::removeListener(OscHandler *object) { if(object == NULL) return false; bool removedAny = false; #ifdef OLD_OSC_MESSAGE_SOURCE oscListenerMutex_.enterWrite(); // Lock the mutex so no incoming messages happen in the middle multimap<string, OscHandler*>::iterator it; // Every time we remove an element from the multimap, the iterator is potentially corrupted. Realistically // there should never be more than one entry with the same object and same path (we check this on insertion). it = noteListeners_.begin(); while(it != noteListeners_.end()) { if(it->second == object) { noteListeners_.erase(it++); removedAny = true; //break; } else ++it; } oscListenerMutex_.exitWrite(); #else ScopedLock sl(oscUpdaterMutex_); // Add this object to the removal list noteListenersForBlanketRemoval_.insert(object); // Also remove this object from anything on the add list, so it doesn't // get put back in by a previous add call. multimap<string, OscHandler*>::iterator it; it = noteListenersToAdd_.begin(); while(it != noteListenersToAdd_.end()) { if(it->second == object) { noteListenersToAdd_.erase(it++); } else ++it; } removedAny = true; // FIXME: do we still need this? #endif #ifdef DEBUG_OSC if(removedAny) cout << "Removed OSC listener from all paths\n"; else cout << "Removal failed to find OSC listener on any path\n"; #endif return removedAny; } // Propagate changes to the listeners to the main noteListeners_ object void OscMessageSource::updateListeners() { ScopedLock sl2(oscListenerMutex_); ScopedLock sl(oscUpdaterMutex_); multimap<string, OscHandler*>::iterator it; // Step 1: remove any objects that need complete removal from all paths set<OscHandler*>::iterator blanketRemovalIterator; for(blanketRemovalIterator = noteListenersForBlanketRemoval_.begin(); blanketRemovalIterator != noteListenersForBlanketRemoval_.end(); ++blanketRemovalIterator) { it = noteListeners_.begin(); while(it != noteListeners_.end()) { if(it->second == *blanketRemovalIterator) { noteListeners_.erase(it++); } else ++it; } } // Step 2: remove any specific path listeners for(it = noteListenersToRemove_.begin(); it != noteListenersToRemove_.end(); ++it) { pair<multimap<string, OscHandler*>::iterator,multimap<string, OscHandler*>::iterator> ret; multimap<string, OscHandler*>::iterator it2; string const& path = it->first; OscHandler *object = it->second; // Find all the objects that match this string and remove ones that correspond to this particular OscHandler ret = noteListeners_.equal_range(path); it2 = ret.first; while(it2 != ret.second) { if(it2->second == object) { noteListeners_.erase(it2++); //break; } else ++it2; } } // Step 3: add any listeners for(it = noteListenersToAdd_.begin(); it != noteListenersToAdd_.end(); ++it) { noteListeners_.insert(pair<string, OscHandler*>(it->first, it->second)); } // Step 4: clear the buffers of pending listeners noteListenersForBlanketRemoval_.clear(); noteListenersToRemove_.clear(); noteListenersToAdd_.clear(); } #pragma mark OscReceiver // OscReceiver::handler() // The main handler method for incoming OSC messages. From here, we farm out the processing depending // on the path. Return 0 if the message has been adequately handled, 1 otherwise (so the server can look // for other functions to pass it to). int OscReceiver::handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *data) { bool matched = false; string pathString(path); if(useThru_) { // Rebroadcast any matching messages if(!pathString.compare(0, thruPrefix_.length(), thruPrefix_)) lo_send_message(thruAddress_, path, msg); } // Check if the incoming message matches the global prefix for this program. If not, discard it. if(pathString.compare(0, globalPrefix_.length(), globalPrefix_)) { #ifdef DEBUG_OSC cout << "OSC message '" << path << "' received\n"; #endif return 1; } // Update the list of OSC listeners to propagate any changes updateListeners(); // Lock the mutex so the list of listeners doesn't change midway through oscListenerMutex_.enter(); // Now remove the global prefix and compare the rest of the message to the registered handlers. multimap<string, OscHandler*>::iterator it; pair<multimap<string, OscHandler*>::iterator,multimap<string, OscHandler*>::iterator> ret; string truncatedPath = pathString.substr(globalPrefix_.length(), pathString.length() - globalPrefix_.length()); string subpath = truncatedPath; ret = noteListeners_.equal_range(truncatedPath); while(ret.first == ret.second) { // No handlers match this range. But maybe there are higher-level handlers // that match all subpaths. // Strip off the last component of the path int pathSeparator = subpath.find_last_of('/'); if(pathSeparator == string::npos) // Not found --> no match break; else { // Reduce string by one path level and add *; compare again subpath = subpath.substr(0, pathSeparator); subpath.push_back('*'); ret = noteListeners_.equal_range(subpath); } } it = ret.first; while(it != ret.second) { OscHandler *object = (*it++).second; #ifdef DEBUG_OSC cout << "Matched OSC path '" << path << "' to handler " << object << endl; #endif object->oscHandlerMethod(truncatedPath.c_str(), types, argc, argv, data); matched = true; } oscListenerMutex_.exit(); if(matched) // This message has been handled return 0; #ifdef DEBUG_OSC printf("Unhandled OSC path: <%s>\n", path); for (int i=0; i<argc; i++) { printf("arg %d '%c' ", i, types[i]); lo_arg_pp((lo_type)types[i], argv[i]); printf("\n"); } #endif return 1; } // Set the current port for the OSC receiver object. This implies stopping and // restarting the server. Returns true on success. bool OscReceiver::setPort(const int port) { // Stop existing server if running if(oscServerThread_ != 0) { lo_server_thread_del_method(oscServerThread_, NULL, NULL); lo_server_thread_stop(oscServerThread_); lo_server_thread_free(oscServerThread_); oscServerThread_ = 0; } // Port value 0 indicates to turn off; this always succeeds. if(port == 0) { return true; } // Now create a new one on the new port char portStr[16]; #ifdef _MSC_VER _snprintf_s(portStr, 16, _TRUNCATE, "%d", port); #else snprintf(portStr, 16, "%d", port); #endif oscServerThread_ = lo_server_thread_new(portStr, staticErrorHandler); if(oscServerThread_ != 0) { lo_server_thread_add_method(oscServerThread_, NULL, NULL, OscReceiver::staticHandler, (void *)this); lo_server_thread_start(oscServerThread_); return true; } return false; } #pragma mark OscTransmitter // Add a new transmit address. Returns the index of the new address. int OscTransmitter::addAddress(const char * host, const char * port, int proto) { lo_address addr = lo_address_new_with_proto(proto, host, port); if(addr == 0) return -1; addresses_.push_back(addr); return (int)addresses_.size() - 1; } // Delete a current transmit address void OscTransmitter::removeAddress(int index) { if(index >= addresses_.size() || index < 0) return; addresses_.erase(addresses_.begin() + index); } // Delete all destination addresses void OscTransmitter::clearAddresses() { vector<lo_address>::iterator it = addresses_.begin(); while(it != addresses_.end()) { lo_address_free(*it++); } addresses_.clear(); } void OscTransmitter::sendMessage(const char * path, const char * type, ...) { if(!enabled_) return; va_list v; va_start(v, type); lo_message msg = lo_message_new(); lo_message_add_varargs(msg, type, v); /*if(debugMessages_) { cout << path << " " << type << ": "; lo_arg **args = lo_message_get_argv(msg); for(int i = 0; i < lo_message_get_argc(msg); i++) { switch(type[i]) { case 'i': cout << args[i]->i << " "; break; case 'f': cout << args[i]->f << " "; break; default: cout << "? "; } } cout << endl; //lo_message_pp(msg); }*/ sendMessage(path, type, msg); lo_message_free(msg); va_end(v); } void OscTransmitter::sendMessage(const char * path, const char * type, const lo_message& message) { if(!enabled_) return; if(debugMessages_) { cout << path << " " << type << " "; int argc = lo_message_get_argc(message); lo_arg **argv = lo_message_get_argv(message); for (int i=0; i<argc; i++) { lo_arg_pp((lo_type)type[i], argv[i]); cout << " "; } cout << endl; } // Send message to everyone who's currently listening for(vector<lo_address>::iterator it = addresses_.begin(); it != addresses_.end(); it++) { lo_send_message(*it, path, message); } } // Send an array of bytes as an OSC message. Bytes will be sent as a blob. void OscTransmitter::sendByteArray(const char * path, const unsigned char * data, int length) { if(!enabled_) return; if(length == 0) return; lo_blob b = lo_blob_new(length, data); lo_message msg = lo_message_new(); lo_message_add_blob(msg, b); if(debugMessages_) { cout << path << " "; lo_message_pp(msg); } // Send message to everyone who's currently listening for(vector<lo_address>::iterator it = addresses_.begin(); it != addresses_.end(); it++) { lo_send_message(*it, path, msg); } lo_blob_free(b); } OscTransmitter::~OscTransmitter() { clearAddresses(); }