-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Patch load/save #21
Comments
Currently, objects that save open, search and write/read to files individually. bool PatchObject::loadConfig(..., ..., string &configFile) I suggest that all objects can save and load to/from an (preconfigured) XHML handle. Also, how do we handle parameter values that can be overridden by inlets ? Sometimes we want to save last inlet value, but mostly we want to save the last manually-entered value, no ? |
Great idea, yes, right now, the handle is the string filepath, passed to every object, that independently load and save their chunk of code inside the xml. Having a xml handle is definitely a better choice. And about the second question, no, right now parameter values saved into the xml are not overridden by inlets, only by GUI. That means that inlets without a GUI do not save their state in the xml. As you say, the really important data to save are the last manually-entered ones. |
Slowly getting to saving functionality in #22 ... Any new ideas in this area ? Do we stick with ofxXmlSettings (based on TinyXML) ?
Another option is ofxPugiXML based on [an old] PugiXML.
Also, regarding the karmaMapper approach (above), it would be great not to pass the full XML handle (mosaicNodes could mess up each other // security). |
I like the idea of having a faster xml library, if PugiXML is really way faster, we can take a look at it, and check if the added code difficulty is worth or not. About the full XML handle, let's clarify how is working right now:
<github>https://github.com/d3cod3/mosaic</github>
<www>https://mosaic.d3cod3.org</www>
<settings>
<output_width>1280</output_width>
<output_height>720</output_height>
<dsp>0</dsp>
<audio_in_device>0</audio_in_device>
<audio_out_device>0</audio_out_device>
<sample_rate_in>44100</sample_rate_in>
<sample_rate_out>44100</sample_rate_out>
<buffer_size>1024</buffer_size>
<input_channels>0</input_channels>
<output_channels>0</output_channels>
<bpm>120</bpm>
</settings>
<!-- ............ -->
</settings>
<object>
<id>1</id>
<name>audio device</name>
<filepath>none</filepath>
<position>
<x>558.000000000</x>
<y>538.000000000</y>
</position>
<outlets>
<link>
<type>4</type>
<name>IN CHANNEL 1</name>
<to>
<id>2</id>
<inlet>0</inlet>
</to>
</link>
</outlets>
<vars></vars>
<inlets>
<link>
<type>4</type>
<name>OUT CHANNEL 1</name>
</link>
<link>
<type>4</type>
<name>OUT CHANNEL 2</name>
</link>
</inlets>
</object>
<object>
<id>2</id>
<name>audio analyzer</name>
<filepath>none</filepath>
<position>
<x>968.000000000</x>
<y>229.000000000</y>
</position>
<outlets>
<link>
<type>2</type>
<name>analysisData</name>
</link>
</outlets>
<vars>
<var>
<name>INPUT_LEVEL</name>
<value>1.000000000</value>
</var>
<var>
<name>SMOOTHING</name>
<value>0.000000000</value>
</var>
</vars>
<inlets>
<link>
<type>4</type>
<name>signal</name>
</link>
<link>
<type>0</type>
<name>level</name>
</link>
<link>
<type>0</type>
<name>smooth</name>
</link>
</inlets>
</object> So, every object append a block on the XML, with basic info, id, name, position on the canvas, filepath ( sometimes used, sometimes not, specifically for file related objects ), a list of outlets ( with the wires ) and inlet ( fixed in most cases, variable for some objects with reconfigure inlets/outlets capabilities ), and finally a list of vars, custom and different for every object needs ( as you see in the example, audio analyzer object have two knob in his gui, which value is saved in the XML as reference for restoring the object when loading the previously saved patch ) And every object just access his block into the XML ( via the id ), never the others. Now, from ofxVisualProgramming, we access the entire XML, for modifying the main patch settings, for deleting a link or a group of links, deleting an object with all his current links, etc... I don't see the problem with the full handle ( or maybe i misinterpreted your question ), but i think this mechanism is pretty similar to what you propose? Anyway, let me know is this clear the idea of loading/saving, and is you think it can work with the new implementation. |
Thanks for the details, and yes the mechanisms are similar. // (pseudo code)
mosaicSaveHandle = XML(file_path);
saveMosaicXML( mosaicSaveHandle );
for(object in patch->Objects){
objectSaveHandle = new XML(); // sandbox
object->saveToXML(objectSaveHandle); // cannot access/mess other object data
if( objectSaveHandle.isValidXML() ) mosaicSaveHandle.append(objectSaveHandle);
}
object::saveToXML(handle){
baseObject::saveToXML(handle); // name, id, etc.
for(param in this->params){
param.saveToXML(handle); // append params
}
handle.append(customStuff); // other ?
} Something like this will definitely work with the current implementation, with or without sandboxing. There's nothing like continuous saving, right ? (is the XML written on any [param/other] change, or only when the [entire] patch is saved [autosave or manual] ?) |
Right now it is autosaving, the XML is automatically written on any param/other change, but it is a good idea to have both options ( autosave and manual ) so the user can choose his style About the sandboxing, do it as you see it better, just a silly question, the append is perfect for creating a new object, but what when we edit a previously created object? We have here the basic logic CREATE/LOAD/EDIT/REMOVE the sandboxing is just for the CREATE part, right? And about the patchfile location, yes, is needed for objects with loading file capabilities for extracting the working patch data folder, this var in patchObject: |
Ok, just updated ofxPugiXML : https://github.com/Daandelange/ofxPugiXML But first, I didn't realize that Mosaic uses autosaving. Then my method won't work; I'll re-consider my proposal. Let's keep the sandboxing idea for later. (it would be where possible) |
The working dir is for patches that loads other files in some of his objects, having a data/ folder ( i called it like this to maintain some relation with OF ) is to porting the patch from one computer to another, basically: i made a patch in my laptop with linux, i want to share it with you, the patch named testingPatch is located in a folder with the same name
and inside the data folder are stored all the files used inside the patch (video files, script files, sound files, etc...) so i zip the testingPatch folder --> testingPatch.zip send the file to you all the needed files are there in the data folder, but the paths ( absolute ) in testingPatch.xml are still the ones working on my laptop, but no problem, at opening the patch, thanks to every node knowing filepath and working dir ( locally ), the paths will be refreshed before loading the nodes into the canvas so you'll have my patch working in your machine without problems |
Ok, that I understand that part. As I understand it, when customVars are edited, autosave is triggered. (Or manual save, not an issue) Now about the file paths and working directory stuff, I see that each object stores an instance of the save file location. Anyways, playing around with PugiXML, it definitely offers more options to traverse and edit xml trees. Note: TinyXML and PugiXML are both DOM parsers, which means that the loadable files are limited by the available memory (RAM). (not an issue for us, except if we target very-small-memory-devices ?) |
Well, the write is not over the entire xml file, if you check the Optimized, no, is not the best solution, was the fastest one at the moment, i was constantly running in the first two years of Mosaic development, due to the strict relation with the use of the software at the university, so there's a lot of stuff "fixed" to just work, and that need a better logic. I like your idea of update the xml from time to time, but i would like to maintain all the possibilities: manual saving, automatic saving every N minutes, and constant auto-saving, having the auto-saving yet implemented, it will not be a problem, and we can adjust the logic to obtain a better code. And the limit over the RAM, not a problem, Mosaic patches are really small, the heaviest object in Mosaic ( sequencer, 5 inlets, 21 outlets, 64x5 = 320 custom vars ) is 34kb in the .xml, and a patch with 60 objects loaded is usually around 60 kb, so we can talk, as average ( minus some specific objects ) 1 kb x object So it's not an issue, considering that this kind of software is not designed to run on a very-small-memory-device (it would be nice to have it compiling for arm64 and running on a raspberry pi4) |
Let's go for several save modes then, starting with auto, the most complicated indeed. Btw, no, it's a detail but it's a total file rewrite. You don't parse all the XML tree, but you traverse it as needed, then you edit it where needed (modifications go to the DOM buffer), then write the whole DOM to a file. if(XML.loadFile(patchFile)){ // puts file buffer in DOM buffer
// [...] do stuff with DOM buffer (traverse, modify)
saved = XML.saveFile(); // writes DOM to a file
} One more question, why is |
Oh, so i always misunderstood the DOM buffer, well, better later than never, thanks for the clarification! The |
Ok, I see, but then Also, I feel we're gonna end up with a new xml syntax, making older patches incompatible. (almost inevitable) |
Yes, i was preoccupied by that, but i too think the incompatibility with previous versions is inevitable. For the filepath, yes, is a custom object var, used only by a bunch of objects, so it can be a fileParameter, or a simpler stringParameter. For all the rest, all good ideas, and good restructuring of the xml, it sounds right! |
I was wondering if there's a way to write only changed nodes in a file, TinyXML still seems to be the best choice. |
Thanks for searching for better options, it seems that the best choice is to stay on TinyXML and ofxXmlSettings Adding some features as sandboxing and maybe some better logic, i'm sure we'll have a more than satisfactory result, until now there were never issues with the xml load/save stuff. |
Whoops, sorry, I meant to say PugiXML is the best choice : a little faster and more future proof than ofxXmlSettings/TinyXML. |
Ok then, we'll switch to PugiXML then, maybe we can write some methods to have it imitating ofxXmlSettings functions, so the port will be super easy? |
It will be super easy, it works the same way with better attribute support and more traversing options. |
I've pulled some basic api for ofxPugiXML to mimic ofxXmlSettings and simplify the future Mosaic port |
Just discovered that pugixml 1.9 is included in OF0.11, and ofXml class is based on that, maybe we could extend the class and use that instead of the ofxAddon? |
Yes, or make the ofxAddon use the embedded library, depending on how much ofXml restricts pugiXML's capabilities. |
The text was updated successfully, but these errors were encountered: