From 1bdf5157d0707e89b1bce0a939fbcfcf33047816 Mon Sep 17 00:00:00 2001 From: mescalinum Date: Wed, 19 Aug 2009 23:51:59 +0000 Subject: [song] is born svn path=/trunk/externals/ffext/; revision=11988 --- composer/arraylist.h | 6 +- composer/common.h | 29 +++++ composer/composer.c | 49 ++++++++ composer/pattern.c | 16 ++- composer/song.c | 19 +++- composer/song_proxy.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++ composer/track.c | 130 +++++++++++++++++++++- composer/track_proxy.c | 201 +++++++++------------------------ 8 files changed, 585 insertions(+), 161 deletions(-) diff --git a/composer/arraylist.h b/composer/arraylist.h index a4b4362..b625379 100644 --- a/composer/arraylist.h +++ b/composer/arraylist.h @@ -3,7 +3,7 @@ /* For information on usage and redistribution, and for a DISCLAIMER OF ALL */ /* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* */ -/* composer: a music composition framework for pure-data */ +/* arraylist.h: macro library for dynamic arrays */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ @@ -21,7 +21,6 @@ /* along with this program; if not, write to the Free Software Foundation, */ /* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ -/* Based on PureData by Miller Puckette and others. */ /* ------------------------------------------------------------------------ */ #include "common.h" @@ -41,7 +40,8 @@ #define ArrayListInit(arrName, type, initSize) \ arrName ## _maxsize = initSize; \ - arrName = (type*)getbytes(sizeof(type) * (initSize)); \ + if(initSize > 0) arrName = (type*)getbytes(sizeof(type) * (initSize)); \ + else arrName = NULL; \ arrName ## _count = 0 #define ArrayListAdd(arrName, type, objToAdd) \ diff --git a/composer/common.h b/composer/common.h index 516836f..f9005cf 100644 --- a/composer/common.h +++ b/composer/common.h @@ -55,6 +55,8 @@ #define TRACK_SELECTOR "#TRACK" #define SONG_SELECTOR "#SONG" +static void list_snconvf(char *buf, size_t bufsz, t_symbol* s, size_t argc, t_atom* argv); + struct _track; struct _pattern; @@ -62,6 +64,7 @@ typedef struct _song { t_symbol* x_name; ArrayListDeclare(x_tracks, struct _track*, t_int); + struct _track* x_mastertrack; } t_song; typedef struct _song_proxy @@ -70,6 +73,7 @@ typedef struct _song_proxy t_outlet* outlet; t_song* x_song; t_int b_editor_open; + t_symbol* rcv; } t_song_proxy; typedef struct _track @@ -99,20 +103,25 @@ typedef struct _pattern } t_pattern; static t_song* song_new(t_symbol* song_name); +static void song_mastertrack_fix_cols(t_song* x); static void song_free(t_song* x); static t_song* song_get(t_symbol* song_name); static int song_exists(t_symbol* song_name); static t_track* track_new(t_symbol* song_name, t_symbol* track_name, t_int columns); +static t_track* mastertrack_new(t_song* song, t_symbol* track_name, t_int columns); +static t_track* song_create_track(t_song* x, t_symbol* track_name, t_int columns); static void track_free(t_track* x); static t_track* track_get(t_symbol* song_name, t_symbol* track_name); static int track_exists(t_symbol* song_name, t_symbol* track_name); +static void track_binbuf_save(t_track* x); static t_pattern* pattern_new(t_track* track, t_symbol* name, t_int rows); static t_pattern* pattern_clone(t_pattern* src, t_symbol* newname); static void pattern_free(t_pattern* x); static void pattern_rename(t_pattern* x, t_symbol* newname); static void pattern_resize(t_pattern *x, t_int newsize); +static void pattern_resize_cols(t_pattern* x, t_int newcols); static void pattern_new_empty_row(t_pattern* x); static t_atom* pattern_getrow(t_pattern* x, t_int row); static t_atom* pattern_clone_row(t_pattern* x, t_atom* row); @@ -127,7 +136,27 @@ static t_song_proxy* song_proxy_new(t_symbol* song_name); static void song_proxy_free(t_song_proxy* x); static void song_proxy_float(t_song_proxy* x, t_floatarg f); static void song_proxy_properties(t_gobj* z, t_glist* owner); +static void song_proxy_properties_close(t_gobj* z, t_glist* owner); static void song_proxy_save(t_gobj* z, t_binbuf* b); +static t_atom* song_proxy_gettracks(t_song_proxy* x); +static void song_proxy_gettracks_o(t_song_proxy* x); +static t_int song_proxy_gettracks_count(t_song_proxy* x); +static void song_proxy_gettracks_count_o(t_song_proxy* x); +static void song_proxy_anything(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv); +static void song_proxy_loaddata(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv); +static t_atom* song_proxy_getpatternlength(t_song_proxy* x, t_symbol* pat_name); +static void song_proxy_editcmd(t_song_proxy* x, t_symbol* s_, int argc, t_atom* argv_); +static void song_proxy_sendgui(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv); +static void song_proxy_setrow(t_song_proxy* x, t_symbol* sel, int argc, t_atom* argv); +static t_atom* song_proxy_getrow(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static t_atom* song_proxy_getrow_with_header(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static void song_proxy_getrow_o(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static void song_proxy_setcell(t_song_proxy* x, t_symbol* sel, int argc, t_atom* argv); +static t_atom* song_proxy_getcell(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static t_atom* song_proxy_getcell_with_header(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static void song_proxy_getcell_o(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static t_pattern* song_proxy_resizepattern(t_song_proxy* x, t_symbol* name, t_floatarg rows); +static t_atom* song_proxy_get_track_names(t_song_proxy* x); void track_proxy_setup(void); static t_track_proxy* track_proxy_new(t_symbol* song_name, t_symbol* track_name, t_floatarg cols); diff --git a/composer/composer.c b/composer/composer.c index dd5ccab..6d86758 100644 --- a/composer/composer.c +++ b/composer/composer.c @@ -34,6 +34,55 @@ ArrayListDeclare(songs, t_song*, int); +void list_snconvf(char *buf, size_t bufsz, t_symbol* s, size_t argc, t_atom* argv) { + static const unsigned int tmpsz = 80; + char* tmp; + size_t i,j,len; + if(bufsz < 1) goto list_snconvf_szbug; + buf[0] = '\0'; + len = 0; + if(s != gensym("")) { + len += strlen(s->s_name) + 1; + if(bufsz <= len) goto list_snconvf_szbug; + strncat(buf, s->s_name, bufsz); + strncat(buf, " ", bufsz); + } + for(i = 0; i < argc; i++) { + if(i > 0) { + len += 1; + if(bufsz <= len) goto list_snconvf_szbug; + strncat(buf, " ", bufsz); + } + if(argv[i].a_type == A_FLOAT) { + tmp = (char*)getbytes(tmpsz*sizeof(char)); + if(argv[i].a_w.w_float == (t_float)(t_int)argv[i].a_w.w_float) + sprintf(tmp, "%ld", (t_int)argv[i].a_w.w_float); + else + sprintf(tmp, "%f", argv[i].a_w.w_float); + len += strlen(tmp); + if(bufsz <= len) goto list_snconvf_szbug; + strncat(buf, tmp, bufsz); + freebytes(tmp, tmpsz*sizeof(char)); + } else if(argv[i].a_type == A_SYMBOL) { + len += strlen(argv[i].a_w.w_symbol->s_name); + if(bufsz <= len) goto list_snconvf_szbug; + strncat(buf, argv[i].a_w.w_symbol->s_name, bufsz); + } else { + len += 4; + if(bufsz <= len) goto list_snconvf_szbug; + strncat(buf, "null", bufsz); + } + } + if(strlen(buf) >= bufsz) { + goto list_snconvf_szbug; + } + return; + +list_snconvf_szbug: + debugprint("track: BUG: list_snconvf_szbug (message too long)"); + bug("track: BUG: list_snconvf_szbug (message too long)"); +} + void composer_setup(void) { debugprint("loading composer library for pd"); sys_vgui("source {window.tk}\n"); diff --git a/composer/pattern.c b/composer/pattern.c index 95afed2..819c585 100644 --- a/composer/pattern.c +++ b/composer/pattern.c @@ -28,7 +28,7 @@ static t_pattern* pattern_new(t_track* track, t_symbol* name, t_int rows) { ArrayListGetByName(track->x_patterns, name, t_pattern*, obj); - debugprint("pattern_new - object lookup {{%s}} => " PTR, name->s_name, obj); + debugprint("pattern_new - object lookup %s => " PTR, name->s_name, obj); if(obj) return obj; t_pattern* x = (t_pattern*)getbytes(sizeof(t_pattern)); @@ -86,6 +86,20 @@ static void pattern_resize(t_pattern *x, t_int newsize) { debugprint("final size: %d", x->x_rows_count); } +/* WARNING: do not call this for track with more than 1 pattern! + * Works only for the mastertrack (song_proxy) + */ +static void pattern_resize_cols(t_pattern* x, t_int newcols) { + int j; + for(j = 0; j < x->x_rows_count; j++) { + if(&x->x_rows[j]) + x->x_rows[j] = (t_atom*)resizebytes(x->x_rows[j], x->x_track->x_ncolumns, newcols); + else + x->x_rows[j] = (t_atom*)getbytes(sizeof(t_atom) * newcols); + } + x->x_track->x_ncolumns = newcols; +} + static void pattern_new_empty_row(t_pattern* x) { t_atom* rowdata = (t_atom*)getbytes(sizeof(t_atom) * x->x_track->x_ncolumns); int j; diff --git a/composer/song.c b/composer/song.c index 7c99877..424c52c 100644 --- a/composer/song.c +++ b/composer/song.c @@ -28,15 +28,17 @@ static t_song* song_new(t_symbol* song_name) { ArrayListGetByName(songs, song_name, t_song*, obj); - debugprint("song_new - object lookup {{%s}} => " PTR, song_name->s_name, obj); + debugprint("song_new - object lookup %s => " PTR, song_name->s_name, obj); if(obj) return obj; t_song* x = (t_song*)getbytes(sizeof(t_song)); x->x_name = song_name; - ArrayListInit(x->x_tracks, struct _track*, 16); + ArrayListInit(x->x_tracks, struct _track*, 16); + // mastertrack is named like the song + x->x_mastertrack = mastertrack_new(x, x->x_name, 0); debugprint("created a song object (" PTR "), " - "creation args: {{%s}}", + "creation args: %s", x, x->x_name->s_name); ArrayListAdd(songs, t_song*, x); @@ -44,6 +46,17 @@ static t_song* song_new(t_symbol* song_name) { return x; } +static void song_mastertrack_fix_cols(t_song* x) { + debugprint("song_mastertrack_fix_cols(" PTR "), new track count: %d", x, x->x_tracks_count); + debugprint("song='%s' mastertrack=" PTR, x->x_name->s_name, x->x_mastertrack); + debugprint("we have %d patterns, sir", x->x_mastertrack->x_patterns_count); + int j; + for(j = 0; j < x->x_mastertrack->x_patterns_count; j++) { + if(j > 0) post("WARNING: mastertrack with more than one pattern!"); + pattern_resize_cols(x->x_mastertrack->x_patterns[j], x->x_tracks_count); + } +} + static void song_free(t_song* x) { // free tracks memory ArrayListFree(x->x_tracks, t_track*); diff --git a/composer/song_proxy.c b/composer/song_proxy.c index 9d40215..e1fd85f 100644 --- a/composer/song_proxy.c +++ b/composer/song_proxy.c @@ -28,6 +28,11 @@ t_class* song_proxy_class; +static t_atom response_pattern_length[2]; +static t_atom response_cell[4]; +static t_atom* response_row; // TODO: memory leaks check +static size_t response_row_sz; + void song_proxy_setup(void) { debugprint("registering 'song' class..."); song_proxy_class = class_new( @@ -52,18 +57,309 @@ static t_song_proxy* song_proxy_new(t_symbol* song_name) { x->outlet = outlet_new(&x->x_obj, &s_list); x->x_song = song_new(song_name); x->b_editor_open = 0; + char rcv_buf[80]; + sprintf(rcv_buf, "track_proxy-%s-%s", x->x_song->x_name->s_name, x->x_song->x_mastertrack->x_name->s_name); + x->rcv = gensym(rcv_buf); + pd_bind(&x->x_obj.ob_pd, x->rcv); + debugprint("created an instance of t_song_proxy: " PTR, x); return x; + + song_proxy_properties_close((t_gobj*) x, NULL); + + pd_bind(&x->x_obj.ob_pd, gensym(SONG_SELECTOR)); + + sys_vgui("pd::composer::init %s %s %s %d %d\n", x->rcv->s_name, x->x_song->x_name->s_name, x->x_song->x_mastertrack->x_name->s_name, x->x_song->x_mastertrack->x_ncolumns, DEBUG_BOOL); + + return x; } static void song_proxy_free(t_song_proxy* x) { + song_proxy_properties_close((t_gobj*) x, NULL); + + pd_unbind(&x->x_obj.ob_pd, gensym(SONG_SELECTOR)); + /* LATER find a way to get SONG_SELECTOR unbound earlier (at end of load?) */ + t_pd* x2; + while (x2 = pd_findbyclass(gensym(SONG_SELECTOR), song_proxy_class)) + pd_unbind(x2, gensym(SONG_SELECTOR)); + + pd_unbind(&x->x_obj.ob_pd, x->rcv); } static void song_proxy_float(t_song_proxy* x, t_floatarg f) { } static void song_proxy_properties(t_gobj* z, t_glist* owner) { + t_song_proxy* x = (t_song_proxy*)z; + sys_vgui("pd::composer::openWindow %s\n", x->rcv->s_name); + x->b_editor_open = 1; +} + +static void song_proxy_properties_close(t_gobj* z, t_glist* owner) { + t_song_proxy* x = (t_song_proxy*)z; + debugprint("song_proxy_properties_close(" PTR ", " PTR ") [editor is %s]", z, owner, x->b_editor_open ? "open" : "closed"); + if(x->b_editor_open) { + debugprint("closing..."); + sys_vgui("pd::composer::closeWindow %s\n", x->rcv->s_name); + x->b_editor_open = 0; + } } static void song_proxy_save(t_gobj* z, t_binbuf* b) { + t_track_proxy* x = (t_track_proxy*)z; + t_track* t = x->x_track; + + binbuf_addv(b, "ssiisssi;", gensym("#X"), gensym("obj"), + (t_int)x->x_obj.te_xpix, (t_int)x->x_obj.te_ypix, + gensym("track"), t->x_song->x_name, t->x_name, t->x_ncolumns); + + track_binbuf_save(t, gensym(TRACK_SELECTOR), b); +} + +static t_atom* song_proxy_gettracks(t_song_proxy* x) { + if(response_row) { + freebytes(response_row, response_row_sz); + response_row = NULL; + response_row_sz = 0; + } + response_row_sz = sizeof(t_atom) * x->x_song->x_tracks_count; + response_row = (t_atom*)getbytes(response_row_sz); + int i; + for(i = 0; i < x->x_song->x_tracks_count; i++) { + SETSYMBOL(&response_row[i], x->x_song->x_tracks[i]->x_name); + } + return &response_row[0]; +} + +static void song_proxy_gettracks_o(t_song_proxy* x) { + t_atom* rsp = song_proxy_gettracks(x); + if(rsp) + outlet_list(x->outlet, &s_list, response_row_sz, rsp); +} + +static t_int song_proxy_gettracks_count(t_song_proxy* x) { + return x->x_song->x_tracks_count; +} + +static void song_proxy_gettracks_count_o(t_song_proxy* x) { + outlet_float(x->outlet, (t_float)song_proxy_gettracks_count(x)); +} + +static void song_proxy_anything(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv) { + debugprint("song_proxy_anything(" PTR ", %s, %d, " PTR ")", x, s->s_name, argc, argv); + + if(s == gensym("DATA")) { + song_proxy_loaddata(x, s, argc, argv); + return; + } else if(s == gensym("EDIT")) { + song_proxy_editcmd(x, s, argc, argv); + return; + } else { + error("unrecognized command for anything method: %s ", s->s_name); + return; + } +} + +static void song_proxy_loaddata(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv) { + track_loaddata(x->x_song->x_mastertrack, argc, argv); +} + +static t_atom* song_proxy_getpatternlength(t_song_proxy* x, t_symbol* pat_name) { + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("song: getpatternlength: no such pattern: '%s'", pat_name->s_name); + return NULL; + } + SETSYMBOL(&response_pattern_length[0], pat->x_name); + SETFLOAT(&response_pattern_length[1], pat->x_rows_count); + return &response_pattern_length[0]; +} + +static void song_proxy_editcmd(t_song_proxy* x, t_symbol* s_, int argc, t_atom* argv_) { + if(argc < 1 || argv_[0].a_type != A_SYMBOL) { + error("track: editcmd: missing method selector"); + return; + } + /*if(argc < 2) { + error("track: editcmd: missing data after selector"); + return; + }*/ + + // route -> selector + t_symbol* s = argv_[0].a_w.w_symbol; + t_atom* argv = &argv_[1]; + argc--; + + t_symbol *s1 = (argc >= 1 && argv[0].a_type == A_SYMBOL) ? argv[0].a_w.w_symbol : NULL; + t_symbol *s2 = (argc >= 2 && argv[1].a_type == A_SYMBOL) ? argv[1].a_w.w_symbol : NULL; + t_float f2 = (argc >= 2 && argv[1].a_type == A_FLOAT) ? argv[1].a_w.w_float : 0; + t_float f3 = (argc >= 3 && argv[2].a_type == A_FLOAT) ? argv[2].a_w.w_float : 0; + + t_pattern* p = NULL; + int i,j; + + if(s == gensym("editor-open")) { + song_proxy_properties((t_gobj*) x, NULL); + } else if(s == gensym("editor-close")) { + song_proxy_properties_close((t_gobj*) x, NULL); + } else if(s == gensym("gettracks")) { + t_atom* rsp = song_proxy_get_track_names(x); + song_proxy_sendgui(x, gensym("tracks"), response_row_sz, rsp); + } else if(s == gensym("gettrackscount")) { + t_atom a; + SETFLOAT(&a, (t_float)song_proxy_gettracks_count(x)); + song_proxy_sendgui(x, gensym("trackscount"), 1, &a); + } else if(s == gensym("getpatternlength")) { + song_proxy_sendgui(x, gensym("patternlength"), 2, song_proxy_getpatternlength(x, s1)); + } else if(s == gensym("getrow")) { + song_proxy_sendgui(x, gensym("row"), 2 + x->x_song->x_mastertrack->x_ncolumns, song_proxy_getrow_with_header(x, s1, f2)); + } else if(s == gensym("setrow")) { + song_proxy_setrow(x, s, argc, argv); + } else if(s == gensym("getcell")) { + song_proxy_sendgui(x, gensym("cell"), 4, song_proxy_getcell_with_header(x, s1, f2, f3)); + } else if(s == gensym("setcell")) { + song_proxy_setcell(x, s, argc, argv); + } else if(s == gensym("resizepattern")) { + p = song_proxy_resizepattern(x, s1, f2); + if(p) { + song_proxy_sendgui(x, gensym("patternlength"), 2, song_proxy_getpatternlength(x, p->x_name)); + } + } else { + error("track: editcmd: unknown command: %s", s->s_name); + } +} + +static void song_proxy_sendgui(t_song_proxy* x, t_symbol* s, int argc, t_atom* argv) { + static const unsigned int bufsz = 8*MAXPDSTRING; + char buf[bufsz]; + list_snconvf(buf, bufsz, s, argc, argv); + debugprint("pd::composer::dispatch %s %s", x->rcv->s_name, buf); + sys_vgui("pd::composer::dispatch %s %s\n", x->rcv->s_name, buf); +} + +static void song_proxy_setrow(t_song_proxy* x, t_symbol* sel, int argc, t_atom* argv) { + if(argc < 2 || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_FLOAT) { + error("song: setrow: usage: setrow ..."); + return; + } + if((argc - 2) != x->x_song->x_mastertrack->x_ncolumns) { + post("argc=%d, ncolumns=%d", argc, x->x_song->x_mastertrack->x_ncolumns); + error("track: setrow: input error: must provide exactly %d elements for the row", x->x_song->x_mastertrack->x_ncolumns); + return; + } + t_symbol* pat_name = argv[0].a_w.w_symbol; + t_int rownum = (t_int)argv[1].a_w.w_float; + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("track: setrow: no such pattern: '%s'", pat_name->s_name); + return; + } + pattern_setrow(pat, rownum, &argv[2]); +} + +static t_atom* song_proxy_getrow(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("song: getrow: no such pattern: '%s'", pat_name->s_name); + return NULL; + } + t_atom* row = pattern_getrow(pat, (t_int)rownum); + debugprint("song_proxy_getrow returning " PTR, row); + return row; +} + +static t_atom* song_proxy_getrow_with_header(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + if(response_row) { + freebytes(response_row, response_row_sz); + response_row = NULL; + response_row_sz = 0; + } + + t_atom* row = song_proxy_getrow(x, pat_name, rownum); + if(!row) { + error("song: getrow: nu such patern: '%s'", pat_name->s_name); + return NULL; + } + response_row_sz = sizeof(t_atom) * (x->x_song->x_mastertrack->x_ncolumns + 2); + t_atom* response_row = (t_atom*)getbytes(response_row_sz); + SETSYMBOL(&response_row[0], pat_name); + SETFLOAT(&response_row[1], rownum); + memcpy(&response_row[2], row, sizeof(t_atom) * x->x_song->x_mastertrack->x_ncolumns); + return &response_row[0]; +} + +static void song_proxy_getrow_o(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + t_atom* row = song_proxy_getrow(x, pat_name, rownum); + if(row) + outlet_list(x->outlet, &s_list, x->x_song->x_mastertrack->x_ncolumns, row); +} + +static void song_proxy_setcell(t_song_proxy* x, t_symbol* sel, int argc, t_atom* argv) { + debugprint("song_proxy_setcell, s=%s", sel->s_name); + if(argc < 4 || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_FLOAT || argv[2].a_type != A_FLOAT) { + error("song: setcell: usage: setcell "); + return; + } + t_symbol* pat_name = argv[0].a_w.w_symbol; + t_int rownum = (t_int)argv[1].a_w.w_float; + t_int colnum = (t_int)argv[2].a_w.w_float; + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("song: setcell: no such pattern: '%s'", pat_name->s_name); + return; + } + pattern_setcell(pat, (t_int)rownum, (t_int)colnum, &argv[3]); +} + +static t_atom* song_proxy_getcell(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("song: getcell: no such pattern: '%s'", pat_name->s_name); + return NULL; + } + return pattern_getcell(pat, (t_int)rownum, (t_int)colnum); +} + +static t_atom* song_proxy_getcell_with_header(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + t_atom* cell = song_proxy_getcell(x, pat_name, rownum, colnum); + if(!cell) { + error("song: getcell: nu such patern: '%s'", pat_name->s_name); + return NULL; + } + SETSYMBOL(&response_cell[0], pat_name); + SETFLOAT(&response_cell[1], rownum); + SETFLOAT(&response_cell[2], colnum); + memcpy(&response_cell[3], cell, sizeof(t_atom)); + return &response_cell[0]; +} + +static void song_proxy_getcell_o(t_song_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + t_atom* cell = song_proxy_getcell(x, pat_name, rownum, colnum); + if(cell) + outlet_list(x->outlet, &s_list, 1, cell); +} + +static t_pattern* song_proxy_resizepattern(t_song_proxy* x, t_symbol* name, t_floatarg rows) { + ArrayListGetByName(x->x_song->x_mastertrack->x_patterns, name, t_pattern*, pat); + if(!pat) { + error("song: resizepattern: no such pattern: '%s'", name->s_name); + return NULL; + } + pattern_resize(pat, (t_int)rows); + return pat; +} + +static t_atom* song_proxy_get_track_names(t_song_proxy* x) { + if(response_row) { + freebytes(response_row, response_row_sz); + response_row = NULL; + response_row_sz = 0; + } + response_row_sz = sizeof(t_atom) * x->x_song->x_tracks_count; + response_row = (t_atom*)getbytes(response_row_sz); + int i; + for(i = 0; i < x->x_song->x_tracks_count; i++) { + SETSYMBOL(&response_row[i], x->x_song->x_tracks[i]->x_name); + } + return &response_row[0]; } diff --git a/composer/track.c b/composer/track.c index a3f5ddc..269a1d9 100644 --- a/composer/track.c +++ b/composer/track.c @@ -27,10 +27,30 @@ #include "common.h" static t_track* track_new(t_symbol* song_name, t_symbol* track_name, t_int columns) { + debugprint("track_new(%s, %s, %d)", song_name->s_name, track_name->s_name, columns); t_song* song = song_new(song_name); + t_track* t = song_create_track(song, track_name, columns); + // add track to song's track list + ArrayListAdd(song->x_tracks, t_track*, t); + song_mastertrack_fix_cols(song); + + return t; +} + +static t_track* mastertrack_new(t_song* song, t_symbol* track_name, t_int columns) { + debugprint("mastertrack_new(%s, %s, %d)", song->x_name->s_name, track_name->s_name, columns); + t_track* t = song_create_track(song, track_name, columns); + debugprint("mastertrack_new add pattern"); + ArrayListAdd(t->x_patterns, t_pattern*, pattern_new(t, gensym("Arrangement"), 16)); + return t; +} + +// both a song method and the track constructor: +static t_track* song_create_track(t_song* song, t_symbol* track_name, t_int columns) { ArrayListGetByName(song->x_tracks, track_name, t_track*, obj); - debugprint("track_new - object lookup {{%s} {%s} {%d}} => " PTR, song_name->s_name, track_name->s_name, columns, obj); + debugprint("song_create_track - object lookup %s, %s, %d => " PTR, + song->x_name->s_name, track_name->s_name, columns, obj); if(obj) return obj; t_track* x = (t_track*)getbytes(sizeof(t_track)); @@ -41,11 +61,10 @@ static t_track* track_new(t_symbol* song_name, t_symbol* track_name, t_int colum x->x_currentpat = 0; debugprint("created a track object (" PTR "), " - "creation args: {{%s} {%s} {%d}}", + "creation args: %s, %s, %d", x, x->x_song->x_name->s_name, x->x_name->s_name, x->x_ncolumns); - // att track to song's track list - ArrayListAdd(song->x_tracks, t_track*, x); + debugprint("song_create_track returns " PTR, x); return x; } @@ -77,3 +96,106 @@ static t_track* track_get(t_symbol* song_name, t_symbol* track_name) { static int track_exists(t_symbol* song_name, t_symbol* track_name) { return track_get(song_name, track_name) != 0L; } + +static void track_loaddata(t_track* x, int argc, t_atom* argv) { + int i,base; + base = 0; + + if(argc < (base+2) || argv[base].a_type != A_SYMBOL || argv[base+1].a_type != A_SYMBOL) { + error("track: data format error 1"); + return; + } + t_symbol* song_name = argv[base+0].a_w.w_symbol; + t_symbol* track_name = argv[base+1].a_w.w_symbol; + base += 2; + + if(x->x_song->x_name != song_name) { + debugprint("WARNING: discarding data from another song: %s", song_name->s_name); + return; + } + + if(x->x_name != track_name) { + debugprint("WARNING: discarding data from another track: %s", track_name->s_name); + return; + } + + t_song* song = song_get(song_name); + if(!song) { + error("track: song '%s' does not exist", song_name->s_name); + return; + } + + if(argc < (base+1) || argv[base].a_type != A_FLOAT) { + error("track: data format error 2"); + return; + } + t_int npatterns = (t_int)argv[base].a_w.w_float; + base += 1; + + debugprint("track: %s-%s: %d patterns to read", song_name->s_name, track_name->s_name, npatterns); + + t_symbol* patname; + t_int patrows; + t_pattern* pat; + + debugprint("track_loaddata(" PTR ", %d, " PTR ")", x, argc, argv); + for(i = 0; i < npatterns; i++) { + debugprint("reading pattern %d...", i); + if(argc < (base + 2)) { + error("track: data format error 3 (i=%d)", i); + return; + } + if(argv[base+0].a_type != A_SYMBOL || argv[base+1].a_type != A_FLOAT) { + error("track: data format error 4 (i=%d)", i); + return; + } + patname = argv[base+0].a_w.w_symbol; + patrows = (t_int)argv[base+1].a_w.w_float; + debugprint("pattern %d: %s-%s-%s, length=%d, RxC=%d", i, + song_name->s_name, track_name->s_name, patname->s_name, + patrows, patrows * x->x_ncolumns); + base += 2; + if(argc >= (base + patrows * x->x_ncolumns) && patrows > 0) { + pat = pattern_new(x, patname, patrows); + debugprint("created new pattern " PTR " ('%s', %d rows) for track " PTR, pat, patname->s_name, patrows, x); + int j,h,k; + for(h = 0, j = base; j < (base + patrows * x->x_ncolumns); j += x->x_ncolumns, h++) { + debugprint(" working on row %d", h); + for(k = 0; k < x->x_ncolumns; k++) { + pattern_setcell(pat, h, k, &argv[j+k]); + } + } + base += patrows * x->x_ncolumns; + } else { + error("track: data format error 8 (i=%d)", i); + return; + } + } +} + +static void track_binbuf_save(t_track* x, t_symbol* selector, t_binbuf* b) { + // data format: + // SELECTOR DATA [ RxC_atoms]*n + + binbuf_addv(b, "ssssi", selector, gensym("DATA"), + t->x_song->x_name, t->x_name, t->x_patterns_count); + + int i,j,k; + for(i = 0; i < t->x_patterns_count; i++) { + t_pattern* pat = t->x_patterns[i]; + binbuf_addv(b, "si", pat->x_name, pat->x_rows_count); + for(j = 0; j < pat->x_rows_count; j++) { + for(k = 0; k < t->x_ncolumns; k++) { + switch(pat->x_rows[j][k].a_type) { + case A_FLOAT: binbuf_addv(b, "f", pat->x_rows[j][k].a_w.w_float); break; + case A_SYMBOL: binbuf_addv(b, "s", pat->x_rows[j][k].a_w.w_symbol); break; + case A_NULL: binbuf_addv(b, "s", gensym("empty")); break; + default: binbuf_addv(b, "s", gensym("unknown")); break; + } + } + //binbuf_add(b, t->x_ncolumns, &pat->x_rows[j]); + } + } + + binbuf_addv(b, ";"); +} diff --git a/composer/track_proxy.c b/composer/track_proxy.c index b4e57ce..e83e474 100644 --- a/composer/track_proxy.c +++ b/composer/track_proxy.c @@ -28,6 +28,11 @@ t_class* track_proxy_class; +static t_atom response_pattern_length[2]; +static t_atom response_cell[4]; +static t_atom* response_row; // TODO: memory leaks check +static size_t response_row_sz; + void track_proxy_setup(void) { debugprint("registering 'track' class..."); ArrayListInit(songs, t_song*, 10); @@ -106,13 +111,17 @@ static void track_proxy_free(t_track_proxy* x) { pd_unbind(&x->x_obj.ob_pd, x->rcv); } -static void track_proxy_sendgui_pattern_names(t_track_proxy* x) { - debugprint("track_proxy_sendgui_pattern_names(" PTR ")", x); +static t_atom* track_proxy_get_pattern_names(t_track_proxy* x) { t_int n = track_get_pattern_count(x->x_track); - t_atom* a = (t_atom*)getbytes(sizeof(t_atom)*n); - track_get_pattern_names(x->x_track, a); - track_proxy_sendgui(x, gensym("patterns"), n, a); - freebytes(a, sizeof(t_atom)*n); + if(response_row) { + freebytes(response_row, response_row_sz); + response_row = NULL; + response_row_sz = 0; + } + response_row_sz = sizeof(t_atom) * n; + response_row = (t_atom*)getbytes(response_row_sz); + track_get_pattern_names(x->x_track, response_row); + return response_row; } static void track_proxy_reload(t_track_proxy* x) { @@ -143,30 +152,7 @@ static void track_proxy_save(t_gobj* z, t_binbuf* b) { (t_int)x->x_obj.te_xpix, (t_int)x->x_obj.te_ypix, gensym("track"), t->x_song->x_name, t->x_name, t->x_ncolumns); - // data format: - // TRACK_SELECTOR DATA [ RxC_atoms]*n - - binbuf_addv(b, "ssssi", gensym(TRACK_SELECTOR), gensym("DATA"), - t->x_song->x_name, t->x_name, t->x_patterns_count); - - int i,j,k; - for(i = 0; i < t->x_patterns_count; i++) { - t_pattern* pat = t->x_patterns[i]; - binbuf_addv(b, "si", pat->x_name, pat->x_rows_count); - for(j = 0; j < pat->x_rows_count; j++) { - for(k = 0; k < t->x_ncolumns; k++) { - switch(pat->x_rows[j][k].a_type) { - case A_FLOAT: binbuf_addv(b, "f", pat->x_rows[j][k].a_w.w_float); break; - case A_SYMBOL: binbuf_addv(b, "s", pat->x_rows[j][k].a_w.w_symbol); break; - case A_NULL: binbuf_addv(b, "s", gensym("empty")); break; - default: binbuf_addv(b, "s", gensym("unknown")); break; - } - } - //binbuf_add(b, t->x_ncolumns, &pat->x_rows[j]); - } - } - - binbuf_addv(b, ";"); + track_binbuf_save(t, gensym(TRACK_SELECTOR), b); } static void track_proxy_sendrow(t_track_proxy* x, t_pattern* pat, t_int row) { @@ -177,7 +163,7 @@ static void track_proxy_sendrow(t_track_proxy* x, t_pattern* pat, t_int row) { } static void track_proxy_anything(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv) { - debugprint("track_proxy_anything(" PTR ", %s, %d, " PTR ")", s, s->s_name, argc, argv); + debugprint("track_proxy_anything(" PTR ", %s, %d, " PTR ")", x, s->s_name, argc, argv); if(s == gensym("DATA")) { track_proxy_loaddata(x, s, argc, argv); @@ -192,90 +178,18 @@ static void track_proxy_anything(t_track_proxy* x, t_symbol* s, int argc, t_atom } static void track_proxy_loaddata(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv) { - int i,base; - base = 0; - - if(argc < (base+2) || argv[base].a_type != A_SYMBOL || argv[base+1].a_type != A_SYMBOL) { - error("track: data format error 1"); - return; - } - t_symbol* song_name = argv[base+0].a_w.w_symbol; - t_symbol* track_name = argv[base+1].a_w.w_symbol; - base += 2; - - if(x->x_track->x_song->x_name != song_name) { - debugprint("WARNING: discarding data from another song: %s", song_name->s_name); - return; - } - - if(x->x_track->x_name != track_name) { - debugprint("WARNING: discarding data from another track: %s", track_name->s_name); - return; - } - - if(argc < (base+1) || argv[base].a_type != A_FLOAT) { - error("track: data format error 2"); - return; - } - t_int npatterns = (t_int)argv[base].a_w.w_float; - base += 1; - - debugprint("track: %s-%s: %d patterns to read", song_name->s_name, track_name->s_name, npatterns); - - t_symbol* patname; - t_int patrows; - t_pattern* pat; - - debugprint("track_proxy_loaddata(" PTR ", %s, %d, " PTR ")", x, s->s_name, argc, argv); - for(i = 0; i < npatterns; i++) { - debugprint("reading pattern %d...", i); - if(argc < (base + 2)) { - error("track: data format error 3 (i=%d)", i); - return; - } - if(argv[base+0].a_type != A_SYMBOL || argv[base+1].a_type != A_FLOAT) { - error("track: data format error 4 (i=%d)", i); - return; - } - patname = argv[base+0].a_w.w_symbol; - patrows = (t_int)argv[base+1].a_w.w_float; - debugprint("pattern %d: %s-%s-%s, length=%d, RxC=%d", i, - song_name->s_name, track_name->s_name, patname->s_name, - patrows, patrows * x->x_track->x_ncolumns); - base += 2; - if(argc >= (base + patrows * x->x_track->x_ncolumns) && patrows > 0) { - pat = pattern_new(x->x_track, patname, patrows); - debugprint("created new pattern " PTR " ('%s', %d rows) for track " PTR, pat, patname->s_name, patrows, x->x_track); - int j,h,k; - for(h = 0, j = base; j < (base + patrows * x->x_track->x_ncolumns); j += x->x_track->x_ncolumns, h++) { - debugprint(" working on row %d", h); - for(k = 0; k < x->x_track->x_ncolumns; k++) { - pattern_setcell(pat, h, k, &argv[j+k]); - } - } - base += patrows * x->x_track->x_ncolumns; - } else { - error("track: data format error 8 (i=%d)", i); - return; - } - } + track_loaddata(x->x_track, argc, argv); } static t_atom* track_proxy_getpatternlength(t_track_proxy* x, t_symbol* pat_name) { - /*if(argc < 1 || argv[0].a_type != A_SYMBOL) { - error("track: getpatternlength: usage: getpatternlength "); - return NULL; - } - t_symbol* pat_name = argv[0].a_w.w_symbol;*/ ArrayListGetByName(x->x_track->x_patterns, pat_name, t_pattern*, pat); if(!pat) { error("track: getpatternlength: no such pattern: '%s'", pat_name->s_name); return NULL; } - t_atom* pl = (t_atom*)getbytes(sizeof(t_atom) * 2); - SETSYMBOL(&pl[0], pat->x_name); - SETFLOAT(&pl[1], pat->x_rows_count); - return pl; + SETSYMBOL(&response_pattern_length[0], pat->x_name); + SETFLOAT(&response_pattern_length[1], pat->x_rows_count); + return &response_pattern_length[0]; } static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s_, int argc, t_atom* argv_) { @@ -306,11 +220,13 @@ static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s_, int argc, t_atom } else if(s == gensym("editor-close")) { track_proxy_properties_close((t_gobj*) x, NULL); } else if(s == gensym("getpatterns")) { - track_proxy_sendgui_pattern_names(x); + t_atom* rsp = track_proxy_get_pattern_names(x); + track_proxy_sendgui(x, gensym("patterns"), response_row_sz, rsp); } else if(s == gensym("getpatternlength")) { track_proxy_sendgui(x, gensym("patternlength"), 2, track_proxy_getpatternlength(x, s1)); } else if(s == gensym("getrow")) { - track_proxy_sendgui(x, gensym("row"), x->x_track->x_ncolumns + 2, track_proxy_getrow_with_header(x, s1, f2)); + t_atom* rsp = track_proxy_getrow_with_header(x, s1, f2); + track_proxy_sendgui(x, gensym("row"), response_row_sz, rsp); } else if(s == gensym("setrow")) { track_proxy_setrow(x, s, argc, argv); } else if(s == gensym("getcell")) { @@ -320,12 +236,14 @@ static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s_, int argc, t_atom } else if(s == gensym("addpattern")) { p = track_proxy_addpattern(x, s1, f2); if(p) { - track_proxy_sendgui_pattern_names(x); + t_atom* rsp = track_proxy_get_pattern_names(x); + track_proxy_sendgui(x, gensym("patterns"), response_row_sz, rsp); } } else if(s == gensym("removepattern")) { j = track_proxy_removepattern(x, s1); if(j) { - track_proxy_sendgui_pattern_names(x); + t_atom* rsp = track_proxy_get_pattern_names(x); + track_proxy_sendgui(x, gensym("patterns"), response_row_sz, rsp); } } else if(s == gensym("resizepattern")) { p = track_proxy_resizepattern(x, s1, f2); @@ -335,12 +253,14 @@ static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s_, int argc, t_atom } else if(s == gensym("renamepattern")) { p = track_proxy_renamepattern(x, s1, s2); if(p) { - track_proxy_sendgui_pattern_names(x); + t_atom* rsp = track_proxy_get_pattern_names(x); + track_proxy_sendgui(x, gensym("patterns"), response_row_sz, rsp); } } else if(s == gensym("copypattern")) { p = track_proxy_copypattern(x, s1, s2); if(p) { - track_proxy_sendgui_pattern_names(x); + t_atom* rsp = track_proxy_get_pattern_names(x); + track_proxy_sendgui(x, gensym("patterns"), response_row_sz, rsp); } } else { error("track: editcmd: unknown command: %s", s->s_name); @@ -348,34 +268,9 @@ static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s_, int argc, t_atom } static void track_proxy_sendgui(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv) { - static const unsigned int tmpsz = 80; static const unsigned int bufsz = 8*MAXPDSTRING; - char* tmp; char buf[bufsz]; - int i,j; - buf[0] = '\0'; - strncat(buf, s->s_name, bufsz); - strncat(buf, " ", bufsz); - for(i = 0; i < argc; i++) { - if(i > 0) strncat(buf, " ", bufsz); - if(argv[i].a_type == A_FLOAT) { - tmp = (char*)getbytes(tmpsz*sizeof(char)); - if(argv[i].a_w.w_float == (t_float)(t_int)argv[i].a_w.w_float) - sprintf(tmp, "%ld", (t_int)argv[i].a_w.w_float); - else - sprintf(tmp, "%f", argv[i].a_w.w_float); - strncat(buf, tmp, bufsz); - } else if(argv[i].a_type == A_SYMBOL) { - strncat(buf, argv[i].a_w.w_symbol->s_name, bufsz); - } else { - strncat(buf, "null", bufsz); - } - } - if(strlen(buf) >= bufsz) { - debugprint("track: sendgui: message too long"); - bug("track: sendgui: message too long"); - return; - } + list_snconvf(buf, bufsz, s, argc, argv); debugprint("pd::composer::dispatch %s %s", x->rcv->s_name, buf); sys_vgui("pd::composer::dispatch %s %s\n", x->rcv->s_name, buf); } @@ -425,16 +320,23 @@ static t_atom* track_proxy_getrow(t_track_proxy* x, t_symbol* pat_name, t_floata } static t_atom* track_proxy_getrow_with_header(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + if(response_row) { + freebytes(response_row, response_row_sz); + response_row = NULL; + response_row_sz = 0; + } + t_atom* row = track_proxy_getrow(x, pat_name, rownum); if(!row) { error("track: getrow: nu such patern: '%s'", pat_name->s_name); return NULL; } - t_atom* row_with_hdr = (t_atom*)getbytes(sizeof(t_atom) * (x->x_track->x_ncolumns + 2)); - SETSYMBOL(&row_with_hdr[0], pat_name); - SETFLOAT(&row_with_hdr[1], rownum); - memcpy(&row_with_hdr[2], row, sizeof(t_atom) * x->x_track->x_ncolumns); - return row_with_hdr; + response_row_sz = sizeof(t_atom) * (x->x_track->x_ncolumns + 2); + t_atom* response_row = (t_atom*)getbytes(response_row_sz); + SETSYMBOL(&response_row[0], pat_name); + SETFLOAT(&response_row[1], rownum); + memcpy(&response_row[2], row, sizeof(t_atom) * x->x_track->x_ncolumns); + return &response_row[0]; } static void track_proxy_getrow_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum) { @@ -475,12 +377,11 @@ static t_atom* track_proxy_getcell_with_header(t_track_proxy* x, t_symbol* pat_n error("track: getcell: nu such patern: '%s'", pat_name->s_name); return NULL; } - t_atom* row_with_hdr = (t_atom*)getbytes(sizeof(t_atom) * 4); - SETSYMBOL(&row_with_hdr[0], pat_name); - SETFLOAT(&row_with_hdr[1], rownum); - SETFLOAT(&row_with_hdr[2], colnum); - memcpy(&row_with_hdr[3], cell, sizeof(t_atom)); - return row_with_hdr; + SETSYMBOL(&response_cell[0], pat_name); + SETFLOAT(&response_cell[1], rownum); + SETFLOAT(&response_cell[2], colnum); + memcpy(&response_cell[3], cell, sizeof(t_atom)); + return &response_cell[0]; } static void track_proxy_getcell_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { -- cgit v1.2.1