Developing an Isadora Plugin
-
Dear Forum,
I Have started to play around with the development of a plugin for Isadora and learned a lot form the SDK code and the Developers threads on the old forum. Slowly I am learning and slowly I am figuring out how stuff works.A lot of helpful information I got out of these two posts:http://forum.troikatronix.com/cgi-bin/forum/gforum.cgi?post=7220;search_string=isadora%20sdk;guest=21882130#7220http://forum.troikatronix.com/cgi-bin/forum/gforum.cgi?post=7554;sb=post_latest_reply;so=ASC;forum_view=forum_view_collapsed;guest=21882130I want to develop a plugin that dynamically generates output based on incoming OSC messages. This means that I need a plugin that listens to OSC, a specific port and then generates different outputs based on the "/name"My questions:- Do I somehow need to bypass the OSC system Isadora uses? Since in Isadora an OSC address/message is forwarded to a channel that you can set in the "stream setup" meaning you cannot set the message header in the OSC listener or let the OSC listener list to different messages.- Could I take a look at the OSC listener source code maybe that will already give me a push in the right direction- Is this at all possibleActually what I want more or less is the functionality of the stream setup in a user actor, but I am assuming that there is a good reason for why it is programmed like it is now...This is question to Mark, but since he is so busy if anybody else has some thoughts on this I would be happy to hear them.Thanks in advance. -
To answer your questions:
Do I somehow need to bypass the OSC system Isadora uses? Since in Isadora an OSC address/message is forwarded to a channel that you can set in the "stream setup" meaning you cannot set the message header in the OSC listener or let the OSC listener list to different messages.
I don't think so. You should just create your own UDP socket and listen on a different port than Isadora normally uses. Then you'll get the data directly, and Isadora won't even see it.
Could I take a look at the OSC listener source code maybe that will already give me a push in the right direction
This probably won't help because it doesn't contain the OSC source code -- at least the part that reads the messages. I can tell you that the source code for the OSC part of Isadora is based on the open source, very original c implementation on OSC that is on the OSC site. That's where I would start.
Is this at all possible
Definitely! I think it's just a good idea to keep the entire subsystem separate from Isadora. I mean, if you wanted to, you could received the OSC messages from via Isadora's sub-system. But if your doing something very specific, the way the data gets passed to you might not be the best route.
Hope that helps
Mark -
Hi Mark,
Thanks for the clear and swift answer this will certainly help me further. Regarding the libraries you used for OSC I am assuming you mean this page: http://opensoundcontrol.org/guide-osc-librariesFrom this list I am most familiar with oscpack since OpenFrameworks is using it in their ofxOSC addon so I will be using that as a starting point.I totally agree with you that it is a good idea to keep the entire subsystem separate from Isadora and I will also follow this road. The reason I pointed to the existing OSClistener is because as mentioned in the first post I can learn a lot from the source code of relevant actors. Being able to look in the code of some of the actors can really help to understand better how to works with the SDK and make plugins.While reading the forum I noticed that you are usually quite thorough and quick in answering Development/SDK related questions so.. I will come back to you in this thread if I have any other question and to share my progress and code of the plugin.Greetings,Machiel -
Dear Machel,
Well, the OSC Listener source code won't tell you much about OSC. The most important call is is in the ActivateScene method, and looks like this:info->mMessageReceiver = CreateActorMessageReceiver_(
ip,
inActorInfo,
ReceiveMessage,
info->mChannel,
kWantBroadcasterMsgs,
(long) inActorInfo);which tells the ReceiveMessage callback to receive all kWantBroadcasterMsgs. These are the messages sent by both the Broadcaster actors and OSC. The Receive Message function starts like this:static void
ReceiveMessage(
IsadoraParameters* ip,
MessageMask /* inMessageMask /,
PortIndex / inPortIndex1 /,
const MsgData inData,
UInt32 /* inLen /,
long inRefCon)
{ActorInfo actorInfoPtr = reinterpret_cast<actorinfo*>(inRefCon);
PluginInfo* info = GetPluginInfo_(actorInfoPtr);
const ValuePtr value = (const ValuePtr)(inData);</actorinfo*>...So the OSC data is already converted a Value struct, which contains the a float, int, etc.And that's the reason I said it wouldn't really help you. By the time it's handled by this actor, all the actual OSC is stripped away.Best Wishes,Mark<actorinfo*>
</actorinfo*> -
Oke I am still busy developing a custom OSC Isadora plugin and learning as I go.
I stumbled upon some problems initiating the plugin with default values I will try to explain as clear as possible.In the sPropertyDefinitionString you define your inputs and outputs with their default values
When you change a input the: HandlePropertyChangeValue is called and with a switch statement you can do something like:switch (inPropertyIndex1) {case kInputUDPadress:info->mUDPadress = inNewValue->u.str->strData;break;}This way you can set the info parameters as the input changes.But what I want is to be able to init the info parameters before the first input change. When I output info->mUDPadress before it's input is changed it is empty.So my question is how can I set info->mUDPadress before the HandlePropertyChangeValue is called with the values I set in the sPropertyDefinitionString ?I can make a global variable with the default value, but I am a bit stuck on how to inject it into the sPropertyDefinitionString. My experience is more in C++ with the std::string and not in C so I notice that it takes quite some effort to wrap my head around something that should be relatively easy. There are also stil somewhat unclraitys on how the data flow through the actor precisely works and where and how the sPropertyDefinitionString is being used.I hope the question is formulated sufficiently clear and that anybody could help me out.P.S.:The plug-in I am building is for use with other custom developed software that we use here: http://maplab.nl to do research with new media in a performative rehearsal context. -
Dear Machel,
Well, it simply doesn't work the way you want it go... the flow goes like this:1a) You activate a scene. Isadora iterators all the actors1b) Each actor receives its initial values via HandlePropertyChangeValue: these will be a) the value specified in the property definition string, OR b) the value in the "initialize" field of the property inspector. (The latter overrides the former.) Note that it is possible to ignore this input because the inInitializing flag will be set to true during this call.2) Actor is activated - Activate() function is called with activate flag set to true3) Actor does its work, generally sending output after it receives new input, or in response to a MessageReceiver callback.4a) You deactivate a scene. Isadora iterators all the actors calling the Activate() function with the inActivate parameter set to falseSo really, it depends on when you send your output. For sure, you should **_never_** send output until after the Activate() function is called with the inActivate parameters set to true, and **_never_** send output after the Activate() function is called with the inActivate parameter set to false.But why do you want a global variable? Why not let the user input the IP address via an IP address input?Best Wishes,Mark -
Hoi Mark,
Thank your for the quick reply it helped me a lot figuring out what was happening. To clarify a bit what I am doing, my first step has been to write a OSC send plugin. I know that Isadora already has that actor, but as a first step in learning to develop plugins and working with OSC I choose to first make a OSC sender plugin before starting with the OSC receive plugin described in the first post of the topic.In this plugin my goals are indeed that the user can set the ip address, port, header and value. Using your description of the flow the OSC sender plugin now works and is attached to this post together with the OSC library I used. I think the code explains better what I was struggling with then I can explain in words.Now my next step will be to start with the OSC receiving plugin. If I run into problems or made some progress I will report back here on the forum.As a side note I am now actually developing with Xcode 5 which I installed/upgraded by accident a couple of days ago. Xcode was nagging me to update the project settings and when I did stuff went wrong so I rolled back the project settings and made a todo for myself to better figure out the impact of Xcode 5 on the development of Isadora plugins.Greetings,Machiel -
Dear Forum,
I ams till further developing an Isadora plugin used to receive and send OSC messages.In this plugin I use some data structures to save date and keep track of things.But when I save the file with the plugin in it and close the file after saving and the reopen it the data is gone.This is not so strange because the pluginInfo is cleared by DisposeActor. But how can I save data within the actor that will stay there even after reopening the file?For instance is you use the text actor and paste some text in it will save that text so that if you reopen the file with that actor the text is still present.If I could have a look at the source code of the text actor that might already help me.any help would be greatly appreciatedMachiel -
I think what you are talking about with the text actor is text that is assigned to an input.
The input data will remain as part of the Isadora file, and be re-input each time the file is opened.As for options for your Plugin to store data, any text file format (txt, xml etc) might be an option. Read and write to a file at a defined location? -
Dear Forum,
I have been digging around some more and I found that the "IsadoraDemoReadSerial" which is part of the SDK has the same functionality as the text editor. (in the sense that you get a textfield if you double click the actor and that the text you type there is saved together with the Isadora file)I am looking into how the :outActorParams->mPrepareActorForFirstUseProc = PrepareForFirstUse;outActorParams->mSetActorPrivateDataProc = SetActorPrivateData;outActorParams->mGetActorPrivateDataLengthProc = GetActorPrivateDataLength;outActorParams->mGetActorPrivateDataProc = GetActorPrivateData;outActorParams->mActorDoubleClickProc = DoubleClickActor;Are implemented and working. I have copied this functionality in my plugin and I am now testing to save a custom data container instead of a char* in the memory. So I am reading up on what memcpy does and how to implement it.I have the feeling I am almost there but any pointers or help would be greatly appreciated.However it might be easier to just save the data as txt, xml etc.. But I do not immediately know how to do that. Is there a snippet or sourcode I can take a look at for that ?Greetings,Machiel -
Another user on the forum created a nice text read/write plugin, that includes the source, you way want to take a look at it.http://troikatronix.com/troikatronixforum/discussion/694/read-text-from-a-file-i-wrote-a-plugin -
Hi DusX,
Ah.. I Forgot about that one.. I will look into the source of the read/write and see if that is an better path to walk then what I am doing now.Thanks for now. -
Yes, just have a look at the source. It's only the reading part, but it should help you write your own plugin, if using a separate text file is the way you want to go.
I basically used C file functions (fopen, fgetc) as I have a C programming background, but you may rather use C++ file streams and <</>> operators, or other file I/O libs (don't know much about these). The algorithm essentially depends on whether you need to read/write the file as a whole or line by line, character by character, etc.You will find lots of examples here and there, but [stackoverflow.com](http://stackoverflow.com/) is a great site for this kind of snippets. Just be careful about platform-specific parts, some functions I used only work on Windows. -
Oke, I am stuck..
using the:SetActorPrivateDataGetActorPrivateDataLengthGetActorPrivateDataI can set a chunk of memory with the right data but if I close and reopen the file the chunk of memory I set to something else and I cannot get to my data. In the IsadoraDemoReadSerial a char* is stored and retrieved and I have been able to duplicate that but I want to store a map with an object inside it not a char* ...I could use some help.Machiel -
I also noticed that with the GetActorPrivateData a variable called outActorData is being set. Which is probably saved in the file somewhere outside of the reach of the SDK...
The type using typeid(outActorData).name() is given as "Pv" which is the same type as the variable to be stored in the IsadoraDemoReadSerial example plugin. While the data I want to store is of the type: PSt3mapISs8PEmoduleSt4lessISsESaISt4pairIKSsS0_EEE which is a map with a string, object pair.So... Maybe I am trying to push an object in to something which is only made to accept char* then of course it will never work...Actually now I think of it "Pv" could mean Pointer variable but then I still do not know what to do with it.So could this be verified? Hopefully by Mark? then I know if I am wasting my time..Greetings,Machiel