ValueTree Binary Serialization

Hi,

I’m using a AudioProcessorValueTreeState to maintain the state of all parameters in the synth and I’m trying to create a data blob to put in a database. My issue is that the ValueTree is only writing out it’s own name and none of the information of it’s properties and children.
The docs for ValueTree::writeToStream say:

Stores this tree (and all its children) in a binary format.

Can anyone say why my use of write to stream is only writing the root name with usage like so?

auto valueTree = ValueTree("params");
//add a bunch of parameters / properties
//assign state container to APVTS
processorState->state = valueTree;

valueTree.getNumChildren() //gives 103

MemoryOutputStream stateStream;
valueTree.writeToStream(stateStream);
const char * data = static_cast<const char *>(stateStream.getData());

String(data, stateStream.getDataSize()); //gives "params"

My first post, much thanks for any help.

Binary is binary, you shouldn’t be handling it as a text string at that point. Why are you trying to construct a Juce text String out of the binary data? If you need to monitor/debug the contents for some reason, you need to do it in some other way. If it is about storing the data, where are you trying to store the data and how?

The conversion to a String isn’t the problem here, that is just a demonstration of doing something else with the data.

Of course it’s a problem. The String constructor you are using does not like character values larger than 127 in the passed in data. That is explained in the Juce String documentation. Also, if the there is a zero character in the data, the conversion of the string is stopped before the maxchars length.

Assumably your database has a way to store binary blob data, then you should be using that facility. If it is missing that, maybe convert the binary data to a base64 encoded string.

3 Likes

You are right about stings, I guess I assumed it could encode the data automatically (silly).

I was trying to get around writing a new ‘route’ into the database for blobs by using the existing string methods. I have moved on to inserting the raw data directly using juce::MemoryBlock.

The problem I have now is getting the data out, which is using juce::var to wrap the various field types. The case for the blob is like:

case SQLITE_BLOB: {
    //allocate block that won't be cleaned up. 
    size_t  blk_size = sqlite3_column_bytes(statement, col);
    auto blk = new MemoryBlock(sqlite3_column_blob(statement, col), blk_size);
    row_values.emplace_back(var(*blk));
    break;
}

This is one clause of a statement result table that is per field and constructs a vector<vector<var>> to return to the ‘client’.

I think there’s something wrong with this approach, too much copying and along the way the var gets destroyed and the block size becomes undefined. I’m aware of the note on var’s with binary data:

Beware when you use this - the MemoryBlock pointer is only valid for the lifetime of the variant that returned it, so be very careful not to call this method on temporary var objects that are the return-value of a function, and which may go out of scope before you use the MemoryBlock!

The question is what is the proper way to employ move semantics on collections of vars? Can you even use a temporary var as a transmission mechanism for binary data?