aboutsummaryrefslogtreecommitdiff
path: root/composer
diff options
context:
space:
mode:
authormescalinum <mescalinum@users.sourceforge.net>2009-08-18 15:08:04 +0000
committermescalinum <mescalinum@users.sourceforge.net>2009-08-18 15:08:04 +0000
commit9a6ab3b333e64c24ee86d929d9387ef367c76ce0 (patch)
treef57e6e10161162119f04b5fa6d3bcc1fe9d92b18 /composer
parent38e419874d5d8f7ceb1e52195f28ac12c2662720 (diff)
import sources
svn path=/trunk/externals/ffext/; revision=11945
Diffstat (limited to 'composer')
-rw-r--r--composer/arraylist.h118
-rw-r--r--composer/common.h166
-rw-r--r--composer/composer.c45
-rw-r--r--composer/makefile25
-rw-r--r--composer/pattern.c142
-rw-r--r--composer/song.c61
-rw-r--r--composer/song_proxy.c69
-rw-r--r--composer/track.c79
-rw-r--r--composer/track_proxy.c518
-rw-r--r--composer/window.tk599
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
+ }
+ }
+ }
+}