diff --git a/pd/doc/5.reference/savestate-example.pd b/pd/doc/5.reference/savestate-example.pd new file mode 100644 index 0000000000000000000000000000000000000000..dc10b95c5fb167c7746026caf8b7df1007e4292c --- /dev/null +++ b/pd/doc/5.reference/savestate-example.pd @@ -0,0 +1,49 @@ +#N canvas 112 69 693 505 12; +#X obj 36 158 osc~; +#X floatatom 103 72 7 0 10000 1 frequency - -; +#X obj 37 336 outlet~; +#X floatatom 103 97 7 0 10000 1 bandwidth - -; +#X obj 109 152 max 1; +#X obj 109 178 t b f; +#X obj 37 279 *~; +#X obj 37 307 expr~ exp(-$v1*$v1); +#X obj 36 125 * 0.5; +#X obj 348 242 f; +#X obj 348 271 pack; +#X obj 288 145 savestate; +#X obj 288 316 unpack; +#X obj 103 207 / 1; +#X text 63 19 this pulse generator is used by the savestate help file. +; +#X text 80 388 This is a waveshaping pulse generator using a Gaussian +table lookup \, as described in Theory and Technique of electronic +music \, chapter 6 The controls are "F" to set fundamental frequency +\, and "BW" to set bandwidth \, both in cycles per second.; +#X text 362 145 right outlet gets bang when parent patch is saved. +In response we send 'savestate' one or more lists that will be stored +as part of the parent patch, f 35; +#X text 344 304 Left outlet returns the list or lists to the abstraction +which can use them to restore its state when it is reloaded., f 38 +; +#X text 389 237 In response \, we build and send a list of parameters +we want to save, f 34; +#X connect 0 0 6 0; +#X connect 1 0 8 0; +#X connect 1 0 9 1; +#X connect 3 0 10 1; +#X connect 3 0 13 0; +#X connect 4 0 5 0; +#X connect 5 0 13 0; +#X connect 5 1 13 1; +#X connect 6 0 7 0; +#X connect 7 0 2 0; +#X connect 8 0 0 0; +#X connect 8 0 4 0; +#X connect 9 0 10 0; +#X connect 10 0 11 0; +#X connect 11 0 12 0; +#X connect 11 1 9 0; +#X connect 12 0 1 0; +#X connect 12 1 3 0; +#X connect 13 0 6 1; +#X coords 0 -1 1 1 125 70 1 100 50; diff --git a/pd/doc/5.reference/savestate-help.pd b/pd/doc/5.reference/savestate-help.pd new file mode 100644 index 0000000000000000000000000000000000000000..83ffe822025608710d64631217b3d0b368b013d4 --- /dev/null +++ b/pd/doc/5.reference/savestate-help.pd @@ -0,0 +1,34 @@ +#N canvas 711 48 578 672 12; +#X obj 82 73 savestate-example; +#A saved 110 660; +#X text 223 71 open the abstraction at left (right- or CTRL- click +and select "open" in popup menu) to see how the savestate object is +used from within., f 46; +#X obj 82 158 savestate-example; +#A saved 221 440; +#X text 223 162 parameters for different copies of the abstraction +are saved and restored independently., f 32; +#X text 348 610 updated for Pd version 0.49.; +#X text 81 445 The abstraction may itself be modified at will without +disturbing the saved states of its copies in any calling patches \, +as long as the usage of the saved and restored lists is kept compatible. +; +#X text 80 512 Multiple savestate objects are not differentated - they +all receive all lists sent to any one of them.; +#X text 81 550 Hint: 'text' objects can be saved/restored using 'text +tolist' and 'text fromlist'.; +#X text 82 422 Abstractions within 'clone' objects are not handled. +; +#X obj 75 27 savestate; +#X text 148 26 - save and restore run-time state from within an abstraction +, f 70; +#X text 81 250 The savestate object is used inside abstractions to +save their state as they are used in a calling (parent) patch. When +the parent patch (such as this one \, which calls the "savestate-example" +abstraction) is saved \, the included savestate object sends a 'bang' +message out its right outlet \, with which the abstraction may respond +by presenting one or more 'list' messages back to the 'savestate' object. +These lists are saved as part of the calling patch. If the calling +patch is reopened later \, the lists are sent out the left outlet of +the savestate object. The abstraction can then use them to restore +its state.; diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index b4e4fd4c86783196027080d8919aa75828132d11..e29d802d7a92cac7aa11515eabde57056edd7efd 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -1391,6 +1391,8 @@ static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, void canvas_popabstraction(t_canvas *x) { newest = &x->gl_pd; + gensym("#A")->s_thing = 0; + pd_bind(newest, gensym("#A")); pd_popsym(&x->gl_pd); //x->gl_loading = 1; //fprintf(stderr,"loading = 1 .x%lx owner=.x%lx\n", x, x->gl_owner); diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c index a3affb88f8ac10b4cd97e6e523a18e420c7387e3..672d536bb12c9ba32c39d515fedfafc944271bf3 100644 --- a/pd/src/g_readwrite.c +++ b/pd/src/g_readwrite.c @@ -17,6 +17,91 @@ file format as in the dialog window for data. #include "g_canvas.h" #include <string.h> +/* object to assist in saving state by abstractions */ +static t_class *savestate_class; + +typedef struct _savestate +{ + t_object x_obj; + t_outlet *x_stateout; + t_outlet *x_bangout; + t_binbuf *x_savetobuf; +} t_savestate; + +static void *savestate_new(void) +{ + t_savestate *x = (t_savestate *)pd_new(savestate_class); + x->x_stateout = outlet_new(&x->x_obj, &s_list); + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + x->x_savetobuf = 0; + return (x); +} + + /* call this when the owning abstraction's parent patch is saved so we + * can add state-restoring messages to binbuf */ +static void savestate_doit(t_savestate *x, t_binbuf *b) +{ + x->x_savetobuf = b; + outlet_bang(x->x_bangout); + x->x_savetobuf = 0; +} + + /* called by abstraction in response to savestate_doit(); lists received + here are added to the parent patch's save buffer after the line that will + create the abstraction, addressed to "#A" which will be this patch after + it is recreated by reopening the parent patch, pasting, or "undo". */ +static void savestate_list(t_savestate *x, t_symbol *s, int argc, t_atom *argv) +{ + if (x->x_savetobuf) + { + binbuf_addv(x->x_savetobuf, "ss", gensym("#A"), gensym("saved")); + binbuf_add(x->x_savetobuf, argc, argv); + binbuf_addv(x->x_savetobuf, ";"); + } + else pd_error(x, "savestate: ignoring message sent when not saving parent"); +} + +static void savestate_setup(void) +{ + savestate_class = class_new(gensym("savestate"), + (t_newmethod)savestate_new, 0, sizeof(t_savestate), 0, 0); + class_addlist(savestate_class, savestate_list); +} + +void canvas_statesavers_doit(t_glist *x, t_binbuf *b) +{ + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + if (g->g_pd == savestate_class) + { + savestate_doit((t_savestate *)g, b); + } + else if (g->g_pd == canvas_class && + !canvas_isabstraction((t_canvas *)g)) + { + canvas_statesavers_doit((t_glist *)g, b); + } + } +} + +void canvas_saved(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + if (g->g_pd == savestate_class) + { + outlet_list(((t_savestate *)g)->x_stateout, 0, argc, argv); + } + else if (g->g_pd == canvas_class && + !canvas_isabstraction((t_canvas *)g)) + { + canvas_saved((t_glist *)g, s, argc, argv); + } + } +} + void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b); /* the following routines read "scalars" from a file into a canvas. */ @@ -921,6 +1006,7 @@ static void canvas_menuprint(t_canvas *x) void g_readwrite_setup(void) { + savestate_setup(); class_addmethod(canvas_class, (t_method)glist_write, gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)glist_read, @@ -931,6 +1017,8 @@ void g_readwrite_setup(void) gensym("savetofile"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0); class_addmethod(canvas_class, (t_method)canvas_saveto, gensym("saveto"), A_CANT, 0); + class_addmethod(canvas_class, (t_method)canvas_saved, + gensym("saved"), A_GIMME, 0); /* ------------------ from the menu ------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menusave, gensym("menusave"), A_DEFFLOAT, 0); diff --git a/pd/src/g_text.c b/pd/src/g_text.c index 4eae176faf5ab4d41d05110fdea158b946211b36..2a1ef4879702c3c1e9827a7f05ebdf2e8e17111e 100644 --- a/pd/src/g_text.c +++ b/pd/src/g_text.c @@ -2182,6 +2182,7 @@ static int text_click(t_gobj *z, struct _glist *glist, else return (0); } +void canvas_statesavers_doit(t_glist *x, t_binbuf *b); void text_save(t_gobj *z, t_binbuf *b) { //fprintf(stderr, "text_save\n"); @@ -2211,6 +2212,7 @@ void text_save(t_gobj *z, t_binbuf *b) //fprintf(stderr, "this must be it\n"); binbuf_addbinbuf(b, x->te_binbuf); //fprintf(stderr, "DONE this must be it\n"); + } else if (x->te_type == T_MESSAGE) { @@ -2292,6 +2294,13 @@ void text_save(t_gobj *z, t_binbuf *b) binbuf_addv(b, ",si", gensym("f"), (int)x->te_width); } binbuf_addv(b, ";"); + + /* if an abstraction, give it a chance to save state */ + if (pd_class(&x->te_pd) == canvas_class && + canvas_isabstraction((t_canvas *)x)) + { + canvas_statesavers_doit((t_glist *)x, b); + } } /* this one is for everyone but "gatoms"; it's imposed in m_class.c */