ASPiK SDK
Messaging Custom Objects

We implemented custom knob as an example of using the same lock-free ring buffer concept to move messages between the plugin core and the custom objects as opposed to moving audio data. Here we have set up a custom data structure that contains the messaging information - some basic stuff is there and you are encouraged to experiment with your own messaging and custom view work. In this example, we will pick up the knob's custom view interface pointer and use it to send a custom message to the object, which will acknowledge back (pretty cool, huh?).

The custom message structure is shown below; in this example, we are only going to use the two std::strings, one to query the object and the other to access information it returns to us:

struct CustomViewMessage
{
// --- show/hide flag
unsigned int message = MESSAGE_HIDE_CONTROL;
bool visible = true;
bool showAlternateGraphic = false;
double controlAlpha = 1.0; // transparency: 0 = invisible (100% transparent) and 1 = solidly visible (0% transparent)
std::string queryString;
std::string replyString;
void* messageData = nullptr;
};
//

In the PluginCore, we'll communicate with the knob when the custom view is registered (only as an example - you can experiment with this all you like). The code for the PLUGINGUI_REGISTER_CUSTOMVIEW is shown here (it follows the registration code for the two custom audio viewer objects). Set a breakpoint in the debugger and run the plugin - when you open the GUI, you can check the communication and reply you get back by examining the return string. Notice also that we set up this object so that it copies it's "this" poitner into the messageData variable on the message data structure. Using this, you can (in theory) obtain a pointer to the underlying object itself, HOWEVER THIS IS NOT RECOMMENDED AS IT IS VERY DANGEROUS WAY OF CODING.

if (messageInfo.inMessageString.compare("CustomKnobView") == 0)
{
// --- (1) get the custom view interface via incoming message data*
if (knobView != static_cast<ICustomView*>(messageInfo.inMessageData))
knobView = static_cast<ICustomView*>(messageInfo.inMessageData);
if (!knobView) return false;
// --- send the view a message
CustomViewMessage knobMessage;
knobMessage.message = MESSAGE_QUERY_CONTROL;
knobMessage.queryString.assign("Hello There!");
// --- send the message
knobView->sendMessage(&knobMessage);
// --- check the reply string
const char* reply = knobMessage.replyString.c_str();
// --- this will dequeue the object's ring buffer
knobView->updateView();
// --- DO NOT DO THIS!!! (but it is possible)
//CAnimKnob* customKnob = static_cast<CAnimKnob*>(knobMessage.messageData);
// --- registered!
return true;
}
//

The knob's ::sendMessage( ) function that decodes the query, fixes the reply, and (risky) copies its THIS pointer to the message structure is here:

void CustomKnobView::sendMessage(void* data)
{
CustomViewMessage* viewMessage = static_cast<CustomViewMessage*>(data);
if (!viewMessage) return;
if (viewMessage->message == MESSAGE_QUERY_CONTROL)
{
if (viewMessage->queryString.compare("Hello There!") == 0)
{
viewMessage->replyString.assign("I'm Here!!");
viewMessage->messageData = this; // <-- example of VERY risky thing to do; not recommended
}
return; // do not process further
}
// --->> CustomViewMessage has =operator
dataQueue->enqueue(*viewMessage);
}
//

At the end of the function, the message is added to the local lock-free ring buffer queue for further processing (not needed here) and in the updateView() method the ring-buffer is emptied (note that we called it after getting the reply message in the previous function).