/* * Copyright (c) 2012-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd for documentation * * This file was originally written for the ofxPd openFrameworks addon: * https://github.com/danomatika/ofxPd * */ #pragma once #include #include #include #include namespace pd { /// \section Pd Patch /// a pd patch /// /// if you use the copy constructor/operator, keep in mind the libpd void* /// pointer patch handle is copied and problems can arise if one object is used /// to close a patch that other copies may be referring to class Patch { public: Patch() : _handle(NULL), _dollarZero(0), _dollarZeroStr("0"), _filename(""), _path("") {} Patch(const std::string &filename, const std::string &path) : _handle(NULL), _dollarZero(0), _dollarZeroStr("0"), _filename(filename), _path(path) {} Patch(void *handle, int dollarZero, const std::string &filename, const std::string &path) : _handle(handle), _dollarZero(dollarZero), _filename(filename), _path(path) { std::stringstream itoa; itoa << dollarZero; _dollarZeroStr = itoa.str(); } /// get the raw pointer to the patch instance void* handle() const {return _handle;} /// get the unqiue instance $0 ID int dollarZero() const {return _dollarZero;} /// get the patch filename std::string filename() const {return _filename;} /// get the parent dir path for the file std::string path() const {return _path;} /// get the unique instance $0 ID as a string std::string dollarZeroStr() const {return _dollarZeroStr;} /// is the patch pointer valid? bool isValid() const {return _handle != NULL;} /// clear patch pointer and dollar zero (does not close patch!) /// /// note: does not clear filename and path so the object can be reused // for opening multiple instances void clear() { _handle = NULL; _dollarZero = 0; _dollarZeroStr = "0"; } /// copy constructor Patch(const Patch &from) { _handle = from._handle; _dollarZero = from._dollarZero; _dollarZeroStr = from._dollarZeroStr; _filename = from._filename; _path = from._path; } /// copy operator void operator=(const Patch &from) { _handle = from._handle; _dollarZero = from._dollarZero; _dollarZeroStr = from._dollarZeroStr; _filename = from._filename; _path = from._path; } /// print info to ostream friend std::ostream& operator<<(std::ostream &os, const Patch &from) { return os << "Patch: \"" << from.filename() << "\" $0: " << from.dollarZeroStr() << " valid: " << from.isValid(); } private: void *_handle; ///< patch handle pointer int _dollarZero; ///< the unique $0 patch ID std::string _dollarZeroStr; ///< $0 as a string std::string _filename; ///< filename std::string _path; ///< full path to parent folder }; /// \section Pd stream interface message objects /// bang event struct Bang { const std::string dest; ///< dest receiver name explicit Bang(const std::string &dest) : dest(dest) {} }; /// float value struct Float { const std::string dest; ///< dest receiver name const float num; ///< the float value Float(const std::string &dest, const float num) : dest(dest), num(num) {} }; /// symbol value struct Symbol { const std::string dest; ///< dest receiver name const std::string symbol; ///< the symbol value Symbol(const std::string &dest, const std::string &symbol) : dest(dest), symbol(symbol) {} }; /// a compound message containing floats and symbols class List { public: List() {} /// \section Read /// check if index is a float type bool isFloat(const unsigned int index) const { if(index < objects.size()) if(objects[index].type == List::FLOAT) return true; return false; } /// check if index is a symbol type bool isSymbol(const unsigned int index) const { if(index < objects.size()) if(objects[index].type == List::SYMBOL) return true; return false; } /// get index as a float float getFloat(const unsigned int index) const { if(!isFloat(index)) { std::cerr << "Pd: List object " << index << " is not a float" << std::endl; return 0; } return objects[index].value; } /// get index as a symbol std::string getSymbol(const unsigned int index) const { if(!isSymbol(index)) { std::cerr << "Pd: List object " << index << " is not a symbol" << std::endl; return ""; } return objects[index].symbol; } /// \section Write /// /// add elements to the list /// /// List list; /// list.addSymbol("hello"); /// list.addFloat(1.23); /// /// add a float to the list void addFloat(const float num) { MsgObject o; o.type = List::FLOAT; o.value = num; objects.push_back(o); typeString += 'f'; } /// add a symbol to the list void addSymbol(const std::string &symbol) { MsgObject o; o.type = List::SYMBOL; o.symbol = symbol; objects.push_back(o); typeString += 's'; } /// \section Write Stream Interface /// /// list << "hello" << 1.23; /// /// add a float to the message List& operator<<(const bool var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const int var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const float var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const double var) { addFloat((float) var); return *this; } /// add a symbol to the message List& operator<<(const char var) { std::string s; s = var; addSymbol(s); return *this; } /// add a symbol to the message List& operator<<(const char *var) { addSymbol((std::string) var); return *this; } /// add a symbol to the message List& operator<<(const std::string &var) { addSymbol((std::string) var); return *this; } /// \section Util /// return number of items unsigned int len() const {return (unsigned int) objects.size();} /// return OSC style type string ie "fsfs" const std::string& types() const {return typeString;} /// clear all objects void clear() { typeString = ""; objects.clear(); } /// get list as a string std::string toString() const { std::string line; std::stringstream itoa; for(int i = 0; i < (int)objects.size(); ++i) { if(isFloat(i)) { itoa << getFloat(i); line += itoa.str(); itoa.str(""); } else { line += getSymbol(i); } if(i < (int)objects.size()-1) { line += " "; } } return line; } /// print to ostream friend std::ostream& operator<<(std::ostream &os, const List &from) { return os << from.toString(); } private: std::string typeString; ///< OSC style type string // object type enum MsgType { FLOAT, SYMBOL }; // object wrapper struct MsgObject { MsgType type; float value; std::string symbol; }; std::vector objects; ///< list objects }; /// start a compound message struct StartMessage { explicit StartMessage() {} }; /// finish a compound message as a list struct FinishList { const std::string dest; ///< dest receiver name explicit FinishList(const std::string &dest) : dest(dest) {} }; /// finish a compound message as a typed message struct FinishMessage { const std::string dest; ///< dest receiver name const std::string msg; ///< target msg at the dest FinishMessage(const std::string &dest, const std::string &msg) : dest(dest), msg(msg) {} }; /// \section Pd stream interface midi objects /// ref: http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html /// send a note on event (set vel = 0 for noteoff) struct NoteOn { const int channel; ///< channel (0 - 15 * dev#) const int pitch; ///< pitch (0 - 127) const int velocity; ///< velocity (0 - 127) NoteOn(const int channel, const int pitch, const int velocity=64) : channel(channel), pitch(pitch), velocity(velocity) {} }; /// change a control value aka send a CC message struct ControlChange { const int channel; ///< channel (0 - 15 * dev#) const int controller; ///< controller (0 - 127) const int value; ///< value (0 - 127) ControlChange(const int channel, const int controller, const int value) : channel(channel), controller(controller), value(value) {} }; /// change a program value (ie an instrument) struct ProgramChange { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (0 - 127) ProgramChange(const int channel, const int value) : channel(channel), value(value) {} }; /// change the pitch bend value struct PitchBend { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (-8192 - 8192) PitchBend(const int channel, const int value) : channel(channel), value(value) {} }; /// change an aftertouch value struct Aftertouch { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (0 - 127) Aftertouch(const int channel, const int value) : channel(channel), value(value) {} }; /// change a poly aftertouch value struct PolyAftertouch { const int channel; ///< channel (0 - 15 * dev#) const int pitch; ///< pitch (0 - 127) const int value; ///< value (0 - 127) PolyAftertouch(const int channel, const int pitch, const int value) : channel(channel), pitch(pitch), value(value) {} }; /// a raw midi byte struct MidiByte { const int port; ///< raw portmidi port ///< see http://en.wikipedia.org/wiki/PortMidi const unsigned char byte; ///< the raw midi byte value MidiByte(const int port, unsigned char byte) : port(port), byte(byte) {} }; /// start a raw midi byte stream struct StartMidi { const int port; ///< raw portmidi port explicit StartMidi(const int port=0) : port(port) {} }; /// start a raw sysex byte stream struct StartSysex { const int port; ///< raw portmidi port explicit StartSysex(const int port=0) : port(port) {} }; /// start a sys realtime byte stream struct StartSysRealTime { const int port; ///< raw portmidi port explicit StartSysRealTime(const int port=0) : port(port) {} }; /// finish a midi byte stream struct Finish { explicit Finish() {} }; } // namespace