Fundamentals
Export Targets
Code Export
Patcher UI
Special Topics
RNBO Raspberry Pi OSCQuery Runner
Sending and Receiving Messages C++
Any inport, outport, in, or out objects in a RNBO patcher will create tagged ports that can send and receive messages. To send a message to an object defined like [inport pitch]
, use sendMessage
.
// Send a bang to pitch
rnboObject.sendMessage(TAG("pitch"), TAG(""));
The first argument of sendMessage
is the tag of the inport that should receive the message. Use the RNBO function TAG
to convert from a string to the internal RNBO tag format. The second argument is mostly reserved for RNBO internal use, and refers to the tag of a specific object id. In most cases, simply use the empty tag by writing TAG("")
. The optional third argument lets you schedule the message to be sent in the future by specifying a specific millisecond time. The default value is equivalent to passing RNBO::RNBOTimeNow
, which schedules the message to be sent immediately.
Internally there's not much different between in and inport, where in simply have a default tag like "in1"
for the first message inlet, "in2"
for the second message inlet, etc. So to send a number to the second inlet, simply send a message as follows:
// Send a number to the second inlet
rnboObject.sendMessage(TAG("in2"), 74, TAG(""));
Finally, to send a list of numerical values, we must first create a unique pointer to a RNBO list. The easiest way to accomplish this is to use the RNBO::make_unique
function.
// Send a list to the third inlet
auto mylist = RNBO::make_unique<RNBO::list>();
mylist->push(1), mylist->push(2), mylist->push(3);
rnboObject.sendMessage(TAG("in3"), std::move(mylist), TAG(""));
Receiving Message Events
Similar to receiving parameter events, you can receive message events by creating a concrete subclass of RNBO::EventHandler
. See the previous section on Getting and Setting Parameters for a more in depth discussion on the threading considerations involved with receiving events. The basic idea is the same: override and implement handleMessageEvent
instead of handleParameterEvent
.
class LoggingMessageHandler : EventHandler {
void eventsAvailable() override {
drainEvents();
}
void handleMessageEvent(const MessageEvent& event) override {
if (event.getType() == MessageEvent::Bang) {
std::cout << "Received bang with tag " << event.getTag() << "\n";
} else if (event.getType() == MessageEvent::Number) {
std::cout << "Received value " << event.getNumValue() << " with tag " << event.getTag() << "\n";
} else if (event.getType() == MessageEvent::List) {
std::cout << "Received list value ";
auto list = event.getListValue().get();
for (int i = 0; i < list->length; i++) {
if (i != 0) std::cout << ", ";
std::cout << list->operator[](i);
}
std::cout << " with tag " << event.getTag() << "\n";
}
}
};
One consideration here is that event.getTag()
will return an internal RNBO tag, which might not be very useful. We could pass the RNBO core object to the message handler class, giving us access to the resolveTag
method of the RNBO core object.
using namespace RNBO;
class LoggingMessageHandler : EventHandler {
public:
LoggingMessageHandler(CoreObject *obj) : m_obj(obj) {}
private:
void eventsAvailable() override {
drainEvents();
}
void handleMessageEvent(const MessageEvent& event) override {
if (event.getType() == MessageEvent::Bang) {
std::cout << "Received bang with tag " << m_obj->resolveTag(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::Number) {
std::cout << "Received value " << event.getNumValue() << " with tag " << m_obj->resolveTag(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::List) {
std::cout << "Received list value ";
auto list = event.getListValue().get();
for (int i = 0; i < list->length; i++) {
if (i != 0) std::cout << ", ";
std::cout << list->operator[](i);
}
std::cout << " with tag " << m_obj->resolveTag(event.getTag()) << "\n";
}
}
CoreObject *m_obj;
};
Or for extra credit, we could simply pass a lambda to resolve the tag.
using namespace RNBO;
class LoggingMessageHandler : EventHandler {
public:
LoggingMessageHandler(std::function<RNBO::MessageTagInfo(RNBO::MessageTag)> tagResolver) : tagResolver(tagResolver) {}
private:
void eventsAvailable() override {
drainEvents();
}
void handleMessageEvent(const MessageEvent& event) override {
if (event.getType() == MessageEvent::Bang) {
std::cout << "Received bang with tag " << tagResolver(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::Number) {
std::cout << "Received value " << event.getNumValue() << " with tag " << tagResolver(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::List) {
std::cout << "Received list value ";
auto list = event.getListValue().get();
for (int i = 0; i < list->length; i++) {
if (i != 0) std::cout << ", ";
std::cout << list->operator[](i);
}
std::cout << " with tag " << tagResolver(event.getTag()) << "\n";
}
}
std::function<RNBO::MessageTagInfo(RNBO::MessageTag)> tagResolver;
};
CoreObject rnboObject;
auto tagResolver = [&rnboObject](RNBO::MessageTag tag) { return rnboObject.resolveTag(tag); };
LoggingMessageHandler handler(tagResolver);
Putting it all together into a complete example might look something like this:
#include <iostream>
#include "RNBO.h"
using namespace RNBO;
class LoggingMessageHandler : EventHandler {
public:
LoggingMessageHandler(std::function<RNBO::MessageTagInfo(RNBO::MessageTag)> tagResolver) : tagResolver(tagResolver) {}
private:
void eventsAvailable() override {
drainEvents();
}
void handleMessageEvent(const MessageEvent& event) override {
if (event.getType() == MessageEvent::Bang) {
std::cout << "Received bang with tag " << tagResolver(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::Number) {
std::cout << "Received value " << event.getNumValue() << " with tag " << tagResolver(event.getTag()) << "\n";
} else if (event.getType() == MessageEvent::List) {
std::cout << "Received list value ";
auto list = event.getListValue().get();
for (int i = 0; i < list->length; i++) {
if (i != 0) std::cout << ", ";
std::cout << list->operator[](i);
}
std::cout << " with tag " << tagResolver(event.getTag()) << "\n";
}
}
std::function<RNBO::MessageTagInfo(RNBO::MessageTag)> tagResolver;
};
int main(int argc, const char * argv[]) {
CoreObject rnboObject;
auto tagResolver = [&rnboObject](RNBO::MessageTag tag) { return rnboObject.resolveTag(tag); };
LoggingMessageHandler handler(tagResolver);
auto interface = rnboObject.createParameterInterface(ParameterEventInterface::MultiProducer, (EventHandler *) &handler);
rnboObject.prepareToProcess(44100, 64);
SampleValue** inputs = nullptr;
SampleValue** outputs = new SampleValue*[1];
outputs[0] = new double[64];
while (true) {
rnboObject.process(inputs, 0, outputs, 1, 64);
// Send a bang to in1
rnboObject.sendMessage(TAG("in1"), TAG(""));
// Send a number to in2
rnboObject.sendMessage(TAG("in2"), 74, TAG(""));
// Send a list to in3
auto l = make_unique<RNBO::list>();
l->push(1), l->push(2), l->push(3);
rnboObject.sendMessage(TAG("in3"), std::move(l), TAG(""));
}
delete [] outputs[0];
delete [] outputs;
return 0;
}