Storing/Retrieving Extension Data

You’re beginning to understand why it’s not trivial to determine the best way to do this :slight_smile:

Oh, I see - that makes sense! (And considering my suggestion of embedding photos in there, I see why you would be worried about excessive size here :sweat_smile:)

But if we discount size for a moment, the in-gig-file approach seems much more obvious and easy (from a programmer’s as well as a user’s perspective) to me.

To combat size issues, you could enforce some kind of quota per extension. On the other hand, if the storage of state inside the gig file works for a huge ecosystem of plugins, why wouldn’t it work for a (presumably) much smaller ecosystem of GP-specific extensions whose developers will test their extension in GP as opposed to any plugin host and are likely fairly well reachable in the GP community?

1 Like

If we go for the getState and restoreState option, will that not leave extension developers needing to track events like duplicating, creating and deleting rackspaces/songs in order to keep our data structures in sync with the contents of a gigfile during programming and creation sessions whereas we could, for instance, react to a null response to a dictionary read to know it doesn’t exist and so then create a default value?

1 Like

I think it’s useful: as it is now popping a string is somewhat a jump in the darkness, especially because you can’t pop it again…

Tomorrow I’ll submit a bug report, though I’m not desperate at this moment :grinning:

You just keep the dictionary yourself, or something even simpler — you’d be performing the same operations. In C++ you might just use a simple map

   std::map<string, string> fDictionary;

and then a couple of functions that convert to and from a MemoryBlock (or perhaps just an XML string)

My initial use case for this was having a way for the user to specify data specific to a widget that would impact how it is displayed on, or utilized by, a control surface extension.

Simple case in point - the Novation SLMK3 keyboards and the Morningstar MC6 Pro foot controller have color displays with fairly decent flexibility in what is displayed. The MK3 also has numerous RGB buttons and pads. To utilize colors, a user needs to communicate to the extension the colors that should be displayed, and they really need to be on a per widget basis.

I end up using text widgets for this, where the “on” color for a button is stored as the background color of the text widget. The “off” color is stored as the outline color. This allows the user an easy way to specify the colors in a simple way. It allows them to copy them, save panels that contain them, duplicate rackspaces that use them, and not have to start from scratch every time. I generally hide them, but clever panel designers may find ways to integrate them into the design.

In these types of cases the data the extension needs/wants really has to be attached to specific widgets. If the widgets are deleted, the data should disappear. If GP doesn’t tie the data to the widget then I don’t know how the extension would know what widget to associate it with or whether it is safe to delete the data. It would also be impossible for the extension to know if the widget names were changed, so it could very easily lose track of what data is attached to which widget.

I don’t expect all extensions would work in this manner, so perhaps there is not a good “one size fits all” solution.

That said, I still favor the “attach XML data to widgets” approach because any extension could require the user to place a text widget (hidden if they prefer) into the global rackspace. If the user decides they no longer want to use that extension they just delete the extension and the configuration widget. If they want to configure the extension differently for each rackspace, then put that widget in each rackspace instead of global. If they want individual rackspaces to override global defaults they can use both global and local ones. (This is what I do in my extensions.)

In my applications there are maybe a dozen tags I might attach to widgets. Things like “on color”, “off color”, “background color”, “text color”, “flash color”, “resolution”, “long name”, “short name”, etc.

My “love to have” scenario would be a “Tags” area or something like that in the advanced section of the widget editor. Since the extensions are already registered with GP, each extension could have an area (or their own dropdown) and in that area GP could show all the key:value pairs. Maybe extensions could register valid key names with GP, so GP could list the valid keys and the user could type in a value if they wanted to utilize it.

A related issue that I’m already struggling with is that the only way for extensions to reference a widget is through the unique OSC/GPScript name. This prevents two different extensions from utilizing the same widget. If there were separate “per Extension” tag areas that would no longer be a challenge.
image

To clean out old data GP could offer a menu option somewhere that would list the Extensions that have data registered in the gigfile in a checkbox type list and the user could check the ones they want to purge using a “Delete data” button.

In the context of control surface extensions, I think both the “store it in a separate file” and “store it in an XML blob” would end up being exceedingly cumbersome for both the extensions and the users.

I’m not really sure if this is a viable/practical approach for rank13’s looper example.

No need — I just implemented it — nothing to do at the airport!

So here is an updated version of the DupString example where you can query the length of the string on the top of the stack before getting it. That will allow you to allocate the appropriate amount of buffer space.
Surprised nobody suggested this before now.

extern "C" void DupString(GPRuntimeEngine* vm)
{

   int stringLength = GP_VM_TopStringLength(vm);  // There had better be a string on the top of the stack or this will crash - YOU HAVE BEEN WARNED

   std::cout << "The string length is " << stringLength << "\n";

   char buffer[100];   
   GP_VM_PopString(vm, buffer, 100);
   std::string s = buffer;
   s = s + s;

   GP_VM_PushString(vm, s.c_str());
}

1 Like

…and we could even kick the fixed-sized buffer now :slight_smile:

extern "C" void DupString(GPRuntimeEngine* vm)
{

   int stringLength = GP_VM_TopStringLength(vm);  // There had better be a string on the top of the stack or this will crash - YOU HAVE BEEN WARNED

   std::cout << "The string length is " << stringLength << "\n";

   std::string s;
   s.reserve(stringLength);   
   GP_VM_PopString(vm, s.data(), stringLength);
   s = s + s;

   GP_VM_PushString(vm, s.c_str());
}

This could even be put into a single function in the C++ part of the SDK, something like:

std::string GP_VM_PopCppString(GPRuntimeEngine* vm) {
    int stringLength = GP_VM_TopStringLength(vm);

    std::string str;
    str.reserve(stringLength);   
    GP_VM_PopString(vm, str.data(), stringLength);

    return str;
}

The fewer sharp edges around this for extension developers the better :slight_smile:

Of course — I just threw that TopStringLength into the top of that function to demonstrate that you can now get the length. I didn’t bother to update the function.

Well, don’t name it with the GP_VM prefix then. Functions with that prefix are being exported from Gig Performer itself and I’d prefer that people not think that GP_VM_PopCppString was exported from Gig Performer

That’s what I assumed :slight_smile: I just wanted to get the revised example code out there, in case anybody wants to just paste it into their program.

Good point :+1:t2: I assume, that you’re going to provide that function in the zipped SDK anyways? (As of now, I have not any new functionality to the GitHub version as opposed to the zip one - the diff is convoluted enough already :sweat_smile:)