On the Current Utility of Binbufs and Atombufs
My slow-burn project, python3 externals for Max project requires that I wrap a useful subset of the Max c-api in cython for use by python scripts running in the external.
This process has brought me into immediate contact with the max c-api interface for a number of objects and data structures in the rich Max ecosystem: atoms, atom arrays, dictionaries, buffers, binbufs, atombufs, patchers, etc..
Sometimes this ends up being very useful, like the outcome of recent work which implemented the python buffer protocol for the Buffer wrapper class allowing python scripts to read and write python builtin arrays and numpy arrays to `buffer~` objects...
Other times, I feel a bit like an archeologist, as when methods for curious objects still exist in the Max headers but where the documentation is sparse or abbreviated or includes obscure hints of prior historical use.
Of these, I found binbufs, and to a much lesser extent, atombufs, to be the most curious, as it was not immediately clear what should be done with them. The 8.2 Max C-API is particularly cryptic about binbufs "..you won’t need to know about the internal structure of a Binbuf, so you can use the void ∗ type to refer to one" and searching for code which uses the 'binbuf' object only revealed that it is still used today in puredata as the textual format for its `.pd` filetype.
Atombufs are also curious as they have an even more limited set of methods and seem to be just a struct container for atoms with an 'argc' and 't_atom *argv' field. I've implemented a wrapper for the methods but I can't figure out where to use them or what methods actually received atombuf objects.
Further research on the web uncovered a copy of the fascinating "Writing External Objects for Max 4.0 and MSP 2.0" by David Zicarelli which was most informative in its description of how binfbufs were used in Max as
"... the mechanism Max uses to save messages, called Binbufs (short for binary buffer)." Binbufs are an “Atomized” counterpart of the Max text file. When you copy or duplicate part of a patch, it’s turned into a Binbuf. The Binbuf can then be “evaluated” because it consists of Max messages."
I was wondering if this functionality still exists in whole or in part in Max 8.x and I took the minimal example used in the book below and copied into the clipboard and then from Max > File > New from Template and I was quite pleased to find that it created a patcher window with the relevant connected objects!
max v2;
#N vpatcher 50 38 450 338;
#P number 138 67 35 0;
#P number 98 67 35 0;
#P number 98 168 35 0;
#P newex 98 127 50 0 +;
#P connect 0 0 1 0;
#P connect 3 0 0 1;
#P connect 2 0 0 0;
#P pop;
I tried to replicate this, with some limited success with the current binbuf methods in the Max c-api, and found that by evaluating something like this "#N buffer~ buf jongly.aif" in a binbuf I could create a hidden buffer object called `buf` in the patcher, but when I tried to send "#P" messages to the patcher as per the current "(script <msg>" format to [thepatcher]", I got stuck with errors and crashes of Max proper, and I could not map from the historical way (binbufs) to the new "scripting" way in Max 8.x..
It would be greatly appreciated if someone in the know, could help uncover the mysteries of binbufs and answer the question of whether the functionality of scripting in the above economical message-oriented format is still fully available at the Max c-api level.
As the author of py2max, a python library for offline generation the .maxpat json format, I'm particularly aware of how useful and entertaining this can be.
S
Hey Shakeeb, I *think* I remember being told by someone who knew more about them that they are an older system still in the code for backwards compatibility and can be ignored for green field work. But I can't remember much beyond that. There might be a conversation with me about them somewhere in search archives?
Hi Iain,
Yup, I've come around to the same conclusion: that in current versions of Max, the t_binbuf object is perhaps there mainly for compatibility and may be even be reduced in power.
This can be tested as follows:
max v2;
#N vpatcher 50 38 450 338;
#P number 138 67 35 0;
#P number 98 67 35 0;
#P number 98 168 35 0;
#P newex 98 127 30 12 + 100;
#P connect 0 0 1 0;
#P connect 3 0 0 1;
#P connect 2 0 0 0;
#P pop;
Copy the above and do a File > "New from clipboard" and it'll work: basically create a new patcher window with connected objects.
But if you create a t_binbuf via binbuf_new() and then do a binbuf_text(), followed by binbuf_eval() with the above, it'll won't throw an error (indicating that it's parsed correctly), but it will do nothing..
S
i'm not sure about binbuf_eval but you could do the "new from clipboard"-way in the code:
char* srctext = "max v2;\
#N vpatcher 50 38 450 338;\
#P number 138 67 35 0;\
#P number 98 67 35 0;\
#P number 98 168 35 0;\
#P newex 98 127 50 0 + ;\
#P connect 0 0 1 0;\
#P connect 3 0 0 1;\
#P connect 2 0 0 0;\
#P pop;";
t_binbuf* myBB = binbuf_new();
binbuf_text(myBB, &srctext, strlen(srctext));
t_object* myClipboard = object_new(_sym_nobox, _sym_clipboard);
object_method(myClipboard, gensym("frombinbuf"), myBB);
object_method(_sym_max->s_thing, gensym("newfromclipboard"));
object_free(myClipboard);
freeobject(myBB);
But why you wan't to use this deprecated stuff? You know that you can use the script interface of [thispatcher] in code as well?
Thanks @11OLSEN
That's really cool, I didn't know you could do that... Actually, to your point I have been focusing on `scripting in the patcher`. Here's my current implementation of the patcher object as a cython extension:
Hi @11OLSEN
I converted your nifty code into a method of the binbuf object:
def new_from_clipboard(self, str text):
"""Evaluate the text in the binbuf by sending it the Max clipboard
Thanks to 11OLSEN for the nifty solution
https://cycling74.com/forums/on-the-current-utility-of-binbufs-and-atombufs
"""
cdef MaxApp app = MaxApp()
cdef mx.t_object* clipboard = <mx.t_object*>mx.object_new(
mx.gensym("nobox"), mx.gensym("clipboard"))
self.add_text(text)
mx.object_method(clipboard, mx.gensym("frombinbuf"), self.ptr)
mx.object_method(app.ptr, mx.gensym("newfromclipboard"))
mx.object_free(clipboard)
I couldn't access `_sym_max->s_thing` for some reason, but the above is equivalent. (-:
Ok, glad it helps.