diff options
author | mescalinum <mescalinum@users.sourceforge.net> | 2009-08-18 15:08:04 +0000 |
---|---|---|
committer | mescalinum <mescalinum@users.sourceforge.net> | 2009-08-18 15:08:04 +0000 |
commit | 9a6ab3b333e64c24ee86d929d9387ef367c76ce0 (patch) | |
tree | f57e6e10161162119f04b5fa6d3bcc1fe9d92b18 /composer | |
parent | 38e419874d5d8f7ceb1e52195f28ac12c2662720 (diff) |
import sources
svn path=/trunk/externals/ffext/; revision=11945
Diffstat (limited to 'composer')
-rw-r--r-- | composer/arraylist.h | 118 | ||||
-rw-r--r-- | composer/common.h | 166 | ||||
-rw-r--r-- | composer/composer.c | 45 | ||||
-rw-r--r-- | composer/makefile | 25 | ||||
-rw-r--r-- | composer/pattern.c | 142 | ||||
-rw-r--r-- | composer/song.c | 61 | ||||
-rw-r--r-- | composer/song_proxy.c | 69 | ||||
-rw-r--r-- | composer/track.c | 79 | ||||
-rw-r--r-- | composer/track_proxy.c | 518 | ||||
-rw-r--r-- | composer/window.tk | 599 |
10 files changed, 1822 insertions, 0 deletions
diff --git a/composer/arraylist.h b/composer/arraylist.h new file mode 100644 index 0000000..a4b4362 --- /dev/null +++ b/composer/arraylist.h @@ -0,0 +1,118 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +#ifndef __ARRAYLIST_H_INCLUDED_ +#define __ARRAYLIST_H_INCLUDED_ + +#define ArrayListDeclare(name, type, sizetype) \ + type* name; \ + sizetype name ## _maxsize; \ + sizetype name ## _count + +#define ArrayListDeclareWithPrefix(prefix, name, type, sizetype) \ + prefix type* name; \ + prefix sizetype name ## _maxsize; \ + prefix sizetype name ## _count + +#define ArrayListInit(arrName, type, initSize) \ + arrName ## _maxsize = initSize; \ + arrName = (type*)getbytes(sizeof(type) * (initSize)); \ + arrName ## _count = 0 + +#define ArrayListAdd(arrName, type, objToAdd) \ + if(arrName ## _count >= arrName ## _maxsize) { \ + arrName = (type*)resizebytes(arrName, arrName ## _maxsize, (arrName ## _maxsize)*2); \ + }; \ + arrName[ arrName ## _count ++ ] = (type) objToAdd; \ + +#define ArrayListRemove(arrName, objToRem) \ + { \ + int i,j; \ + for(i=0; i< arrName ## _count; i++) \ + if(arrName[i] == objToRem) { \ + for(j=i; j< arrName ## _count - 1; j++) { \ + arrName[j] = arrName[j+1]; \ + } \ + arrName[ arrName ## _count -- ] = 0L; \ + break; \ + } \ + } + +#define ArrayListRemoveByIndex(arrName, index) \ + { \ + int i,j; \ + for(i=0; i< arrName ## _count; i++) \ + if(i == index) { \ + for(j=i; j< arrName ## _count - 1; j++) { \ + arrName[j] = arrName[j+1]; \ + } \ + arrName[ arrName ## _count -- ] = 0L; \ + break; \ + } \ + } + +#define ArrayListRemoveByName(arrName, name) \ + { \ + int i,j; \ + for(i=0; i< arrName ## _count; i++) \ + if(arrName[i] && arrName[i]->x_name == name) { \ + for(j=i; j< arrName ## _count - 1; j++) { \ + arrName[j] = arrName[j+1]; \ + } \ + arrName[ arrName ## _count -- ] = 0L; \ + break; \ + } \ + } + +#define ArrayListGetByName(arrName, n, type, result) \ + type result = (type) 0L; \ + if(arrName) { \ + int i; \ + for(i=0; i< arrName ## _count; i++) { \ + if(arrName[i] && arrName[i]->x_name == n) { \ + result = arrName[i]; break; \ + } \ + } \ + } + +#define ArrayListGetIndexByName(arrName, n, type, result) \ + type result = (type) -1; \ + if(arrName) { \ + int i; \ + for(i=0; i< arrName ## _count; i++) { \ + if(arrName[i] && arrName[i]->x_name == n) { \ + result = i; \ + break; \ + } \ + } \ + } + +#define ArrayListFree(arrName, type) \ + freebytes(arrName, arrName ## _maxsize * sizeof(type)) + +#endif // __ARRAYLIST_H_INCLUDED_ diff --git a/composer/common.h b/composer/common.h new file mode 100644 index 0000000..b149265 --- /dev/null +++ b/composer/common.h @@ -0,0 +1,166 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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. */ +/* ------------------------------------------------------------------------ */ + +#ifndef COMPOSER_COMMON_H_INCLUDED +#define COMPOSER_COMMON_H_INCLUDED + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "m_pd.h" +#include "m_imp.h" +#include "g_canvas.h" +#include "s_stuff.h" +#include "t_tk.h" +#include <unistd.h> +#include <stdio.h> +#include "arraylist.h" + +#define PTR "0x%x" +#ifdef DEBUG +#define debugprint(args...) post( args ) +#define DEBUG_BOOL 1 +#else +#define debugprint(args...) +#define DEBUG_BOOL 0 +#endif + +#define STRINGIFY(x) #x + +#define WRAP(v, w) (((v) < 0 ? (1+(int)((-(v))/(w)))*(w) : (v)) % w) + +#define TRACK_SELECTOR "#TRACK" +#define SONG_SELECTOR "#SONG" + +extern t_symbol s_list; + +struct _track; +struct _pattern; + +typedef struct _song +{ + t_symbol* x_name; + ArrayListDeclare(x_tracks, struct _track*, t_int); +} t_song; + +typedef struct _song_proxy +{ + t_object x_obj; + t_outlet* outlet; + t_song* x_song; + t_int b_editor_open; +} t_song_proxy; + +typedef struct _track +{ + t_symbol* x_name; + t_song* x_song; + t_int x_ncolumns; + t_outlet* outlet; + ArrayListDeclare(x_patterns, struct _pattern*, t_int); + t_float x_currentpat; +} t_track; + +typedef struct _track_proxy +{ + t_object x_obj; + t_outlet* outlet; + t_track* x_track; + t_int b_editor_open; + t_symbol* rcv; +} t_track_proxy; + +typedef struct _pattern +{ + t_symbol* x_name; + t_track* x_track; + ArrayListDeclare(x_rows, t_atom*, t_int); +} t_pattern; + +static t_song* song_new(t_symbol* song_name); +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 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 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_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); +static t_atom* pattern_getcell(t_pattern* x, t_int row, t_int col); +static void pattern_setrow(t_pattern* x, t_int row, t_atom* rowdata); +static void pattern_setcell(t_pattern* x, t_int row, t_int col, t_atom* a); +static t_pattern* pattern_get(t_symbol* song_name, t_symbol* track_name, t_symbol* pattern_name); +static int pattern_exists(t_symbol* song_name, t_symbol* track_name, t_symbol* pattern_name); + +void song_proxy_setup(void); +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_save(t_gobj* z, t_binbuf* b); + +void track_proxy_setup(void); +static t_track_proxy* track_proxy_new(t_symbol* song_name, t_symbol* track_name, t_floatarg cols); +static void track_proxy_free(t_track_proxy* x); +static void track_proxy_reload(t_track_proxy* x); +static void track_proxy_properties(t_gobj* z, t_glist* owner); +static void track_proxy_properties_close(t_gobj* z, t_glist* owner); +static void track_proxy_save(t_gobj* z, t_binbuf* b); +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); +static void track_proxy_loaddata(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv); +static t_atom* track_proxy_getpatternlength(t_track_proxy* x, t_symbol* pat_name); +static void track_proxy_editcmd(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv); +static void track_proxy_sendgui(t_track_proxy* x, t_symbol* s, int argc, t_atom* argv); +static void track_proxy_float(t_track_proxy* x, t_floatarg f); +static void track_proxy_setrow(t_track_proxy* x, t_symbol* sel, int argc, t_atom* argv); +static t_atom* track_proxy_getrow(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static t_atom* track_proxy_getrow_with_header(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static void track_proxy_getrow_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum); +static void track_proxy_setcell(t_track_proxy* x, t_symbol* sel, int argc, t_atom* argv); +static t_atom* track_proxy_getcell(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static t_atom* track_proxy_getcell_with_header(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static void track_proxy_getcell_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum); +static t_pattern* track_proxy_addpattern(t_track_proxy* x, t_symbol* name, t_floatarg rows); +static int track_proxy_removepattern(t_track_proxy* x, t_symbol* name); +static t_pattern* track_proxy_resizepattern(t_track_proxy* x, t_symbol* name, t_floatarg rows); +static t_pattern* track_proxy_renamepattern(t_track_proxy* x, t_symbol* name, t_symbol* newname); +static t_pattern* track_proxy_copypattern(t_track_proxy* x, t_symbol* src, t_symbol* dst); + +ArrayListDeclareWithPrefix(extern, songs, t_song*, int); + +void composer_setup(void); + +#endif // COMPOSER_COMMON_H_INCLUDED diff --git a/composer/composer.c b/composer/composer.c new file mode 100644 index 0000000..493ae37 --- /dev/null +++ b/composer/composer.c @@ -0,0 +1,45 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +#include "song.c" +#include "track.c" +#include "pattern.c" +#include "song_proxy.c" +#include "track_proxy.c" + +ArrayListDeclare(songs, t_song*, int); + +t_symbol s_list = {"list", 0, 0}; + +void composer_setup(void) { + debugprint("loading composer library for pd"); +/* #include "window.tk2c" */ + sys_vgui("source {window.tk}\n"); + song_proxy_setup(); + track_proxy_setup(); +} diff --git a/composer/makefile b/composer/makefile new file mode 100644 index 0000000..28e45f8 --- /dev/null +++ b/composer/makefile @@ -0,0 +1,25 @@ +all: pd_linux + +.SUFFIXES: .pd_linux + +pd_linux: composer.pd_linux + +LINUXCFLAGS = -DPD -DUNIX -DDEBUG -DPIC -fPIC \ + -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wno-shadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch \ +#LINUXINCLUDE = -I/usr/include +LINUXINCLUDE = -I/usr/src/pd/0.41.4/src +LINUXLDFLAGS = -export_dynamic -shared + +composer.pd_linux: song.c track.c pattern.c \ + song_proxy.c track_proxy.c \ + composer.c common.h arraylist.h + $(CC) $(LINUXCFLAGS) $(LINUXINCLUDE) -o composer.o -c composer.c + $(LD) $(LINUXLDFLAGS) -o composer.pd_linux composer.o -lc -lm + strip --strip-unneeded composer.pd_linux + +clean: + rm -f *.o *.pd_linux + + diff --git a/composer/pattern.c b/composer/pattern.c new file mode 100644 index 0000000..134b802 --- /dev/null +++ b/composer/pattern.c @@ -0,0 +1,142 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +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); + if(obj) return obj; + + t_pattern* x = (t_pattern*)getbytes(sizeof(t_pattern)); + x->x_name = name; + x->x_track = track; + ArrayListInit(x->x_rows, t_atom*, rows); + + int i; + for(i = 0; i < rows; i++) { + debugprint("x->x_rows[%d] = " PTR, i, x->x_rows[i]); + pattern_new_empty_row(x); + debugprint("x->x_rows[%d] <- " PTR, i, x->x_rows[i]); + } + + debugprint("created new pattern " PTR " with %d rows (x_rows = " PTR ")", x, x->x_rows_count, x->x_rows); + + // add pattern to track's pattern list + ArrayListAdd(x->x_track->x_patterns, t_pattern*, x); + return x; +} + +static t_pattern* pattern_clone(t_pattern* src, t_symbol* newname) { + t_pattern* x = (t_pattern*)getbytes(sizeof(t_pattern)); + x->x_name = newname; + x->x_track = src->x_track; + ArrayListInit(x->x_rows, t_atom*, src->x_rows_count); + + int i; + for(i = 0; i < src->x_rows_count; i++) { + ArrayListAdd(x->x_rows, t_atom*, pattern_clone_row(x, src->x_rows[i])); + } + + ArrayListAdd(x->x_track->x_patterns, t_pattern*, x); + return x; +} + +static void pattern_free(t_pattern* x) { + // free rows memory + ArrayListFree(x->x_rows, t_atom*); + // remove pattern from track's pattern list + ArrayListRemove(x->x_track->x_patterns, x); +} + +static void pattern_rename(t_pattern* x, t_symbol* newname) { + x->x_name = newname; +} + +static void pattern_resize(t_pattern *x, t_int newsize) { + debugprint("pattern_resize(" PTR ", %d)", x, newsize); + debugprint("initial size: %d", x->x_rows_count); + while(x->x_rows_count < newsize) + pattern_new_empty_row(x); + while(x->x_rows_count > newsize) + ArrayListRemoveByIndex(x->x_rows, x->x_rows_count - 1); + debugprint("final size: %d", x->x_rows_count); +} + +static void pattern_new_empty_row(t_pattern* x) { + t_atom* rowdata = (t_atom*)getbytes(sizeof(t_atom) * x->x_track->x_ncolumns); + SETSYMBOL(&(rowdata[0]), gensym("test")); + int j; + for(j = 1; j < x->x_track->x_ncolumns; j++) + SETFLOAT(&(rowdata[j]), j); + ArrayListAdd(x->x_rows, t_atom*, rowdata); +} + +static t_atom* pattern_getrow(t_pattern* x, t_int row) { + debugprint("pattern_getrow(" PTR ", %d)", x, row); + row = WRAP(row, x->x_rows_count); + t_atom* rowdata = x->x_rows[row]; + return rowdata; +} + +static t_atom* pattern_clone_row(t_pattern* x, t_atom* rowdata) { + debugprint("pattern_clone_row(" PTR ", " PTR ")", x, rowdata); + t_atom* clone = (t_atom*)copybytes(rowdata, sizeof(t_atom) * x->x_track->x_ncolumns); + return clone; +} + +static t_atom* pattern_getcell(t_pattern* x, t_int row, t_int col) { + row = WRAP(row, x->x_rows_count); + col = WRAP(col, x->x_track->x_ncolumns); + return &(x->x_rows[row][col]); +} + +static void pattern_setrow(t_pattern* x, t_int row, t_atom* rowdata) { + debugprint("pattern_setrow(" PTR ", %d, " PTR ")", x, row, rowdata); + row = WRAP(row, x->x_rows_count); + debugprint("x->x_rows[%d] = " PTR, row, x->x_rows[row]); + t_atom *myrowdata = x->x_rows[row]; + memcpy(myrowdata, rowdata, sizeof(t_atom) * x->x_track->x_ncolumns); + debugprint("x->x_rows[%d] <- " PTR, row, x->x_rows[row]); +} + +static void pattern_setcell(t_pattern* x, t_int row, t_int col, t_atom* a) { + row = WRAP(row, x->x_rows_count); + col = WRAP(col, x->x_track->x_ncolumns); + debugprint("about to write an atom (size=%d) at address " PTR, sizeof(t_atom), &(x->x_rows[row][col])); + memcpy(&(x->x_rows[row][col]), a, sizeof(t_atom)); +} + +static t_pattern* pattern_get(t_symbol* song_name, t_symbol* track_name, t_symbol* pattern_name) { + t_track* track = track_get(song_name, track_name); + if(!track || !track->x_patterns) return (t_pattern*) 0L; + ArrayListGetByName(track->x_patterns, pattern_name, t_pattern*, result); + return result; +} + +static int pattern_exists(t_symbol* song_name, t_symbol* track_name, t_symbol* pattern_name) { + return pattern_get(song_name, track_name, pattern_name) != 0L; +} diff --git a/composer/song.c b/composer/song.c new file mode 100644 index 0000000..7c99877 --- /dev/null +++ b/composer/song.c @@ -0,0 +1,61 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +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); + 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); + + debugprint("created a song object (" PTR "), " + "creation args: {{%s}}", + x, x->x_name->s_name); + + ArrayListAdd(songs, t_song*, x); + debugprint("registered song object to global song collection"); + return x; +} + +static void song_free(t_song* x) { + // free tracks memory + ArrayListFree(x->x_tracks, t_track*); + // remove song from global song collection + ArrayListRemove(songs, x); +} + +static t_song* song_get(t_symbol* song_name) { + ArrayListGetByName(songs, song_name, t_song*, result); + return result; +} + +static int song_exists(t_symbol* song_name) { + return song_get(song_name) != 0L; +} diff --git a/composer/song_proxy.c b/composer/song_proxy.c new file mode 100644 index 0000000..9d40215 --- /dev/null +++ b/composer/song_proxy.c @@ -0,0 +1,69 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +t_class* song_proxy_class; + +void song_proxy_setup(void) { + debugprint("registering 'song' class..."); + song_proxy_class = class_new( + gensym("song"), + (t_newmethod)song_proxy_new, + (t_method)song_proxy_free, + sizeof(t_song_proxy), + CLASS_DEFAULT, //0, + A_SYMBOL, + 0 + ); + class_addfloat(song_proxy_class, song_proxy_float); +#if PD_MINOR_VERSION >= 37 + class_setpropertiesfn(song_proxy_class, song_proxy_properties); + class_setsavefn(song_proxy_class, song_proxy_save); +#endif + class_sethelpsymbol(song_proxy_class, gensym("song.pd")); +} + +static t_song_proxy* song_proxy_new(t_symbol* song_name) { + t_song_proxy *x = (t_song_proxy*)pd_new(song_proxy_class); + x->outlet = outlet_new(&x->x_obj, &s_list); + x->x_song = song_new(song_name); + x->b_editor_open = 0; + debugprint("created an instance of t_song_proxy: " PTR, x); + return x; +} + +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_save(t_gobj* z, t_binbuf* b) { +} diff --git a/composer/track.c b/composer/track.c new file mode 100644 index 0000000..a3f5ddc --- /dev/null +++ b/composer/track.c @@ -0,0 +1,79 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +static t_track* track_new(t_symbol* song_name, t_symbol* track_name, t_int columns) { + t_song* song = song_new(song_name); + + 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); + if(obj) return obj; + + t_track* x = (t_track*)getbytes(sizeof(t_track)); + x->x_name = track_name; + x->x_song = song; + x->x_ncolumns = columns; + ArrayListInit(x->x_patterns, struct _pattern*, 4); + x->x_currentpat = 0; + + debugprint("created a track object (" PTR "), " + "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); + return x; +} + +static void track_free(t_track* x) { + // free patterns memory + ArrayListFree(x->x_patterns, t_pattern*); + // remove track from song's track list + ArrayListRemove(x->x_song->x_tracks, x); +} + +static t_int track_get_pattern_count(t_track* x) { + return x->x_patterns_count; +} + +static void track_get_pattern_names(t_track* x, t_atom* /* OUT */ out) { + int i; + for(i = 0; i < x->x_patterns_count; i++) { + SETSYMBOL(&out[i], x->x_patterns[i]->x_name); + } +} + +static t_track* track_get(t_symbol* song_name, t_symbol* track_name) { + t_song* song = song_get(song_name); + if(!song || !song->x_tracks) return (t_track*) 0L; + ArrayListGetByName(song->x_tracks, track_name, t_track*, result); + return result; +} + +static int track_exists(t_symbol* song_name, t_symbol* track_name) { + return track_get(song_name, track_name) != 0L; +} diff --git a/composer/track_proxy.c b/composer/track_proxy.c new file mode 100644 index 0000000..1f1d769 --- /dev/null +++ b/composer/track_proxy.c @@ -0,0 +1,518 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) 2009 Federico Ferri. */ +/* 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 */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* 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" + +t_class* track_proxy_class; + +void track_proxy_setup(void) { + debugprint("registering 'track' class..."); + ArrayListInit(songs, t_song*, 10); + track_proxy_class = class_new( + gensym("track"), + (t_newmethod)track_proxy_new, + (t_method)track_proxy_free, + sizeof(t_track_proxy), + CLASS_DEFAULT, //0, + A_SYMBOL, A_SYMBOL, A_FLOAT, + 0 + ); + class_addfloat(track_proxy_class, track_proxy_float); + class_addanything(track_proxy_class, track_proxy_anything); + class_addmethod(track_proxy_class, (t_method)track_proxy_properties, \ + gensym("editor-open"), 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_properties_close, \ + gensym("editor-close"), 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_reload, \ + gensym("reload"), 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_setrow, \ + gensym("setrow"), A_GIMME, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_getrow_o, \ + gensym("getrow"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_setcell, \ + gensym("setcell"), A_GIMME, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_getcell_o, \ + gensym("getcell"), A_SYMBOL, A_FLOAT, A_FLOAT, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_addpattern, \ + gensym("addpattern"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_removepattern, \ + gensym("removepattern"), A_SYMBOL, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_resizepattern, \ + gensym("resizepattern"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_renamepattern, \ + gensym("renamepattern"), A_SYMBOL, A_SYMBOL, 0); + class_addmethod(track_proxy_class, (t_method)track_proxy_copypattern, \ + gensym("copypattern"), A_SYMBOL, A_SYMBOL, 0); +#if PD_MINOR_VERSION >= 37 + class_setpropertiesfn(track_proxy_class, track_proxy_properties); + class_setsavefn(track_proxy_class, track_proxy_save); +#endif + class_sethelpsymbol(track_proxy_class, gensym("track.pd")); +} + +static t_track_proxy* track_proxy_new(t_symbol* song_name, t_symbol* track_name, t_floatarg cols) { + t_track_proxy *x = (t_track_proxy*)pd_new(track_proxy_class); + x->outlet = outlet_new(&x->x_obj, &s_list); + x->x_track = track_new(song_name, track_name, (t_int)cols); + x->b_editor_open = 0; + char rcv_buf[80]; + sprintf(rcv_buf, "track_proxy-%s-%s", x->x_track->x_song->x_name->s_name, x->x_track->x_name->s_name); + x->rcv = gensym(rcv_buf); + pd_bind(&x->x_obj.ob_pd, x->rcv); + debugprint("created an instance of t_track_proxy " PTR ", to_track = " PTR, x, x->x_track); + + track_proxy_properties_close((t_gobj*) x, NULL); + + pd_bind(&x->x_obj.ob_pd, gensym(TRACK_SELECTOR)); + + sys_vgui("pd::composer::init %s %s %s %d %d\n", x->rcv->s_name, x->x_track->x_song->x_name->s_name, x->x_track->x_name->s_name, x->x_track->x_ncolumns, DEBUG_BOOL); + + return x; +} + +static void track_proxy_free(t_track_proxy* x) { + track_proxy_properties_close((t_gobj*) x, NULL); + + pd_unbind(&x->x_obj.ob_pd, gensym(TRACK_SELECTOR)); + /* LATER find a way to get #TRACK unbound earlier (at end of load?) */ + t_pd* x2; + while (x2 = pd_findbyclass(gensym(TRACK_SELECTOR), track_proxy_class)) + pd_unbind(x2, gensym(TRACK_SELECTOR)); + + 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); + 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); +} + +static void track_proxy_reload(t_track_proxy* x) { + sys_vgui("source {window.tk}\n"); +} + +static void track_proxy_properties(t_gobj* z, t_glist* owner) { + t_track_proxy* x = (t_track_proxy*)z; + sys_vgui("pd::composer::openWindow %s\n", x->rcv->s_name); + x->b_editor_open = 1; +} + +static void track_proxy_properties_close(t_gobj* z, t_glist* owner) { + t_track_proxy* x = (t_track_proxy*)z; + debugprint("track_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 track_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); + + // data format: + // #TRACK DATA <npatterns> [<pat_name> <pat rows> RxC_atoms]*n + + binbuf_addv(b, "ssi", gensym(TRACK_SELECTOR), gensym("DATA"), 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, "i", 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; + default: binbuf_addv(b, "s", gensym("?")); break; + } + } + } + } + + binbuf_addv(b, ";"); +} + +static void track_proxy_sendrow(t_track_proxy* x, t_pattern* pat, t_int row) { + t_track* t = x->x_track; + if(row < 0) row = 0; + row = row % pat->x_rows_count; + outlet_list(x->outlet, &s_list, t->x_ncolumns, pat->x_rows[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); + + if(s == gensym("DATA")) { + track_proxy_loaddata(x, s, argc, argv); + return; + } else if(s == gensym("EDIT")) { + track_proxy_editcmd(x, s, argc, argv); + return; + } else { + error("unrecognized command for anything method: %s ", s->s_name); + return; + } +} + +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+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; + debugprint("track: %d patterns to read", npatterns); + base += 1; + + 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: name='%s', length=%d, RxC=%d", i, 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; + } + } +} + +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 <pattern_name>"); + 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; +} + +static void track_proxy_editcmd(t_track_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")) { + track_proxy_properties((t_gobj*) x, NULL); + } 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); + } 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)); + } else if(s == gensym("setrow")) { + track_proxy_setrow(x, s, argc, argv); + } else if(s == gensym("getcell")) { + track_proxy_sendgui(x, gensym("cell"), 4, track_proxy_getcell_with_header(x, s1, f2, f3)); + } else if(s == gensym("setcell")) { + track_proxy_setcell(x, s, argc, argv); + } else if(s == gensym("addpattern")) { + p = track_proxy_addpattern(x, s1, f2); + if(p) { + debugprint("BAMBOLOOOOOO"); + //for(i = 0; i < p->x_rows_count; i++) + // track_proxy_sendgui(x, gensym("row"), x->x_track->x_ncolumns + 2, track_proxy_getrow_with_header(x, p->x_name, i)); + track_proxy_sendgui_pattern_names(x); + } + } else if(s == gensym("removepattern")) { + j = track_proxy_removepattern(x, s1); + if(j) { + track_proxy_sendgui_pattern_names(x); + } + } else if(s == gensym("resizepattern")) { + p = track_proxy_resizepattern(x, s1, f2); + if(p) { + track_proxy_sendgui(x, gensym("patternlength"), 2, track_proxy_getpatternlength(x, p->x_name)); + } + } else if(s == gensym("renamepattern")) { + p = track_proxy_renamepattern(x, s1, s2); + if(p) { + track_proxy_sendgui_pattern_names(x); + } + } else if(s == gensym("copypattern")) { + p = track_proxy_copypattern(x, s1, s2); + if(p) { + //for(i = 0; i < p->x_rows_count; i++) + // track_proxy_sendgui(x, gensym("row"), x->x_track->x_ncolumns + 2, track_proxy_getrow_with_header(x, p->x_name, i)); + track_proxy_sendgui_pattern_names(x); + } + } else { + error("track: editcmd: unknown command: %s", s->s_name); + } +} + +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) { + bug("track: sendgui: message too long"); + return; + } + sys_vgui("pd::composer::dispatch %s %s\n", x->rcv->s_name, buf); +} + +static void track_proxy_float(t_track_proxy* x, t_floatarg f) { + t_track* t = x->x_track; + if(t->x_patterns_count < 1) return; + + t_int curpat_i = (t_int) t->x_currentpat; + + if(curpat_i < 0) curpat_i = 0; + curpat_i = curpat_i % t->x_patterns_count; + + t_pattern* curpat = t->x_patterns[curpat_i]; + track_proxy_sendrow(x, curpat, (t_int) f); +} + +static void track_proxy_setrow(t_track_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("track: setrow: usage: setrow <pattern_name> <row#> <atom0> <atom1> ..."); + return; + } + if((argc - 2) != x->x_track->x_ncolumns) { + post("argc=%d, ncolumns=%d", argc, x->x_track->x_ncolumns); + error("track: setrow: input error: must provide exactly %d elements for the row", x->x_track->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_track->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* track_proxy_getrow(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + ArrayListGetByName(x->x_track->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("track: getrow: no such pattern: '%s'", pat_name->s_name); + return NULL; + } + t_atom* row = pattern_getrow(pat, (t_int)rownum); + debugprint("track_proxy_getrow returning " PTR, row); + return row; +} + +static t_atom* track_proxy_getrow_with_header(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + 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; +} + +static void track_proxy_getrow_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum) { + t_atom* row = track_proxy_getrow(x, pat_name, rownum); + if(row) + outlet_list(x->outlet, &s_list, x->x_track->x_ncolumns, row); +} + +static void track_proxy_setcell(t_track_proxy* x, t_symbol* sel, int argc, t_atom* argv) { + debugprint("track_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("track: setcell: usage: setcell <pattern_name> <row#> <col#> <atom>"); + 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_track->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("track: setcell: no such pattern: '%s'", pat_name->s_name); + return; + } + pattern_setcell(pat, (t_int)rownum, (t_int)colnum, &argv[3]); +} + +static t_atom* track_proxy_getcell(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + ArrayListGetByName(x->x_track->x_patterns, pat_name, t_pattern*, pat); + if(!pat) { + error("track: getcell: no such pattern: '%s'", pat_name->s_name); + return NULL; + } + return pattern_getcell(pat, (t_int)rownum, (t_int)colnum); +} + +static t_atom* track_proxy_getcell_with_header(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + t_atom* cell = track_proxy_getcell(x, pat_name, rownum, colnum); + if(!cell) { + 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; +} + +static void track_proxy_getcell_o(t_track_proxy* x, t_symbol* pat_name, t_floatarg rownum, t_floatarg colnum) { + t_atom* cell = track_proxy_getcell(x, pat_name, rownum, colnum); + if(cell) + outlet_list(x->outlet, &s_list, 1, cell); +} + +static t_pattern* track_proxy_addpattern(t_track_proxy* x, t_symbol* name, t_floatarg rows) { + return pattern_new(x->x_track, name, (t_int)rows); +} + +static int track_proxy_removepattern(t_track_proxy* x, t_symbol* name) { + ArrayListGetByName(x->x_track->x_patterns, name, t_pattern*, pat); + if(!pat) { + error("track: removepattern: no such pattern: '%s'", name->s_name); + return 0; //FAIL + } + pattern_free(pat); + return 1; //OK +} + +static t_pattern* track_proxy_resizepattern(t_track_proxy* x, t_symbol* name, t_floatarg rows) { + ArrayListGetByName(x->x_track->x_patterns, name, t_pattern*, pat); + if(!pat) { + error("track: resizepattern: no such pattern: '%s'", name->s_name); + return NULL; + } + pattern_resize(pat, (t_int)rows); + return pat; +} + +static t_pattern* track_proxy_renamepattern(t_track_proxy* x, t_symbol* name, t_symbol* newname) { + ArrayListGetByName(x->x_track->x_patterns, name, t_pattern*, pat); + if(!pat) { + error("track: renamepattern: no such pattern: '%s'", name->s_name); + return NULL; + } + pattern_rename(pat, newname); + return pat; +} + +static t_pattern* track_proxy_copypattern(t_track_proxy* x, t_symbol* src, t_symbol* dst) { + ArrayListGetByName(x->x_track->x_patterns, src, t_pattern*, pat); + if(!pat) { + error("track: copypattern: no such pattern: '%s'", src->s_name); + return NULL; + } + ArrayListGetByName(x->x_track->x_patterns, dst, t_pattern*, pat2); + if(pat2) { + error("track: copypattern: destination '%s' exists", pat2->x_name->s_name); + return NULL; + } + return pattern_clone(pat, dst); +} diff --git a/composer/window.tk b/composer/window.tk new file mode 100644 index 0000000..7e6b3eb --- /dev/null +++ b/composer/window.tk @@ -0,0 +1,599 @@ +# ------------------------------------------------------------------------ +# Copyright (c) 2009 Federico Ferri. +# 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 +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See file LICENSE for further informations on licensing terms. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# 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. +# ------------------------------------------------------------------------ + +package require Tcl 8.5 +package require Tk 8.5 +package require Tktable 2.9 + +if {1 || ![info exists pd] && [info exists netsend]} { + set ::sendgui "netsend" +} else { + set ::sendgui "pd" +} + +namespace eval pd::composer { + variable debug + variable w + array set w {} + variable songname + array set songname {} + variable trackname + array set trackname {} + variable currentpattern + array set currentpattern {} + variable length + array set length {} + variable columns + array set columns {} + variable patterns + array set patterns {} + + variable quirks_fix_int_floats 0 + + proc debugPrint {args} { + variable debug + if {![info exists debug]} {set debug 0} + if {$debug} {puts stderr "composer-TCL: $args"} + } + + proc createPattern {id name len} { + debugPrint [info level 0] + variable length + variable patterns + + if {$name in $patterns($id)} { + return -code error "Pattern '$name' already exists" + } + set len [expr {int($len)}] + if {$len <= 0} { + return -code error "Length must be positive integer" + } + + $::sendgui [concat $id EDIT addpattern $name $len \;] + #lappend patterns($id) $name + #dict set length($id) $name $len + #$w($id).f.p configure -values $patterns($id) + } + + proc removePattern {id name} { + debugPrint [info level 0] + variable length; + variable patterns; + + $::sendgui [concat $id EDIT removepattern $name \;] + + #set oldidx [lsearch -exact $patterns($id) $name] + #set length($id) [dict remove $length($id) $name] + #set patterns($id) [lsearch -all -inline -not -exact $patterns($id) $name] + #while {$oldidx >= [llength $patterns($id)]} {incr oldidx -1} + #if {$oldidx < 0} {set oldidx 0} + #displayPattern $id [lindex $patterns($id) $oldidx] + } + + proc copyPattern {id src dst} { + debugPrint [info level 0] + variable length; + #upvar 0 $::datavar($w)_$src SRC + #upvar 0 $::datavar($w)_$dst DST + + $::sendgui [concat $id EDIT copypattern $src $dst \;] + + #createPattern $w $dst [dict get $length($id) $src] + #array set DST [array get SRC] + } + + proc resizePattern {id name newSize} { + debugPrint [info level 0] + variable currentpattern; + variable length; + + if {$newSize == [dict get $length($id) $name]} return + + set len [expr {int($newSize)}] + if {$len <= 0} { + return -code error "Length must pe positive integer" + } + + $::sendgui [concat $id EDIT resizepattern $name $len \;] + + #dict set length($id) $name $len + #if {$name == $currentpattern($id)} { + # refresh stuff + # displayPattern $id $name + #} + } + + proc renamePattern {id name newName} { + debugPrint [info level 0] + variable length; + variable patterns; + #upvar 0 $::datavar($w)_$name SRC + #upvar 0 $::datavar($w)_$newName DST + + if {$name == $newName} return + if {$newName in $patterns($id)} { + return -code error "Pattern name '$newName' already exists" + } + set idx [lsearch -exact $patterns($id) $name] + if {$idx == -1} { + return -code error "No such pattern: '$name'" + } + + $::sendgui [concat $id EDIT renamepattern $name $newName \;] + + #lset patterns($id) $idx $newName + #array set DST [array get SRC] + #array unset SRC + + #dict set length($id) $newName [dict get $length($id) $name] + #set length($id) [dict remove $length($id) $name] + + #displayPattern $id $newName + } + + proc generateNewPatternName {id} { + debugPrint [info level 0] + variable patterns; + set n 0 + while 1 { + set t "P[format %.02d $n]" + if {$t in $patterns($id)} {incr n} else {return $t} + } + } + + proc displayPattern {id name} { + debugPrint "request-pattern-length" + $::sendgui [concat $id EDIT getpatternlength $name \;] + } + + proc displayPattern_async {id name} { + debugPrint [info level 0] + variable currentpattern + variable patterns + variable length + variable columns + variable w + variable songname + variable trackname + variable [getDataVar $id] + + wm title $w($id) "Song: $songname($id) Track: $trackname($id)" + debugPrint "checkExit" + if {$name == {}} {return} + if {$name ni $patterns($id)} { + debugPrint "Pattern '$name' does not exist" + return -code error "Pattern '$name' does not exist" + } + debugPrint "s1" + set rows [dict get $length($id) $name] + set cols $columns($id) + debugPrint "resizing tktable widget to ${rows}x${cols}" + grid $w($id).t -row 10 -column 0 -sticky news + $w($id).t configure -state normal -variable [getDataVar $id] -rows $rows -cols [expr {1+$cols}] + debugPrint "s2" + $w($id).f.p configure -values $patterns($id) + debugPrint "s3" + $w($id).f.p current [lsearch -exact $patterns($id) $name] + debugPrint "setCurPattern" + set currentpattern($id) $name + debugPrint "setTitle" + wm title $w($id) "Song: $songname($id) Track: $trackname($id) Pattern: $name" + } + + proc displayCurrentPattern {id} { + debugPrint [info level 0] + variable currentpattern + variable w + debugPrint "current pattern is {$currentpattern($id)}" + displayPattern $id $currentpattern($id) + } + + proc rowTag {id r} { + debugPrint [info level 0] + if {$r % $::div1 == 0} {return "alt0"} + if {$r % $::div2 == 0} {return "alt1"} + } + + proc refreshGrid {id} { + debugPrint [info level 0] + variable currentpattern + debugPrint "currentPattern is {$currentpattern($id)}" + variable w + $w($id).t configure -padx [$w($id).t cget -padx] + } + + proc edit {id r c bwrite value} { + # NOT USED + #if {$bwrite} { + # $::sendgui [concat $id EDIT setcell $currentpattern($id) $r $c $value \;] + #} else { + #} + } + + proc getDataVar {id {P {}}} { + variable currentpattern + variable songname + variable trackname + if {$P == ""} {set P $currentpattern($id)} + set n "[namespace current]::data_$songname($id)_$trackname($id)_$P" + if {![info exists $n]} {array set $n {}} + return $n + } + + proc createMainWindow {id} { + debugPrint [info level 0] + variable currentpattern; + variable w + variable songname + variable trackname + variable patterns + variable [getDataVar $id] + + catch {destroy $w($id)} + debugPrint "creating window with path = '$w($id)'" + toplevel $w($id) + + debugPrint "top-toolbar(frame)" + grid [ttk::frame $w($id).f] \ + -row 5 -columnspan 2 -sticky news + debugPrint "label" + grid [ttk::label $w($id).f.l -text "Pattern: "] \ + -row 0 -column 0 -in $w($id).f + debugPrint "combobox patterns" + grid [ttk::combobox $w($id).f.p -textvariable "[namespace current]::currentpattern($id)"] \ + -row 0 -column 1 -in $w($id).f + debugPrint "divs" + grid [ttk::label $w($id).f.ld1 -text "Div1: "] \ + -row 0 -column 2 -in $w($id).f + grid [spinbox $w($id).f.d1 -command "[namespace current]::refreshGrid $id" -from 8 -to 64 \ + -increment 1 -format %3.0f -width 3 -textvar ::div1] \ + -row 0 -column 3 -in $w($id).f + grid [ttk::label $w($id).f.ld2 -text "Div2: "] \ + -row 0 -column 4 -in $w($id).f + grid [spinbox $w($id).f.d2 -command "[namespace current]::refreshGrid $id" -from 2 -to 64 \ + -increment 1 -format %3.0f -width 3 -textvar ::div2] \ + -row 0 -column 5 -in $w($id).f + + debugPrint "step2" + $w($id).f.p state readonly + + # movet to async counterpart + #$w($id).f.p configure -values $patterns($id) + + debugPrint "request-patterns" + $::sendgui [concat $id EDIT getpatterns \;] + + debugPrint "bindevent" + bind $w($id).f.p <<ComboboxSelected>> "[namespace current]::displayCurrentPattern $id" + debugPrint "table" + #grid [ + table $w($id).t -state disabled \ + -insertofftime 0 \ + -bordercursor sb_h_double_arrow \ + -colorigin -1 -sparsearray 1 \ + -relief ridge -takefocus 1 -borderwidth 1 -colwidth 4 \ + -browsecmd "[namespace current]::activeCellChanges $id %r %c" \ + -cols 0 -rows 0 \ + -cache 0 \ + -usecommand 0 -command "[namespace current]::edit $id %r %c %i %s" \ + -colstretchmode none -rowstretchmode none \ + -flashmode 1 -flashtime 2 -autoclear 1 \ + -justify left -multiline 0 -resizeborders col \ + -selectmode extended -selecttype cell \ + -titlecols 1 -titlerows 0 -validate 1 \ + -validatecommand "[namespace current]::validateCommand $id %r %c %s %S" \ + -variable [getDataVar $id] -exportselection 1 \ + -xscrollcommand "$w($id).hscroll set" \ + -yscrollcommand "$w($id).vscroll set" \ + -rowtagcommand "[namespace current]::rowTag $id" \ + + #] -row 10 -column 0 -sticky news + + #grid $w($id).t -row 10 -column 0 -sticky news + + debugPrint "scrollbars" + grid [ttk::scrollbar $w($id).vscroll -orient vertical -command "$w($id).t yview"] -row 10 -column 1 -sticky ns + grid [ttk::scrollbar $w($id).hscroll -orient horizontal -command "$w($id).t xview"] -row 15 -column 0 -sticky ew + #grid [ttk::sizegrip $w($id).resize] -row 15 -column 1 -sticky se + + grid [ttk::entry $w($id).eval] -row 20 -columnspan 2 -sticky ew + bind $w($id).eval <Return> "set cmd \[$w($id).eval get]; namespace eval [namespace current] \$cmd; $w($id).eval delete 0 end" + + debugPrint "grid" + grid columnconfigure $w($id) 0 -weight 1 + grid rowconfigure $w($id) 10 -weight 1 + + debugPrint "table-tags" + $w($id).t tag configure active -relief solid -background gray -foreground black + $w($id).t tag configure flash -background red -foreground white + $w($id).t tag configure sel -background blue -foreground white + $w($id).t tag configure title -background gray -foreground white -justify right + $w($id).t tag configure alt0 -background "#20a8b8" + $w($id).t tag configure alt1 -background "#0f7f9f" + $w($id).t tag configure notecol -background "#dddded" + + debugPrint "wm" + wm minsize $w($id) 300 150 + wm protocol $w($id) WM_DELETE_WINDOW "$::sendgui $id EDIT editor-close \\;" + + debugPrint "menu" + menu $w($id).m -tearoff 0 + $w($id).m add command -label "New pattern..." \ + -command "[namespace current]::newPatternDialog $id" + $w($id).m add command -label "Pattern properties..." \ + -command "[namespace current]::patternPropertiesDialog $id" + $w($id).m add command -label "Remove pattern..." \ + -command "[namespace current]::removePatternConfirm $id" + $w($id).m add command -label "Create copy..." \ + -command "[namespace current]::copyPatternDialog $id" + + menu $w($id).mb + $w($id) configure -menu $w($id).mb + menu $w($id).mb.tools -tearoff 0 + $w($id).mb.tools add command -label {Reload} \ + -command {uplevel 0 {source $argv0}} + $w($id).mb add cascade -label {Pattern} -menu $w($id).m + $w($id).mb add cascade -label {Utils} -menu $w($id).mb.tools + + debugPrint "more-bind-events" + bind $w($id).t <ButtonPress-3> "$w($id).t activate @%x,%y; tk_popup $w($id).m %X %Y" + bind $w($id).t <Control-t> "switchColumnType $id" + + return $w($id) + } + + proc switchColumnType {id} { + debugPrint [info level 0] + variable w + global colType + set curcol [$w($id).t index active col] + set tag {} + if {![info exists colType(c$curcol)]} { + set colType(c$curcol) {notes} + set tag {notecol} + } elseif {$colType(c$curcol) == {notes}} { + unset colType(c$curcol) + } + + $w($id).t tag col $tag $curcol + #refreshGrid + } + + proc activeCellChanges {id row col} { + debugPrint [info level 0] + variable w + if {$col < 0} {$w($id).t activate $row,[incr col]} + } + + proc validateCommand {id row col curVal newVal} { + debugPrint [info level 0] + variable currentpattern + + $::sendgui [concat $id EDIT setcell $currentpattern($id) $row $col $newVal \;] + + return 1 + } + + proc patternPropertiesDialog_common {id options} { + debugPrint [info level 0] + variable currentpattern + variable w + set bname 1 + set blength 1 + set old_name {} + catch {set old_name $currentpattern($id)} + set vname {} + set vlength {} + set action_cancel {destroy %w} + set action_ok $action_cancel + + set opts {vname vlength bname blength action_ok action_cancel} + foreach opt $opts { + catch {set $opt [dict get $options $opt]} + } + foreach {o v} $options { + if {$o ni $opts} { + return -code error "Invalid option: $o" + } + } + + set w_ $w($id).patternProps + catch {destroy $w_} + toplevel $w_ + + foreach v {action_ok action_cancel} { + set $v [string map [list %ns [namespace current] %id $id %w $w_ %old_name $old_name] [set $v]] + } + + grid [ttk::label $w_.lname -text "Name: "] -row 0 -column 0 + grid [ttk::label $w_.llength -text "Rows: "] -row 1 -column 0 + grid [ttk::entry $w_.ename] -row 0 -column 1 + grid [ttk::entry $w_.elength] -row 1 -column 1 + grid [ttk::frame $w_.b] -row 2 -columnspan 2 + grid [ttk::button $w_.b.ok -text Ok -command $action_ok] -row 0 -column 0 -in $w_.b + grid [ttk::button $w_.b.cancel -text Cancel -command $action_cancel] -row 0 -column 1 -in $w_.b + + $w_.ename insert 0 $vname + $w_.elength insert 0 $vlength + + if {!$bname} {$w_.ename configure -state disabled} + if {!$blength} {$w_.elength configure -state disabled} + } + + proc patternPropertiesDialog {id {options {}}} { + debugPrint [info level 0] + variable currentpattern; + variable length; + + dict set options action_ok { + %ns::resizePattern %id %old_name [%w.elength get] + %ns::renamePattern %id %old_name [%w.ename get] + destroy %w + } + dict set options vname $currentpattern($id) + dict set options vlength [dict get $length($id) $currentpattern($id)] + patternPropertiesDialog_common $id $options + } + + proc copyPatternDialog {id {options {}}} { + debugPrint [info level 0] + variable w; + variable length; + + dict set options blength 0 + dict set options vname [generateNewPatternName $id] + dict set options vlength [dict get $length($id) $currentpattern($id)] + dict set options action_ok { + %ns::copyPattern %id %old_name [%w.ename get] + %ns::displayPattern %id [%w.ename get] + destroy %w + } + patternPropertiesDialog_common $id $options + } + + proc removePatternConfirm {id} { + debugPrint [info level 0] + variable currentpattern; + if {[tk_messageBox \ + -type yesno -default no \ + -title "Question" \ + -icon question -message "Do you confirm pattern delete?" \ + -detail "The operation cannot be undone" \ + -parent $w] == {yes}} { + removePattern $id $currentpattern($id) + } + } + + proc newPatternDialog {id {options {}}} { + debugPrint [info level 0] + dict set options action_ok { + %ns::createPattern %id [%w.ename get] [%w.elength get] + destroy %w + } + # %ns::displayPattern %id [%w.ename get] + dict set options vname [generateNewPatternName $id] + dict set options vlength 16 + patternPropertiesDialog_common $id $options + } + + #entrypoint + proc init {id song_name track_name cols debug_flag} { + debugPrint [info level 0] + variable debug + set debug [expr {$debug_flag != 0}] + variable w + variable songname + variable trackname + variable length + variable currentpattern + variable columns + set w($id) .w_${song_name}_${track_name} + set songname($id) $song_name + set trackname($id) $track_name + set currentpattern($id) {} + set length($id) {} + set columns($id) $cols + } + + proc openWindow {id} { + debugPrint [info level 0] + createMainWindow $id + } + + proc closeWindow {id} { + debugPrint [info level 0] + variable w + destroy $w($id) + } + + proc dispatch {id args} { + debugPrint [info level 0] + variable w + variable patterns + variable length + variable currentpattern + variable quirks_fix_int_floats + switch -exact [lindex $args 0] { + patterns { + set patterns($id) [lrange $args 1 end] + debugPrint "tk::patterns <- $patterns($id)" + $w($id).f.p configure -values $patterns($id) + if {0 && [llength $patterns($id)] > 0 && $currentpattern($id) == {}} { + set firstpat [lindex $patterns($id) 0] + debugPrint "showing pattern '$firstpat'" + displayPattern $id $firstpat + } + } + patternlength { + set pat_name [lindex $args 1] + set pat_length [lindex $args 2] + debugPrint "got patternlength: '$pat_name' (len=$pat_length)" + if {![dict exists $length($id) $pat_name] || [dict get $length($id) $pat_name] != $pat_length} { + dict set length($id) $pat_name $pat_length + for {set i 0} {$i < $pat_length} {incr i} { + $::sendgui [concat $id EDIT getrow $pat_name $i \;] + } + } + displayPattern_async $id $pat_name + } + row { + set pat_name [lindex $args 1] + if {$quirks_fix_int_floats} { + set row_num [expr {int([lindex $args 2])}] + } else { + set row_num [lindex $args 2] + } + set row [lrange $args 3 end] + debugPrint "got row: '$pat_name' ($row_num) {$row}" + debugPrint "dataVar = [getDataVar $id $pat_name]" + upvar 0 [getDataVar $id $pat_name] data + set data($row_num,-1) [expr {1+$row_num}] + for {set i 0} {$i < [llength $row]} {incr i} { + debugPrint "set data($row_num,$i) [lindex $row $i]" + set data($row_num,$i) [lindex $row $i] + } + if {$row_num + 1 == [dict get $length($id) $pat_name]} { + #refreshGrid $id + } + } + cell { + set pat_name [lindex $args 1] + if {$quirks_fix_int_floats} { + set row_num [expr {int([lindex $args 2])}] + set col_num [expr {int([lindex $args 3])}] + } else { + set row_num [lindex $args 2] + set col_num [lindex $args 3] + } + set cell [lindex $args 4] + debugPrint "got cell: '$pat_name' ($row_num,$col_num) {$cell}" + debugPrint "dataVar = [getDataVar $id $pat_name]" + upvar 0 [getDataVar $id $pat_name] data + set data($row_num,$col_num) $cell + } + } + } +} |