Xybrid's various save formats are CBOR objects; CBOR was chosen over msgpack due to Qt5.12 having built-in support

probably have a field of raw data for (some) node saves, and keep it resident in case of plugin crashes

extensions {
    .xyp - project
    .xyg - graph
}






project: [
    "xybrid:project"
    (int version)
    {
        "meta": { "artist": ... "title": ... "comment": ... etc. }
        "patterns": [ array of pattern structs ]
        "sequence": [ array of pattern numbers, int, separator is anything negative ]
        "graph": { root graph (contents) }
    }
]

pattern: {
    "name": "asdf"
    "channels": [ {
        "name": "asdf"
        "rows": [ // probably better name...
            [ int port, int note, n*(int c, int amt) ] // for each row
            // any number of empty rows is expressed as an integer number of rows skipped
            // this number is only written if there are any non-empty rows after
        ]
    } ] // if no channels have their last row filled, an integer representing the
        // total number of rows is written as the last entry of the channel array
}

sample: {
    "name": "asdf"
    "type": "raw"
    "rate": 48000
    "channels": 2
    "data" : [ len*channels float32s as raw bytefield ]
}

also sample variant using flac?
