├── .gitignore ├── Makefile ├── README.md ├── addons.make ├── scripts ├── linux │ └── install.sh ├── osx │ └── install.sh ├── package_release_osx.sh └── windows │ └── install.sh └── src ├── main.cpp ├── ofApp.cpp └── ofApp.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | local_addons 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Attempt to load a config.make file. 2 | # If none is found, project defaults in config.project.make will be used. 3 | ifneq ($(wildcard config.make),) 4 | include config.make 5 | endif 6 | 7 | # make sure the the OF_ROOT location is defined 8 | ifndef OF_ROOT 9 | OF_ROOT=../../.. 10 | endif 11 | 12 | # call the project makefile! 13 | include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ofOSCDebugger 2 | 3 | ## Description 4 | 5 | ofOSCDebugger is command line tool to monitor and send OSC messages. 6 | 7 | ## Build status 8 | [![Build Status](https://travis-ci.com/thomasgeissl/ofOSCDebugger.svg?branch=master)](https://travis-ci.com/thomasgeissl/ofOSCDebugger) 9 | 10 | ## Installation 11 | 12 | ### OSX 13 | 14 | On OSX you can install it via brew. 15 | 16 | ``` 17 | brew tap thomasgeissl/tools 18 | brew install ofoscdebugger 19 | # brew upgrade ofoscdebugger 20 | ``` 21 | 22 | This will download the most recent version of the OSC debugger and add it to your search path. 23 | 24 | ### Building 25 | 26 | And as always you can clone it to your openFrameworks apps directory and build it by running make. 27 | 28 | ## Usage 29 | 30 | - listen to incomig OSC messages: `ofOSCDebugger -p 8000` 31 | - send an OSC message: `ofOSCDebugger -h localhost -p 8000 -m "/address 0 0.0 zero false"` 32 | - add -i to send an OSC message and then go into the interactive mode to be able to enter a new message and send it to the specified receiver: `ofOSCDebugger -h localhost -p 8000 -m "/address 0 0.0 zero false" -i` 33 | - whitespaces in string args has to be written as `---`, e.g.: `ofOSCDebugger -h localhost -p 8000 -m "/address oneStringArg anotherStringArg first---second---third"` 34 | - listen to incomig OSC messages and save OSC sequence as json: `ofOSCDebugger -p 8000 -o data.json` 35 | - replay an OSC sequence: `ofOSCDebugger -h localhost -p 8000 -j data.json` 36 | - broadcast incoming messages: `ofOSCDebugger -p 8000 -b "localhost 8001 localhost 8002"` 37 | 38 | ## Changelog 39 | 40 | ### 1.2.0 41 | 42 | - added broadcast option 43 | 44 | ### 1.1.1 45 | 46 | - Fixed relative paths for input and output paths 47 | 48 | ### 1.1.0 49 | 50 | - Added support for string args that contain whitespaces 51 | - Added record and replay 52 | 53 | ### 1.0.0 54 | 55 | - First stable version 56 | 57 | ## License 58 | 59 | This software is distributed under the [MIT License](https://en.wikipedia.org/wiki/MIT_License), please note that dependencies might be released differently. 60 | 61 | Copyright (c) 2019 Thomas Geissl 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 64 | 65 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 68 | -------------------------------------------------------------------------------- /addons.make: -------------------------------------------------------------------------------- 1 | ofxOsc 2 | local_addons/ofxCommandLineUtils#https://github.com/thomasgeissl/ofxCommandLineUtils.git@7b0c2e182b2e05cee9edc7f32ca0da470c137b5b 3 | -------------------------------------------------------------------------------- /scripts/linux/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | OFVERSION=0.11.0 3 | OFPLATFORM=linux64gcc6 4 | GH_USER=$(dirname $TRAVIS_REPO_SLUG) 5 | GH_REPO=$(basename $TRAVIS_REPO_SLUG) 6 | 7 | curl -O https://openframeworks.cc/versions/v${OFVERSION}/of_v${OFVERSION}_${OFPLATFORM}_release.tar.gz 8 | tar -zxf of_v${OFVERSION}_${OFPLATFORM}_release.tar.gz 9 | rm of_v${OFVERSION}_${OFPLATFORM}_release.tar.gz 10 | sudo of_v${OFVERSION}_${OFPLATFORM}_release/scripts/linux/ubuntu/install_dependencies.sh -y 11 | cd of_v${OFVERSION}_${OFPLATFORM}_release/apps 12 | mkdir ${GH_USER} && cd ${GH_USER} 13 | git clone https://github.com/${TRAVIS_REPO_SLUG}.git && cd ${GH_REPO} 14 | make -------------------------------------------------------------------------------- /scripts/osx/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | OFVERSION=0.11.0 3 | OFPLATFORM=osx 4 | GH_USER=$(dirname $TRAVIS_REPO_SLUG) 5 | GH_REPO=$(basename $TRAVIS_REPO_SLUG) 6 | 7 | curl -O https://openframeworks.cc/versions/v${OFVERSION}/of_v${OFVERSION}_${OFPLATFORM}_release.zip 8 | unzip -a -qq of_v${OFVERSION}_${OFPLATFORM}_release.zip 9 | rm of_v${OFVERSION}_${OFPLATFORM}_release.zip 10 | cd of_v${OFVERSION}_${OFPLATFORM}_release/apps 11 | mkdir ${GH_USER} && cd $_ 12 | git clone https://github.com/${TRAVIS_REPO_SLUG}.git && cd ${GH_REPO} 13 | make -------------------------------------------------------------------------------- /scripts/package_release_osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd "$(dirname "$0")" 3 | cd .. 4 | make 5 | cd scripts 6 | 7 | mkdir ../../tmp 8 | cd ../../tmp 9 | rm -rf * 10 | cp -R ../ofOSCDebugger ofOSCDebugger 11 | zip -r ofOSCDebugger_osx_version.zip ofOSCDebugger 12 | openssl dgst -sha256 ofOSCDebugger_osx_version.zip -------------------------------------------------------------------------------- /scripts/windows/install.sh: -------------------------------------------------------------------------------- 1 | OFVERSION=0.11.0 2 | OFPLATFORM=msys2 3 | GH_USER=$(dirname $TRAVIS_REPO_SLUG) 4 | GH_REPO=$(basename $TRAVIS_REPO_SLUG) 5 | 6 | curl -O https://openframeworks.cc/versions/v${OFVERSION}/of_v${OFVERSION}_${OFPLATFORM}_release.zip 7 | unzip -a -qq of_v${OFVERSION}_${OFPLATFORM}_release.zip 8 | rm of_v${OFVERSION}_${OFPLATFORM}_release.zip 9 | of_v${OFVERSION}_${OFPLATFORM}_release/scripts/${OFPLATFORM}/install_dependencies.sh --noconfirm 10 | cd of_v${OFVERSION}_${OFPLATFORM}_release/apps 11 | mkdir ${GH_USER} && cd $_ 12 | git clone https://github.com/${TRAVIS_REPO_SLUG}.git && cd ${GH_REPO} 13 | make -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofAppNoWindow.h" 2 | #include "ofApp.h" 3 | #include 4 | #include "ofxCommandLineUtils.h" 5 | 6 | std::string getAbsolutePath(std::string path) 7 | { 8 | filesystem::path exePath = filesystem::current_path(); 9 | 10 | if (!ofFilePath::isAbsolute(path)) 11 | { 12 | path = ofFilePath::join(exePath, path); 13 | } 14 | return path; 15 | } 16 | 17 | cxxopts::ParseResult parse(int argc, char *argv[]) 18 | { 19 | try 20 | { 21 | cxxopts::Options options(argv[0], " - OSC monitor and sender"); 22 | options.add_options()("h,host", "host", cxxopts::value()->default_value("localhost"))("p,port", "port", cxxopts::value()->default_value("8000"))("o,output", "output", cxxopts::value())("i,interactive", "interactive")("j,input", "input", cxxopts::value())("n,notBundled", "notBundled")("m,message", "message", cxxopts::value())("b,broadcast", "broadcast", cxxopts::value()); 23 | auto result = options.parse(argc, argv); 24 | return result; 25 | } 26 | catch (const cxxopts::exceptions::exception &e) 27 | { 28 | std::cout << "error parsing options: " << e.what() << std::endl; 29 | exit(-1); 30 | } 31 | } 32 | 33 | int main(int argc, char *argv[]) 34 | { 35 | auto result = parse(argc, argv); 36 | auto arguments = result.arguments(); 37 | 38 | ofAppNoWindow window; 39 | ofSetupOpenGL(&window, 0, 0, OF_WINDOW); 40 | if (result.count("input") > 0) 41 | { 42 | ofRunApp(new ofApp(result["host"].as(), result["port"].as(), getAbsolutePath(result["input"].as()))); 43 | } 44 | else if (result.count("message") == 0) 45 | { 46 | if (result.count("output") > 0) 47 | { 48 | ofRunApp(new ofApp(result["port"].as(), true, getAbsolutePath(result["output"].as()))); 49 | } 50 | else if (result.count("broadcast") > 0) 51 | { 52 | auto port = result["port"].as(); 53 | auto receivers = result["broadcast"].as(); 54 | auto parts = ofSplitString(receivers, " "); 55 | std::vector hosts; 56 | std::vector ports; 57 | if (parts.size() % 2 == 0) 58 | { 59 | for (auto i = 0; i < parts.size(); i++) 60 | { 61 | if (i % 2 == 0) 62 | { 63 | hosts.push_back(parts[i]); 64 | } 65 | else 66 | { 67 | ports.push_back(ofToInt(parts[i])); 68 | } 69 | } 70 | } 71 | ofRunApp(new ofApp(result["port"].as(), hosts, ports)); 72 | } 73 | else 74 | { 75 | ofRunApp(new ofApp(result["port"].as(), false, "")); 76 | } 77 | } 78 | 79 | else 80 | { 81 | ofRunApp(new ofApp(result["host"].as(), result["port"].as(), result["message"].as(), result["interactive"].as(), result["notBundled"].as())); 82 | } 83 | return 0; 84 | } -------------------------------------------------------------------------------- /src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | #include 3 | #include 4 | 5 | ofApp::ofApp(int port, bool record, std::string outputPath) : _outputPath(outputPath) 6 | { 7 | _receiver.setup(port); 8 | _record.set("record", record); 9 | _parameters.add(_record); 10 | _messages = ofJson::array(); 11 | } 12 | ofApp::ofApp(int port, std::vector hosts, std::vector outputPorts) 13 | { 14 | _receiver.setup(port); 15 | _senders.resize(outputPorts.size()); 16 | if (hosts.size() != outputPorts.size()) 17 | { 18 | ofLogNotice() << "number of hosts and ports does not match"; 19 | ofExit(); 20 | } 21 | for (auto i = 0; i < outputPorts.size(); i++) 22 | { 23 | _senders[i].setup(hosts[i], outputPorts[i]); 24 | } 25 | } 26 | ofApp::ofApp(std::string host, int port, std::string message, bool interactive, bool notBundled) 27 | { 28 | _sender.setup(host, port); 29 | sendMessage(message, notBundled); 30 | 31 | if (interactive) 32 | { 33 | while (true) 34 | { 35 | ofLogNotice(_name) << "please enter a new message: address firstArg secondArg thirdArg"; 36 | getline(cin, message); 37 | sendMessage(message, notBundled); 38 | } 39 | } 40 | else 41 | { 42 | ofExit(0); 43 | } 44 | } 45 | ofApp::ofApp(std::string host, int port, std::string inputPath) 46 | { 47 | _sender.setup(host, port); 48 | ofFile file(inputPath); 49 | if (file.exists()) 50 | { 51 | int lastTimeStamp = 0; 52 | file >> _messages; 53 | for (auto &message : _messages) 54 | { 55 | ofxOscMessage msg; 56 | msg.setAddress(message["address"]); 57 | for (auto &arg : message["args"]) 58 | { 59 | if (arg["type"] == "i") 60 | { 61 | int value = arg["value"]; 62 | msg.addIntArg(value); 63 | } 64 | else if (arg["type"] == "f") 65 | { 66 | float value = arg["value"]; 67 | msg.addFloatArg(value); 68 | } 69 | else if (arg["type"] == "s") 70 | { 71 | std::string value = arg["value"]; 72 | msg.addStringArg(value); 73 | } 74 | else if (arg["type"] == "F") 75 | { 76 | msg.addBoolArg(false); 77 | } 78 | else if (arg["type"] == "T") 79 | { 80 | msg.addBoolArg(true); 81 | } 82 | } 83 | 84 | int timeStamp = message["timeStamp"]; 85 | ofLogNotice() << "waiting for " << timeStamp - lastTimeStamp << "ms and then sending message"; 86 | ofSleepMillis(timeStamp - lastTimeStamp); 87 | _sender.sendMessage(msg, true); 88 | lastTimeStamp = message["timeStamp"]; 89 | } 90 | ofExit(0); 91 | } 92 | else 93 | { 94 | ofLogError(_name) << "file does not exist: " << inputPath; 95 | ofExit(-1); 96 | } 97 | } 98 | 99 | void ofApp::update() 100 | { 101 | ofSetWindowTitle(ofToString((int)(ofGetFrameRate()))); 102 | while (_receiver.hasWaitingMessages()) 103 | { 104 | ofxOscMessage m; 105 | _receiver.getNextMessage(m); 106 | if (_senders.size()) 107 | { 108 | for (auto &sender : _senders) 109 | { 110 | sender.sendMessage(m); 111 | } 112 | } 113 | ofJson messageJson; 114 | messageJson["address"] = m.getAddress(); 115 | messageJson["timeStamp"] = ofGetElapsedTimeMillis(); 116 | std::string msgString; 117 | msgString = m.getAddress(); 118 | messageJson["address"] = m.getAddress(); 119 | msgString += ":"; 120 | for (size_t i = 0; i < m.getNumArgs(); i++) 121 | { 122 | ofJson argJson; 123 | argJson["type"] = m.getArgTypeName(i); 124 | // get the argument type 125 | msgString += " "; 126 | msgString += m.getArgTypeName(i); 127 | msgString += ":"; 128 | 129 | // display the argument - make sure we get the right type 130 | if (m.getArgType(i) == OFXOSC_TYPE_INT32) 131 | { 132 | msgString += ofToString(m.getArgAsInt32(i)); 133 | argJson["value"] = m.getArgAsInt32(i); 134 | } 135 | else if (m.getArgType(i) == OFXOSC_TYPE_FLOAT) 136 | { 137 | msgString += ofToString(m.getArgAsFloat(i)); 138 | argJson["value"] = m.getArgAsFloat(i); 139 | } 140 | else if (m.getArgType(i) == OFXOSC_TYPE_TRUE) 141 | { 142 | msgString.pop_back(); 143 | } 144 | else if (m.getArgType(i) == OFXOSC_TYPE_FALSE) 145 | { 146 | msgString.pop_back(); 147 | } 148 | else if (m.getArgType(i) == OFXOSC_TYPE_STRING) 149 | { 150 | msgString += m.getArgAsString(i); 151 | argJson["value"] = m.getArgAsString(i); 152 | } 153 | else 154 | { 155 | msgString += "unhandled argument type " + m.getArgTypeName(i); 156 | } 157 | messageJson["args"].push_back(argJson); 158 | } 159 | ofLogNotice(_name) << msgString; 160 | if (_record) 161 | { 162 | _messages.push_back(messageJson); 163 | } 164 | } 165 | } 166 | 167 | void ofApp::exit() 168 | { 169 | if (_record) 170 | { 171 | ofSaveJson(_outputPath, _messages); 172 | } 173 | } 174 | 175 | void ofApp::sendMessage(std::string message, bool notBundled) 176 | { 177 | auto parts = ofSplitString(message, " "); 178 | auto address = parts[0]; 179 | parts.erase(parts.begin()); 180 | 181 | ofxOscMessage msg; 182 | msg.setAddress(address); 183 | std::regex floatRegex{R"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)"}; 184 | std::regex intRegex{R"(\d+)"}; 185 | for (auto arg : parts) 186 | { 187 | if (std::regex_match(arg, intRegex)) 188 | { 189 | istringstream inputStream(arg); 190 | int i; 191 | inputStream >> i; 192 | msg.addIntArg(i); 193 | } 194 | else if (std::regex_match(arg, floatRegex)) 195 | { 196 | istringstream inputStream(arg); 197 | float f; 198 | inputStream >> f; 199 | msg.addFloatArg(f); 200 | } 201 | else 202 | { 203 | // TODO: blob, impulse, timetag 204 | if (arg == "TRUE" || arg == "true" || arg == "T") 205 | { 206 | msg.addBoolArg(true); 207 | } 208 | else if (arg == "FALSE" || arg == "false" || arg == "F") 209 | { 210 | msg.addBoolArg(false); 211 | } 212 | else 213 | { 214 | 215 | auto words = ofSplitString(arg, "---"); 216 | if (words.size() > 1) 217 | { 218 | std::string multiWordArg = ""; 219 | for (auto word : words) 220 | { 221 | multiWordArg += word; 222 | multiWordArg += " "; 223 | } 224 | multiWordArg = multiWordArg.substr(0, multiWordArg.size() - 1); 225 | msg.addStringArg(multiWordArg); 226 | } 227 | else 228 | { 229 | msg.addStringArg(arg); 230 | } 231 | } 232 | } 233 | } 234 | 235 | _sender.sendMessage(msg, !notBundled); 236 | } 237 | -------------------------------------------------------------------------------- /src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "ofxOsc.h" 5 | 6 | class ofApp : public ofBaseApp 7 | { 8 | public: 9 | ofApp(int port, bool record, std::string outputPath); 10 | ofApp(int port, std::vector hosts, std::vector outputPorts); 11 | ofApp(std::string host, int port, std::string message, bool interactive, bool notBundled = false); 12 | ofApp(std::string host, int port, std::string inputPath); 13 | void update(); 14 | void exit(); 15 | void sendMessage(std::string message, bool notBundled); 16 | 17 | ofxOscReceiver _receiver; 18 | ofxOscSender _sender; 19 | std::vector _senders; 20 | std::string _name = "ofOSCDebugger"; 21 | ofJson _messages; 22 | 23 | ofParameterGroup _parameters; 24 | ofParameter _record; 25 | std::string _outputPath; 26 | }; 27 | --------------------------------------------------------------------------------