aboutsummaryrefslogtreecommitdiff
path: root/externals
diff options
context:
space:
mode:
authorN.N. <matju@users.sourceforge.net>2005-10-04 02:02:15 +0000
committerN.N. <matju@users.sourceforge.net>2005-10-04 02:02:15 +0000
commit5e2a1bc9e56003349e533f7e5841041ba5c04e28 (patch)
treead040f6894d9383b732423a74420e732f62a66a5 /externals
parent520a243c297175386ab31c78c84693a664934a69 (diff)
starting to commit gridflow 0.8.0 ...
if you know how to use "cvs import" please mail me and i'll use it for 0.8.1 svn path=/trunk/; revision=3646
Diffstat (limited to 'externals')
-rw-r--r--externals/gridflow/base/bitpacking.c309
-rw-r--r--externals/gridflow/base/flow_objects.c1189
-rw-r--r--externals/gridflow/base/flow_objects.rb1476
-rw-r--r--externals/gridflow/base/flow_objects_for_image.c618
-rw-r--r--externals/gridflow/base/flow_objects_for_matrix.c77
-rw-r--r--externals/gridflow/base/grid.c604
-rw-r--r--externals/gridflow/base/grid.h1109
-rw-r--r--externals/gridflow/base/main.c647
-rw-r--r--externals/gridflow/base/main.rb369
-rw-r--r--externals/gridflow/base/number.c365
-rw-r--r--externals/gridflow/base/source_filter.rb274
-rw-r--r--externals/gridflow/base/test.rb1086
-rw-r--r--externals/gridflow/bridge/placebo.rb43
-rw-r--r--externals/gridflow/bridge/puredata.c772
-rw-r--r--externals/gridflow/bridge/puredata.rb194
-rw-r--r--externals/gridflow/cpu/mmx.rb225
-rw-r--r--externals/gridflow/devices4ruby/ParallelPort.rb72
-rw-r--r--externals/gridflow/devices4ruby/SoundMixer.rb152
-rw-r--r--externals/gridflow/devices4ruby/SoundPCM.rb102
-rw-r--r--externals/gridflow/devices4ruby/extconf.rb112
-rw-r--r--externals/gridflow/devices4ruby/ioctl.rb66
-rw-r--r--externals/gridflow/doc/moulinette.rb612
-rw-r--r--externals/gridflow/extra/jmax_format.rb167
-rw-r--r--externals/gridflow/extra/puredata_format.rb129
-rw-r--r--externals/gridflow/extra/server_1_grid.rb26
-rw-r--r--externals/gridflow/extra/server_1_ppm.rb20
-rw-r--r--externals/gridflow/extra/server_2.rb65
-rw-r--r--externals/gridflow/extra/smpte.rb23
-rw-r--r--externals/gridflow/format/aalib.c168
-rw-r--r--externals/gridflow/format/dc1394.c346
-rw-r--r--externals/gridflow/format/jpeg.c130
-rw-r--r--externals/gridflow/format/main.rb788
-rw-r--r--externals/gridflow/format/mpeg3.c98
-rw-r--r--externals/gridflow/format/png.c138
-rw-r--r--externals/gridflow/format/quicktimeapple.c500
-rw-r--r--externals/gridflow/format/quicktimehw.c244
-rw-r--r--externals/gridflow/format/sdl.c123
-rw-r--r--externals/gridflow/format/videodev.c544
-rw-r--r--externals/gridflow/format/x11.c655
-rw-r--r--externals/gridflow/optional/usb.c358
40 files changed, 14995 insertions, 0 deletions
diff --git a/externals/gridflow/base/bitpacking.c b/externals/gridflow/base/bitpacking.c
new file mode 100644
index 00000000..143f6edb
--- /dev/null
+++ b/externals/gridflow/base/bitpacking.c
@@ -0,0 +1,309 @@
+/*
+ $Id: bitpacking.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "grid.h.fcs"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CONVERT1 t = \
+ (((in[0] << hb[0]) >> 7) & mask[0]) | \
+ (((in[1] << hb[1]) >> 7) & mask[1]) | \
+ (((in[2] << hb[2]) >> 7) & mask[2])
+
+#define CONVERT2 \
+ for (t=0,i=0; i<self->size; i++) t |= (((in[i] << hb[i]) >> 7) & mask[i]);
+
+#define CONVERT3 \
+ for (t=0,i=0; i<self->size; i++) { \
+ t |= ((in[i]>>(7-hb[i]))|(in[i]<<(hb[i]-7))) & mask[i]; \
+ }
+
+#define WRITE_LE \
+ for (int bytes = self->bytes; bytes; bytes--, t>>=8) *out++ = t;
+
+#define WRITE_BE { int bytes; \
+ bytes = self->bytes; \
+ while (bytes--) { out[bytes] = t; t>>=8; }\
+ out += self->bytes; }
+
+/* this macro would be faster if the _increment_
+ was done only once every loop. or maybe gcc does it, i dunno */
+#define NTIMES(_x_) \
+ for (; n>=4; n-=4) { _x_ _x_ _x_ _x_ } \
+ for (; n; n--) { _x_ }
+
+/* this could be faster (use asm) */
+void swap32 (int n, Pt<uint32> data) {
+ NTIMES({
+ uint32 x = *data;
+ x = (x<<16) | (x>>16);
+ x = ((x&0xff00ff)<<8) | ((x>>8)&0xff00ff);
+ *data++ = x;
+ })
+}
+
+/* this could be faster (use asm or do it in int32 chunks) */
+void swap16 (int n, Pt<uint16> data) {
+ NTIMES({ uint16 x = *data; *data++ = (x<<8) | (x>>8); })
+}
+
+/* **************************************************************** */
+
+template <class T>
+static void default_pack(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ uint32 t;
+ int i;
+ int hb[4];
+ uint32 mask[4];
+ int sameorder = self->endian==2 || self->endian==::is_le();
+ int size = self->size;
+
+ for (i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]);
+ memcpy(mask,self->mask,size*sizeof(uint32));
+
+ if (sameorder && size==3) {
+ switch(self->bytes) {
+ case 2: NTIMES(t=CONVERT1; *((int16 *)out)=t; out+=2; in+=3;) return;
+ case 4: NTIMES(t=CONVERT1; *((int32 *)out)=t; out+=4; in+=3;) return;
+ }
+ }
+ if (self->is_le()) {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_LE;} break;
+ case 4: for (; n--; in+=4) {CONVERT3; WRITE_LE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_LE;}}
+ } else {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_BE;} break;
+ case 4: for (; n--; in+=4) {CONVERT3; WRITE_BE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_BE;}}
+ }
+}
+
+#define LOOP_UNPACK(_reader_) \
+ for (; n; n--) { \
+ int bytes=0; uint32 temp=0; _reader_; \
+ for (int i=0; i<self->size; i++, out++) { \
+ uint32 t=temp&self->mask[i]; \
+ *out = (t<<(7-hb[i]))|(t>>(hb[i]-7)); \
+ } \
+ }
+// *out++ = ((temp & self->mask[i]) << 7) >> hb[i];
+
+template <class T>
+static void default_unpack(BitPacking *self, int n, Pt<uint8> in, Pt<T> out) {
+ int hb[4];
+ for (int i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]);
+ if (is_le()) { // smallest byte first
+ LOOP_UNPACK(
+ for(; self->bytes>bytes; bytes++, in++) temp |= *in<<(8*bytes);
+ )
+ } else { // biggest byte first
+ LOOP_UNPACK(
+ bytes=self->bytes; for (; bytes; bytes--, in++) temp=(temp<<8)|*in;
+ )
+ }
+}
+
+/* **************************************************************** */
+
+template <class T>
+static void pack2_565(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ const int hb[3] = {15,10,4};
+ const uint32 mask[3] = {0x0000f800,0x000007e0,0x0000001f};
+ uint32 t;
+ NTIMES( t=CONVERT1; *((short *)out)=t; out+=2; in+=3; )
+}
+
+template <class T>
+static void pack3_888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = (in[5]<<24) | (in[ 0]<<16) | (in[ 1]<<8) | in[2];
+ o32[1] = (in[7]<<24) | (in[ 8]<<16) | (in[ 3]<<8) | in[4];
+ o32[2] = (in[9]<<24) | (in[10]<<16) | (in[11]<<8) | in[6];
+ o32+=3; in+=12;
+ n-=4;
+ }
+ out = (Pt<uint8>)o32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+
+/*
+template <>
+static void pack3_888(BitPacking *self, int n, Pt<uint8> in, Pt<uint8> out) {
+ Pt<uint32> o32 = Pt<uint32>((uint32 *)out.p,n*3/4);
+ Pt<uint32> i32 = Pt<uint32>((uint32 *)in.p,n*3/4);
+ while (n>=4) {
+#define Z(w,i) ((word##w>>(i*8))&255)
+ uint32 word0 = i32[0];
+ uint32 word1 = i32[1];
+ uint32 word2 = i32[2];
+ o32[0] = (Z(1,1)<<24) | (Z(0,0)<<16) | (Z(0,1)<<8) | Z(0,2);
+ o32[1] = (Z(1,3)<<24) | (Z(2,0)<<16) | (Z(0,3)<<8) | Z(1,0);
+ o32[2] = (Z(2,1)<<24) | (Z(2,2)<<16) | (Z(2,3)<<8) | Z(1,2);
+ o32+=3; i32+=3;
+ n-=4;
+ }
+#undef Z
+ out = (Pt<uint8>)o32;
+ in = (Pt<uint8>)i32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+*/
+
+template <class T>
+static void pack3_888b(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = (in[0]<<16) | (in[1]<<8) | in[2];
+ o32[1] = (in[3]<<16) | (in[4]<<8) | in[5];
+ o32[2] = (in[6]<<16) | (in[7]<<8) | in[8];
+ o32[3] = (in[9]<<16) | (in[10]<<8) | in[11];
+ o32+=4; in+=12;
+ n-=4;
+ }
+ NTIMES( o32[0] = (in[0]<<16) | (in[1]<<8) | in[2]; o32++; in+=3; )
+}
+
+/* (R,G,B,?) -> B:8,G:8,R:8,0:8 */
+template <class T>
+static void pack3_bgrn8888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+/* NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=4; in+=4; ) */
+ Pt<int32> i32 = (Pt<int32>)in;
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff);
+ o32[1] = ((i32[1]&0xff)<<16) | (i32[1]&0xff00) | ((i32[1]>>16)&0xff);
+ o32[2] = ((i32[2]&0xff)<<16) | (i32[2]&0xff00) | ((i32[2]>>16)&0xff);
+ o32[3] = ((i32[3]&0xff)<<16) | (i32[3]&0xff00) | ((i32[3]>>16)&0xff);
+ o32+=4; i32+=4; n-=4;
+ }
+ NTIMES( o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff); o32++; i32++; )
+}
+
+static uint32 bp_masks[][4] = {
+ {0x0000f800,0x000007e0,0x0000001f,0},
+ {0x00ff0000,0x0000ff00,0x000000ff,0},
+};
+
+static Packer bp_packers[] = {
+ {default_pack, default_pack, default_pack},
+ {pack2_565, pack2_565, pack2_565},
+ {pack3_888, pack3_888, pack3_888},
+ {pack3_888b, default_pack, default_pack},
+ {pack3_bgrn8888, default_pack, default_pack},
+};
+
+static Unpacker bp_unpackers[] = {
+ {default_unpack, default_unpack, default_unpack},
+};
+
+static BitPacking builtin_bitpackers[] = {
+ BitPacking(2, 2, 3, bp_masks[0], &bp_packers[1], &bp_unpackers[0]),
+ BitPacking(1, 3, 3, bp_masks[1], &bp_packers[2], &bp_unpackers[0]),
+ BitPacking(1, 4, 3, bp_masks[1], &bp_packers[3], &bp_unpackers[0]),
+ BitPacking(1, 4, 4, bp_masks[1], &bp_packers[4], &bp_unpackers[0]),
+};
+
+/* **************************************************************** */
+
+bool BitPacking::eq(BitPacking *o) {
+ if (!(bytes == o->bytes)) return false;
+ if (!(size == o->size)) return false;
+ for (int i=0; i<size; i++) {
+ if (!(mask[i] == o->mask[i])) return false;
+ }
+ if (endian==o->endian) return true;
+ /* same==little on a little-endian; same==big on a big-endian */
+ return (endian ^ o->endian ^ ::is_le()) == 2;
+}
+
+BitPacking::BitPacking(int endian, int bytes, int size, uint32 *mask,
+Packer *packer, Unpacker *unpacker) {
+ this->endian = endian;
+ this->bytes = bytes;
+ this->size = size;
+ for (int i=0; i<size; i++) this->mask[i] = mask[i];
+ if (packer) {
+ this->packer = packer;
+ this->unpacker = unpacker;
+ return;
+ }
+ int packeri=-1;
+ this->packer = &bp_packers[0];
+ this->unpacker = &bp_unpackers[0];
+
+ for (int i=0; i<(int)(sizeof(builtin_bitpackers)/sizeof(BitPacking)); i++) {
+ BitPacking *bp = &builtin_bitpackers[i];
+ if (this->eq(bp)) {
+ this->packer = bp->packer;
+ this->unpacker = bp->unpacker;
+ packeri=i;
+ goto end;
+ }
+ }
+end:;
+/*
+ ::gfpost("Bitpacking: endian=%d bytes=%d size=%d packeri=%d",
+ endian, bytes, size, packeri);
+ ::gfpost(" packer=0x%08x unpacker=0x%08x",this->packer,this->unpacker);
+ ::gfpost(" mask=[0x%08x,0x%08x,0x%08x,0x%08x]",mask[0],mask[1],mask[2],mask[3]);
+*/
+}
+
+bool BitPacking::is_le() {
+ return endian==1 || (endian ^ ::is_le())==3;
+}
+
+template <class T>
+void BitPacking::pack(int n, Pt<T> in, Pt<uint8> out) {
+ switch (NumberTypeE_type_of(*in)) {
+ case uint8_e: packer->as_uint8(this,n,(Pt<uint8>)in,out); break;
+ case int16_e: packer->as_int16(this,n,(Pt<int16>)in,out); break;
+ case int32_e: packer->as_int32(this,n,(Pt<int32>)in,out); break;
+ default: RAISE("argh");
+ }
+}
+
+template <class T>
+void BitPacking::unpack(int n, Pt<uint8> in, Pt<T> out) {
+ switch (NumberTypeE_type_of(*out)) {
+ case uint8_e: unpacker->as_uint8(this,n,in,(Pt<uint8>)out); break;
+ case int16_e: unpacker->as_int16(this,n,in,(Pt<int16>)out); break;
+ case int32_e: unpacker->as_int32(this,n,in,(Pt<int32>)out); break;
+ default: RAISE("argh");
+ }
+}
+
+// i'm sorry... see the end of grid.c for an explanation...
+//static
+void make_hocus_pocus () {
+// exit(1);
+#define FOO(S) \
+ ((BitPacking*)0)->pack(0,Pt<S>(),Pt<uint8>()); \
+ ((BitPacking*)0)->unpack(0,Pt<uint8>(),Pt<S>());
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+}
diff --git a/externals/gridflow/base/flow_objects.c b/externals/gridflow/base/flow_objects.c
new file mode 100644
index 00000000..ac3305d6
--- /dev/null
+++ b/externals/gridflow/base/flow_objects.c
@@ -0,0 +1,1189 @@
+/*
+ $Id: flow_objects.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <math.h>
+#include "grid.h.fcs"
+
+// BAD HACK: GCC complains: unimplemented (--debug|--debug-harder mode only)
+#ifdef HAVE_DEBUG
+#define SCOPY(a,b,n) COPY(a,b,n)
+#else
+#define SCOPY(a,b,n) SCopy<n>::f(a,b)
+#endif
+
+template <int n> class SCopy {
+public: template <class T> static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {
+ *a=*b; SCopy<n-1>::f(a+1,b+1);}};
+template <> class SCopy<0> {
+public: template <class T> static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {}};
+
+/*template <> class SCopy<4> {
+public: template <class T>
+ static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {
+ *a=*b; SCopy<3>::f(a+1,b+1);}
+ // wouldn't gcc 2.95 complain here?
+ static inline void __attribute__((always_inline)) f(Pt<uint8> a, Pt<uint8> b)
+ { *(int32 *)a=*(int32 *)b; }
+};*/
+
+Numop *op_add, *op_sub, *op_mul, *op_div, *op_mod, *op_shl, *op_and, *op_put;
+
+static void expect_dim_dim_list (P<Dim> d) {
+ if (d->n!=1) RAISE("dimension list should be Dim[n], not %s",d->to_s());}
+//static void expect_min_one_dim (P<Dim> d) {
+// if (d->n<1) RAISE("minimum 1 dimension");}
+static void expect_max_one_dim (P<Dim> d) {
+ if (d->n>1) { RAISE("expecting Dim[] or Dim[n], got %s",d->to_s()); }}
+//static void expect_exactly_one_dim (P<Dim> d) {
+// if (d->n!=1) { RAISE("expecting Dim[n], got %s",d->to_s()); }}
+
+//****************************************************************
+\class GridCast < GridObject
+struct GridCast : GridObject {
+ \attr NumberTypeE nt;
+ \decl void initialize (NumberTypeE nt);
+ \grin 0
+};
+
+GRID_INLET(GridCast,0) {
+ out = new GridOutlet(this,0,in->dim,nt);
+} GRID_FLOW {
+ out->send(n,data);
+} GRID_END
+
+\def void initialize (NumberTypeE nt) {
+ rb_call_super(argc,argv);
+ this->nt = nt;
+}
+
+\classinfo { IEVAL(rself,"install '#cast',1,1"); }
+\end class GridCast
+
+//****************************************************************
+//{ ?,Dim[B] -> Dim[*Cs] }
+// out0 nt to be specified explicitly
+\class GridImport < GridObject
+struct GridImport : GridObject {
+ \attr NumberTypeE cast;
+ \attr P<Dim> dim; // size of grids to send
+ PtrGrid dim_grid;
+ GridImport() { dim_grid.constrain(expect_dim_dim_list); }
+ ~GridImport() {}
+ \decl void initialize(Ruby x, NumberTypeE cast=int32_e);
+ \decl void _0_cast(NumberTypeE cast);
+ \decl void _0_reset();
+ \decl void _0_symbol(Symbol x);
+ \decl void _0_list(...);
+ \decl void _1_per_message();
+ \grin 0
+ \grin 1 int32
+ template <class T> void process (int n, Pt<T> data) {
+ while (n) {
+ if (!out || !out->dim) out = new GridOutlet(this,0,dim?dim:in[0]->dim,cast);
+ int32 n2 = min((int32)n,out->dim->prod()-out->dex);
+ out->send(n2,data);
+ n-=n2; data+=n2;
+ }
+ }
+};
+
+GRID_INLET(GridImport,0) {} GRID_FLOW { process(n,data); } GRID_END
+GRID_INPUT(GridImport,1,dim_grid) { dim = dim_grid->to_dim(); } GRID_END
+
+\def void _0_symbol(Symbol x) {
+ const char *name = rb_sym_name(argv[0]);
+ int n = strlen(name);
+ if (!dim) out=new GridOutlet(this,0,new Dim(n));
+ process(n,Pt<uint8>((uint8 *)name,n));
+}
+
+\def void _0_cast(NumberTypeE cast) { this->cast = cast; }
+
+\def void _0_list(...) {
+ if (in.size()<1 || !in[0]) _0_grid(0,0); //HACK: enable grid inlet...
+ in[0]->from_ruby_list(argc,argv,cast);
+}
+
+\def void _1_per_message() { dim=0; dim_grid=0; }
+
+\def void initialize(Ruby x, NumberTypeE cast) {
+ rb_call_super(argc,argv);
+ this->cast = cast;
+ if (argv[0]!=SYM(per_message)) {
+ dim_grid=new Grid(argv[0]);
+ dim = dim_grid->to_dim();
+ }
+}
+
+\def void _0_reset() {
+ STACK_ARRAY(int32,foo,1); *foo=0;
+ while (out->dim) out->send(1,foo);
+}
+
+\classinfo { IEVAL(rself,"install '#import',2,1"); }
+\end class GridImport
+
+//****************************************************************
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridExport < GridObject
+struct GridExport : GridObject {
+ \grin 0
+};
+
+template <class T>
+static Ruby INTORFLOAT2NUM(T value) {return INT2NUM(value);}
+static Ruby INTORFLOAT2NUM(int64 value) {return gf_ll2num(value);}
+static Ruby INTORFLOAT2NUM(float32 value) {return rb_float_new(value);}
+static Ruby INTORFLOAT2NUM(float64 value) {return rb_float_new(value);}
+
+GRID_INLET(GridExport,0) {
+} GRID_FLOW {
+ for (int i=0; i<n; i++) {
+ Ruby a[] = { INT2NUM(0), INTORFLOAT2NUM(data[i]) };
+ send_out(COUNT(a),a);
+ }
+} GRID_END
+\classinfo { IEVAL(rself,"install '#export',1,1"); }
+\end class GridExport
+
+/* **************************************************************** */
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridExportList < GridObject
+struct GridExportList : GridObject {
+ Ruby /*Array*/ list;
+ int n;
+ \grin 0
+};
+
+GRID_INLET(GridExportList,0) {
+ int n = in->dim->prod();
+ if (n>250000) RAISE("list too big (%d elements)", n);
+ list = rb_ary_new2(n+2);
+ this->n = n;
+ rb_ivar_set(rself,SI(@list),list); // keep
+ rb_ary_store(list,0,INT2NUM(0));
+ rb_ary_store(list,1,bsym._list);
+} GRID_FLOW {
+ for (int i=0; i<n; i++, data++)
+ rb_ary_store(list,in->dex+i+2,INTORFLOAT2NUM(*data));
+} GRID_FINISH {
+ send_out(rb_ary_len(list),rb_ary_ptr(list));
+ list = 0;
+ rb_ivar_set(rself,SI(@list),Qnil); // unkeep
+} GRID_END
+
+\classinfo { IEVAL(rself,"install '#export_list',1,1"); }
+\end class GridExportList
+
+/* **************************************************************** */
+// GridStore ("@store") is the class for storing a grid and restituting
+// it on demand. The right inlet receives the grid. The left inlet receives
+// either a bang (which forwards the whole image) or a grid describing what
+// to send.
+//{ Dim[*As,B],Dim[*Cs,*Ds] -> Dim[*As,*Ds] }
+// in0: integer nt
+// in1: whatever nt
+// out0: same nt as in1
+\class GridStore < GridObject
+struct GridStore : GridObject {
+ PtrGrid r; // can't be \attr
+ PtrGrid put_at; // can't be //\attr
+ \attr Numop *op;
+ int32 wdex [Dim::MAX_DIMENSIONS]; // temporary buffer, copy of put_at
+ int32 fromb[Dim::MAX_DIMENSIONS];
+ int32 to2 [Dim::MAX_DIMENSIONS];
+ int lsd; // lsd = Last Same Dimension (for put_at)
+ int d; // goes with wdex
+ \decl void initialize (Grid *r=0);
+ \decl void _0_bang ();
+ \decl void _0_op (Numop *op);
+ \decl void _1_reassign ();
+ \decl void _1_put_at (Grid *index);
+ \grin 0 int
+ \grin 1
+ GridStore() { put_at.constrain(expect_max_one_dim); }
+ template <class T> void compute_indices(Pt<T> v, int nc, int nd);
+};
+
+// takes the backstore of a grid and puts it back into place. a backstore
+// is a grid that is filled while the grid it would replace has not
+// finished being used.
+static void snap_backstore (PtrGrid &r) {
+ if (r.next) {r=r.next.p; r.next=0;}
+}
+
+template <class T> void GridStore::compute_indices(Pt<T> v, int nc, int nd) {
+ for (int i=0; i<nc; i++) {
+ uint32 wrap = r->dim->v[i];
+ bool fast = lowest_bit(wrap)==highest_bit(wrap); // is power of two?
+ if (i) {
+ if (fast) op_shl->map(nd,v,(T)highest_bit(wrap));
+ else op_mul->map(nd,v,(T)wrap);
+ }
+ if (fast) op_and->map(nd,v+nd*i,(T)(wrap-1));
+ else op_mod->map(nd,v+nd*i,(T)(wrap));
+ if (i) op_add->zip(nd,v,v+nd*i);
+ }
+}
+
+// !@#$ i should ensure that n is not exceedingly large
+// !@#$ worse: the size of the foo buffer may still be too large
+GRID_INLET(GridStore,0) {
+ // snap_backstore must be done before *anything* else
+ snap_backstore(r);
+ int na = in->dim->n;
+ int nb = r->dim->n;
+ int nc = in->dim->get(na-1);
+ STACK_ARRAY(int32,v,Dim::MAX_DIMENSIONS);
+ if (na<1) RAISE("must have at least 1 dimension.",na,1,1+nb);
+ int lastindexable = r->dim->prod()/r->dim->prod(nc) - 1;
+ int ngreatest = nt_greatest((T *)0);
+ if (lastindexable > ngreatest) {
+ RAISE("lastindexable=%d > ngreatest=%d (ask matju)",lastindexable,ngreatest);
+ }
+ if (nc > nb)
+ RAISE("wrong number of elements in last dimension: "
+ "got %d, expecting <= %d", nc, nb);
+ int nd = nb - nc + na - 1;
+ COPY(v,in->dim->v,na-1);
+ COPY(v+na-1,r->dim->v+nc,nb-nc);
+ out=new GridOutlet(this,0,new Dim(nd,v),r->nt);
+ if (nc>0) in->set_factor(nc);
+} GRID_FLOW {
+ int na = in->dim->n;
+ int nc = in->dim->get(na-1);
+ int size = r->dim->prod(nc);
+ assert((n % nc) == 0);
+ int nd = n/nc;
+ STACK_ARRAY(T,w,n);
+ Pt<T> v=w;
+ if (sizeof(T)==1 && nc==1 && r->dim->v[0]<=256) {
+ // bug? shouldn't modulo be done here?
+ v=data;
+ } else {
+ COPY(v,data,n);
+ for (int k=0,i=0; i<nc; i++) for (int j=0; j<n; j+=nc) v[k++] = data[i+j];
+ compute_indices(v,nc,nd);
+ }
+#define FOO(type) { \
+ Pt<type> p = (Pt<type>)*r; \
+ if (size<=16) { \
+ Pt<type> foo = ARRAY_NEW(type,nd*size); \
+ int i=0; \
+ switch (size) { \
+ case 1: for (; i<nd&-4; i+=4, foo+=4) { \
+ foo[0] = p[v[i+0]]; \
+ foo[1] = p[v[i+1]]; \
+ foo[2] = p[v[i+2]]; \
+ foo[3] = p[v[i+3]]; \
+ } break; \
+ case 2: for (; i<nd; i++, foo+=2) SCOPY(foo,p+2*v[i],2); break; \
+ case 3: for (; i<nd; i++, foo+=3) SCOPY(foo,p+3*v[i],3); break; \
+ case 4: for (; i<nd; i++, foo+=4) SCOPY(foo,p+4*v[i],4); break; \
+ default:; }; \
+ for (; i<nd; i++, foo+=size) COPY(foo,p+size*v[i],size); \
+ out->give(size*nd,foo-size*nd); \
+ } else { \
+ for (int i=0; i<nd; i++) out->send(size,p+size*v[i]); \
+ } \
+}
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+} GRID_FINISH {
+ if (in->dim->prod()==0) {
+ int n = in->dim->prod(0,-2);
+ int size = r->dim->prod();
+#define FOO(T) while (n--) out->send(size,(Pt<T>)*r);
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+ }
+} GRID_END
+
+GRID_INLET(GridStore,1) {
+ NumberTypeE nt = NumberTypeE_type_of(*data);
+ if (!put_at) { // reassign
+ if (in[0].dim)
+ r.next = new Grid(in->dim,nt);
+ else
+ r = new Grid(in->dim,nt);
+ return;
+ }
+ // put_at ( ... )
+ //!@#$ should check types. if (r->nt!=in->nt) RAISE("shoo");
+ int nn=r->dim->n, na=put_at->dim->v[0], nb=in->dim->n;
+ STACK_ARRAY(int32,sizeb,nn);
+ for (int i=0; i<nn; i++) { fromb[i]=0; sizeb[i]=1; }
+ COPY(Pt<int32>(wdex,nn) ,(Pt<int32>)*put_at ,put_at->dim->prod());
+ COPY(Pt<int32>(fromb,nn)+nn-na,(Pt<int32>)*put_at ,na);
+ COPY(Pt<int32>(sizeb,nn)+nn-nb,(Pt<int32>)in->dim->v,nb);
+ for (int i=0; i<nn; i++) to2[i] = fromb[i]+sizeb[i];
+ d=0;
+ // find out when we can skip computing indices
+ //!@#$ should actually also stop before blowing up packet size
+ lsd=nn;
+ while (lsd>=nn-in->dim->n) {
+ lsd--;
+ int cs = in->dim->prod(lsd-nn+in->dim->n);
+ if (cs>GridOutlet::MAX_PACKET_SIZE || fromb[lsd]!=0 || sizeb[lsd]!=r->dim->v[lsd]) break;
+ }
+ lsd++;
+ int cs = in->dim->prod(lsd-nn+in->dim->n);
+ in->set_factor(cs);
+} GRID_FLOW {
+ if (!put_at) { // reassign
+ COPY(((Pt<T>)*(r.next ? r.next.p : &*r.p))+in->dex, data, n);
+ return;
+ }
+ // put_at ( ... )
+ int nn=r->dim->n;
+ int cs = in->factor(); // chunksize
+ STACK_ARRAY(int32,v,lsd);
+ Pt<int32> x = Pt<int32>(wdex,nn);
+ while (n) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<lsd;d++) x[d]=fromb[d];
+ COPY(v,x,lsd);
+ compute_indices(v,lsd,1);
+ op->zip(cs,(Pt<T>)*r+v[0]*cs,data);
+ data+=cs;
+ n-=cs;
+ // find next set of indices; here d is the dim# to increment
+ for(;;) {
+ d--;
+ if (d<0) goto end;
+ x[d]++;
+ if (x[d]<to2[d]) break;
+ }
+ end:; // why here ??? or why at all?
+ d++;
+ }
+ //end:; // why not here ???
+} GRID_END
+\def void _0_op(Numop *op) { this->op=op; }
+\def void _0_bang () { rb_funcall(rself,SI(_0_list),3,INT2NUM(0),SYM(#),INT2NUM(0)); }
+\def void _1_reassign () { put_at=0; }
+\def void _1_put_at (Grid *index) { put_at=index; }
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->r = r?r:new Grid(new Dim(),int32_e,true);
+ op = op_put;
+}
+\classinfo { IEVAL(rself,"install '#store',2,1"); }
+\end class GridStore
+
+//****************************************************************
+//{ Dim[*As]<T> -> Dim[*As]<T> } or
+//{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As]<T> }
+\class GridOp < GridObject
+struct GridOp : GridObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \decl void initialize(Numop *op, Grid *r=0);
+ \grin 0
+ \grin 1
+ \decl void _0_op(Numop *op);
+};
+
+GRID_INLET(GridOp,0) {
+ snap_backstore(r);
+ SAME_TYPE(in,r);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_mode(6);
+} GRID_FLOW {
+ Pt<T> rdata = (Pt<T>)*r;
+ int loop = r->dim->prod();
+ if (sizeof(T)==8) {
+ fprintf(stderr,"1: data=%p rdata=%p\n",data.p,rdata.p);
+ WATCH(n,data);
+ }
+ if (loop>1) {
+ if (in->dex+n <= loop) {
+ op->zip(n,data,rdata+in->dex);
+ } else {
+ // !@#$ should prebuild and reuse this array when "loop" is small
+ STACK_ARRAY(T,data2,n);
+ int ii = mod(in->dex,loop);
+ int m = min(loop-ii,n);
+ COPY(data2,rdata+ii,m);
+ int nn = m+((n-m)/loop)*loop;
+ for (int i=m; i<nn; i+=loop) COPY(data2+i,rdata,loop);
+ if (n>nn) COPY(data2+nn,rdata,n-nn);
+ if (sizeof(T)==8) {
+ fprintf(stderr,"2: data=%p data2=%p\n",data.p,data2.p);
+ WATCH(n,data); WATCH(n,data2);
+ }
+ op->zip(n,data,data2);
+ if (sizeof(T)==8) {WATCH(n,data); WATCH(n,data2);}
+ }
+ } else {
+ op->map(n,data,*rdata);
+ }
+ out->give(n,data);
+} GRID_END
+
+GRID_INPUT2(GridOp,1,r) {} GRID_END
+\def void _0_op(Numop *op) { this->op=op; }
+
+\def void initialize(Numop *op, Grid *r=0) {
+ rb_call_super(argc,argv);
+ this->op=op;
+ this->r = r?r:new Grid(new Dim(),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#',2,1"); }
+\end class GridOp
+
+//****************************************************************
+\class GridFold < GridObject
+struct GridFold : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \decl void initialize (Numop *op);
+ \decl void _0_op (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+};
+
+GRID_INLET(GridFold,0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the seed (%d vs %d)",an,bn);
+ STACK_ARRAY(int32,v,an-1);
+ int yi = an-bn-1;
+ COPY(v,in->dim->v,yi);
+ COPY(v+yi,in->dim->v+an-bn,bn);
+ if (seed) SAME_DIM(an-(yi+1),in->dim,(yi+1),seed->dim,0);
+ out=new GridOutlet(this,0,new Dim(an-1,v),in->nt);
+ int k = seed ? seed->dim->prod() : 1;
+ in->set_factor(in->dim->get(yi)*k);
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ int yn = in->dim->v[an-bn-1];
+ int zn = in->dim->prod(an-bn);
+ STACK_ARRAY(T,buf,n/yn);
+ int nn=n;
+ int yzn=yn*zn;
+ for (int i=0; n; i+=zn, data+=yzn, n-=yzn) {
+ if (seed) COPY(buf+i,((Pt<T>)*seed),zn);
+ else CLEAR(buf+i,zn);
+ op->fold(zn,yn,buf+i,data);
+ }
+ out->send(nn/yn,buf);
+} GRID_END
+
+\def void _0_op (Numop *op ) { this->op =op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\def void initialize (Numop *op) { rb_call_super(argc,argv); this->op=op; }
+\classinfo { IEVAL(rself,"install '#fold',1,1"); }
+\end class GridFold
+
+\class GridScan < GridObject
+struct GridScan : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \decl void initialize (Numop *op);
+ \decl void _0_op (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+};
+
+GRID_INLET(GridScan,0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,B,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the right hand");
+ if (seed) SAME_DIM(bn,in->dim,an-bn,seed->dim,0);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->prod(an-bn-1));
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ int yn = in->dim->v[an-bn-1];
+ int zn = in->dim->prod(an-bn);
+ int factor = in->factor();
+ STACK_ARRAY(T,buf,n);
+ COPY(buf,data,n);
+ if (seed) {
+ for (int i=0; i<n; i+=factor) op->scan(zn,yn,(Pt<T>)*seed,buf+i);
+ } else {
+ STACK_ARRAY(T,seed,zn);
+ CLEAR(seed,zn);
+ for (int i=0; i<n; i+=factor) op->scan(zn,yn,seed,buf+i);
+ }
+ out->send(n,buf);
+} GRID_END
+
+\def void _0_op (Numop *op ) { this->op =op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\def void initialize (Numop *op) { rb_call_super(argc,argv); this->op = op; }
+\classinfo { IEVAL(rself,"install '#scan',1,1"); }
+\end class GridScan
+
+//****************************************************************
+//{ Dim[*As,C]<T>,Dim[C,*Bs]<T> -> Dim[*As,*Bs]<T> }
+\class GridInner < GridObject
+struct GridInner : GridObject {
+ \attr Numop *op_para;
+ \attr Numop *op_fold;
+ \attr PtrGrid seed;
+ PtrGrid r;
+ PtrGrid r2;
+ GridInner() {}
+ \decl void initialize (Grid *r=0);
+ \decl void _0_op (Numop *op);
+ \decl void _0_fold (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+ \grin 1
+};
+
+template <class T> void inner_child_a (Pt<T> buf, Pt<T> data, int rrows, int rcols, int chunk) {
+ Pt<T> bt = buf, dt = data;
+ for (int j=0; j<chunk; j++, bt+=rcols, dt+=rrows) op_put->map(rcols,bt,*dt);
+}
+template <class T, int rcols> void inner_child_b (Pt<T> buf, Pt<T> data, int rrows, int chunk) {
+ Pt<T> bt = buf, dt = data;
+ for (int j=0; j<chunk; j++, bt+=rcols, dt+=rrows) {
+ for (int k=0; k<rcols; k++) bt[k] = *dt;
+ }
+}
+GRID_INLET(GridInner,0) {
+ SAME_TYPE(in,r);
+ SAME_TYPE(in,seed);
+ P<Dim> a = in->dim;
+ P<Dim> b = r->dim;
+ if (a->n<1) RAISE("a: minimum 1 dimension");
+ if (b->n<1) RAISE("b: minimum 1 dimension");
+ if (seed->dim->n != 0) RAISE("seed must be a scalar");
+ int a_last = a->get(a->n-1);
+ int n = a->n+b->n-2;
+ SAME_DIM(1,a,a->n-1,b,0);
+ STACK_ARRAY(int32,v,n);
+ COPY(v,a->v,a->n-1);
+ COPY(v+a->n-1,b->v+1,b->n-1);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+ in->set_factor(a_last);
+
+ int rrows = in->factor();
+ int rsize = r->dim->prod();
+ int rcols = rsize/rrows;
+ Pt<T> rdata = (Pt<T>)*r;
+ int chunk = GridOutlet::MAX_PACKET_SIZE/rsize;
+ r2=new Grid(new Dim(chunk*rsize),r->nt);
+ Pt<T> buf3 = (Pt<T>)*r2;
+ for (int i=0; i<rrows; i++)
+ for (int j=0; j<chunk; j++)
+ COPY(buf3+(j+i*chunk)*rcols,rdata+i*rcols,rcols);
+} GRID_FLOW {
+ int rrows = in->factor();
+ int rsize = r->dim->prod();
+ int rcols = rsize/rrows;
+ int chunk = GridOutlet::MAX_PACKET_SIZE/rsize;
+ STACK_ARRAY(T,buf ,chunk*rcols);
+ STACK_ARRAY(T,buf2,chunk*rcols);
+ int off = chunk;
+ while (n) {
+ if (chunk*rrows>n) chunk=n/rrows;
+ op_put->map(chunk*rcols,buf2,*(T *)*seed);
+ for (int i=0; i<rrows; i++) {
+ switch (rcols) {
+ case 1: inner_child_b<T,1>(buf,data+i,rrows,chunk); break;
+ case 2: inner_child_b<T,2>(buf,data+i,rrows,chunk); break;
+ case 3: inner_child_b<T,3>(buf,data+i,rrows,chunk); break;
+ case 4: inner_child_b<T,4>(buf,data+i,rrows,chunk); break;
+ default: inner_child_a(buf,data+i,rrows,rcols,chunk);
+ }
+ op_para->zip(chunk*rcols,buf,(Pt<T>)*r2+i*off*rcols);
+ op_fold->zip(chunk*rcols,buf2,buf);
+ }
+ out->send(chunk*rcols,buf2);
+ n-=chunk*rrows;
+ data+=chunk*rrows;
+ }
+} GRID_FINISH {
+ r2=0;
+} GRID_END
+
+GRID_INPUT(GridInner,1,r) {} GRID_END
+
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->op_para = op_mul;
+ this->op_fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+}
+
+\def void _0_op (Numop *op ) { this->op_para=op; }
+\def void _0_fold (Numop *op ) { this->op_fold=op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\classinfo { IEVAL(rself,"install '#inner',2,1"); }
+\end class GridInner
+
+/* **************************************************************** */
+/*{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As,*Bs]<T> }*/
+\class GridOuter < GridObject
+struct GridOuter : GridObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \decl void initialize (Numop *op, Grid *r=0);
+ \grin 0
+ \grin 1
+};
+
+GRID_INLET(GridOuter,0) {
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ P<Dim> b = r->dim;
+ int n = a->n+b->n;
+ STACK_ARRAY(int32,v,n);
+ COPY(v,a->v,a->n);
+ COPY(v+a->n,b->v,b->n);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int b_prod = r->dim->prod();
+ if (b_prod > 4) {
+ STACK_ARRAY(T,buf,b_prod);
+ while (n) {
+ for (int j=0; j<b_prod; j++) buf[j] = *data;
+ op->zip(b_prod,buf,(Pt<T>)*r);
+ out->send(b_prod,buf);
+ data++; n--;
+ }
+ return;
+ }
+ n*=b_prod;
+ Pt<T> buf = ARRAY_NEW(T,n);
+ STACK_ARRAY(T,buf2,b_prod*64);
+ for (int i=0; i<64; i++) COPY(buf2+i*b_prod,(Pt<T>)*r,b_prod);
+ switch (b_prod) {
+ #define Z buf[k++]=data[i]
+ case 1: for (int i=0,k=0; k<n; i++) {Z;} break;
+ case 2: for (int i=0,k=0; k<n; i++) {Z;Z;} break;
+ case 3: for (int i=0,k=0; k<n; i++) {Z;Z;Z;} break;
+ case 4: for (int i=0,k=0; k<n; i++) {Z;Z;Z;Z;} break;
+ default:for (int i=0,k=0; k<n; i++) for (int j=0; j<b_prod; j++, k++) Z;
+ }
+ #undef Z
+ int ch=64*b_prod;
+ int nn=(n/ch)*ch;
+ for (int j=0; j<nn; j+=ch) op->zip(ch,buf+j,buf2);
+ op->zip(n-nn,buf+nn,buf2);
+ out->give(n,buf);
+} GRID_END
+
+GRID_INPUT(GridOuter,1,r) {} GRID_END
+
+\def void initialize (Numop *op, Grid *r) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#outer',2,1"); }
+\end class GridOuter
+
+//****************************************************************
+//{ Dim[]<T>,Dim[]<T>,Dim[]<T> -> Dim[A]<T> } or
+//{ Dim[B]<T>,Dim[B]<T>,Dim[B]<T> -> Dim[*As,B]<T> }
+\class GridFor < GridObject
+struct GridFor : GridObject {
+ \attr PtrGrid from;
+ \attr PtrGrid to;
+ \attr PtrGrid step;
+ GridFor () {
+ from.constrain(expect_max_one_dim);
+ to .constrain(expect_max_one_dim);
+ step.constrain(expect_max_one_dim);
+ }
+ \decl void initialize (Grid *from, Grid *to, Grid *step);
+ \decl void _0_set (Grid *r=0);
+ \decl void _0_bang ();
+ \grin 0 int
+ \grin 1 int
+ \grin 2 int
+ template <class T> void trigger (T bogus);
+};
+
+\def void initialize (Grid *from, Grid *to, Grid *step) {
+ rb_call_super(argc,argv);
+ this->from=from;
+ this->to =to;
+ this->step=step;
+}
+
+template <class T>
+void GridFor::trigger (T bogus) {
+ int n = from->dim->prod();
+ int32 nn[n+1];
+ STACK_ARRAY(T,x,64*n);
+ Pt<T> fromb = (Pt<T>)*from;
+ Pt<T> tob = (Pt<T>)*to ;
+ Pt<T> stepb = (Pt<T>)*step;
+ STACK_ARRAY(T,to2,n);
+
+ for (int i=step->dim->prod()-1; i>=0; i--)
+ if (!stepb[i]) RAISE("step must not contain zeroes");
+ for (int i=0; i<n; i++) {
+ nn[i] = (tob[i] - fromb[i] + stepb[i] - cmp(stepb[i],(T)0)) / stepb[i];
+ if (nn[i]<0) nn[i]=0;
+ to2[i] = fromb[i]+stepb[i]*nn[i];
+ }
+ P<Dim> d;
+ if (from->dim->n==0) { d = new Dim(*nn); }
+ else { nn[n]=n; d = new Dim(n+1,nn); }
+ int total = d->prod();
+ out=new GridOutlet(this,0,d,from->nt);
+ if (total==0) return;
+ int k=0;
+ for(int d=0;;d++) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<n;d++) x[k+d]=fromb[d];
+ k+=n;
+ if (k==64*n) {out->send(k,x); k=0; COPY(x,x+63*n,n);}
+ else { COPY(x+k,x+k-n,n);}
+ d--;
+ // here d is the dim# to increment
+ for(;;d--) {
+ if (d<0) goto end;
+ x[k+d]+=stepb[d];
+ if (x[k+d]!=to2[d]) break;
+ }
+ }
+ end: if (k) out->send(k,x);
+}
+
+\def void _0_bang () {
+ SAME_TYPE(from,to);
+ SAME_TYPE(from,step);
+ if (!from->dim->equal(to->dim) || !to->dim->equal(step->dim))
+ RAISE("dimension mismatch");
+#define FOO(T) trigger((T)0);
+ TYPESWITCH_NOFLOAT(from->nt,FOO,);
+#undef FOO
+}
+
+\def void _0_set (Grid *r) { from=new Grid(argv[0]); }
+GRID_INPUT(GridFor,2,step) {} GRID_END
+GRID_INPUT(GridFor,1,to) {} GRID_END
+GRID_INPUT(GridFor,0,from) {_0_bang(0,0);} GRID_END
+\classinfo { IEVAL(rself,"install '#for',3,1"); }
+\end class GridFor
+
+//****************************************************************
+\class GridFinished < GridObject
+struct GridFinished : GridObject {
+ \grin 0
+};
+GRID_INLET(GridFinished,0) {
+ in->set_mode(0);
+} GRID_FINISH {
+ Ruby a[] = { INT2NUM(0), bsym._bang };
+ send_out(COUNT(a),a);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#finished',1,1"); }
+\end class GridFinished
+
+\class GridDim < GridObject
+struct GridDim : GridObject {
+ \grin 0
+};
+GRID_INLET(GridDim,0) {
+ GridOutlet out(this,0,new Dim(in->dim->n));
+ out.send(in->dim->n,Pt<int32>(in->dim->v,in->dim->n));
+ in->set_mode(0);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#dim',1,1"); }
+\end class GridDim
+
+\class GridType < GridObject
+struct GridType : GridObject {
+ \grin 0
+};
+GRID_INLET(GridType,0) {
+ Ruby a[] = { INT2NUM(0), SYM(symbol), number_type_table[in->nt].sym };
+ send_out(COUNT(a),a);
+ in->set_mode(0);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#type',1,1"); }
+\end class GridType
+
+//****************************************************************
+//{ Dim[*As]<T>,Dim[B] -> Dim[*Cs]<T> }
+\class GridRedim < GridObject
+struct GridRedim : GridObject {
+ \attr P<Dim> dim;
+ PtrGrid dim_grid;
+ PtrGrid temp; // temp->dim is not of the same shape as dim
+ GridRedim() { dim_grid.constrain(expect_dim_dim_list); }
+ ~GridRedim() {}
+ \decl void initialize (Grid *d);
+ \grin 0
+ \grin 1 int32
+};
+
+GRID_INLET(GridRedim,0) {
+ int a = in->dim->prod(), b = dim->prod();
+ if (a<b) temp=new Grid(new Dim(a),in->nt);
+ out=new GridOutlet(this,0,dim,in->nt);
+} GRID_FLOW {
+ int i = in->dex;
+ if (!temp) {
+ int b = dim->prod();
+ int n2 = min(n,b-i);
+ if (n2>0) out->send(n2,data);
+ // discard other values if any
+ } else {
+ int a = in->dim->prod();
+ int n2 = min(n,a-i);
+ COPY((Pt<T>)*temp+i,data,n2);
+ if (n2>0) out->send(n2,data);
+ }
+} GRID_FINISH {
+ if (!!temp) {
+ int a = in->dim->prod(), b = dim->prod();
+ if (a) {
+ for (int i=a; i<b; i+=a) out->send(min(a,b-i),(Pt<T>)*temp);
+ } else {
+ STACK_ARRAY(T,foo,1);
+ foo[0]=0;
+ for (int i=0; i<b; i++) out->send(1,foo);
+ }
+ }
+ temp=0;
+} GRID_END
+
+GRID_INPUT(GridRedim,1,dim_grid) { dim = dim_grid->to_dim(); } GRID_END
+
+\def void initialize (Grid *d) {
+ rb_call_super(argc,argv);
+ dim_grid=d;
+ dim = dim_grid->to_dim();
+}
+
+\classinfo { IEVAL(rself,"install '#redim',2,1"); }
+\end class GridRedim
+
+//****************************************************************
+\class GridJoin < GridObject
+struct GridJoin : GridObject {
+ \attr int which_dim;
+ PtrGrid r;
+ \grin 0
+ \grin 1
+ \decl void initialize (int which_dim=-1, Grid *r=0);
+};
+
+GRID_INLET(GridJoin,0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> d = in->dim;
+ if (d->n != r->dim->n) RAISE("wrong number of dimensions");
+ int w = which_dim;
+ if (w<0) w+=d->n;
+ if (w<0 || w>=d->n)
+ RAISE("can't join on dim number %d on %d-dimensional grids",
+ which_dim,d->n);
+ STACK_ARRAY(int32,v,d->n);
+ for (int i=0; i<d->n; i++) {
+ v[i] = d->get(i);
+ if (i==w) {
+ v[i]+=r->dim->v[i];
+ } else {
+ if (v[i]!=r->dim->v[i]) RAISE("dimensions mismatch: dim #%i, left is %d, right is %d",i,v[i],r->dim->v[i]);
+ }
+ }
+ out=new GridOutlet(this,0,new Dim(d->n,v),in->nt);
+ if (d->prod(w)) in->set_factor(d->prod(w));
+} GRID_FLOW {
+ int w = which_dim;
+ if (w<0) w+=in->dim->n;
+ int a = in->factor();
+ int b = r->dim->prod(w);
+ Pt<T> data2 = (Pt<T>)*r + in->dex*b/a;
+ if (a==3 && b==1) {
+ int m = n+n*b/a;
+ STACK_ARRAY(T,data3,m);
+ Pt<T> data4 = data3;
+ while (n) {
+ SCOPY(data4,data,3); SCOPY(data4+3,data2,1);
+ n-=3; data+=3; data2+=1; data4+=4;
+ }
+ out->send(m,data3);
+ } else if (a+b<=16) {
+ int m = n+n*b/a;
+ STACK_ARRAY(T,data3,m);
+ int i=0;
+ while (n) {
+ COPY(data3+i,data,a); data+=a; i+=a; n-=a;
+ COPY(data3+i,data2,b); data2+=b; i+=b;
+ }
+ out->send(m,data3);
+ } else {
+ while (n) {
+ out->send(a,data);
+ out->send(b,data2);
+ data+=a; data2+=b; n-=a;
+ }
+ }
+} GRID_FINISH {
+ if (in->dim->prod()==0) out->send(r->dim->prod(),(Pt<T>)*r);
+} GRID_END
+
+GRID_INPUT(GridJoin,1,r) {} GRID_END
+
+\def void initialize (int which_dim, Grid *r) {
+ rb_call_super(argc,argv);
+ this->which_dim = which_dim;
+ if (r) this->r=r;
+}
+
+\classinfo { IEVAL(rself,"install '@join',2,1"); }
+\end class GridJoin
+
+//****************************************************************
+\class GridGrade < GridObject
+struct GridGrade : GridObject {
+ \grin 0
+};
+
+template <class T> struct GradeFunction {
+ static int comparator (const void *a, const void *b) {
+ return **(T**)a - **(T**)b;}};
+#define FOO(S) \
+template <> struct GradeFunction<S> { \
+ static int comparator (const void *a, const void *b) { \
+ S x = **(S**)a - **(S**)b; \
+ return x<0 ? -1 : x>0;}};
+FOO(int64)
+FOO(float32)
+FOO(float64)
+#undef FOO
+
+GRID_INLET(GridGrade,0) {
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->get(in->dim->n-1));
+} GRID_FLOW {
+ int m = in->factor();
+ STACK_ARRAY(T*,foo,m);
+ STACK_ARRAY(T,bar,m);
+ for (; n; n-=m,data+=m) {
+ for (int i=0; i<m; i++) foo[i] = &data[i];
+ qsort(foo,m,sizeof(T),GradeFunction<T>::comparator);
+ for (int i=0; i<m; i++) bar[i] = foo[i]-(T *)data;
+ out->send(m,bar);
+ }
+} GRID_END
+
+\classinfo { IEVAL(rself,"install '#grade',1,1"); }
+\end class GridGrade
+
+//****************************************************************
+//\class GridMedian < GridObject
+//****************************************************************
+
+\class GridTranspose < GridObject
+struct GridTranspose : GridObject {
+ \attr int dim1;
+ \attr int dim2;
+ int d1,d2,na,nb,nc,nd; // temporaries
+ \decl void initialize (int dim1=0, int dim2=1);
+ \decl void _1_float (int dim1);
+ \decl void _2_float (int dim2);
+ \grin 0
+};
+
+\def void _1_float (int dim1) { this->dim1=dim1; }
+\def void _2_float (int dim2) { this->dim2=dim2; }
+
+GRID_INLET(GridTranspose,0) {
+ STACK_ARRAY(int32,v,in->dim->n);
+ COPY(v,in->dim->v,in->dim->n);
+ d1=dim1; d2=dim2;
+ if (d1<0) d1+=in->dim->n;
+ if (d2<0) d2+=in->dim->n;
+ if (d1>=in->dim->n || d2>=in->dim->n || d1<0 || d2<0)
+ RAISE("would swap dimensions %d and %d but this grid has only %d dimensions",
+ dim1,dim2,in->dim->n);
+ memswap(v+d1,v+d2,1);
+ if (d1==d2) {
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ } else {
+ nd = in->dim->prod(1+max(d1,d2));
+ nc = in->dim->v[max(d1,d2)];
+ nb = in->dim->prod(1+min(d1,d2))/nc/nd;
+ na = in->dim->v[min(d1,d2)];
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ in->set_factor(na*nb*nc*nd);
+ }
+ // Turns a Grid[*,na,*nb,nc,*nd] into a Grid[*,nc,*nb,na,*nd].
+} GRID_FLOW {
+ STACK_ARRAY(T,res,na*nb*nc*nd);
+ if (dim1==dim2) { out->send(n,data); return; }
+ for (; n; n-=na*nb*nc*nd, data+=na*nb*nc*nd) {
+ for (int a=0; a<na; a++)
+ for (int b=0; b<nb; b++)
+ for (int c=0; c<nc; c++)
+ COPY(res +((c*nb+b)*na+a)*nd,
+ data+((a*nb+b)*nc+c)*nd,nd);
+ out->send(na*nb*nc*nd,res);
+ }
+} GRID_END
+
+\def void initialize (int dim1=0, int dim2=1) {
+ rb_call_super(argc,argv);
+ this->dim1 = dim1;
+ this->dim2 = dim2;
+}
+
+\classinfo { IEVAL(rself,"install '#transpose',3,1"); }
+\end class GridTranspose
+
+//****************************************************************
+\class GridReverse < GridObject
+struct GridReverse : GridObject {
+ \attr int dim1; // dimension to act upon
+ int d; // temporaries
+ \decl void initialize (int dim1=0);
+ \decl void _1_float (int dim1);
+ \grin 0
+};
+
+\def void _1_float (int dim1) { this->dim1=dim1; }
+
+GRID_INLET(GridReverse,0) {
+ d=dim1;
+ if (d<0) d+=in->dim->n;
+ if (d>=in->dim->n || d<0)
+ RAISE("would reverse dimension %d but this grid has only %d dimensions",
+ dim1,in->dim->n);
+ out=new GridOutlet(this,0,new Dim(in->dim->n,in->dim->v), in->nt);
+ in->set_factor(in->dim->prod(d));
+} GRID_FLOW {
+ int f1=in->factor(), f2=in->dim->prod(d+1);
+ while (n) {
+ int hf1=f1/2;
+ Pt<T> data2 = data+f1-f2;
+ for (int i=0; i<hf1; i+=f2) memswap(data+i,data2-i,f2);
+ out->send(f1,data);
+ data+=f1; n-=f1;
+ }
+} GRID_END
+
+\def void initialize (int dim1=0) {
+ rb_call_super(argc,argv);
+ this->dim1 = dim1;
+}
+
+\classinfo { IEVAL(rself,"install '#reverse',2,1"); }
+\end class GridReverse
+
+//****************************************************************
+\class GridCentroid2 < GridObject
+struct GridCentroid2 : GridObject {
+ \decl void initialize ();
+ \grin 0 int
+ int sumx,sumy,sum,y; // temporaries
+};
+
+GRID_INLET(GridCentroid2,0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dims");
+ if (in->dim->v[2] != 1) RAISE("expecting 1 channel");
+ in->set_factor(in->dim->prod(1));
+ out=new GridOutlet(this,0,new Dim(2), in->nt);
+ sumx=0; sumy=0; sum=0; y=0;
+} GRID_FLOW {
+ int sx = in->dim->v[1];
+ while (n) {
+ for (int x=0; x<sx; x++) {
+ sumx+=x*data[x];
+ sumy+=y*data[x];
+ sum += data[x];
+ }
+ n-=sx;
+ data+=sx;
+ y++;
+ }
+} GRID_FINISH {
+ STACK_ARRAY(int32,blah,2);
+ blah[0] = sum ? sumy/sum : 0;
+ blah[1] = sum ? sumx/sum : 0;
+ out->send(2,blah);
+} GRID_END
+
+\def void initialize () {
+ rb_call_super(argc,argv);
+}
+
+\classinfo { IEVAL(rself,"install '#centroid2',1,1"); }
+\end class GridCentroid2
+
+//****************************************************************
+\class GridPerspective < GridObject
+struct GridPerspective : GridObject {
+ \attr int32 z;
+ \grin 0
+ \decl void initialize (int32 z=256);
+};
+
+GRID_INLET(GridPerspective,0) {
+ int n = in->dim->n;
+ STACK_ARRAY(int32,v,n);
+ COPY(v,in->dim->v,n);
+ v[n-1]--;
+ in->set_factor(in->dim->get(in->dim->n-1));
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int m = in->factor();
+ for (; n; n-=m,data+=m) {
+ op_mul->map(m-1,data,(T)z);
+ op_div->map(m-1,data,data[m-1]);
+ out->send(m-1,data);
+ }
+} GRID_END
+
+\def void initialize (int32 z) {rb_call_super(argc,argv); this->z=z; }
+\classinfo { IEVAL(rself,"install '#perspective',1,1"); }
+\end class GridPerspective
+
+static Numop *OP(Ruby x) { return FIX2PTR(Numop,rb_hash_aref(op_dict,x)); }
+
+void startup_flow_objects () {
+ op_add = OP(SYM(+));
+ op_sub = OP(SYM(-));
+ op_mul = OP(SYM(*));
+ op_shl = OP(SYM(<<));
+ op_mod = OP(SYM(%));
+ op_and = OP(SYM(&));
+ op_div = OP(SYM(/));
+ op_put = OP(SYM(put));
+ \startall
+}
diff --git a/externals/gridflow/base/flow_objects.rb b/externals/gridflow/base/flow_objects.rb
new file mode 100644
index 00000000..6ea06c56
--- /dev/null
+++ b/externals/gridflow/base/flow_objects.rb
@@ -0,0 +1,1476 @@
+=begin
+ $Id: flow_objects.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+module GridFlow
+
+#-------- fClasses for: control + misc
+
+# a dummy class that gives access to any stuff global to GridFlow.
+FObject.subclass("gridflow",1,1) {
+ def _0_profiler_reset
+ GridFlow.fobjects.each {|o,*| o.total_time = 0 }
+ GridFlow.profiler_reset2 if GridFlow.respond_to? :profiler_reset2
+ end
+ def _0_profiler_dump
+ ol = []
+ total=0
+ post "-"*32
+ post "microseconds percent pointer constructor"
+ GridFlow.fobjects.each {|o,*| ol.push o }
+
+ # HACK: BitPacking is not a real fobject
+ # !@#$ is this still necessary?
+ ol.delete_if {|o| not o.respond_to? :total_time }
+
+ ol.sort! {|a,b| a.total_time <=> b.total_time }
+ ol.each {|o| total += o.total_time }
+ total=1 if total<1
+ total_us = 0
+ ol.each {|o|
+ ppm = o.total_time * 1000000 / total
+ us = (o.total_time*1E6/GridFlow.cpu_hertz).to_i
+ total_us += us
+ post "%12d %2d.%04d %08x %s", us,
+ ppm/10000, ppm%10000, o.object_id, o.args
+ }
+ post "-"*32
+ post "sum of accounted microseconds: #{total_us}"
+ if GridFlow.respond_to? :memcpy_calls then
+ post "memcpy calls: #{GridFlow.memcpy_calls} "+
+ "; bytes: #{GridFlow.memcpy_bytes}"+
+ "; time: #{GridFlow.memcpy_time}"
+ end
+ if GridFlow.respond_to? :malloc_calls then
+ post "malloc calls: #{GridFlow.malloc_calls} "+
+ "; bytes: #{GridFlow.malloc_bytes}"+
+ "; time: #{GridFlow.malloc_time}"
+ end
+ post "-"*32
+ end
+ def _0_formats
+ post "-"*32
+ GridFlow.fclasses.each {|k,v|
+ next if not /#in:/ =~ k
+ modes = case v.flags
+ when 2; "#out"
+ when 4; "#in"
+ when 6; "#in/#out"
+ end
+ post "%s %s: %s", modes, k, v.description
+ if v.respond_to? :info then
+ post "-> %s", v.info
+ end
+ }
+ post "-"*32
+ end
+ # security issue if patches shouldn't be allowed to do anything they want
+ def _0_eval(*l)
+ s = l.map{|x|x.to_i.chr}.join""
+ post "ruby: %s", s
+ post "returns: %s", eval(s).inspect
+ end
+ add_creator "@global"
+ GridFlow.bind "gridflow", "gridflow" rescue Exception
+}
+FObject.subclass("fps",1,1) {
+ def initialize(*options)
+ super
+ @history = [] # list of delays between incoming messages
+ @last = 0.0 # when was last time
+ @duration = 0.0 # how much delay since last summary
+ @period = 1 # minimum delay between summaries
+ @detailed = false
+ @mode = :real
+ options.each {|o|
+ case o
+ when :detailed; @detailed=true
+ when :real,:user,:system,:cpu; @mode=o
+ end
+ }
+ def @history.moment(n=1)
+ sum = 0
+ each {|x| sum += x**n }
+ sum/length
+ end
+ end
+ def method_missing(*a) end # ignore non-bangs
+ def _0_period x; @period=x end
+ def publish
+ @history.sort!
+ n=@history.length
+ fps = @history.length/@duration
+ if not @detailed then send_out 0, fps; return end
+ send_out 0, fps,
+ 1000*@history.min,
+ 500*(@history[n/2]+@history[(n-1)/2]),
+ 1000*@history.max,
+ 1000/fps,
+ 1000*(@history.moment(2) - @history.moment(1)**2)**0.5
+ end
+ def _0_bang
+ t = case @mode
+ when :real; Time.new.to_f
+ when :user; Process.times.utime
+ when :system; Process.times.stime
+ when :cpu; GridFlow.rdtsc/GridFlow.cpu_hertz
+ end
+ @history.push t-@last
+ @duration += t-@last
+ @last = t
+ return if @duration<@period
+ fps = @history.length/@duration
+ publish if fps>0.001
+ @history.clear
+ @duration = 0
+ end
+}
+
+# to see what the messages look like when they get on the Ruby side.
+FObject.subclass("rubyprint",1,0) {
+ def initialize(*a)
+ super
+ @time = !!(a.length and a[0]==:time)
+ end
+
+ def method_missing(s,*a)
+ s=s.to_s
+ pre = if @time then sprintf "%10.6f ", Time.new.to_f else "" end
+ case s
+ when /^_0_/; post "%s","#{pre}#{s[3..-1]}: #{a.inspect}"
+ else super
+ end
+ end
+}
+FObject.subclass("printargs",0,0) {
+ def initialize(*a) super; post a.inspect end
+}
+GridObject.subclass("#print",1,0) {
+ install_rgrid 0, true
+ attr_accessor :name
+ def initialize(name=nil)
+ super # don't forget super!!!
+ if name then @name = name.to_s+": " else @name="" end
+ @base=10; @format="d"; @trunc=70; @maxrows=50
+ end
+ def end_hook; end # other hijackability
+ def format
+ case @nt
+ when :float32; '%6.6f'
+ when :float64; '%14.14f'
+ else "%#{@columns}#{@format}" end
+ end
+ def _0_base(x)
+ @format = (case x
+ when 2; "b"
+ when 8; "o"
+ when 10; "d"
+ when 16; "x"
+ else raise "base #{x} not supported" end)
+ @base = x
+ end
+ def _0_trunc(x)
+ x=x.to_f
+ (0..240)===x or raise "out of range (not in 0..240 range)"
+ @trunc = x
+ end
+ def _0_maxrows(x) @maxrows = x.to_i end
+ def make_columns udata
+ min = udata.min
+ max = udata.max
+ @columns = "" # huh?
+ @columns = [
+ sprintf(format,min).length,
+ sprintf(format,max).length].max
+ end
+ def unpack data
+ ps = GridFlow.packstring_for_nt @nt
+ data.unpack ps
+ end
+ def _0_rgrid_begin
+ @dim = inlet_dim 0
+ @nt = inlet_nt 0
+ @data = ""
+ end
+ def _0_rgrid_flow(data) @data << data end
+ def _0_rgrid_end
+ head = "#{name}Dim[#{@dim.join','}]"
+ head << "(#{@nt})" if @nt!=:int32
+ head << ": "
+ if @dim.length > 3 then
+ post head+" (not printed)"
+ elsif @dim.length < 2 then
+ udata = unpack @data
+ make_columns udata
+ post trunc(head + dump(udata))
+ elsif @dim.length == 2 then
+ post head
+ udata = unpack @data
+ make_columns udata
+ sz = udata.length/@dim[0]
+ rown = 1
+ for row in 0...@dim[0] do
+ post trunc(dump(udata[sz*row,sz]))
+ rown += 1
+ (post "..."; break) if rown>@maxrows
+ end
+ elsif @dim.length == 3 then
+ post head
+ make_columns unpack(@data)
+ sz = @data.length/@dim[0]
+ sz2 = sz/@dim[1]
+ rown = 1
+ for row in 0...@dim[0]
+ column=0; str=""
+ for col in 0...@dim[1]
+ str << "(" << dump(unpack(@data[sz*row+sz2*col,sz2])) << ")"
+ break if str.length>@trunc
+ end
+ post trunc(str)
+ rown += 1
+ (post "..."; break) if rown>@maxrows
+ end
+ end
+ @data,@dim,@nt = nil
+ end_hook
+ end
+ def dump(udata,sep=" ")
+ f = format
+ udata.map{|x| sprintf f,x }.join sep
+ end
+ def trunc s
+ if s.length>@trunc then s[0...@trunc]+" [...]" else s end
+ end
+}
+GridPack =
+GridObject.subclass("#pack",1,1) {
+ install_rgrid 0
+ class<<self;attr_reader :ninlets;end
+ def initialize(n=nil,cast=:int32)
+ n||=self.class.ninlets
+ n>=16 and raise "too many inlets"
+ super
+ @data=[0]*n
+ @cast=cast
+ @ps =GridFlow.packstring_for_nt cast
+ end
+ def initialize2
+ return if self.class.ninlets>1
+ add_inlets @data.length-1
+ end
+ def _0_cast(cast)
+ @ps = GridFlow.packstring_for_nt cast
+ @cast = cast
+ end
+ def self.define_inlet i
+ module_eval "
+ def _#{i}_int x; @data[#{i}]=x; _0_bang; end
+ def _#{i}_float x; @data[#{i}]=x; _0_bang; end
+ "
+ end
+ (0...15).each {|x| define_inlet x }
+ def _0_bang
+ send_out_grid_begin 0, [@data.length], @cast
+ send_out_grid_flow 0, @data.pack(@ps), @cast
+ end
+ self
+}
+
+# the install_rgrids in the following are hacks so that
+# outlets can work. (install_rgrid is supposed to be for receiving)
+# maybe GF-0.8 doesn't need that.
+GridPack.subclass("@two", 2,1) { install_rgrid 0 }
+GridPack.subclass("@three",3,1) { install_rgrid 0 }
+GridPack.subclass("@four", 4,1) { install_rgrid 0 }
+GridPack.subclass("@eight",8,1) { install_rgrid 0 }
+GridObject.subclass("#unpack",1,0) {
+ install_rgrid 0, true
+ def initialize(n)
+ @n=n
+ n>=10 and raise "too many outlets"
+ super
+ end
+ def initialize2; add_outlets @n end
+ def _0_rgrid_begin
+ inlet_dim(0)==[@n] or raise "expecting Dim[#{@n}], got Dim#{@dim}"
+ inlet_set_factor 0,@n
+ end
+ def _0_rgrid_flow data
+ @ps = GridFlow.packstring_for_nt inlet_nt(0)
+ duh = data.unpack(@ps)
+ i=duh.size-1
+ until i<0 do send_out i,duh[i]; i-=1 end
+ end
+ def _0_rgrid_end; end
+}
+
+GridObject.subclass("#export_symbol",1,1) {
+ install_rgrid 0
+ def _0_rgrid_begin; @data="" end
+ def _0_rgrid_flow data; @data << data; end
+ def _0_rgrid_end
+ send_out 0, :symbol, @data.unpack("I*").pack("c*").intern
+ end
+}
+GridObject.subclass("unix_time",1,3) {
+ install_rgrid 0
+ def _0_bang
+ t = Time.new
+ tt = t.to_s
+ send_out_grid_begin 0, [tt.length], :uint8
+ send_out_grid_flow 0, tt, :uint8
+ send_out 1, t.to_i
+ send_out 2, t.to_f-t.to_f.floor
+ end
+}
+### test with "shell xlogo &" -> [exec]
+FObject.subclass("exec",1,0) {
+ def _0_shell(*a) system(a.map!{|x| x.to_s }.join(" ")) end
+}
+FObject.subclass("renamefile",1,0) {
+ def initialize; end
+ def _0_list(a,b) File.rename(a.to_s,b.to_s) end
+}
+FObject.subclass("ls",1,1) {
+ def _0_symbol(s) send_out 0, :list, *Dir.new(s.to_s).map {|x| x.intern } end
+}
+
+#-------- fClasses for: math
+
+FPatcher.subclass("gfmessagebox",1,1) {
+ def initialize(*a) @a=a end
+ def _0_float(x) send_out 0, *@a.map {|y| if y=="$1".intern then x else y end } end
+ def _0_symbol(x) send_out 0, *@a.map {|y| if y=="$1".intern then x else y end } end
+}
+
+FPatcher.subclass("@!",1,1) {
+ @fobjects = ["# +","#type","gfmessagebox list $1 #"]
+ @wires = [-1,0,1,0, 1,0,2,0, 2,0,0,1, -1,0,0,0, 0,0,-1,0]
+ def initialize(sym)
+ super
+ @fobjects[0].send_in 0, case sym
+ when :rand; "op rand"; when :sqrt; "op sqrt"
+ when :abs; "op abs-"; when :sq; "op sq-"
+ else raise "bork BORK bork" end
+ end
+}
+FPatcher.subclass("@fold",2,1) {
+ @fobjects = ["#fold +","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,1, 0,0,-1,0]
+ def initialize(op,seed=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :seed, seed end
+}
+FPatcher.subclass("@scan",2,1) {
+ @fobjects = ["#scan +","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,1, 0,0,-1,0]
+ def initialize(op,seed=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :seed, seed end
+}
+FPatcher.subclass("@inner",3,1) {
+ @fobjects = ["#inner","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,0, 0,0,-1,0, -1,2,0,1]
+ def initialize(op=:*,fold=:+,seed=0,r=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :fold, fold
+ o.send_in 0, :seed, seed; o.send_in 1, r end
+}
+FPatcher.subclass("@convolve",2,1) {
+ @fobjects = ["#convolve"]
+ @wires = [-1,0,0,0, -1,2,0,1, 0,0,-1,0]
+ def initialize(op=:*,fold=:+,seed=0,r=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :fold, fold
+ o.send_in 0, :seed, seed; o.send_in 1, r end
+}
+
+#-------- fClasses for: video
+
+FPatcher.subclass("@scale_to",2,1) {
+ @fobjects = [
+ "@for {0 0} {42 42} {1 1}","@ *","@ /",
+ "@store","#dim","@redim {2}","#finished",
+ ]
+ @wires = []
+ for i in 1..3 do @wires.concat [i-1,0,i,0] end
+ @wires.concat [3,0,-1,0, 4,0,5,0, 5,0,1,1, 6,0,0,0,
+ -1,0,4,0, -1,0,3,1, -1,0,6,0, -1,1,0,1, -1,1,2,1]
+ def initialize(size)
+ (size.length==2 and Numeric===size[0] and Numeric===size[1]) or
+ raise "expecting {height width}"
+ super
+ send_in 1, size
+ end
+}
+
+#<vektor> told me to:
+# RGBtoYUV : @fobjects = ["#inner ( 3 3 # 66 -38 112 128 -74 -94 25 112 -18 )",
+# "@ >> 8","@ + {16 128 128}"]
+# YUVtoRGB : @fobjects = ["@ - ( 16 128 128 )",
+# "#inner ( 3 3 # 298 298 298 0 -100 516 409 -208 0 )","@ >> 8"]
+
+FPatcher.subclass("#rotate",2,1) {
+ @fobjects = ["@inner * + 0","@ >> 8"]
+ @wires = [-1,0,0,0, 0,0,1,0, 1,0,-1,0]
+ def update_rotator
+ rotator = (0...@axis[2]).map {|i|
+ (0...@axis[2]).map {|j|
+ if i==j then 256 else 0 end
+ }
+ }
+ th = @angle * Math::PI / 18000
+ scale = 1<<8
+ (0...2).each {|i|
+ (0...2).each {|j|
+ rotator[@axis[i]][@axis[j]] =
+ (scale*Math.cos(th+(j-i)*Math::PI/2)).to_i
+ }
+ }
+ @fobjects[0].send_in 2,
+ @axis[2], @axis[2], "#".intern, *rotator.flatten
+ end
+ def _0_axis(from,to,total)
+ total>=0 or raise "total-axis number incorrect"
+ from>=0 and from<total or raise "from-axis number incorrect"
+ to >=0 and to <total or raise "to-axis number incorrect"
+ @axis = [from,to,total]
+ update_rotator
+ end
+ def initialize(rot=0,axis=[0,1,2])
+ super
+ @angle=0
+ _0_axis(*axis)
+ send_in 1, rot
+ end
+ def _1_int(angle) @angle = angle; update_rotator end
+ alias _1_float _1_int
+}
+
+FObject.subclass("foreach",1,1) {
+ def initialize() super end
+ def _0_list(*a)
+ a.each {|e|
+ if Symbol===e then
+ send_out 0,:symbol,e
+ else
+ send_out 0,e
+ end
+ }
+ end
+}
+FObject.subclass("listflatten",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0,:list,*a.flatten end
+}
+FObject.subclass("rubysprintf",2,1) {
+ def initialize(*format) _1_list(format) end
+ def _0_list(*a) send_out 0, :symbol, (sprintf @format, *a).intern end
+ alias _0_float _0_list
+ alias _0_symbol _0_list
+ def _1_list(*format) @format = format.join(" ") end
+ alias _1_symbol _1_list
+}
+
+#-------- fClasses for: jMax compatibility
+
+class JMaxUDPSend < FObject
+ def initialize(host,port)
+ super
+ @socket = UDPSocket.new
+ @host,@port = host.to_s,port.to_i
+ end
+ def encode(x)
+ case x
+ when Integer; "\x03" + [x].pack("N")
+ when Float; "\x04" + [x].pack("g")
+ when Symbol, String; "\x01" + x.to_s + "\x02"
+ end
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s.sub(/^_\d_/, "")
+ @socket.send encode(sel) +
+ args.map{|arg| encode(arg) }.join("") + "\x0b",
+ 0, @host, @port
+ end
+ def delete; @socket.close end
+ install "jmax_udpsend", 1, 0
+end
+
+class JMaxUDPReceive < FObject
+ def initialize(port)
+ super
+ @socket = UDPSocket.new
+ @port = port.to_i
+ @socket.bind nil, @port
+ @clock = Clock.new self
+ @clock.delay 0
+ end
+ def decode s
+ n = s.length
+ i=0
+ m = []
+ case s[i]
+ when 3; i+=5; m << s[i-4,4].unpack("N")[0]
+ when 4; i+=5; m << s[i-4,4].unpack("g")[0]
+ when 1; i2=s.index("\x02",i); m << s[i+1..i2-1].intern; i=i2+1
+ when 11; break
+ else raise "unknown code in udp packet"
+ end while i<n
+ m
+ end
+ def call
+ ready_to_read = IO.select [@socket],[],[],0
+ return if not ready_to_read
+ data,sender = @socket.recvfrom 1024
+ return if not data
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ @clock.delay 50
+ end
+ def delete; @clock.unset; @socket.close end
+ install "jmax_udpreceive", 0, 2
+end
+
+class JMax4UDPSend < FObject
+ def initialize(host,port)
+ super
+ @socket = UDPSocket.new
+ @host,@port = host.to_s,port.to_i
+ @symbols = {}
+ end
+ def encode(x)
+ case x
+ when Integer; "\x01" + [x].pack("N")
+ when Float; "\x02" + [x].pack("G")
+ when Symbol, String
+ x = x.to_s
+ y = x.intern
+ if not @symbols[y]
+ @symbols[y]=true
+ "\x04" + [y].pack("N") + x + "\0"
+ else
+ "\x03" + [y].pack("N")
+ end
+ end
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s.sub(/^_\d_/, "")
+ sel=(case sel; when "int","float"; ""; else encode(sel) end)
+ args=args.map{|arg| encode(arg) }.join("")
+ @socket.send(sel+args+"\x0f", 0, @host, @port)
+ end
+ def delete; @socket.close end
+ install "jmax4_udpsend", 1, 0
+end
+
+class JMax4UDPReceive < FObject
+ def initialize(port)
+ super
+ @socket = UDPSocket.new
+ @port = port.to_i
+ @socket.bind nil, @port
+ @clock = Clock.new self
+ @clock.delay 0
+ @symbols = {}
+ end
+ def decode s
+ n = s.length
+ i=0
+ m = []
+ case s[i]
+ when 1; i+=5; m << s[i-4,4].unpack("N")[0]
+ when 2; i+=9; m << s[i-8,8].unpack("G")[0]
+ when 3
+ i+=5; sid = s[i-4,4].unpack("N")[0]
+ m << @symbols[sid]
+ when 4
+ i+=5; sid = s[i-4,4].unpack("N")[0]
+ i2=s.index("\x00",i)
+ @symbols[sid] = s[i..i2-1].intern
+ m << @symbols[sid]
+ i=i2+1
+ when 15; break
+ else post "unknown code %d in udp packet %s", s[i], s.inspect; return m
+ end while i<n
+ m
+ end
+ def call
+ ready_to_read = IO.select [@socket],[],[],0
+ return if not ready_to_read
+ data,sender = @socket.recvfrom 1024
+ return if not data
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ @clock.delay 50
+ end
+ def delete; @clock.unset; @socket.close end
+ install "jmax4_udpreceive", 0, 2
+end
+
+class PDNetSocket < FObject
+ def initialize(host,port,protocol=:udp,*options)
+ super
+ _1_connect(host,port,protocol)
+ @options = {}
+ options.each {|k|
+ k=k.intern if String===k
+ @options[k]=true
+ }
+ end
+ def _1_connect(host,port,protocol=:udp)
+ host = host.to_s
+ port = port.to_i
+ @host,@port,@protocol = host.to_s,port.to_i,protocol
+ case protocol
+ when :udp
+ @socket = UDPSocket.new
+ if host=="-" then
+ @socket.bind nil, port
+ end
+ when :tcp
+ if host=="-" then
+ @server = TCPServer.new("localhost",port)
+ else
+ @socket = TCPSocket.new(host,port)
+ end
+
+ end
+ @clock = Clock.new self
+ @clock.delay 0
+ @data = ""
+ end
+ def encode(x)
+ x=x.to_i if @options[:nofloat] and Float===x
+ x.to_s
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s
+ sel.sub!(/^_\d_/, "") or return super
+ sel=(case sel; when "int","float"; ""; else encode(sel) end)
+ msg = [sel,*args.map{|arg| encode(arg) }].join(" ")
+ if @options[:nosemicolon] then msg << "\n" else msg << ";\n" end
+ post "encoding as: %s", msg.inspect if @options[:debug]
+ case @protocol
+ when :udp; @socket.send msg, 0, @host, @port
+ when :tcp; @socket.send msg, 0
+ end
+ end
+ def delete; @clock.unset; @socket.close end
+ def decode s
+ post "decoding from: %s", s.inspect if @options[:debug]
+ s.chomp!("\n")
+ s.chomp!("\r")
+ s.chomp!(";")
+ a=s.split(/[\s\0]+/)
+ a.shift if a[0]==""
+ a.map {|x|
+ case x
+ when /-?\d+$/; x.to_i
+ when /-?\d/; x.to_f
+ else x.intern
+ end
+ }
+ end
+ def call
+ ready_to_accept = IO.select [@server],[],[],0 if @server
+ if ready_to_accept
+ @socket.close if @socket
+ @socket = @server.accept
+ end
+ ready_to_read = IO.select [@socket],[],[],0 if @socket
+ return if not ready_to_read
+ case @protocol
+ when :udp
+ data,sender = @socket.recvfrom 1024
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ when :tcp
+ @data << @socket.sysread(1024)
+ sender = @socket.peeraddr
+ loop do
+ n = /\n/ =~ @data
+ break if not n
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode @data.slice!(0..n))
+ end
+ end
+ @clock.delay 50
+ end
+ install "pd_netsocket", 2, 2
+end
+
+PDNetSocket.subclass("pd_netsend",1,0) {}
+PDNetSocket.subclass("pd_netreceive",0,2) {
+ def initialize(port) super("-",port) end
+}
+
+# bogus class for representing objects that have no recognized class.
+FObject.subclass("broken",0,0) {
+ def args; a=@args.dup; a[7,0] = " "+classname; a end
+}
+
+FObject.subclass("fork",1,2) {
+ def method_missing(sel,*args)
+ sel.to_s =~ /^_(\d)_(.*)$/ or super
+ send_out 1,$2.intern,*args
+ send_out 0,$2.intern,*args
+ end
+}
+FObject.subclass("shunt",2,0) {
+ def initialize(n=2,i=0) super; @n=n; @i=i end
+ def initialize2; add_outlets @n end
+ def method_missing(sel,*args)
+ sel.to_s =~ /^_(\d)_(.*)$/ or super
+ send_out @i,$2.intern,*args
+ end
+ def _1_int i; @i=i.to_i % @n end
+ alias :_1_float :_1_int
+ # hack: this is an alias.
+ class Demux < self; install "demux", 2, 0; end
+}
+
+#-------- fClasses for: jmax2pd
+
+ FObject.subclass("button",1,1) {
+ def method_missing(*) send_out 0 end
+ }
+ FObject.subclass("toggle",1,1) {
+ def _0_bang; @state ^= true; trigger end
+ def _0_int x; @state = x!=0; trigger end
+ def trigger; send_out 0, (if @state then 1 else 0 end) end
+ }
+ FObject.subclass("jpatcher",0,0) {
+ def initialize(*a) super; @subobjects={} end
+ attr_accessor :subobjects
+ }
+ FObject.subclass("jcomment",0,0) {}
+ FObject.subclass("loadbang",0,1) { def trigger; send_out 0 end }
+ FObject.subclass("messbox",1,1) {
+ def _0_bang; send_out 0, *@argv end
+ def clear; @argv=[]; end
+ def append(*argv) @argv<<argv; end
+ }
+
+#-------- fClasses for: list manipulation (jMax-compatible)
+
+ FObject.subclass("listmake",2,1) {
+ def initialize(*a) @a=a end
+ def _0_list(*a) @a=a; _0_bang end
+ def _1_list(*a) @a=a end
+ def _0_bang; send_out 0, :list, *@a end
+ }
+ FObject.subclass("listlength",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0, a.length end
+ }
+ FObject.subclass("listelement",2,1) {
+ def initialize(i=0) super; @i=i.to_i end
+ def _1_int(i) @i=i.to_i end; alias _1_float _1_int
+ def _0_list(*a)
+ e=a[@i]
+ if Symbol===e then
+ send_out 0, :symbol, e
+ else
+ send_out 0, e
+ end
+ end
+ }
+ FObject.subclass("listsublist",3,1) {
+ def initialize(i=0,n=1) super; @i,@n=i.to_i,n.to_i end
+ def _1_int(i) @i=i.to_i end; alias _1_float _1_int
+ def _2_int(n) @n=n.to_i end; alias _2_float _2_int
+ def _0_list(*a) send_out 0, :list, *a[@i,@n] end
+ }
+ FObject.subclass("listprepend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_list(*a) a[0,0]=@b; send_out 0, :list, *a end
+ def _1_list(*b) @b=b end
+ }
+ FObject.subclass("listappend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_list(*a) a[a.length,0]=@b; send_out 0, :list, *a end
+ def _1_list(*b) @b=b end
+ }
+ FObject.subclass("listreverse",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0,:list,*a.reverse end
+ }
+ FObject.subclass("messageprepend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_(*a) a[0,0]=@b; send_out 0, *a end
+ def _1_list(*b) @b=b end
+ def method_missing(sym,*a)
+ (m = /(_\d_)(.*)/.match sym.to_s) or return super
+ _0_ m[2].intern, *a
+ end
+ }
+ FObject.subclass("messageappend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_(*a) a[a.length,0]=@b; send_out 0, *a end
+ def _1_list(*b) @b=b end
+ def method_missing(sym,*a)
+ (m = /(_\d_)(.*)/.match sym.to_s) or return super
+ _0_ m[2].intern, *a
+ end
+ }
+
+# this was the original demo for the Ruby/jMax/PureData bridges
+# FObjects are Ruby Objects that are exported to the PureData system.
+# _0_bang means bang message on inlet 0
+# FObject#send_out sends a message through an outlet
+FObject.subclass("for",3,1) {
+ attr_accessor :start, :stop, :step
+ def cast(key,val)
+ val = Integer(val) if Float===val
+ raise ArgumentError, "#{key} isn't a number" unless Integer===val
+ end
+ def initialize(start,stop,step)
+ super
+ cast("start",start)
+ cast("stop",stop)
+ cast("step",step)
+ @start,@stop,@step = start,stop,step
+ end
+ def _0_bang
+ x = start
+ if step > 0
+ (send_out 0, x; x += step) while x < stop
+ elsif step < 0
+ (send_out 0, x; x += step) while x > stop
+ end
+ end
+ def _0_float(x) self.start=x; _0_bang end
+ alias _1_float stop=
+ alias _2_float stop=
+}
+FObject.subclass("oneshot",2,1) {
+ def initialize(state=true) @state=state!=0 end
+ def method_missing(sel,*a)
+ m = /^_0_(.*)$/.match(sel.to_s) or return super
+ send_out 0, m[1].intern, *a if @state
+ @state=false
+ end
+ def _1_int(state) @state=state!=0 end
+ alias _1_float _1_int
+ def _1_bang; @state=true end
+}
+FObject.subclass("inv+",2,1) {
+ def initialize(b=0) @b=b end; def _1_float(b) @b=b end
+ def _0_float(a) send_out 0, :float, @b-a end
+}
+FObject.subclass("inv*",2,1) {
+ def initialize(b=0) @b=b end; def _1_float(b) @b=b end
+ def _0_float(a) send_out 0, :float, @b/a end
+}
+FObject.subclass("range",1,1) {
+ def initialize(*a) @a=a end
+ def initialize2
+ add_inlets @a.length
+ add_outlets @a.length
+ end
+ def _0_float(x) i=0; i+=1 until @a[i]==nil or x<@a[i]; send_out i,x end
+ def method_missing(sel,*a)
+ m = /^(_\d+_)(.*)/.match(sel.to_s) or return super
+ m[2]=="float" or return super
+ @a[m[1].to_i-1] = a[0]
+ post "setting a[#{m[1].to_i-1}] = #{a[0]}"
+ end
+}
+
+#-------- fClasses for: GUI
+
+module Gooey # to be included in any FObject class
+ def initialize(*)
+ super
+ @selected=false
+ @bg = "#ffffff" # white background
+ @bgb = "#000000" # black border
+ @bgs = "#0000ff" # blue border when selected
+ @fg = "#000000" # black foreground
+ @rsym = "#{self.class}#{self.object_id}".intern # unique id for use in Tcl
+ @can = nil # the canvas number
+ @canvas = nil # the canvas string
+ @y,@x = 0,0 # position on canvas
+ @sy,@sx = 16,16 # size on canvas
+ @font = "Courier -12"
+ @vis = nil
+ end
+ attr_reader :canvas
+ attr_reader :selected
+ def canvas=(can)
+ @can = can if Integer===can
+ @canvas = case can
+ when String; can
+ when Integer; ".x%x.c"%(4*can)
+ else raise "huh?"
+ end
+ end
+ def initialize2(*) GridFlow.bind self, @rsym.to_s end
+ def pd_displace(can,x,y) self.canvas||=can; @x+=x; @y+=y; pd_show(can) end
+ def pd_activate(can,*) self.canvas||=can end
+ def quote(text) # for tcl (isn't completely right ?)
+ text=text.gsub(/[\{\}]/) {|x| "\\"+x }
+ "{#{text}}"
+ end
+ def pd_vis(can,vis)
+ self.canvas||=can; @vis=vis!=0; update end
+ def update; pd_show @can if @vis end
+ def pd_getrect(can)
+ self.canvas||=can
+ @x,@y = get_position(can)
+ # the extra one-pixel on each side was for #peephole only
+ # not sure what to do with this
+ [@x-1,@y-1,@x+@sx+1,@y+@sy+1]
+ end
+ def pd_click(can,x,y,shift,alt,dbl,doit) return 0 end
+ def outline; if selected then @bgs else "#000000" end end
+ def pd_select(can,sel)
+ self.canvas||=can
+ @selected=sel!=0
+ GridFlow.gui %{ #{canvas} itemconfigure #{@rsym} -outline #{outline} \n }
+ end
+ def pd_delete(can) end
+ def pd_show(can)
+ self.canvas||=can
+ @x,@y = get_position can if can
+ end
+ def highlight(color,ratio) # doesn't use self
+ c = /^#(..)(..)(..)/.match(color)[1..3].map {|x| x.hex }
+ c.map! {|x| [255,(x*ratio).to_i].min }
+ "#%02x%02x%02x" % c
+ end
+end
+
+class Display < FObject; include Gooey
+ attr_accessor :text
+ def initialize()
+ super
+ @sel = nil; @args = [] # contents of last received message
+ @text = "..."
+ @sy,@sx = 16,80 # default size of the widget
+ @bg,@bgs,@fg = "#6774A0","#00ff80","#ffff80"
+ end
+ def _0_set_size(sy,sx) @sy,@sx=sy,sx end
+ def atom_to_s a
+ case a
+ when Float; sprintf("%.5f",a).gsub(/\.?0+$/, "")
+ else a.to_s
+ end
+ end
+ def method_missing(sel,*args)
+ m = /^(_\d+_)(.*)/.match(sel.to_s) or return super
+ @sel,@args = m[2].intern,args
+ @text = case @sel
+ when nil; "..."
+ when :float; atom_to_s @args[0]
+ else @sel.to_s + ": " + @args.map{|a| atom_to_s a }.join(' ')
+ end
+ update
+ end
+ def pd_show(can)
+ super
+ return if not canvas or not @vis # can't show for now...
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym}TEXT
+ set y #{@y+2}
+ foreach line [split #{quote @text} \\n] {
+ $canvas create text #{@x+2} $y -fill #{@fg} -font #{quote @font}\
+ -text $line -anchor nw -tag #{@rsym}TEXT
+ set y [expr $y+14]
+ }
+ foreach {x1 y1 x2 y2} [$canvas bbox #{@rsym}TEXT] {}
+ set sx [expr $x2-$x1+1]
+ set sy [expr $y2-$y1+3]
+ $canvas delete #{@rsym}
+ $canvas create rectangle #{@x} #{@y} \
+ [expr #{@x}+$sx] [expr #{@y}+$sy] -fill #{@bg} \
+ -tags #{@rsym} -outline #{outline}
+ $canvas lower #{@rsym} #{@rsym}TEXT
+ pd \"#{@rsym} set_size $sy $sx;\n\";
+ }
+ end
+ def pd_delete(can)
+ if @vis
+ GridFlow.gui %{ #{canvas} delete #{@rsym} #{@rsym}TEXT \n}
+ end
+ super
+ end
+ def delete; super end
+ def _0_grid(*foo) # big hack!
+ # hijacking a [#print]
+ gp = FObject["#print"]
+ @text = ""
+ overlord = self
+ gp.instance_eval { @overlord = overlord }
+ def gp.post(fmt,*args) @overlord.text << sprintf(fmt,*args) << "\n" end
+ def gp.end_hook
+ @overlord.instance_eval{@text.chomp!}
+ @overlord.update
+ end
+ #gp.send_in 0, :trunc, 70
+ gp.send_in 0, :maxrows, 20
+ gp.send_in 0, :grid, *foo
+ end
+
+ install "display", 1, 1
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+end
+
+class GridEdit < GridObject; include Gooey
+ def initialize(grid)
+ super
+ @store = GridStore.new
+ @store.connect 0,self,2
+ @fin = GridFinished.new
+ @fin.connect 0,self,3
+ @bg,@bgs,@fg = "#609068","#0080ff","#ff80ff"
+ @bghi = highlight(@bg,1.25) # "#80C891" # highlighted @bg
+ #@bghihi = highlight(@bghi,1.5) # very highlighted @bg
+ @cellsy,@cellsx = 16,48
+ @i,@j = nil,nil # highlighted cell dex
+ send_in 0, grid
+ end
+ def _0_cell_size(sy,sx) @cellsy,@cellsx=sy,sx; update end
+ def _0_float(*a) @store.send_in 1,:float,*a; @store.send_in 0; update end
+ def _0_list (*a) @store.send_in 1, :list,*a; @store.send_in 0; update end
+ def _0_grid (*a) @store.send_in 1, :grid,*a; @fin.send_in 0, :grid,*a end
+ def _3_bang; @store.send_in 0; update end
+ def edit_start(i,j)
+ edit_end if @i
+ @i,@j=i,j
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas itemconfigure #{@rsym}CELL_#{@i}_#{@j} -fill #{@bghi}
+ }
+ end
+ def edit_end
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas itemconfigure #{@rsym}CELL_#{@i}_#{@j} -fill #{@bg}
+ }
+ unfocus @can
+ end
+ def _2_rgrid_begin
+ @data = []
+ @dim = inlet_dim 2
+ @nt = inlet_nt 2
+ post "_2_rgrid_begin: dim=#{@dim.inspect} nt=#{@nt.inspect}"
+ send_out_grid_begin 0, @dim, @nt
+ end
+ def _2_rgrid_flow data
+ ps = GridFlow.packstring_for_nt @nt
+ @data[@data.length,0] = data.unpack(ps)
+ post "_2_rgrid_flow: data=#{@data.inspect}"
+ send_out_grid_flow 0, data
+ end
+ def _2_rgrid_end
+ post "_2_rgrid_end"
+ end
+ def pd_click(can,x,y,shift,alt,dbl,doit)
+ post "pd_click: %s", [can,x,y,shift,alt,dbl,doit].inspect
+ return 0 if not doit!=0
+ i = (y-@y-1)/@cellsy
+ j = (x-@x-1)/@cellsx
+ post "%d,%d", i,j
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ if (0...ny)===i and (0...nx)===j then
+ focus @can,x,y
+ edit_start i,j
+ end
+ return 0
+ end
+ def pd_key(key)
+ post "pd_key: %s", [key].inspect
+ if key==0 then unfocus @can; return end
+ end
+ def pd_motion(dx,dy)
+ post "pd_motion: %s", [dx,dy].inspect
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ k = @i*nx+@j
+ post "@data[#{k}]=#{@data[k]} before"
+ @data[k]-=dy
+ @store.send_in 1, :put_at, [@i,@j]
+ @store.send_in 1, @data[k]
+ @store.send_in 0
+ post "@data[#{k}]=#{@data[k]} after"
+ update
+ end
+ def pd_show(can)
+ super
+ return if not can
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ @sy = 2+@cellsy*ny
+ @sx = 2+@cellsx*nx
+ g = %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym} #{@rsym}CELL
+ $canvas create rectangle #{@x} #{@y} #{@x+@sx} #{@y+@sy} \
+ -fill #{@bg} -tags #{@rsym} -outline #{outline}
+ }
+ ny.times {|i|
+ nx.times {|j|
+ y1 = @y+1+i*@cellsy; y2 = y1+@cellsy
+ x1 = @x+1+j*@cellsx; x2 = x1+@cellsx
+ v = @data[i*nx+j]
+ g << %{
+ $canvas create rectangle #{x1} #{y1} #{x2} #{y2} -fill #{@bg} \
+ -tags {#{@rsym}CELL #{@rsym}CELL_#{i}_#{j}} -outline #{outline}
+ $canvas create text #{x2-4} #{y1+2} -text "#{v}" -anchor ne -fill #ffffff \
+ -tags {#{@rsym}CELL_#{i}_#{j}_T}
+ }
+ }
+ }
+ GridFlow.gui g
+ end
+ install "#edit", 2, 1
+ install_rgrid 2, true
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+end
+
+class Peephole < FPatcher; include Gooey
+ @fobjects = ["#dim","#export_list","#downscale_by 1 smoothly","#out","#scale_by 1",
+ proc{Demux.new(2)}]
+ @wires = [-1,0,0,0, 0,0,1,0, -1,0,5,0, 2,0,3,0, 4,0,3,0, 5,0,2,0, 5,1,4,0, 3,0,-1,0]
+ def initialize(sy=32,sx=32,*args)
+ super
+ @fobjects[1].connect 0,self,2
+ post "Peephole#initialize: #{sx} #{sy} #{args.inspect}"
+ @scale = 1
+ @down = false
+ @sy,@sx = sy,sx # size of the widget
+ @fy,@fx = 0,0 # size of last frame after downscale
+ @bg,@bgs = "#A07467","#00ff80"
+ end
+ def pd_show(can)
+ super
+ return if not can
+ if not @open
+ GridFlow.gui %{
+ pd \"#{@rsym} open [eval list [winfo id #{@canvas}]] 1;\n\";
+ }
+ @open=true
+ end
+ # round-trip to ensure this is done after the open
+ GridFlow.gui %{
+ pd \"#{@rsym} set_geometry #{@y} #{@x} #{@sy} #{@sx};\n\";
+ }
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym}
+ $canvas create rectangle #{@x} #{@y} #{@x+@sx} #{@y+@sy} \
+ -fill #{@bg} -tags #{@rsym} -outline #{outline}
+ }
+ set_geometry_for_real_now
+ end
+ def set_geometry_for_real_now
+ @fy,@fx=@sy,@sx if @fy<1 or @fx<1
+ @down = (@fx>@sx or @fy>@sx)
+ if @down then
+ @scale = [(@fy+@sy-1)/@sy,(@fx+@sx-1)/@sx].max
+ @scale=1 if @scale<1 # what???
+ @fobjects[2].send_in 1, @scale
+ sy2 = @fy/@scale
+ sx2 = @fx/@scale
+ else
+ @scale = [@sy/@fy,@sx/@fx].min
+ @fobjects[4].send_in 1, @scale
+ sy2 = @fy*@scale
+ sx2 = @fx*@scale
+ end
+ begin
+ @fobjects[5].send_in 1, (if @down then 0 else 1 end)
+ x2=@y+(@sy-sy2)/2
+ y2=@x+(@sx-sx2)/2
+ @fobjects[3].send_in 0, :set_geometry,
+ x2, y2, sy2, sx2
+ rescue StandardError => e
+ post "peeperr: %s", e.inspect
+ end
+ post "set_geometry_for_real_now (%d,%d) (%d,%d) (%d,%d) (%d,%d) (%d,%d)",
+ @x+1,@y+1,@sx,@sy,@fx,@fy,sx2,sy2,x2,y2
+ end
+ def _0_open(wid,use_subwindow)
+ post "%s", [wid,use_subwindow].inspect
+ @use_subwindow = use_subwindow==0 ? false : true
+ if @use_subwindow then
+ @fobjects[3].send_in 0, :open,:x11,:here,:embed_by_id,wid
+ end
+ end
+ def _0_set_geometry(y,x,sy,sx)
+ @sy,@sx = sy,sx
+ @y,@x = y,x
+ set_geometry_for_real_now
+ end
+ def _0_fall_thru(flag) # never worked ?
+ post "fall_thru: #{flag}"
+ @fobjects[3].send_in 0, :fall_thru, flag
+ end
+ # note: the numbering here is a FPatcher gimmick... -1,0 goes to _1_.
+ def _1_position(y,x,b)
+ s=@scale
+ if @down then y*=s;x*=s else y*=s;x*=s end
+ send_out 0,:position,y,x,b
+ end
+ def _2_list(sy,sx,chans)
+ @fy,@fx = sy,sx
+ set_geometry_for_real_now
+ end
+ def _0_paint()
+ post "paint()"
+ @fobjects[3].send_in 0, "draw"
+ end
+ def delete
+ post "deleting peephole"
+ GridFlow.gui %{ #{canvas} delete #{@rsym} \n}
+ @fobjects[3].send_in 0, :close
+ super
+ end
+ def method_missing(s,*a)
+ #post "%s: %s", s.to_s, a.inspect
+ super rescue NameError
+ end
+
+ install "#peephole", 1, 1
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+ #GridFlow.addtomenu "#peephole" # was this IMPD-specific ?
+end
+
+#-------- fClasses for: Hardware
+
+if const_defined? :USB
+
+class<<USB
+ attr_reader :busses
+end
+
+class DelcomUSB < GridFlow::FObject
+ Vendor,Product=0x0FC5,0x1222
+ def self.find
+ r=[]
+ USB.busses.each {|dir,bus|
+ bus.each {|dev|
+ r<<dev if dev.idVendor==Vendor and dev.idProduct==Product
+ }
+ }
+ r
+ end
+ def initialize #(bus=nil,dev=nil)
+ r=DelcomUSB.find
+ raise "no such device" if r.length<1
+ raise "#{r.length} such devices (which one???)" if r.length>1
+ @usb=USB.new(r[0])
+ if_num=nil
+ r[0].config.each {|config|
+ config.interface.each {|interface|
+ if_num = interface.bInterfaceNumber
+ }
+ }
+ # post "Interface # %i\n", if_num
+ @usb.set_configuration 1
+ @usb.claim_interface if_num
+ @usb.set_altinterface 0 rescue ArgumentError
+ end
+ # libdelcom had this:
+ # uint8 recipient, deviceModel, major, minor, dataL, dataM;
+ # uint16 length; uint8[8] extension;
+ def _0_send_command(major,minor,dataL,dataM,extension="\0\0\0\0\0\0\0\0")
+ raise "closed" if not @usb
+ raise "extension.length!=8" if extension.length!=8
+ @usb.control_msg(
+ 0x000000c8, 0x00000012,
+ minor*0x100+major,
+ dataM*0x100+dataL,
+ extension, 5000)
+ end
+ def delete; @usb.close; end
+ install "delcomusb", 1, 1
+end
+
+# Klippeltronics
+FObject.subclass("multio",1,1) {
+ Vendor,Product=0xDEAD,0xBEEF
+ def self.find
+ r=[]
+ USB.busses.each {|dir,bus|
+ bus.each {|dev|
+ post "dir=%s, vendor=%x, product=%x",
+ dir, dev.idVendor, dev.idProduct
+ r<<dev if dev.idVendor==Vendor and dev.idProduct==Product
+ }
+ }
+ r
+ end
+ def initialize
+ r=self.class.find
+ raise "no such device" if r.length<1
+ raise "#{r.length} such devices (which one???)" if r.length>1
+ $iobox=@usb=USB.new(r[0])
+ if_num=nil
+ r[0].config.each {|config|
+ config.interface.each {|interface|
+ #post "interface=%s", interface.to_s
+ if_num = interface.bInterfaceNumber
+ }
+ }
+ # post "Interface # %i\n", if_num
+ # @usb.set_configuration 0
+ @usb.claim_interface if_num
+ @usb.set_altinterface 0 rescue ArgumentError
+ end
+ #@usb.control_msg(0b10100001,0x01,0,0,"",1000)
+ #@usb.control_msg(0b10100001,0x01,0,1," ",0)
+ def delete; @usb.close; end
+}
+end # if const_defined? :USB
+
+# requires Ruby 1.8.0 because of bug in Ruby 1.6.x
+FObject.subclass("joystick_port",0,1) {
+ def initialize(port)
+ raise "sorry, requires Ruby 1.8" if RUBY_VERSION<"1.8"
+ @f = File.open(port.to_s,"r+")
+ @status = nil
+ @clock = Clock.new self
+ @clock.delay 0
+ @f.nonblock=true
+ end
+ def delete; @clock.unset; @f.close end
+ def call
+ loop{
+ begin
+ event = @f.read(8)
+ rescue Errno::EAGAIN
+ @clock.delay 0
+ return
+ end
+ return if not event
+ return if event.length<8
+ send_out 0, *event.unpack("IsCC")
+ }
+ end
+}
+
+# plotter control (HPGL)
+FObject.subclass("plotter_control",1,1) {
+ def puts(x)
+ x<<"\n"
+ x.each_byte {|b| send_out 0, b }
+ send_out 0
+ end
+ def _0_pu; puts "PU;" end
+ def _0_pd; puts "PD;" end
+ def _0_pa x,y; puts "PA#{x},#{y};" end
+ def _0_sp c; puts "SP#{c};"; end
+ def _0_ip(*v) puts "IP#{v.join','};" end
+ def _0_other(command,*v) puts "#{command.to_s.upcase}#{v.join','};" end
+ def _0_print(*text) puts "LB#{text.join(' ')}\003;" end
+ def _0_print_from_ascii(*codes)
+ _0_print codes.map{|code| code.chr }.join("")
+ end
+}
+
+(begin require "linux/ParallelPort"; true; rescue LoadError; false end) and
+FObject.subclass("parallel_port",1,3) {
+ def initialize(port,manually=0)
+ @f = File.open(port.to_s,"r+")
+ @f.extend Linux::ParallelPort
+ @status = nil
+ @flags = nil
+ @manually = manually!=0
+ @clock = (if @manually then nil else Clock.new self end)
+ @clock.delay 0 if @clock
+ end
+ def delete; @clock.unset unless @manually; @f.close end
+ def _0_int(x) @f.write x.to_i.chr; @f.flush end
+ alias _0_float _0_int
+ def call
+ flags = @f.port_flags
+ send_out 2, flags if @flags != flags
+ @flags = flags
+ status = @f.port_status
+ send_out 1, status if @status != status
+ @status = status
+ @clock.delay 20 if @clock
+ end
+ def _0_bang
+ @status = @flags = nil
+ call
+ end
+ # outlet 0 reserved (future use)
+}
+
+(begin require "linux/SoundMixer"; true; rescue LoadError; false end) and
+FObject.subclass("SoundMixer",1,1) {
+ # BUG? i may have the channels (left,right) backwards
+ def initialize(filename)
+ super
+ @file = File.open filename.to_s, 0
+ @file.extend Linux::SoundMixer
+ $sm = self
+ end
+ @@vars = Linux::SoundMixer.instance_methods.grep(/=/)
+ @@vars_h = {}
+ @@vars.each {|attr|
+ attr.chop!
+ eval %{ def _0_#{attr}(x) @file.#{attr} = x[0]*256+x[1] end }
+ @@vars_h[attr]=true
+ }
+ def _0_get(sel=nil)
+ if sel then
+ sels=sel.to_s
+ sel=sels.intern
+ raise if not @@vars_h.include? sel.to_s
+ begin
+ x = @file.send sel
+ send_out 0, sel, "(".intern, (x>>8)&255, x&255, ")".intern
+ rescue
+ send_out 0, sel, "(".intern, -1, -1, ")".intern
+ end
+ else
+ @@vars.each {|var| _0_get var }
+ end
+ end
+}
+
+# experimental
+FObject.subclass("rubyarray",2,1) {
+ def initialize() @a=[]; @i=0; end
+ def _0_float i; @i=i; send_out 0, *@a[@i]; end
+ def _1_list(*l) @a[@i]=l; end
+ def _0_save(filename,format=nil)
+ f=File.open(filename.to_s,"w")
+ if format then
+ @a.each {|x| f.puts(format.to_s%x) }
+ else
+ @a.each {|x| f.puts(x.join(",")) }
+ end
+ f.close
+ end
+ def _0_load(filename)
+ f=File.open(filename.to_s,"r")
+ @a.clear
+ f.each {|x| @a.push x.split(",").map {|y| Float(y) rescue y.intern }}
+ f.close
+ end
+}
+
+end # module GridFlow
diff --git a/externals/gridflow/base/flow_objects_for_image.c b/externals/gridflow/base/flow_objects_for_image.c
new file mode 100644
index 00000000..2041c3e1
--- /dev/null
+++ b/externals/gridflow/base/flow_objects_for_image.c
@@ -0,0 +1,618 @@
+/*
+ $Id: flow_objects_for_image.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <math.h>
+#include "grid.h.fcs"
+
+static void expect_picture (P<Dim> d) {
+ if (d->n!=3) RAISE("(height,width,chans) dimensions please");}
+static void expect_rgb_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=3) RAISE("(red,green,blue) channels please");}
+static void expect_rgba_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=4) RAISE("(red,green,blue,alpha) channels please");}
+static void expect_max_one_dim (P<Dim> d) {
+ if (d->n>1) { RAISE("expecting Dim[] or Dim[n], got %s",d->to_s()); }}
+
+//****************************************************************
+//{ Dim[A,B,*Cs]<T>,Dim[D,E]<T> -> Dim[A,B,*Cs]<T> }
+
+static void expect_convolution_matrix (P<Dim> d) {
+ if (d->n != 2) RAISE("only exactly two dimensions allowed for now (got %d)",
+ d->n);
+}
+
+// entry in a compiled convolution kernel
+struct PlanEntry { int y,x; bool neutral; };
+
+\class GridConvolve < GridObject
+struct GridConvolve : GridObject {
+ \attr Numop *op_para;
+ \attr Numop *op_fold;
+ \attr PtrGrid seed;
+ \attr PtrGrid b;
+ PtrGrid a;
+ int plann;
+ PlanEntry *plan; //Pt?
+ int margx,margy; // margins
+ GridConvolve () : plan(0) { b.constrain(expect_convolution_matrix); plan=0; }
+ \decl void initialize (Grid *r=0);
+ \decl void _0_op (Numop *op);
+ \decl void _0_fold (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+ \grin 1
+ template <class T> void copy_row (Pt<T> buf, int sx, int y, int x);
+ template <class T> void make_plan (T bogus);
+ ~GridConvolve () {if (plan) delete[] plan;}
+};
+
+template <class T> void GridConvolve::copy_row (Pt<T> buf, int sx, int y, int x) {
+ int day = a->dim->get(0), dax = a->dim->get(1), dac = a->dim->prod(2);
+ y=mod(y,day); x=mod(x,dax);
+ Pt<T> ap = (Pt<T>)*a + y*dax*dac;
+ while (sx) {
+ int sx1 = min(sx,dax-x);
+ COPY(buf,ap+x*dac,sx1*dac);
+ x=0;
+ buf += sx1*dac;
+ sx -= sx1;
+ }
+}
+
+static Numop *OP(Ruby x) {return FIX2PTR(Numop,rb_hash_aref(op_dict,x));}
+
+template <class T> void GridConvolve::make_plan (T bogus) {
+ P<Dim> da = a->dim, db = b->dim;
+ int dby = db->get(0);
+ int dbx = db->get(1);
+ if (plan) delete[] plan;
+ plan = new PlanEntry[dbx*dby];
+ int i=0;
+ for (int y=0; y<dby; y++) {
+ for (int x=0; x<dbx; x++) {
+ T rh = ((Pt<T>)*b)[y*dbx+x];
+ bool neutral = op_para->on(rh)->is_neutral(rh,at_right);
+ bool absorbent = op_para->on(rh)->is_absorbent(rh,at_right);
+ STACK_ARRAY(T,foo,1);
+ if (absorbent) {
+ foo[0] = 0;
+ op_para->map(1,foo,rh);
+ absorbent = op_fold->on(rh)->is_neutral(foo[0],at_right);
+ }
+ if (absorbent) continue;
+ plan[i].y = y;
+ plan[i].x = x;
+ plan[i].neutral = neutral;
+ i++;
+ }
+ }
+ plann = i;
+}
+
+GRID_INLET(GridConvolve,0) {
+ SAME_TYPE(in,b);
+ SAME_TYPE(in,seed);
+ P<Dim> da = in->dim, db = b->dim;
+ if (!db) RAISE("right inlet has no grid");
+ if (!seed) RAISE("seed missing");
+ if (db->n != 2) RAISE("right grid must have two dimensions");
+ if (da->n < 2) RAISE("left grid has less than two dimensions");
+ if (seed->dim->n != 0) RAISE("seed must be scalar");
+ if (da->get(0) < db->get(0)) RAISE("grid too small (y): %d < %d", da->get(0), db->get(0));
+ if (da->get(1) < db->get(1)) RAISE("grid too small (x): %d < %d", da->get(1), db->get(1));
+ margy = (db->get(0)-1)/2;
+ margx = (db->get(1)-1)/2;
+ a=new Grid(in->dim,in->nt);
+ out=new GridOutlet(this,0,da,in->nt);
+} GRID_FLOW {
+ COPY((Pt<T>)*a+in->dex, data, n);
+} GRID_FINISH {
+ Numop *op_put = OP(SYM(put));
+ make_plan((T)0);
+ int dbx = b->dim->get(1);
+ int day = a->dim->get(0);
+ int n = a->dim->prod(1);
+ int sx = a->dim->get(1)+dbx-1;
+ int n2 = sx*a->dim->prod(2);
+ STACK_ARRAY(T,buf,n);
+ STACK_ARRAY(T,buf2,n2);
+ T orh=0;
+ for (int iy=0; iy<day; iy++) {
+ op_put->map(n,buf,*(T *)*seed);
+ for (int i=0; i<plann; i++) {
+ int jy = plan[i].y;
+ int jx = plan[i].x;
+ T rh = ((Pt<T>)*b)[jy*dbx+jx];
+ if (i==0 || plan[i].y!=plan[i-1].y || orh!=rh) {
+ copy_row(buf2,sx,iy+jy-margy,-margx);
+ if (!plan[i].neutral) op_para->map(n2,buf2,rh);
+ }
+ op_fold->zip(n,buf,buf2+jx*a->dim->prod(2));
+ orh=rh;
+ }
+ out->send(n,buf);
+ }
+ a=0;
+} GRID_END
+
+GRID_INPUT(GridConvolve,1,b) {} GRID_END
+
+\def void _0_op (Numop *op ) { this->op_para=op; }
+\def void _0_fold (Numop *op ) { this->op_fold=op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->op_para = op_mul;
+ this->op_fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->b= r ? r : new Grid(new Dim(1,1),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#convolve',2,1"); }
+\end class GridConvolve
+
+/* ---------------------------------------------------------------- */
+/* "#scale_by" does quick scaling of pictures by integer factors */
+/*{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }*/
+\class GridScaleBy < GridObject
+struct GridScaleBy : GridObject {
+ \attr PtrGrid scale; // integer scale factor
+ int scaley;
+ int scalex;
+ \decl void initialize (Grid *factor=0);
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((Pt<int32>)*scale)[0];
+ scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(GridScaleBy,0) {
+ P<Dim> a = in->dim;
+ expect_picture(a);
+ out=new GridOutlet(this,0,new Dim(a->get(0)*scaley,a->get(1)*scalex,a->get(2)),in->nt);
+ in->set_factor(a->get(1)*a->get(2));
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ STACK_ARRAY(T,buf,rowsize*scalex);
+ int chans = in->dim->get(2);
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0; data+=rowsize, n-=rowsize) {
+ int p=0;
+ #define LOOP(z) \
+ for (int i=0; i<rowsize; i+=z) \
+ for (int k=0; k<scalex; k++, p+=3)
+ switch (chans) {
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default: LOOP(chans) {for (int c=0; c<chans; c++) Z(c);}
+ }
+ #undef LOOP
+ for (int j=0; j<scaley; j++) out->send(rowsize*scalex,buf);
+ }
+ #undef Z
+} GRID_END
+
+static void expect_scale_factor (P<Dim> dim) {
+ if (dim->prod()!=1 && dim->prod()!=2)
+ RAISE("expecting only one or two numbers");
+}
+
+GRID_INPUT(GridScaleBy,1,scale) { prepare_scale_factor(); } GRID_END
+
+\def void initialize (Grid *factor) {
+ scale.constrain(expect_scale_factor);
+ rb_call_super(argc,argv);
+ scale=new Grid(INT2NUM(2));
+ if (factor) scale=factor;
+ prepare_scale_factor();
+}
+
+\classinfo { IEVAL(rself,"install '#scale_by',2,1"); }
+\end class GridScaleBy
+
+// ----------------------------------------------------------------
+//{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }
+\class GridDownscaleBy < GridObject
+struct GridDownscaleBy : GridObject {
+ \attr PtrGrid scale;
+ \attr bool smoothly;
+ int scaley;
+ int scalex;
+ PtrGrid temp;
+ \decl void initialize (Grid *factor=0, Symbol option=Qnil);
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((Pt<int32>)*scale)[0];
+ scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(GridDownscaleBy,0) {
+
+ P<Dim> a = in->dim;
+ if (a->n!=3) RAISE("(height,width,chans) please");
+ out=new GridOutlet(this,0,new Dim(a->get(0)/scaley,a->get(1)/scalex,a->get(2)),in->nt);
+ in->set_factor(a->get(1)*a->get(2));
+ // i don't remember why two rows instead of just one.
+ temp=new Grid(new Dim(2,in->dim->get(1)/scalex,in->dim->get(2)),in->nt);
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ int rowsize2 = temp->dim->prod(1);
+ Pt<T> buf = (Pt<T>)*temp; //!@#$ maybe should be something else than T ?
+ int xinc = in->dim->get(2)*scalex;
+ int y = in->dex / rowsize;
+ int chans=in->dim->get(2);
+ #define Z(z) buf[p+z]+=data[i+z]
+ if (smoothly) {
+ while (n>0) {
+ if (y%scaley==0) CLEAR(buf,rowsize2);
+ #define LOOP(z) \
+ for (int i=0,p=0; p<rowsize2; p+=z) \
+ for (int j=0; j<scalex; j++,i+=z)
+ switch (chans) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);} break;
+ }
+ #undef LOOP
+ y++;
+ if (y%scaley==0 && out->dim) {
+ op_div->map(rowsize2,buf,(T)(scalex*scaley));
+ out->send(rowsize2,buf);
+ CLEAR(buf,rowsize2);
+ }
+ data+=rowsize;
+ n-=rowsize;
+ }
+ #undef Z
+ } else {
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0 && out->dim; data+=rowsize, n-=rowsize,y++) {
+ if (y%scaley!=0) continue;
+ #define LOOP(z) for (int i=0,p=0; p<rowsize2; i+=xinc, p+=z)
+ switch(in->dim->get(2)) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);}break;
+ }
+ #undef LOOP
+ out->send(rowsize2,buf);
+ }
+ }
+ #undef Z
+} GRID_END
+
+GRID_INPUT(GridDownscaleBy,1,scale) { prepare_scale_factor(); } GRID_END
+
+\def void initialize (Grid *factor, Symbol option) {
+ scale.constrain(expect_scale_factor);
+ rb_call_super(argc,argv);
+ scale=new Grid(INT2NUM(2));
+ if (factor) scale=factor;
+ prepare_scale_factor();
+ smoothly = option==SYM(smoothly);
+}
+
+\classinfo { IEVAL(rself,"install '#downscale_by',2,1"); }
+\end class GridDownscaleBy
+
+//****************************************************************
+\class GridLayer < GridObject
+struct GridLayer : GridObject {
+ PtrGrid r;
+ GridLayer() { r.constrain(expect_rgb_picture); }
+ \grin 0 int
+ \grin 1 int
+};
+
+GRID_INLET(GridLayer,0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ expect_rgba_picture(a);
+ if (a->get(1)!=r->dim->get(1)) RAISE("same width please");
+ if (a->get(0)!=r->dim->get(0)) RAISE("same height please");
+ in->set_factor(a->prod(2));
+ out=new GridOutlet(this,0,r->dim);
+} GRID_FLOW {
+ Pt<T> rr = ((Pt<T>)*r) + in->dex*3/4;
+ STACK_ARRAY(T,foo,n*3/4);
+#define COMPUTE_ALPHA(c,a) \
+ foo[j+c] = (data[i+c]*data[i+a] + rr[j+c]*(256-data[i+a])) >> 8
+ for (int i=0,j=0; i<n; i+=4,j+=3) {
+ COMPUTE_ALPHA(0,3);
+ COMPUTE_ALPHA(1,3);
+ COMPUTE_ALPHA(2,3);
+ }
+#undef COMPUTE_ALPHA
+ out->send(n*3/4,foo);
+} GRID_END
+
+GRID_INPUT(GridLayer,1,r) {} GRID_END
+
+\classinfo { IEVAL(rself,"install '#layer',2,1"); }
+\end class GridLayer
+
+// ****************************************************************
+// pad1,pad2 only are there for 32-byte alignment
+struct Line { int32 y1,x1,y2,x2,x,m,pad1,pad2; };
+
+static void expect_polygon (P<Dim> d) {
+ if (d->n!=2 || d->get(1)!=2) RAISE("expecting Dim[n,2] polygon");
+}
+
+\class DrawPolygon < GridObject
+struct DrawPolygon : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid color;
+ \attr PtrGrid polygon;
+ PtrGrid color2;
+ PtrGrid lines;
+ int lines_start;
+ int lines_stop;
+ DrawPolygon() {
+ color.constrain(expect_max_one_dim);
+ polygon.constrain(expect_polygon);
+ }
+ \decl void initialize (Numop *op, Grid *color=0, Grid *polygon=0);
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ void init_lines();
+
+};
+
+void DrawPolygon::init_lines () {
+ int nl = polygon->dim->get(0);
+ lines=new Grid(new Dim(nl,8), int32_e);
+ Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl);
+ Pt<int32> pd = *polygon;
+ for (int i=0,j=0; i<nl; i++) {
+ ld[i].y1 = pd[j+0];
+ ld[i].x1 = pd[j+1];
+ j=(j+2)%(2*nl);
+ ld[i].y2 = pd[j+0];
+ ld[i].x2 = pd[j+1];
+ if (ld[i].y1>ld[i].y2) memswap(Pt<int32>(ld+i)+0,Pt<int32>(ld+i)+2,2);
+ }
+}
+
+static int order_by_starting_scanline (const void *a, const void *b) {
+ return ((Line *)a)->y1 - ((Line *)b)->y1;
+}
+
+static int order_by_column (const void *a, const void *b) {
+ return ((Line *)a)->x - ((Line *)b)->x;
+}
+
+GRID_INLET(DrawPolygon,0) {
+ NOTEMPTY(color);
+ NOTEMPTY(polygon);
+ NOTEMPTY(lines);
+ SAME_TYPE(in,color);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ if (in->dim->get(2)!=color->dim->get(0))
+ RAISE("image does not have same number of channels as stored color");
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ lines_start = lines_stop = 0;
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+ int nl = polygon->dim->get(0);
+ qsort((int32 *)*lines,nl,sizeof(Line),order_by_starting_scanline);
+ int cn = color->dim->prod();
+ color2=new Grid(new Dim(cn*16), color->nt);
+ for (int i=0; i<16; i++) COPY((Pt<T>)*color2+cn*i,(Pt<T>)*color,cn);
+} GRID_FLOW {
+ int nl = polygon->dim->get(0);
+ Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl);
+ int f = in->factor();
+ int y = in->dex/f;
+ int cn = color->dim->prod();
+ Pt<T> cd = (Pt<T>)*color2;
+
+ while (n) {
+ while (lines_stop != nl && ld[lines_stop].y1<=y) lines_stop++;
+ for (int i=lines_start; i<lines_stop; i++) {
+ if (ld[i].y2<=y) {
+ memswap(ld+i,ld+lines_start,1);
+ lines_start++;
+ }
+ }
+ if (lines_start == lines_stop) {
+ out->send(f,data);
+ } else {
+ int32 xl = in->dim->get(1);
+ Pt<T> data2 = ARRAY_NEW(T,f);
+ COPY(data2,data,f);
+ for (int i=lines_start; i<lines_stop; i++) {
+ Line &l = ld[i];
+ l.x = l.x1 + (y-l.y1)*(l.x2-l.x1+1)/(l.y2-l.y1+1);
+ }
+ qsort(ld+lines_start,lines_stop-lines_start,
+ sizeof(Line),order_by_column);
+ for (int i=lines_start; i<lines_stop-1; i+=2) {
+ int xs = max(ld[i].x,(int32)0), xe = min(ld[i+1].x,xl);
+ if (xs>=xe) continue; /* !@#$ WHAT? */
+ while (xe-xs>=16) { op->zip(16*cn,data2+cn*xs,cd); xs+=16; }
+ op->zip((xe-xs)*cn,data2+cn*xs,cd);
+ }
+ out->give(f,data2);
+ }
+ n-=f;
+ data+=f;
+ y++;
+ }
+} GRID_END
+
+GRID_INPUT(DrawPolygon,1,color) {} GRID_END
+GRID_INPUT(DrawPolygon,2,polygon) {init_lines();} GRID_END
+
+\def void initialize (Numop *op, Grid *color, Grid *polygon) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ if (color) this->color=color;
+ if (polygon) { this->polygon=polygon; init_lines(); }
+}
+
+\classinfo { IEVAL(rself,"install '#draw_polygon',3,1"); }
+\end class DrawPolygon
+
+//****************************************************************
+static void expect_position(P<Dim> d) {
+ if (d->n!=1) RAISE("position should have 1 dimension, not %d", d->n);
+ if (d->v[0]!=2) RAISE("position dim 0 should have 2 elements, not %d", d->v[0]);
+}
+
+\class DrawImage < GridObject
+struct DrawImage : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid image;
+ \attr PtrGrid position;
+ \attr bool alpha;
+ \attr bool tile;
+
+ DrawImage() : alpha(false), tile(false) {
+ position.constrain(expect_position);
+ image.constrain(expect_picture);
+ }
+
+ \decl void initialize (Numop *op, Grid *image=0, Grid *position=0);
+ \decl void _0_alpha (bool v=true);
+ \decl void _0_tile (bool v=true);
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ // draw row # ry of right image in row buffer buf, starting at xs
+ // overflow on both sides has to be handled automatically by this method
+ template <class T> void draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0);
+};
+
+#define COMPUTE_ALPHA(c,a) obuf[j+(c)] = ibuf[j+(c)] + (rbuf[a])*(obuf[j+(c)]-ibuf[j+(c)])/256;
+#define COMPUTE_ALPHA4(b) \
+ COMPUTE_ALPHA(b+0,b+3); \
+ COMPUTE_ALPHA(b+1,b+3); \
+ COMPUTE_ALPHA(b+2,b+3); \
+ obuf[b+3] = rbuf[b+3] + (255-rbuf[b+3])*(ibuf[j+b+3])/256;
+
+template <class T> void DrawImage::draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0) {
+ if (ry<0 || ry>=image->dim->get(0)) return; // outside of image
+ int sx = in[0]->dim->get(1), rsx = image->dim->get(1);
+ int sc = in[0]->dim->get(2), rsc = image->dim->get(2);
+ Pt<T> rbuf = (Pt<T>)*image + ry*rsx*rsc;
+ if (x0>sx || x0<=-rsx) return; // outside of buffer
+ int n=rsx;
+ if (x0+n>sx) n=sx-x0;
+ if (x0<0) { rbuf-=rsc*x0; n+=x0; x0=0; }
+ if (alpha && rsc==4 && sc==3) { // RGB by RGBA //!@#$ optimise
+ int j=sc*x0;
+ for (; n; n--, rbuf+=4, j+=3) {
+ op->zip(sc,obuf+j,rbuf); COMPUTE_ALPHA(0,3); COMPUTE_ALPHA(1,3); COMPUTE_ALPHA(2,3);
+ }
+ } else if (alpha && rsc==4 && sc==4) { // RGBA by RGBA
+ op->zip(n*rsc,obuf+x0*rsc,rbuf);
+ int j=sc*x0;
+ for (; n>=4; n-=4, rbuf+=16, j+=16) {
+ COMPUTE_ALPHA4(0);COMPUTE_ALPHA4(4);
+ COMPUTE_ALPHA4(8);COMPUTE_ALPHA4(12);
+ }
+ for (; n; n--, rbuf+=4, j+=4) {
+ COMPUTE_ALPHA4(0);
+ }
+ } else { // RGB by RGB, etc
+ op->zip(n*rsc,obuf+sc*x0,rbuf);
+ }
+}
+
+GRID_INLET(DrawImage,0) {
+ NOTEMPTY(image);
+ NOTEMPTY(position);
+ SAME_TYPE(in,image);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ int lchan = in->dim->get(2);
+ int rchan = image->dim->get(2);
+ if (alpha && rchan!=4) {
+ RAISE("alpha mode works only with 4 channels in right_hand");
+ }
+ if (lchan != rchan-(alpha?1:0) && lchan != rchan) {
+ RAISE("right_hand has %d channels, alpha=%d, left_hand has %d, expecting %d or %d",
+ rchan, alpha?1:0, lchan, rchan-(alpha?1:0), rchan);
+ }
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+} GRID_FLOW {
+ int f = in->factor();
+ int y = in->dex/f;
+ if (position->nt != int32_e) RAISE("position has to be int32");
+ int py = ((int32*)*position)[0], rsy = image->dim->v[0], sy=in->dim->get(0);
+ int px = ((int32*)*position)[1], rsx = image->dim->v[1], sx=in->dim->get(1);
+ for (; n; y++, n-=f, data+=f) {
+ int ty = div2(y-py,rsy);
+ if (tile || ty==0) {
+ Pt<T> data2 = ARRAY_NEW(T,f);
+ COPY(data2,data,f);
+ if (tile) {
+ for (int x=px-div2(px+rsx-1,rsx)*rsx; x<sx; x+=rsx) {
+ draw_segment(data2,data,mod(y-py,rsy),x);
+ }
+ } else {
+ draw_segment(data2,data,y-py,px);
+ }
+ out->give(f,data2);
+ } else {
+ out->send(f,data);
+ }
+ }
+} GRID_END
+
+GRID_INPUT(DrawImage,1,image) {} GRID_END
+GRID_INPUT(DrawImage,2,position) {} GRID_END
+\def void _0_alpha (bool v=true) { alpha = v; gfpost("ALPHA=%d",v); }
+\def void _0_tile (bool v=true) { tile = v; }
+
+\def void initialize (Numop *op, Grid *image, Grid *position) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ if (image) this->image=image;
+ if (position) this->position=position;
+ else this->position=new Grid(new Dim(2),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#draw_image',3,1"); }
+\end class DrawImage
+
+void startup_flow_objects_for_image () {
+ \startall
+}
diff --git a/externals/gridflow/base/flow_objects_for_matrix.c b/externals/gridflow/base/flow_objects_for_matrix.c
new file mode 100644
index 00000000..d245a10f
--- /dev/null
+++ b/externals/gridflow/base/flow_objects_for_matrix.c
@@ -0,0 +1,77 @@
+/*
+ $Id: flow_objects_for_matrix.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <math.h>
+#include "grid.h.fcs"
+
+// produce an upper triangular matrix with ones on the diagonal
+// will also affect any additional columns using the same row-operations
+
+void expect_complete_matrix (P<Dim> d) {
+ if (d->n!=2) RAISE("bletch");
+ if (d->get(0)>d->get(1)) RAISE("argh");
+}
+
+\class GridMatrixSolve < GridObject
+struct GridMatrixSolve : GridObject {
+ Numop *op_sub;
+ Numop *op_mul;
+ Numop *op_div;
+ PtrGrid matrix;
+ GridMatrixSolve() {
+ matrix.constrain(expect_complete_matrix);
+ }
+ \decl void initialize ();
+ \grin 0 float
+};
+
+GRID_INPUT(GridMatrixSolve,0,matrix) {
+ int n = matrix->dim->get(0); // # rows
+ int m = matrix->dim->get(1); // # columns
+ Pt<T> mat = (Pt<T>)*matrix;
+ for (int j=0; j<n; j++) {
+ op_div->map(m,mat+j*m,mat[j*m+j]);
+ for (int i=j+1; i<n; i++) {
+ STACK_ARRAY(T,row,m);
+ COPY(row,mat+j,m);
+ op_mul->map(m,row,mat[i*m+j]);
+ op_sub->zip(m,mat+i*m,row);
+ }
+ }
+ GridOutlet out(this,0,matrix->dim);
+ out.send(n*m,mat);
+} GRID_END
+
+\def void initialize () {
+ rb_call_super(argc,argv);
+ this->op_sub = op_sub;
+ this->op_mul = op_mul;
+ this->op_div = op_div;
+}
+
+\classinfo { IEVAL(rself,"install '#matrix_solve',1,1"); }
+\end class
+
+void startup_flow_objects_for_matrix () {
+ \startall
+}
diff --git a/externals/gridflow/base/grid.c b/externals/gridflow/base/grid.c
new file mode 100644
index 00000000..877a460f
--- /dev/null
+++ b/externals/gridflow/base/grid.c
@@ -0,0 +1,604 @@
+/*
+ $Id: grid.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "grid.h.fcs"
+#include <ctype.h>
+
+/* copied from bridge/puredata.c (sorry: linkage issue) */
+struct Pointer : CObject { void *p; Pointer(void *_p) : p(_p) {}};
+Ruby Pointer_s_noo (void *ptr) {
+ return Data_Wrap_Struct(EVAL("GridFlow::Pointer"), 0, 0, new Pointer(ptr));}
+static void *Pointer_gut (Ruby rself) {DGS(Pointer); return self->p;}
+
+//#define TRACE fprintf(stderr,"%s %s [%s:%d]\n",INFO(parent),__PRETTY_FUNCTION__,__FILE__,__LINE__);assert(this);
+#define TRACE assert(this);
+
+#define CHECK_TYPE(d) \
+ if (NumberTypeE_type_of(d)!=this->nt) RAISE("%s(%s): " \
+ "type mismatch during transmission (got %s expecting %s)", \
+ INFO(parent), \
+ __PRETTY_FUNCTION__, \
+ number_type_table[NumberTypeE_type_of(d)].name, \
+ number_type_table[this->nt].name);
+
+#define CHECK_BUSY(s) \
+ if (!dim) RAISE("%s: " #s " not busy",INFO(parent));
+
+#define CHECK_ALIGN(d) \
+ {int bytes = number_type_table[nt].size/8; \
+ int align = ((long)(void*)d)%bytes; \
+ if (align) {L;gfpost("%s(%s): Alignment Warning: %p is not %d-aligned: %d", \
+ INFO(parent), __PRETTY_FUNCTION__, (void*)d,bytes,align);}}
+
+#define CHECK_ALIGN2(d,nt) \
+ {int bytes = number_type_table[nt].size/8; \
+ int align = ((long)(void*)d)%bytes; \
+ if (align) {L;gfpost("Alignment Warning: %p is not %d-aligned: %d", \
+ (void*)d,bytes,align);}}
+
+// **************** Grid ******************************************
+
+#define FOO(S) static inline void NUM(Ruby x, S &y) {y=convert(x,(int32*)0);}
+EACH_INT_TYPE(FOO)
+#undef FOO
+
+#define FOO(S) \
+static inline void NUM(Ruby x, S &y) { \
+ if (TYPE(x)==T_FLOAT) y = RFLOAT(x)->value; \
+ else if (INTEGER_P(x)) y = convert(x,(S*)0); \
+ else RAISE("expected Float (or at least Integer)");}
+EACH_FLOAT_TYPE(FOO)
+#undef FOO
+
+void Grid::init_from_ruby_list(int n, Ruby *a, NumberTypeE nt) {
+ Ruby delim = SYM(#);
+ for (int i=0; i<n; i++) {
+ if (a[i] == delim) {
+ STACK_ARRAY(int32,v,i);
+ if (i!=0 && TYPE(a[i-1])==T_SYMBOL) nt=NumberTypeE_find(a[--i]);
+ for (int j=0; j<i; j++) v[j] = convert(a[j],(int32*)0);
+ init(new Dim(i,v),nt);
+ CHECK_ALIGN2(this->data,nt);
+ if (a[i] != delim) i++;
+ i++; a+=i; n-=i;
+ goto fill;
+ }
+ }
+ if (n!=0 && TYPE(a[0])==T_SYMBOL) {
+ nt = NumberTypeE_find(a[0]);
+ a++, n--;
+ }
+ init(new Dim(n),nt);
+ CHECK_ALIGN2(this->data,nt);
+ fill:
+ int nn = dim->prod();
+ n = min(n,nn);
+#define FOO(type) { \
+ Pt<type> p = (Pt<type>)*this; \
+ if (n==0) CLEAR(p,nn); \
+ else { \
+ for (int i=0; i<n; i++) NUM(a[i],p[i]); \
+ for (int i=n; i<nn; i+=n) COPY(p+i,p,min(n,nn-i)); }}
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+}
+
+void Grid::init_from_ruby(Ruby x) {
+ if (TYPE(x)==T_ARRAY) {
+ init_from_ruby_list(rb_ary_len(x),rb_ary_ptr(x));
+ } else if (INTEGER_P(x) || FLOAT_P(x)) {
+ init(new Dim(),int32_e);
+ CHECK_ALIGN2(this->data,nt);
+ ((Pt<int32>)*this)[0] = INT(x);
+ } else {
+ rb_funcall(
+ EVAL("proc{|x| raise \"can't convert to grid: #{x.inspect}\"}"),
+ SI(call),1,x);
+ }
+}
+
+// **************** GridInlet *************************************
+
+// must be set before the end of GRID_BEGIN phase, and so cannot be changed
+// afterwards. This is to allow some optimisations. Anyway there is no good reason
+// why this would be changed afterwards.
+void GridInlet::set_factor(int factor) {
+ if(!dim) RAISE("huh?");
+ if(factor<=0) RAISE("%s: factor=%d should be >= 1",INFO(parent),factor);
+ if (dim->prod() && dim->prod() % factor)
+ RAISE("%s: set_factor: expecting divisor",INFO(parent));
+ if (factor > 1) {
+ buf=new Grid(new Dim(factor), nt);
+ bufi=0;
+ } else {
+ buf=0;
+ }
+}
+
+static Ruby GridInlet_begin_1(GridInlet *self) {
+#define FOO(T) self->gh->flow(self,-1,Pt<T>()); break;
+ TYPESWITCH(self->nt,FOO,)
+#undef FOO
+ return Qnil;
+}
+
+static Ruby GridInlet_begin_2(GridInlet *self) {
+ self->dim = 0; // hack
+ return (Ruby) 0;
+}
+
+bool GridInlet::supports_type(NumberTypeE nt) {
+#define FOO(T) return !! gh->flow_##T;
+ TYPESWITCH(nt,FOO,return false)
+#undef FOO
+}
+
+Ruby GridInlet::begin(int argc, Ruby *argv) {TRACE;
+ if (!argc) return PTR2FIX(this);
+ GridOutlet *back_out = (GridOutlet *) Pointer_gut(argv[0]);
+ nt = (NumberTypeE) INT(argv[1]);
+ argc-=2, argv+=2;
+ PROF(parent) {
+ if (dim) {
+ gfpost("%s: grid inlet conflict; aborting %s in favour of %s",
+ INFO(parent), INFO(sender), INFO(back_out->parent));
+ abort();
+ }
+ sender = back_out->parent;
+ if ((int)nt<0 || (int)nt>=(int)number_type_table_end)
+ RAISE("%s: inlet: unknown number type",INFO(parent));
+ if (!supports_type(nt))
+ RAISE("%s: number type %s not supported here",
+ INFO(parent), number_type_table[nt].name);
+ STACK_ARRAY(int32,v,argc);
+ for (int i=0; i<argc; i++) v[i] = NUM2INT(argv[i]);
+ P<Dim> dim = this->dim = new Dim(argc,v);
+ dex=0;
+ buf=0;
+ int r = rb_ensure(
+ (RMethod)GridInlet_begin_1,(Ruby)this,
+ (RMethod)GridInlet_begin_2,(Ruby)this);
+ if (!r) {abort(); goto hell;}
+ this->dim = dim;
+ back_out->callback(this);
+ hell:;} // PROF
+ return Qnil;
+}
+
+template <class T> void GridInlet::flow(int mode, int n, Pt<T> data) {TRACE;
+ CHECK_BUSY(inlet);
+ CHECK_TYPE(*data);
+ CHECK_ALIGN(data);
+ PROF(parent) {
+ if (this->mode==0) {dex += n; return;} // ignore data
+ if (n==0) return; // no data
+ switch(mode) {
+ case 4:{
+ int d = dex + bufi;
+ if (d+n > dim->prod()) {
+ gfpost("grid input overflow: %d of %d from [%s] to [%s]",
+ d+n, dim->prod(), INFO(sender), 0);
+ n = dim->prod() - d;
+ if (n<=0) return;
+ }
+ int bufn = factor();
+ if (buf && bufi) {
+ Pt<T> bufd = (Pt<T>)*buf;
+ int k = min(n,bufn-bufi);
+ COPY(bufd+bufi,data,k);
+ bufi+=k; data+=k; n-=k;
+ if (bufi==bufn) {
+ int newdex = dex+bufn;
+ if (this->mode==6) {
+ Pt<T> data2 = ARRAY_NEW(T,bufn);
+ COPY(data2,bufd,bufn);
+ CHECK_ALIGN(data2);
+ gh->flow(this,bufn,data2);
+ } else {
+ CHECK_ALIGN(bufd);
+ gh->flow(this,bufn,bufd);
+ }
+ dex = newdex;
+ bufi = 0;
+ }
+ }
+ int m = (n/bufn)*bufn;
+ if (m) {
+ int newdex = dex + m;
+ if (this->mode==6) {
+ Pt<T> data2 = ARRAY_NEW(T,m);
+ COPY(data2,data,m);
+ CHECK_ALIGN(data2);
+ gh->flow(this,m,data2);
+ } else {
+ gh->flow(this,m,data);
+ }
+ dex = newdex;
+ }
+ data += m;
+ n -= m;
+ if (buf && n>0) COPY((Pt<T>)*buf+bufi,data,n), bufi+=n;
+ }break;
+ case 6:{
+ assert(!buf);
+ int newdex = dex + n;
+ gh->flow(this,n,data);
+ if (this->mode==4) delete[] (T *)data;
+ dex = newdex;
+ }break;
+ case 0: break; // ignore data
+ default: RAISE("%s: unknown inlet mode",INFO(parent));
+ }} // PROF
+}
+
+void GridInlet::end() {TRACE;
+ assert(this);
+ if (!dim) RAISE("%s: inlet not busy",INFO(parent));
+ if (dim->prod() != dex) {
+ gfpost("incomplete grid: %d of %d from [%s] to [%s]",
+ dex, dim->prod(), INFO(sender), INFO(parent));
+ }
+ PROF(parent) {
+#define FOO(T) gh->flow(this,-2,Pt<T>());
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+ } // PROF
+ dim=0;
+ buf=0;
+ dex=0;
+}
+
+template <class T> void GridInlet::from_grid2(Grid *g, T foo) {TRACE;
+ nt = g->nt;
+ dim = g->dim;
+ int n = g->dim->prod();
+ gh->flow(this,-1,Pt<T>());
+ if (n>0 && this->mode!=0) {
+ Pt<T> data = (Pt<T>)*g;
+ CHECK_ALIGN(data);
+ int size = g->dim->prod();
+ if (this->mode==6) {
+ Pt<T> d = data;
+ data = ARRAY_NEW(T,size); // problem with int64,float64 here.
+ COPY(data,d,size);
+ CHECK_ALIGN(data);
+ gh->flow(this,n,data);
+ } else {
+ int ntsz = number_type_table[nt].size;
+ int m = GridOutlet::MAX_PACKET_SIZE/*/ntsz*//factor();
+ if (!m) m++;
+ m *= factor();
+ while (n) {
+ if (m>n) m=n;
+ CHECK_ALIGN(data);
+ gh->flow(this,m,data);
+ data+=m; n-=m; dex+=m;
+ }
+ }
+ }
+ gh->flow(this,-2,Pt<T>());
+ //!@#$ add error handling.
+ // rescue; abort(); end ???
+ dim = 0;
+ dex = 0;
+}
+
+void GridInlet::from_grid(Grid *g) {TRACE;
+ if (!supports_type(g->nt))
+ RAISE("%s: number type %s not supported here",
+ INFO(parent), number_type_table[g->nt].name);
+#define FOO(T) from_grid2(g,(T)0);
+ TYPESWITCH(g->nt,FOO,)
+#undef FOO
+}
+
+/* **************** GridOutlet ************************************ */
+
+void GridOutlet::begin(int woutlet, P<Dim> dim, NumberTypeE nt) {TRACE;
+ int n = dim->count();
+ this->nt = nt;
+ this->dim = dim;
+ Ruby a[n+4];
+ a[0] = INT2NUM(woutlet);
+ a[1] = bsym._grid;
+ a[2] = Pointer_s_noo(this);
+ a[3] = INT2NUM(nt);
+ for(int i=0; i<n; i++) a[4+i] = INT2NUM(dim->get(i));
+ parent->send_out(COUNT(a),a);
+ frozen=true;
+ if (!dim->prod()) {end(); return;}
+ int32 lcm_factor = 1;
+ for (uint32 i=0; i<inlets.size(); i++) lcm_factor = lcm(lcm_factor,inlets[i]->factor());
+ if (nt != buf->nt) {
+ // biggest packet size divisible by lcm_factor
+ int32 v = (MAX_PACKET_SIZE/lcm_factor)*lcm_factor;
+ if (v==0) v=MAX_PACKET_SIZE; // factor too big. don't have a choice.
+ buf=new Grid(new Dim(v),nt);
+ }
+}
+
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send_direct(int n, Pt<T> data) {TRACE;
+ assert(data); assert(frozen);
+ CHECK_BUSY(outlet); CHECK_TYPE(*data); CHECK_ALIGN(data);
+ for (; n>0; ) {
+ int pn = min(n,MAX_PACKET_SIZE);
+ for (uint32 i=0; i<inlets.size(); i++) inlets[i]->flow(4,pn,data);
+ data+=pn, n-=pn;
+ }
+}
+
+void GridOutlet::flush() {TRACE;
+ if (!bufi) return;
+#define FOO(T) send_direct(bufi,(Pt<T>)*buf);
+ TYPESWITCH(buf->nt,FOO,)
+#undef FOO
+ bufi = 0;
+}
+
+template <class T, class S>
+static void convert_number_type(int n, Pt<T> out, Pt<S> in) {
+ for (int i=0; i<n; i++) out[i]=(T)in[i];
+}
+
+//!@#$ buffering in outlet still is 8x faster...?
+//!@#$ should use BitPacking for conversion...?
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send(int n, Pt<T> data) {TRACE;
+ assert(data); assert(frozen);
+ if (!n) return;
+ CHECK_BUSY(outlet); CHECK_ALIGN(data);
+ if (NumberTypeE_type_of(*data)!=nt) {
+ int bs = MAX_PACKET_SIZE;
+#define FOO(T) { \
+ STACK_ARRAY(T,data2,bs); \
+ for (;n>=bs;n-=bs,data+=bs) { \
+ convert_number_type(bs,data2,data); send(bs,data2);} \
+ convert_number_type(n,data2,data); \
+ send(n,data2); }
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+ } else {
+ dex += n;
+ assert(dex <= dim->prod());
+ if (n > MIN_PACKET_SIZE || bufi + n > MAX_PACKET_SIZE) flush();
+ if (n > MIN_PACKET_SIZE) {
+ send_direct(n,data);
+ } else {
+ COPY((Pt<T>)*buf+bufi,data,n);
+ bufi += n;
+ }
+ if (dex==dim->prod()) end();
+ }
+}
+
+template <class T>
+void GridOutlet::give(int n, Pt<T> data) {TRACE;
+ assert(data); CHECK_BUSY(outlet); assert(frozen);
+ assert(dex+n <= dim->prod()); CHECK_ALIGN(data);
+ if (NumberTypeE_type_of(*data)!=nt) {
+ send(n,data);
+ delete[] (T *)data;
+ return;
+ }
+ if (inlets.size()==1 && inlets[0]->mode == 6) {
+ // this is the copyless buffer passing
+ flush();
+ inlets[0]->flow(6,n,data);
+ dex += n;
+ } else {
+ flush();
+ send_direct(n,data);
+ dex += n;
+ delete[] (T *)data;
+ }
+ if (dex==dim->prod()) end();
+}
+
+void GridOutlet::callback(GridInlet *in) {TRACE;
+ CHECK_BUSY(outlet); assert(!frozen);
+ int mode = in->mode;
+ assert(mode==6 || mode==4 || mode==0);
+ inlets.push_back(in);
+}
+
+\class GridObject < FObject
+
+//!@#$ does not handle types properly
+//!@#$ most possibly a big hack
+template <class T>
+void GridObject_r_flow(GridInlet *in, int n, Pt<T> data) {
+ GridObject *self = in->parent;
+ uint32 i;
+ for (i=0; i<self->in.size(); i++) if (in==self->in[i].p) break;
+ if (i==self->in.size()) RAISE("inlet not found?");
+ if (n==-1) {
+ rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_begin));
+ } else if (n>=0) {
+ Ruby buf = rb_str_new((char *)((uint8 *)data),n*sizeof(T));
+ rb_funcall(self->rself,SI(send_in),3,INT2NUM(i),SYM(rgrid_flow),buf);
+ } else {
+ rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_end));
+ }
+}
+
+\def Symbol inlet_nt (int inln) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) return Qnil;
+ return number_type_table[inl->nt].sym;
+}
+
+\def Array inlet_dim (int inln) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) return Qnil;
+ int n=inl->dim->count();
+ Ruby a = rb_ary_new2(n);
+ for(int i=0; i<n; i++) rb_ary_push(a,INT2NUM(inl->dim->v[i]));
+ return a;
+}
+
+\def void inlet_set_factor (int inln, int factor) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) RAISE("inlet #%d not active",inln);
+ inl->set_factor(factor);
+}
+
+\def void send_out_grid_begin (int outlet, Array buf, NumberTypeE nt=int32_e) {
+ if (outlet<0) RAISE("bad outlet number");
+ int n = rb_ary_len(buf);
+ Ruby *p = rb_ary_ptr(buf);
+ STACK_ARRAY(int32,v,n);
+ for (int i=0; i<n; i++) v[i] = convert(p[i],(int32*)0);
+ out = new GridOutlet(this,outlet,new Dim(n,v),nt); // valgrind says leak?
+}
+
+template <class T>
+void send_out_grid_flow_2(P<GridOutlet> go, Ruby s, T bogus) {
+ int n = rb_str_len(s) / sizeof(T);
+ Pt<T> p = rb_str_pt(s,T);
+ go->send(n,p);
+}
+
+\def void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e) {
+ if (outlet<0) RAISE("bad outlet number");
+#define FOO(T) send_out_grid_flow_2(out,argv[1],(T)0);
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+}
+
+// install_rgrid(Integer inlet, Boolean multi_type? = true)
+static Ruby GridObject_s_install_rgrid(int argc, Ruby *argv, Ruby rself) {
+ if (argc<1 || argc>2) RAISE("er...");
+ IEVAL(rself,"@handlers||=[]");
+ Ruby handlers = rb_ivar_get(rself,SI(@handlers));
+ GridHandler *gh = new GridHandler;
+ bool mt = argc>1 ? argv[1]==Qtrue : 0; /* multi_type? */
+ if (mt) {
+#define FOO(S) gh->flow_##S = GridObject_r_flow;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ } else {
+#define FOO(S) gh->flow_##S = 0;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ }
+ gh->flow_int32 = GridObject_r_flow;
+ //IEVAL(rself,"self.class_eval { def _0_grid(*a) ___grid(0,*a) end }");
+ rb_funcall(handlers,SI([]=),2,INT2NUM(INT(argv[0])),PTR2FIX(gh));
+ return Qnil;
+}
+
+static Ruby GridObject_s_instance_methods(int argc, Ruby *argv, Ruby rself) {
+ static const char *names[] = {"grid","list","float"};
+ Ruby list = rb_class_instance_methods(argc,argv,rself);
+ Ruby handlers = rb_ivar_get(rself,SI(@handlers));
+ if (handlers==Qnil) return list;
+ for (int i=0; i<rb_ary_len(handlers); i++) {
+ Ruby ghp = rb_ary_ptr(handlers)[i];
+ if (ghp==Qnil) continue;
+ GridHandler *gh = FIX2PTR(GridHandler,ghp);
+ char buf[256];
+ for (int j=0; j<COUNT(names); j++) {
+ sprintf(buf,"_%d_%s",i,names[j]);
+ rb_ary_push(list,rb_str_new2(buf));
+ }
+ }
+ return list;
+}
+
+// this does auto-conversion of list/float to grid
+// this also (will) do grid inputs for ruby stuff.
+\def Ruby method_missing (...) {
+ {
+ if (argc<1) RAISE("not enough arguments");
+ if (!SYMBOL_P(argv[0])) RAISE("expected symbol");
+ const char *name = rb_sym_name(argv[0]);
+ char *endp;
+ if (*name++!='_') goto hell;
+ int i = strtol(name,&endp,10);
+ if (name==endp) goto hell;
+ if (*endp++!='_') goto hell;
+ if (strcmp(endp,"grid")==0) {
+ Ruby handlers = rb_ivar_get(rb_obj_class(rself),SI(@handlers));
+ if (TYPE(handlers)!=T_ARRAY) {
+ rb_p(handlers);
+ RAISE("gridhandler-list missing (maybe forgot install_rgrid ?)"
+ " while trying to receive on inlet %d",i);
+ }
+ if (i>=rb_ary_len(handlers)) RAISE("BORK");
+ GridHandler *gh = FIX2PTR(GridHandler, rb_ary_ptr(handlers)[i]);
+ if (in.size()<=(uint32)i) in.resize(i+1);
+ if (!in[i]) in[i]=new GridInlet((GridObject *)this,gh);
+ return in[i]->begin(argc-1,argv+1);
+ }
+ // we call the grid method recursively to ask it its GridInlet*
+ // don't do this before checking the missing method is exactly that =)
+ char foo[42];
+ sprintf(foo,"_%d_grid",i);
+ P<GridInlet> inl = FIX2PTR(GridInlet,rb_funcall(rself,rb_intern(foo),0));
+ if (strcmp(endp,"list" )==0) return inl->from_ruby_list(argc-1,argv+1), Qnil;
+ if (strcmp(endp,"float")==0) return inl->from_ruby (argc-1,argv+1), Qnil;
+ }
+ hell: return rb_call_super(argc,argv);
+}
+
+\classinfo {
+ IEVAL(rself,"install 'GridObject',0,0");
+ // define in Ruby-metaclass
+ rb_define_singleton_method(rself,"instance_methods",(RMethod)GridObject_s_instance_methods,-1);
+ rb_define_singleton_method(rself,"install_rgrid",(RMethod)GridObject_s_install_rgrid,-1);
+ rb_enable_super(rb_singleton_class(rself),"instance_methods");
+}
+\end class GridObject
+
+Ruby cGridObject;
+
+void startup_grid () {
+ \startall
+ cGridObject = rb_const_get(mGridFlow,SI(GridObject));
+}
+
+// never call this. this is a hack to make some things work.
+// i'm trying to circumvent either a bug in the compiler or i don't have a clue. :-(
+void make_gimmick () {
+ GridOutlet foo(0,0,0);
+#define FOO(S) foo.give(0,Pt<S>());
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+}
+
diff --git a/externals/gridflow/base/grid.h b/externals/gridflow/base/grid.h
new file mode 100644
index 00000000..4aaf2aa3
--- /dev/null
+++ b/externals/gridflow/base/grid.h
@@ -0,0 +1,1109 @@
+/*
+ $Id: grid.h,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#ifndef __GF_GRID_H
+#define __GF_GRID_H
+
+// current version number as string literal
+#define GF_VERSION "0.8.0"
+#define GF_COMPILE_TIME __DATE__ ", " __TIME__
+
+#include <new>
+#include <vector>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <math.h>
+
+extern "C" {
+#include <ruby.h>
+#include <rubyio.h>
+#include <version.h>
+};
+
+#ifndef RUBY_H
+#error "Can't do anything without ruby.h"
+#endif
+
+#include "../config.h"
+
+#ifdef __WIN32__
+#define INT winINT
+#define random rand
+#undef send
+#undef close
+#define sigjmp_buf jmp_buf
+#define siglongjmp longjmp
+#endif
+
+#define BUG(s,args...) {fprintf(stderr,s "\nat: %s\n",args,__PRETTY_FUNCTION__); ::raise(11);}
+
+// !@#$ what am I going to do about this? should this be changed?
+// should I wrap all of the Ruby API for C++-style convenience?
+typedef VALUE Ruby;
+// typedef struct Ruby { VALUE x };
+
+#ifdef IS_BRIDGE
+#define RAISE(args...) rb_raise(rb_eArgError,args)
+#else
+#define RAISE(args...) rb_raise0(__FILE__,__LINE__,__PRETTY_FUNCTION__,rb_eArgError,args)
+#endif
+
+// avoid ruby warning
+#ifndef rb_enable_super
+#define rb_enable_super(a,b) \
+ if (RUBY_RELEASE_CODE < 20030716) rb_enable_super(a,b)
+#endif
+
+/* undocumented function from Ruby that is one thing we need to fix a very elusive bug
+that manifests itself when embedding ruby inside a plugin of another app. This exists
+for all versions of Ruby up to now, and I don't know when it gets fixed. */
+extern "C" void Init_stack(VALUE *addr);
+
+extern "C" {
+void rb_raise0(
+const char *file, int line, const char *func, VALUE exc, const char *fmt, ...)
+__attribute__ ((noreturn));
+};
+#define L fprintf(stderr,"%s:%d in %s\n",__FILE__,__LINE__,__PRETTY_FUNCTION__);
+#define SI(_sym_) (rb_intern(#_sym_))
+#define SYM(_sym_) (ID2SYM(SI(_sym_)))
+#define DGS(_class_) \
+ _class_ *self; \
+ Data_Get_Struct(rself,_class_,self); \
+ self->check_magic();
+
+// returns the size of a statically defined array
+// warning: does not work with STACK_ARRAY()
+#define COUNT(_array_) ((int)(sizeof(_array_) / sizeof((_array_)[0])))
+
+static inline long rb_str_len(Ruby s) {return RSTRING(s)->len;}
+static inline char *rb_str_ptr(Ruby s) {return RSTRING(s)->ptr;}
+static inline long rb_ary_len(Ruby s) {return RARRAY(s)->len;}
+static inline Ruby *rb_ary_ptr(Ruby s) {return RARRAY(s)->ptr;}
+static inline const char *rb_sym_name(Ruby sym) {return rb_id2name(SYM2ID(sym));}
+#define rb_str_pt(s,t) Pt<t>((t*)rb_str_ptr(s),rb_str_len(s))
+
+// shorthands
+#define IEVAL(_self_,s) rb_funcall(_self_,SI(instance_eval),1,rb_str_new2(s))
+#define EVAL(s) rb_eval_string(s)
+#define rassert(_p_) if (!(_p_)) RAISE(#_p_);
+
+// because of older versions of Ruby (1.6.?)
+#define rb_obj_class(o) rb_funcall((o),SI(class),0)
+
+#define WATCH(n,ar) { \
+ char foo[16*1024], *p=foo; \
+ p += sprintf(p,"%s: ",#ar); \
+ for (int q=0; q<n; q++) p += sprintf(p,"%lld ",(long long)ar[q]); \
+ gfpost("%s",foo); \
+}
+
+// we're gonna override assert, so load it first, to avoid conflicts
+#include <assert.h>
+
+#undef assert
+#define assert(_expr_) \
+ if (!(_expr_)) { \
+ fprintf(stderr, "%s:%d: assertion failed: %s is false\n", \
+ __FILE__, __LINE__, #_expr_); \
+ ::abort(); }
+
+// disabling assertion checking?
+#ifndef HAVE_DEBUG
+#undef assert
+#define assert(_foo_)
+#endif
+
+#ifdef HAVE_TSC_PROFILING
+#define HAVE_PROFILING
+#endif
+
+#ifdef HAVE_PROFILING
+#define PROF(_self_) for (GFStackMarker gf_marker(_self_);gf_marker.once();)
+#else
+#define PROF(_self_)
+#endif // HAVE_PROFILING
+
+static inline Ruby PTR2FIX (const void *ptr) {
+ long p = (long)ptr;
+ if ((p&3)!=0) BUG("unaligned pointer: %08x\n",(long)(ptr));
+ return LONG2NUM(p>>2);
+}
+#define FIX2PTR(type,ruby) ((type *)(TO(long,ruby)<<2))
+
+//****************************************************************
+
+/* int32 was long before, now int, because of amd64 */
+typedef char int8; typedef unsigned char uint8;
+typedef short int16; typedef unsigned short uint16;
+typedef int int32; typedef unsigned int uint32;
+typedef long long int64;typedef unsigned long long uint64;
+typedef float float32;
+typedef double float64;
+
+// three-way comparison (T is assumed Comparable)
+template <class T> static inline T cmp(T a, T b) { return a<b ? -1 : a>b; }
+
+// a remainder function such that div2(a,b)*b+mod(a,b) = a and for which
+// mod(a,b) is in [0;b) or (b;0]. in contrast to C-language builtin a%b,
+// this one has uniform behaviour around zero.
+static inline int mod(int a, int b) {
+int c=a%b; c+=b&-(c&&(a<0)^(b<0)); return c;}
+
+// counterpart of mod(a,b), just like a/b and a%b are counterparts
+static inline int div2(int a, int b) {
+int c=a<0; return (a/b)-(c&&!!(a%b));}
+
+static inline int32 gf_abs( int32 a) { return a>0?a:-a; }
+static inline int64 gf_abs( int64 a) { return a>0?a:-a; }
+static inline float32 gf_abs(float32 a) { return fabs(a); }
+static inline float64 gf_abs(float64 a) { return fabs(a); }
+
+// integer powers in log(b) time. T is assumed Integer
+template <class T> static inline T ipow(T a, T b) {
+ for(T r=1;;) {if (b&1) r*=a; b>>=1; if (!b) return r; a*=a;}
+}
+
+// kludge
+static inline float32 ipow(float32 a, float32 b) { return pow(a,b); }
+static inline float64 ipow(float64 a, float64 b) { return pow(a,b); }
+
+#undef min
+#undef max
+// minimum/maximum functions; T is assumed to be Comparable
+template <class T> static inline T min(T a, T b) { return a<b?a:b; }
+template <class T> static inline T max(T a, T b) { return a>b?a:b; }
+//template <class T> inline T min(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); }
+//template <class T> inline T max(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); }
+
+// greatest common divisor, by euclid's algorithm
+// this runs in log(a+b) number operations
+template <class T> static T gcd (T a, T b) {
+ while (b) {T c=mod(a,b); a=b; b=c;}
+ return a;
+}
+
+// greatest common divisor, the binary algorithm. haven't tried yet.
+template <class T> static T gcd2 (T a, T b) {
+ int s=0;
+ while ((a|b)&1==0) { a>>=1; b>>=1; s++; }
+ while (a) {
+ if (a&1==0) a>>=1;
+ else if (b&1==0) b>>=1;
+ else {T t=abs(a-b); if (a<b) b=t; else a=t;}
+ }
+ return b<<s;
+}
+
+// least common multiple; this runs in log(a+b) like gcd.
+template <class T> static inline T lcm (T a, T b) {return a*b/gcd(a,b);}
+
+// returns the position (0..31) of highest bit set in a word, or 0 if none.
+#define FOO(N) if ((x>>N)&(((typeof(x))1<<N)-1)) { x>>=N; i+=N; }
+static int highest_bit(uint8 x) {int i=0; FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint16 x) {int i=0; FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint32 x) {int i=0; FOO(16)FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint64 x) {int i=0;FOO(32)FOO(16)FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+#undef FOO
+// returns the position (0..31) of lowest bit set in a word, or 0 if none.
+template <class T> static int lowest_bit(T n) { return highest_bit((~n+1)&n); }
+
+static double drand() { return 1.0*rand()/(RAND_MAX+1.0); }
+
+// is little-endian
+static inline bool is_le() {int x=1; return ((char *)&x)[0];}
+
+#if defined(HAVE_PENTIUM)
+static inline uint64 rdtsc() {
+ uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x;
+}
+#elif defined(HAVE_PPC)
+static inline uint64 rdtsc() {
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+/* see AbsoluteToNanoseconds(), Microseconds()
+http://www.simdtech.org/apps/group_public/email/altivec/msg01956.html
+and mach_absolute_time() and mach_timebase_info(&info),
+then ns=(time*info.numer)/info.denom;
+and the timebase_info is constant
+(double)mach_absolute_time*gTimeBaseToNS*/
+return 0;
+}
+#else
+static inline uint64 rdtsc() {return 0;}
+#endif
+
+#ifdef HAVE_LITE
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32)
+#define EACH_FLOAT_TYPE(MACRO)
+#else
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32) MACRO(int64)
+#define EACH_FLOAT_TYPE(MACRO) MACRO(float32) MACRO(float64)
+#endif
+#define EACH_NUMBER_TYPE(MACRO) EACH_INT_TYPE(MACRO) EACH_FLOAT_TYPE(MACRO)
+
+// note: loop unrolling macros assume N!=0
+// btw this may cause alignment problems when 8 does not divide N
+#define UNROLL_8(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&7; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ case 4:MACRO(4); case 5:MACRO(5); case 6:MACRO(6); case 7:MACRO(7); \
+ PTR+=8; N-=8; ARGS; if (N) goto start; }
+#define UNROLL_4(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&3; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ PTR+=4; N-=4; ARGS; if (N) goto start; }
+
+//****************************************************************
+// hook into pointer manipulation. will help find memory corruption bugs.
+
+template <class T> class Pt {
+typedef ptrdiff_t /* and not int nor long */ Z;
+public:
+ T *p;
+#ifdef HAVE_DEBUG
+ T *start;
+ Z n;
+ Pt() : p(0), start(0), n(0) {}
+ Pt(T *q, Z _n) : p(q), start(q), n(_n) {}
+ Pt(T *q, Z _n, T *_start) : p(q), start(_start), n(_n) {}
+#else
+ Pt() : p(0) {}
+ Pt(T *q, Z _n, T *_start=0) : p(q) {}
+#endif
+ T &operator *() { return *p; }
+ Pt operator+=(Z i) { p+=i; return *this; }
+ Pt operator-=(Z i) { p-=i; return *this; }
+ Pt operator++( ) { p++; return *this; }
+ Pt operator--( ) { p--; return *this; }
+ Pt operator++(int) { Pt f(*this); ++*this; return f; }
+ Pt operator--(int) { Pt f(*this); --*this; return f; }
+ T &operator[](Z i) {
+#ifdef HAVE_DEBUG_HARDER
+ if (!(p+i>=start && p+i<start+n))
+ BUG("BUFFER OVERFLOW: 0x%08lx[%ld]=0x%08lx is not in 0x%08lx..0x%08lx",
+ (long)p, (long)i, (long)(p+i),(long)start,(long)(start+n));
+#endif
+ return p[i];
+ }
+
+ void will_use(int k) {
+#ifdef HAVE_DEBUG_HARDER
+ if (k==0) return;
+ T *q = p+k;
+ if (!(p>=start && p<start+n && q>=start && q<=start+n))
+ BUG("BUFFER OVERFLOW: 0x%08lx...0x%08lx is not all inside 0x%08lx...0x%08lx",
+ (long)p,(long)q,start,(long)(start+n));
+#endif
+ }
+
+ Z operator-(Pt x) { return p-x.p; }
+ operator bool () { return (bool )p; }
+ operator void *() { return (void *)p; }
+ operator int8 *() { return (int8 *)p; }
+
+/* how do i make typecast operators that are not also default conversions??? */
+/* this should be found so i can clean up the following: */
+#define FOO(S) operator S *() { return (S *)p; }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#ifdef HAVE_DEBUG
+#define FOO(S) operator Pt<S>() { return Pt<S>((S *)p,n*sizeof(T)/1,(S *)start); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#else
+#define FOO(S) operator Pt<S>() { return Pt<S>((S *)p,0); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#endif
+/* end 0.8 (TESTING) */
+
+#ifdef HAVE_DEBUG
+ template <class U> Pt operator+(U x) { return Pt(p+x,n,start); }
+ template <class U> Pt operator-(U x) { return Pt(p-x,n,start); }
+#else
+ template <class U> Pt operator+(U x) { return Pt(p+x,0); }
+ template <class U> Pt operator-(U x) { return Pt(p-x,0); }
+#endif
+};
+
+//template <class T> class P : Pt<T> {};
+//a reference counting pointer class
+template <class T> class P {
+public:
+#define INCR if (p) p->refcount++;
+#define DECR if (p) {p->refcount--; if (!p->refcount) delete p;}
+ T *p;
+ P() : p(0) {}
+ P(T *_p) { p = _p; INCR; }
+ P(const P<T> &_p) { p = _p.p; INCR; }
+ P<T> &operator =(T * _p) { DECR; p=_p; INCR; return *this; }
+ P<T> &operator =(P<T> _p) { DECR; p=_p.p; INCR; return *this; }
+ bool operator ==(P<T> _p) { return p == _p.p; }
+ bool operator !=(P<T> _p) { return p != _p.p; }
+ ~P() { DECR; }
+ bool operator !(){ return !p; }
+ operator bool() { return !!p; }
+ T &operator *() { return *p; }
+ T *operator ->() { return p; }
+//#undef INCR
+//#undef DECR
+};
+
+#ifndef IS_BRIDGE
+extern "C" void *gfmalloc(size_t n);
+extern "C" void gffree(void *p);
+inline void *::operator new (size_t n) { return gfmalloc(n); }
+inline void *::operator new[] (size_t n) { return gfmalloc(n); }
+inline void ::operator delete (void *p) { gffree(p); }
+inline void ::operator delete[] (void *p) { gffree(p); }
+#endif
+
+#define STACK_ARRAY(T,V,N) T V##_foo[N]; Pt<T> V(V##_foo,N);
+#define ARRAY_NEW(T,N) (Pt<T>((T *)new T[N],N))
+
+void gfmemcopy(uint8 *out, const uint8 *in, int n);
+template <class T> inline void COPY(Pt<T> dest, Pt<T> src, int n) {
+ src.will_use(n); dest.will_use(n);
+ gfmemcopy((uint8*)dest,(const uint8*)src,n*sizeof(T));
+}
+template <class T> inline void CLEAR(Pt<T> dest, int n) {
+ dest.will_use(n);
+ memset(dest,0,n*sizeof(T));
+}
+template <class T> static void memswap (Pt<T> a, Pt<T> b, int n) {
+ STACK_ARRAY(T,c,n); COPY(c,a,n); COPY(a,b,n); COPY(b,c,n);
+}
+
+//****************************************************************
+// my own little Ruby <-> C++ layer
+
+struct Arg { Ruby a; };
+struct ArgList { int n; Pt<Arg> v; };
+static inline bool INTEGER_P(Ruby x) {return FIXNUM_P(x)||TYPE(x)==T_BIGNUM;}
+static inline bool FLOAT_P(Ruby x) {return TYPE(x)==T_FLOAT;}
+#define INT(x) TO(int32,x)
+#define TO(t,x) convert(x,(t*)0)
+
+// not using NUM2INT because Ruby can convert Symbol to int
+// (by compatibility with Ruby 1.4)
+static inline int32 convert(Ruby x, int32 *foo) {
+ if (INTEGER_P(x)) return NUM2INT(x);
+ if (FLOAT_P(x)) return NUM2INT(rb_funcall(x,SI(round),0));
+ RAISE("expected Integer or Float (got %s)",
+ rb_str_ptr(rb_funcall(x,SI(inspect),0)));
+}
+static int16 convert(Ruby x, int16 *foo) {
+ int v = INT(x);
+ if (v<-0x8000 || v>=0x8000) RAISE("value %d is out of range",v);
+ return v;}
+static uint16 convert(Ruby x, uint16 *foo) {
+ int v = INT(x);
+ if (v<0 || v>=0x10000) RAISE("value %d is out of range",v);
+ return v;}
+static bool convert(Ruby x, bool *foo) {
+ if (x==Qtrue) return true;
+ if (x==Qfalse) return false;
+ switch (TYPE(x)) {
+ case T_FIXNUM: case T_BIGNUM: case T_FLOAT: return !!INT(x);
+ default: RAISE("can't convert to bool");
+ }
+}
+
+static uint64 convert(Ruby val, uint64 *foo) {
+ if (FIXNUM_P(val)) return (uint64)FIX2LONG(val);
+ if (TYPE(val)!=T_BIGNUM) RAISE("type error");
+ uint64 v = (uint64)NUM2UINT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32;
+ return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));}
+static int64 convert(Ruby val, int64 *foo) {
+ if (FIXNUM_P(val)) return (int64)FIX2LONG(val);
+ if (TYPE(val)!=T_BIGNUM) RAISE("type error");
+ int64 v = (int64)NUM2INT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32;
+ return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));}
+
+static Ruby gf_ull2num(uint64 val) {
+ return rb_funcall(
+ rb_funcall(UINT2NUM((uint32)(val>>32)),SI(<<),1,INT2FIX(32)),
+ SI(+),1,UINT2NUM((uint32)val));}
+static Ruby gf_ll2num(int64 val) {
+ return rb_funcall(
+ rb_funcall(INT2NUM((int32)(val>>32)),SI(<<),1,INT2FIX(32)),
+ SI(+),1,UINT2NUM((uint32)val));}
+
+static long convert(Ruby x, long *foo) {
+ return sizeof(long)==sizeof(int32) ?
+ convert(x,(int32 *)0) :
+ convert(x,(int64 *)0);
+}
+static float64 convert(Ruby x, float64 *foo) {
+ if (INTEGER_P(x)) return INT(x);
+ if (TYPE(x)!=T_FLOAT) RAISE("not a Float");
+ return ((RFloat*)x)->value;}
+static float32 convert(Ruby x, float32 *foo) {
+ return (float32) convert(x,(float64 *)0);}
+typedef Ruby Symbol, Array, String, Integer;
+static Ruby convert(Ruby x, Ruby *bogus) { return x; }
+typedef Ruby (*RMethod)(...); /* !@#$ fishy */
+
+#define BUILTIN_SYMBOLS(MACRO) \
+ MACRO(_grid,"grid") MACRO(_bang,"bang") MACRO(_float,"float") \
+ MACRO(_list,"list") MACRO(_sharp,"#") \
+ MACRO(iv_outlets,"@outlets") \
+ MACRO(iv_ninlets,"@ninlets") \
+ MACRO(iv_noutlets,"@noutlets")
+extern struct BuiltinSymbols {
+#define FOO(_sym_,_str_) Ruby _sym_;
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+} bsym;
+
+//****************************************************************
+// CObject is the base class for C++ classes that get exported to Ruby.
+// BTW: It's quite convenient to have virtual-methods in the base class
+// because otherwise the vtable* isn't at the beginning of the object
+// and that's pretty confusing to a lot of people, especially when simple
+// casting causes a pointer to change its value.
+struct CObject {
+#define OBJECT_MAGIC 1618033989
+ int32 magic;
+ int32 refcount;
+ Ruby rself; // point to Ruby peer
+ CObject() : magic(OBJECT_MAGIC), refcount(0), rself(0) {}
+ void check_magic () {
+ if (magic != OBJECT_MAGIC) {
+ fprintf(stderr,"Object memory corruption! (ask the debugger)\n");
+ for (int i=-2; i<=2; i++)
+ fprintf(stderr,"this[%d]=0x%08x\n",i,((int*)this)[i]);
+ raise(11);
+ }
+ }
+ virtual ~CObject() { magic = 0xDEADBEEF; }
+ virtual void mark() {} // not used for now
+};
+void CObject_free (void *);
+
+// you shouldn't use MethodDecl directly (used by source_filter.rb)
+struct MethodDecl { const char *selector; RMethod method; };
+void define_many_methods(Ruby rself, int n, MethodDecl *methods);
+
+extern Ruby mGridFlow, cFObject, cGridObject, cFormat;
+
+//****************************************************************
+// a Dim is a list of dimensions that describe the shape of a grid
+\class Dim < CObject
+struct Dim : CObject {
+ static const int MAX_DIMENSIONS=16; // maximum number of dimensions in a grid
+ int n;
+ Pt<int32> v; // safe pointer
+ int32 v2[MAX_DIMENSIONS]; // real stuff
+ void check(); // test invariants
+ Dim(int n, Pt<int32> v) {
+ this->v = Pt<int32>(v2,MAX_DIMENSIONS);
+ this->n = n;
+ COPY(this->v,v,n); check();
+ }
+ Dim(int n, int32* v) {
+ this->v = Pt<int32>(v2,MAX_DIMENSIONS);
+ this->n = n;
+ COPY(this->v,Pt<int32>(v,n),n); check();
+ }
+ Dim() {v=Pt<int32>(v2,MAX_DIMENSIONS); n=0; check();}
+ Dim(int a) {v=Pt<int32>(v2,MAX_DIMENSIONS); n=1;v[0]=a; check();}
+ Dim(int a,int b) {v=Pt<int32>(v2,MAX_DIMENSIONS); n=2;v[0]=a;v[1]=b; check();}
+ Dim(int a,int b,int c){v=Pt<int32>(v2,MAX_DIMENSIONS); n=3;v[0]=a;v[1]=b;v[2]=c;check();}
+ int count() {return n;}
+ int get(int i) { return v[i]; }
+ int32 prod(int start=0, int end=-1) {
+ if (end<0) end+=n;
+ int32 tot=1;
+ for (int i=start; i<=end; i++) tot *= v[i];
+ return tot;
+ }
+ char *to_s();
+ bool equal(P<Dim> o) {
+ if (n!=o->n) return false;
+ for (int i=0; i<n; i++) if (v[i]!=o->v[i]) return false;
+ return true;
+ }
+};
+\end class Dim
+
+//****************************************************************
+//BitPacking objects encapsulate optimised loops of conversion
+struct BitPacking;
+// those are the types of the optimised loops of conversion
+// inputs are const
+struct Packer {
+#define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt<S> in, Pt<uint8> out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+struct Unpacker {
+#define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt<uint8> in, Pt<S> out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+
+\class BitPacking < CObject
+struct BitPacking : CObject {
+ Packer *packer;
+ Unpacker *unpacker;
+ unsigned int endian; // 0=big, 1=little, 2=same, 3=different
+ int bytes;
+ int size;
+ uint32 mask[4];
+ BitPacking(){::abort();} // don't call, but don't remove. sorry.
+ BitPacking(int endian, int bytes, int size, uint32 *mask,
+ Packer *packer=0, Unpacker *unpacker=0);
+ bool is_le();
+ bool eq(BitPacking *o);
+ \decl void initialize(Ruby foo1, Ruby foo2, Ruby foo3);
+ \decl String pack2(String ins, String outs=Qnil);
+ \decl String unpack2(String ins, String outs=Qnil);
+ \decl String to_s();
+// main entrances to Packers/Unpackers
+ template <class T> void pack( int n, Pt<T> in, Pt<uint8> out);
+ template <class T> void unpack(int n, Pt<uint8> in, Pt<T> out);
+};
+\end class
+
+int high_bit(uint32 n);
+int low_bit(uint32 n);
+void swap32 (int n, Pt<uint32> data);
+void swap16 (int n, Pt<uint16> data);
+
+#define NT_UNSIGNED (1<<0)
+#define NT_FLOAT (1<<1)
+#define NT_UNIMPL (1<<2)
+#define NUMBER_TYPE_LIMITS(T,a,b,c) \
+ inline T nt_smallest(T *bogus) {return a;} \
+ inline T nt_greatest(T *bogus) {return b;} \
+ inline T nt_all_ones(T *bogus) {return c;}
+
+NUMBER_TYPE_LIMITS( uint8,0,255,255)
+NUMBER_TYPE_LIMITS( int16,-0x8000,0x7fff,-1)
+NUMBER_TYPE_LIMITS( int32,-0x80000000,0x7fffffff,-1)
+NUMBER_TYPE_LIMITS( int64,-0x8000000000000000LL,0x7fffffffffffffffLL,-1)
+NUMBER_TYPE_LIMITS(float32,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+NUMBER_TYPE_LIMITS(float64,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+
+#ifdef HAVE_LITE
+#define NT_NOTLITE NT_UNIMPL
+#define NONLITE(x...)
+#else
+#define NT_NOTLITE 0
+#define NONLITE(x...) x
+#endif
+#define NUMBER_TYPES(MACRO) \
+ MACRO( uint8, 8,NT_UNSIGNED, "u8,b") MACRO( int8, 8,NT_UNIMPL, "i8") \
+ MACRO( uint16,16,NT_UNSIGNED|NT_UNIMPL, "u16") MACRO(int16,16,0, "i16,s") \
+ MACRO( uint32,32,NT_UNSIGNED|NT_UNIMPL, "u32") MACRO(int32,32,0, "i32,i") \
+ MACRO( uint64,64,NT_UNSIGNED|NT_UNIMPL, "u64") MACRO(int64,64,NT_NOTLITE, "i64,l") \
+ MACRO(float32,32,NT_NOTLITE|NT_FLOAT, "f32,f") \
+ MACRO(float64,64,NT_NOTLITE|NT_FLOAT, "f64,d")
+
+enum NumberTypeE {
+#define FOO(_sym_,args...) _sym_##_e,
+NUMBER_TYPES(FOO)
+#undef FOO
+ number_type_table_end
+};
+
+#define FOO(_type_) \
+inline NumberTypeE NumberTypeE_type_of(_type_ &x) { \
+ return _type_##_e; }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+
+\class NumberType < CObject
+struct NumberType : CObject {
+ Ruby /*Symbol*/ sym;
+ const char *name;
+ int size;
+ int flags;
+ const char *aliases;
+ NumberTypeE index;
+ NumberType(const char *name_, int size_, int flags_, const char *aliases_) :
+ name(name_), size(size_), flags(flags_), aliases(aliases_) {}
+ \decl Symbol sym_m();
+ \decl int size_m();
+ \decl int flags_m();
+ \decl int index_m();
+};
+\end class
+
+NumberTypeE NumberTypeE_find (Ruby sym);
+
+#define TYPESWITCH(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break; \
+ case float32_e: C(float32) break; case float64_e: C(float64) break;) \
+ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);}
+#define TYPESWITCH_NOFLOAT(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break;)\
+ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);}
+
+// Numop objects encapsulate optimised loops of simple operations
+
+enum LeftRight { at_left, at_right };
+
+template <class T>
+struct NumopOn : CObject {
+ // Function Vectorisations
+ typedef void (*Map )( int n, T *as, T b ); Map op_map;
+ typedef void (*Zip )( int n, T *as, T *bs); Zip op_zip;
+ typedef void (*Fold)(int an, int n, T *as, T *bs); Fold op_fold;
+ typedef void (*Scan)(int an, int n, T *as, T *bs); Scan op_scan;
+ // Algebraic Properties (those involving simply numeric types)
+ typedef bool (*AlgebraicCheck)(T x, LeftRight side);
+ // neutral: right: forall y {f(x,y)=x}; left: forall x {f(x,y)=y};
+ // absorbent: right: exists a forall y {f(x,y)=a}; ...
+ AlgebraicCheck is_neutral, is_absorbent;
+ NumopOn(Map m, Zip z, Fold f, Scan s, AlgebraicCheck n, AlgebraicCheck a) :
+ op_map(m), op_zip(z), op_fold(f), op_scan(s),
+ is_neutral(n), is_absorbent(a) {}
+ NumopOn() {}
+ NumopOn(const NumopOn &z) {
+ op_map = z.op_map; op_zip = z.op_zip;
+ op_fold = z.op_fold; op_scan = z.op_scan;
+ is_neutral = z.is_neutral; is_absorbent = z.is_absorbent; }
+};
+
+// semigroup property: associativity: f(a,f(b,c))=f(f(a,b),c)
+#define OP_ASSOC (1<<0)
+// abelian property: commutativity: f(a,b)=f(b,a)
+#define OP_COMM (1<<1)
+
+\class Numop < CObject
+struct Numop : CObject {
+ Ruby /*Symbol*/ sym;
+ const char *name;
+ int flags;
+//private:
+#define FOO(T) \
+ NumopOn<T> on_##T; \
+ NumopOn<T> *on(T &foo) { \
+ if (!on_##T.op_map) \
+ RAISE("operator %s does not support type "#T,rb_sym_name(sym)); \
+ return &on_##T;}
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+//public:
+ template <class T> inline void map(int n, Pt<T> as, T b) {
+ as.will_use(n);
+ on(*as)->op_map(n,(T *)as,b);}
+ template <class T> inline void zip(int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(n);
+ bs.will_use(n);
+ on(*as)->op_zip(n,(T *)as,(T *)bs);}
+ template <class T> inline void fold(int an, int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(an);
+ bs.will_use(an*n);
+ typename NumopOn<T>::Fold f = on(*as)->op_fold;
+ if (!f) RAISE("operator %s does not support fold",rb_sym_name(sym));
+ f(an,n,(T *)as,(T *)bs);}
+ template <class T> inline void scan(int an, int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(an);
+ bs.will_use(an*n);
+ typename NumopOn<T>::Scan f = on(*as)->op_scan;
+ if (!f) RAISE("operator %s does not support scan",rb_sym_name(sym));
+ f(an,n,(T *)as,(T *)bs);}
+
+ \decl void map_m (NumberTypeE nt, int n, String as, String b);
+ \decl void zip_m (NumberTypeE nt, int n, String as, String bs);
+ \decl void fold_m (NumberTypeE nt, int an, int n, String as, String bs);
+ \decl void scan_m (NumberTypeE nt, int an, int n, String as, String bs);
+
+ Numop(Ruby /*Symbol*/ sym_, const char *name_,
+#define FOO(T) NumopOn<T> op_##T,
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ int flags_) : sym(sym_), name(name_), flags(flags_) {
+#define FOO(T) on_##T = op_##T;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ }
+};
+\end class
+
+extern NumberType number_type_table[];
+extern Ruby number_type_dict; // GridFlow.@number_type_dict={}
+extern Ruby op_dict; // GridFlow.@op_dict={}
+
+static inline NumberTypeE convert(Ruby x, NumberTypeE *bogus) {
+ return NumberTypeE_find(x);
+}
+
+#ifndef IS_BRIDGE
+static Numop *convert(Ruby x, Numop **bogus) {
+ Ruby s = rb_hash_aref(rb_ivar_get(mGridFlow,SI(@op_dict)),x);
+ if (s==Qnil) RAISE("expected two-input-operator");
+ return FIX2PTR(Numop,s);
+}
+#endif
+
+// ****************************************************************
+\class Grid < CObject
+struct Grid : CObject {
+ P<Dim> dim;
+ NumberTypeE nt;
+ void *data;
+ void *rdata;
+ Grid(P<Dim> dim, NumberTypeE nt, bool clear=false) : dim(0), nt(int32_e), data(0) {
+ if (!dim) RAISE("hell");
+ init(dim,nt);
+ if (clear) {int size = bytes(); CLEAR(Pt<char>((char *)data,size),size);}
+ }
+ Grid(Ruby x) : dim(0), nt(int32_e), data(0) { init_from_ruby(x); }
+ Grid(int n, Ruby *a, NumberTypeE nt=int32_e) : dim(0), nt(int32_e), data(0) {
+ init_from_ruby_list(n,a,nt);
+ }
+ int32 bytes() { return dim->prod()*number_type_table[nt].size/8; }
+ P<Dim> to_dim () { return new Dim(dim->prod(),(Pt<int32>)*this); }
+#define FOO(type) \
+ operator type *() { return (type *)data; } \
+ operator Pt<type>() { return Pt<type>((type *)data,dim->prod()); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ Grid *dup () {
+ Grid *foo=new Grid(dim,nt);
+ memcpy(foo->data,data,bytes());
+ return foo;
+ }
+ ~Grid() {if (rdata) delete[] (uint8 *)rdata;}
+private:
+ void init(P<Dim> dim, NumberTypeE nt) {
+ this->dim = dim;
+ this->nt = nt;
+ rdata = dim ? new int64[1+(bytes()+7)/8] : 0;
+ int align = ((long)rdata) & 7;
+ data = (char *)rdata + ((8-align)&7);
+ //fprintf(stderr,"rdata=%p data=%p align=%d\n",rdata,data,align);
+ }
+ void init_from_ruby(Ruby x);
+ void init_from_ruby_list(int n, Ruby *a, NumberTypeE nt=int32_e);
+};
+\end class Grid
+
+static inline Grid *convert (Ruby r, Grid **bogus) {
+ return r ? new Grid(r) : 0;
+}
+
+// DimConstraint interface:
+// return if d is acceptable
+// else RAISE with proper descriptive message
+typedef void (*DimConstraint)(P<Dim> d);
+
+struct PtrGrid : public P<Grid> {
+ DimConstraint dc;
+ void constrain(DimConstraint dc_) { dc=dc_; }
+ P<Grid> next;
+ PtrGrid() : P<Grid>(), dc(0), next(0) {}
+ PtrGrid(const PtrGrid &_p) : P<Grid>(), dc(0), next(0) {dc=_p.dc; p=_p.p; INCR;}
+ PtrGrid &operator =( Grid *_p) {if(dc&&p)dc(_p->dim); DECR; p=_p; INCR; return *this;}
+ PtrGrid &operator =(P<Grid> _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+ PtrGrid &operator =(PtrGrid _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+};
+
+#ifndef IS_BRIDGE
+static inline P<Dim> convert(Ruby x, P<Dim> *foo) {
+ Grid *d = convert(x,(Grid **)0);
+ if (!d) RAISE("urgh");
+ if (d->dim->n!=1) RAISE("dimension list must have only one dimension itself");
+ return new Dim(d->dim->v[0],(int32 *)(d->data));
+}
+
+static inline PtrGrid convert(Ruby x, PtrGrid *foo) {
+ PtrGrid pg;
+ pg = convert(x,(Grid **)0);
+ return pg;
+}
+#endif
+
+//****************************************************************
+// GridInlet represents a grid-aware inlet
+
+// four-part macro for defining the behaviour of a gridinlet in a class
+// C:Class I:Inlet
+#define GRID_INLET(C,I) \
+ template <class T> void C::grinw_##I (GridInlet *in, int n, Pt<T> data) { \
+ ((C*)(in->parent))->grin_##I(in,n,data); } \
+ template <class T> void C::grin_##I (GridInlet *in, int n, Pt<T> data) { \
+ if (n==-1)
+#define GRID_ALLOC else if (n==-3)
+#define GRID_FLOW else if (n>=0)
+#define GRID_FINISH else if (n==-2)
+#define GRID_END }
+
+/* macro for defining a gridinlet's behaviour as just storage (no backstore) */
+// V is a PtrGrid instance-var
+#define GRID_INPUT(C,I,V) \
+GRID_INLET(C,I) { V=new Grid(in->dim,NumberTypeE_type_of(*data)); } \
+GRID_FLOW { COPY((Pt<T>)*(V)+in->dex, data, n); } GRID_FINISH
+
+// macro for defining a gridinlet's behaviour as just storage (with backstore)
+// V is a PtrGrid instance-var
+#define GRID_INPUT2(C,I,V) \
+ GRID_INLET(C,I) { \
+ if (is_busy_except(in)) { \
+ V.next = new Grid(in->dim,NumberTypeE_type_of(*data)); \
+ } else V= new Grid(in->dim,NumberTypeE_type_of(*data)); \
+ } GRID_FLOW { \
+ COPY(((Pt<T>)*(V.next?V.next.p:&*V.p))+in->dex, data, n); \
+ } GRID_FINISH
+
+typedef struct GridInlet GridInlet;
+typedef struct GridHandler {
+#define FOO(T) \
+ void (*flow_##T)(GridInlet *in, int n, Pt<T> data); \
+ void flow(GridInlet *in, int n, Pt<T> data) const { \
+ assert(flow_##T); flow_##T(in,n,data); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+} GridHandler;
+
+typedef struct GridObject GridObject;
+\class GridInlet < CObject
+struct GridInlet : CObject {
+ GridObject *parent;
+ const GridHandler *gh;
+ GridObject *sender;
+ P<Dim> dim;
+ NumberTypeE nt;
+ int dex;
+ PtrGrid buf;// factor-chunk buffer
+ int bufi; // buffer index: how much of buf is filled
+ int mode; // 0=ignore; 4=ro; 6=rw
+
+ int allocfactor,allocmin,allocmax,allocn;
+ Pt<uint8> alloc;
+
+// methods
+ GridInlet(GridObject *parent_, const GridHandler *gh_) :
+ parent(parent_), gh(gh_), sender(0),
+ dim(0), nt(int32_e), dex(0), bufi(0), mode(4) {}
+ ~GridInlet() {}
+ void set_factor(int factor);
+ void set_mode(int mode_) { mode=mode_; }
+ int32 factor() {return buf?buf->dim->prod():1;}
+ Ruby begin(int argc, Ruby *argv);
+
+ // n=-1 is begin, and n=-2 is _finish_. the name "end" is now used
+ // as an end-marker for inlet definitions... sorry for the confusion
+ // GF-0.8: n=-3 is alloc.
+ template <class T> void flow(int mode, int n, Pt<T> data);
+ void end(); // this one ought to be called finish().
+ void from_ruby_list(int argc, Ruby *argv, NumberTypeE nt=int32_e) {
+ Grid t(argc,argv,nt); from_grid(&t);
+ }
+ void from_ruby(int argc, Ruby *argv) {
+ Grid t(argv[0]); from_grid(&t);
+ }
+ void from_grid(Grid *g);
+ bool supports_type(NumberTypeE nt);
+private:
+ template <class T> void from_grid2(Grid *g, T foo);
+};
+\end class GridInlet
+
+//****************************************************************
+// for use by source_filter.rb ONLY (for \grin and \classinfo)
+
+// C is for class, I for inlet number
+// GRIN1 : int32 only
+// GRIN4 : all types
+// GRIN2 : integers only; no floats
+// GRINF : floats only; no integers
+#ifndef HAVE_LITE
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI,TL,TF,TD}
+#else
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI}
+#endif // HAVE_LITE
+
+#define GRIN1(C,I) GRIN(0,0,C::grinw_##I,0,0,0)
+#define GRIN4(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I)
+#define GRIN2(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,0,0)
+#define GRINF(C,I) GRIN(0,0,0,0,C::grinw_##I,C::grinw_##I)
+
+struct FClass { // 0.7.8: removed all GridObject-specific stuff.
+ void *(*allocator)(); // returns a new C++ object
+ void (*startup)(Ruby rself); // initializer for the Ruby class
+ const char *name; // C++/Ruby name (not PD name)
+ int methodsn; MethodDecl *methods; // C++ -> Ruby methods
+};
+
+//****************************************************************
+// GridOutlet represents a grid-aware outlet
+\class GridOutlet < CObject
+struct GridOutlet : CObject {
+// number of (minimum,maximum) BYTES to send at once
+// starting with version 0.8, this is amount of BYTES, not amount of NUMBERS.
+ static const int MIN_PACKET_SIZE = 1<<8;
+ static const int MAX_PACKET_SIZE = 1<<12;
+// those are set only once
+ GridObject *parent; // not a P<> because of circular refs
+ P<Dim> dim; // dimensions of the grid being sent
+ NumberTypeE nt;
+ PtrGrid buf; // temporary buffer
+ bool frozen; // is the "begin" phase finished?
+ std::vector<GridInlet *> inlets; // which inlets are we connected to
+// those are updated during transmission
+ int dex; // how many numbers were already sent in this connection
+ int bufi; // number of bytes used in the buffer
+// methods
+ GridOutlet(GridObject *parent_, int woutlet, P<Dim> dim_, NumberTypeE nt_=int32_e) :
+ parent(parent_), dim(dim_), nt(nt_), frozen(false), dex(0), bufi(0) {
+ int ntsz = number_type_table[nt].size;
+ buf=new Grid(new Dim(MAX_PACKET_SIZE/*/ntsz*/), nt);
+ begin(woutlet,dim,nt);
+ }
+ ~GridOutlet() {}
+ void callback(GridInlet *in);
+
+ // send/send_direct: data belongs to caller, may be stack-allocated,
+ // receiver doesn't modify the data; in send(), there is buffering;
+ // in send_direct(), there is not. When switching from buffered to
+ // unbuffered mode, flush() must be called
+ template <class T> void send(int n, Pt<T> data);
+ void flush(); // goes with send();
+
+ // give: data must be dynamically allocated as a whole: the data
+ // will be deleted eventually, and should not be used by the caller
+ // beyond the call to give().
+ template <class T> void give(int n, Pt<T> data);
+
+ // third way to send (upcoming, in GF-0.8.??) is called "ask".
+ template <class T> void ask(int &n, Pt<T> &data, int factor, int min, int max);
+
+private:
+ void begin(int woutlet, P<Dim> dim, NumberTypeE nt=int32_e);
+ template <class T> void send_direct(int n, Pt<T> data);
+ void end() {
+ flush();
+ for (uint32 i=0; i<inlets.size(); i++) inlets[i]->end();
+ dim=0;
+ }
+};
+\end class GridOutlet
+
+//****************************************************************
+
+typedef struct BFObject BFObject; // Pd t_object or something
+
+// represents objects that have inlets/outlets
+\class FObject < CObject
+struct FObject : CObject {
+ BFObject *bself; // point to PD peer
+ uint64 total_time;
+ FObject() : bself(0), total_time(0) {}
+ const char *args() {
+ Ruby s=rb_funcall(rself,SI(args),0);
+ if (s==Qnil) return 0;
+ return rb_str_ptr(s);
+ }
+ \decl Ruby total_time_get();
+ \decl Ruby total_time_set(Ruby x);
+ \decl void send_in (...);
+ \decl void send_out (...);
+ \decl void delete_m ();
+};
+\end class FObject
+
+\class GridObject < FObject
+struct GridObject : FObject {
+ std::vector<P<GridInlet> > in;
+ P<GridOutlet> out;
+ // Make sure you distinguish #close/#delete, and C++'s delete. The first
+ // two are quite equivalent and should never make an object "crashable".
+ // C++'s delete is called by Ruby's garbage collector or by PureData's delete.
+ GridObject() {}
+ ~GridObject() {check_magic();}
+ bool is_busy_except(P<GridInlet> gin) {
+ for (uint32 i=0; i<in.size(); i++)
+ if (in[i] && in[i]!=gin && in[i]->dim) return true;
+ return false;
+ }
+ \decl Ruby method_missing(...);
+ \decl Array inlet_dim(int inln);
+ \decl Symbol inlet_nt(int inln);
+ \decl void inlet_set_factor(int inln, int factor);
+ \decl void send_out_grid_begin(int outlet, Array dim, NumberTypeE nt=int32_e);
+ \decl void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e);
+};
+\end class GridObject
+
+uint64 gf_timeofday();
+extern "C" void Init_gridflow ();
+void gfpost(const char *fmt, ...);
+extern Numop *op_add,*op_sub,*op_mul,*op_div,*op_mod,*op_shl,*op_and,*op_put;
+
+#define INFO(OBJ) rb_str_ptr(rb_funcall(OBJ->rself,SI(info),0))
+//#define INFO(OBJ) "(bleh)"
+#define NOTEMPTY(_a_) if (!(_a_)) RAISE("in [%s], '%s' is empty",INFO(this), #_a_);
+#define SAME_TYPE(_a_,_b_) \
+ if ((_a_)->nt != (_b_)->nt) RAISE("%s: same type please (%s has %s; %s has %s)", \
+ INFO(this), \
+ #_a_, number_type_table[(_a_)->nt].name, \
+ #_b_, number_type_table[(_b_)->nt].name);
+static void SAME_DIM(int n, P<Dim> a, int ai, P<Dim> b, int bi) {
+ if (ai+n > a->n) RAISE("left hand: not enough dimensions");
+ if (bi+n > b->n) RAISE("right hand: not enough dimensions");
+ for (int i=0; i<n; i++) {
+ if (a->v[ai+i] != b->v[bi+i]) {
+ RAISE("mismatch: left dim #%d is %d, right dim #%d is %d",
+ ai+i, a->v[ai+i],
+ bi+i, b->v[bi+i]);}}}
+
+// a stack for the profiler, etc.
+#define GF_STACK_MAX 256
+struct GFStack {
+ struct GFStackFrame {
+ FObject *o;
+ void *bp; // a pointer into system stack
+ uint64 time;
+ }; // sizeof() == 16 (in 32-bit mode)
+ GFStackFrame s[GF_STACK_MAX];
+ int n;
+ GFStack() { n = 0; }
+ void push (FObject *o) __attribute__((noinline));
+ void pop () __attribute__((noinline));
+};
+extern GFStack gf_stack;
+struct GFStackMarker {
+ int n;
+ bool flag;
+ GFStackMarker(FObject *o) { n = gf_stack.n; gf_stack.push(o); flag=true; }
+ ~GFStackMarker() { while (gf_stack.n != n) gf_stack.pop(); }
+ bool once () {
+ if (flag) { flag=false; return true; } else return false;
+ }
+};
+
+typedef GridObject Format;
+
+#endif // __GF_GRID_H
diff --git a/externals/gridflow/base/main.c b/externals/gridflow/base/main.c
new file mode 100644
index 00000000..5cdd92f4
--- /dev/null
+++ b/externals/gridflow/base/main.c
@@ -0,0 +1,647 @@
+/*
+ $Id: main.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "grid.h.fcs"
+#include "../config.h"
+#include <assert.h>
+#include <limits.h>
+
+BuiltinSymbols bsym;
+GFStack gf_stack;
+Ruby mGridFlow;
+Ruby cFObject;
+
+extern "C"{
+void rb_raise0(
+const char *file, int line, const char *func, VALUE exc, const char *fmt, ...) {
+ va_list args;
+ char buf[BUFSIZ];
+ va_start(args,fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ buf[BUFSIZ-1]=0;
+ va_end(args);
+ VALUE e = rb_exc_new2(exc, buf);
+ char buf2[BUFSIZ];
+ snprintf(buf2, BUFSIZ, "%s:%d:in `%s'", file, line, func);
+ buf2[BUFSIZ-1]=0;
+ VALUE ary = rb_funcall(e,SI(caller),0);
+ if (gf_stack.n) {
+ rb_funcall(ary,SI(unshift),2,rb_str_new2(buf2),
+ rb_str_new2(INFO(gf_stack.s[gf_stack.n-1].o)));
+ } else {
+ rb_funcall(ary,SI(unshift),1,rb_str_new2(buf2));
+ }
+ rb_funcall(e,SI(set_backtrace),1,ary);
+ rb_exc_raise(e);
+}};
+
+Ruby rb_ary_fetch(Ruby rself, int i) {
+ Ruby argv[] = { INT2NUM(i) };
+ return rb_ary_aref(COUNT(argv),argv,rself);
+}
+
+//----------------------------------------------------------------
+// CObject
+
+static void CObject_mark (void *z) {}
+void CObject_free (void *foo) {
+ CObject *self = (CObject *)foo;
+ self->check_magic();
+ if (!self->rself) {
+ fprintf(stderr,"attempt to free object that has no rself\n");
+ abort();
+ }
+ self->rself = 0; /* paranoia */
+ delete self;
+}
+
+//----------------------------------------------------------------
+// Dim
+
+void Dim::check() {
+ if (n>MAX_DIMENSIONS) RAISE("too many dimensions");
+ for (int i=0; i<n; i++) if (v[i]<0) RAISE("Dim: negative dimension");
+}
+
+// !@#$ big leak machine?
+// returns a string like "Dim[240,320,3]"
+char *Dim::to_s() {
+ // if you blow 256 chars it's your own fault
+ char buf[256];
+ char *s = buf;
+ s += sprintf(s,"Dim[");
+ for(int i=0; i<n; i++) s += sprintf(s,"%s%d", ","+!i, v[i]);
+ s += sprintf(s,"]");
+ return strdup(buf);
+}
+
+//----------------------------------------------------------------
+\class FObject < CObject
+
+static void FObject_prepare_message(int &argc, Ruby *&argv, Ruby &sym, FObject *foo=0) {
+ if (argc<1) {
+ sym = bsym._bang;
+ } else if (argc>1 && !SYMBOL_P(*argv)) {
+ sym = bsym._list;
+ } else if (INTEGER_P(*argv)||FLOAT_P(*argv)) {
+ sym = bsym._float;
+ } else if (SYMBOL_P(*argv)) {
+ sym = *argv;
+ argc--, argv++;
+ } else if (argc==1 && TYPE(*argv)==T_ARRAY) {
+ sym = bsym._list;
+ argc = rb_ary_len(*argv);
+ argv = rb_ary_ptr(*argv);
+ } else {
+ RAISE("%s received bad message: argc=%d; argv[0]=%s",foo?INFO(foo):"", argc,
+ argc ? rb_str_ptr(rb_inspect(argv[0])) : "");
+ }
+}
+
+struct Helper {
+ int argc;
+ Ruby *argv;
+ FObject *self;
+ Ruby rself;
+ int n; // stack level
+};
+
+static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv);
+
+// inlet #-1 is reserved for SystemInlet messages
+// inlet #-2 is for inlet #0 messages that happen at start time
+static void send_in_2 (Helper *h) { PROF(h->self) {
+ int argc = h->argc;
+ Ruby *argv = h->argv;
+ if (h->argc<1) RAISE("not enough args");
+ int inlet = INT(argv[0]);
+ argc--, argv++;
+ Ruby foo;
+ if (argc==1 && TYPE(argv[0])==T_STRING /* && argv[0] =~ / / */) {
+ foo = rb_funcall(mGridFlow,SI(parse),1,argv[0]);
+ argc = rb_ary_len(foo);
+ argv = rb_ary_ptr(foo);
+ }
+ if (argc>1) {
+ foo = rb_ary_new4(argc,argv);
+ GridFlow_handle_braces(0,foo);
+ argc = rb_ary_len(foo);
+ argv = rb_ary_ptr(foo);
+ }
+ if (inlet==-2) {
+ Array init_messages = rb_ivar_get(h->rself,SI(@init_messages));
+ rb_ary_push(init_messages, rb_ary_new4(argc,argv));
+ inlet=0;
+ }
+ if (inlet<0 || inlet>9 /*|| inlet>real_inlet_max*/)
+ if (inlet!=-3 && inlet!=-1) RAISE("invalid inlet number: %d", inlet);
+ Ruby sym;
+ FObject_prepare_message(argc,argv,sym,h->self);
+// if (rb_const_get(mGridFlow,SI(@verbose))==Qtrue) gfpost m.inspect
+ char buf[256];
+ if (inlet==-1) sprintf(buf,"_sys_%s",rb_sym_name(sym));
+ else sprintf(buf,"_%d_%s",inlet,rb_sym_name(sym));
+ rb_funcall2(h->rself,rb_intern(buf),argc,argv);
+} /* PROF */ }
+
+static void send_in_3 (Helper *h) {
+ while (gf_stack.n > h->n) gf_stack.pop();
+}
+
+\def void send_in (...) {
+ Helper h = {argc,argv,this,rself,gf_stack.n};
+ rb_ensure(
+ (RMethod)send_in_2,(Ruby)&h,
+ (RMethod)send_in_3,(Ruby)&h);
+}
+
+\def void send_out (...) {
+ int n=0;
+ if (argc<1) RAISE("not enough args");
+ int outlet = INT(*argv);
+ if (outlet<0 || outlet>9 /*|| outlet>real_outlet_max*/)
+ RAISE("invalid outlet number: %d",outlet);
+ argc--, argv++;
+ Ruby sym;
+ FObject_prepare_message(argc,argv,sym,this);
+ Ruby noutlets2 = rb_ivar_get(rb_obj_class(rself),SYM2ID(SYM(@noutlets)));
+ if (TYPE(noutlets2)!=T_FIXNUM) {
+ IEVAL(rself,"STDERR.puts inspect");
+ RAISE("don't know how many outlets this has");
+ }
+ int noutlets = INT(noutlets2);
+ //if (outlet<0 || outlet>=noutlets) RAISE("outlet %d does not exist",outlet);
+ // was PROF(0) a hack because of exception-handling problems?
+ PROF(0) {
+ Ruby argv2[argc+2];
+ for (int i=0; i<argc; i++) argv2[2+i] = argv[i];
+ argv2[0] = INT2NUM(outlet);
+ argv2[1] = sym;
+ rb_funcall2(rself,SI(send_out2), argc+2, argv2);
+
+ Ruby ary = rb_ivar_defined(rself,SYM2ID(bsym.iv_outlets)) ?
+ rb_ivar_get(rself,SYM2ID(bsym.iv_outlets)) : Qnil;
+ if (ary==Qnil) goto end;
+ if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array");
+ ary = rb_ary_fetch(ary,outlet);
+ if (ary==Qnil) goto end;
+ if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array");
+ n = rb_ary_len(ary);
+
+ for (int i=0; i<n; i++) {
+ Ruby conn = rb_ary_fetch(ary,i);
+ Ruby rec = rb_ary_fetch(conn,0);
+ int inl = INT(rb_ary_fetch(conn,1));
+ argv2[0] = INT2NUM(inl);
+ rb_funcall2(rec,SI(send_in),argc+2,argv2);
+ }
+ } /* PROF */
+end:;
+}
+
+Ruby FObject_s_new(Ruby argc, Ruby *argv, Ruby qlass) {
+ Ruby allocator = rb_ivar_defined(qlass,SI(@allocator)) ?
+ rb_ivar_get(qlass,SI(@allocator)) : Qnil;
+ FObject *self;
+ if (allocator==Qnil) {
+ // this is a pure-ruby FObject/GridObject
+ // !@#$ GridObject is in FObject constructor (ugly)
+ self = new GridObject;
+ } else {
+ // this is a C++ FObject/GridObject
+ void*(*alloc)() = (void*(*)())FIX2PTR(void,allocator);
+ self = (FObject *)alloc();
+ }
+ self->check_magic();
+ Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects));
+ self->bself = 0;
+ Ruby rself = Data_Wrap_Struct(qlass, CObject_mark, CObject_free, self);
+ self->rself = rself;
+ rb_hash_aset(keep,rself,Qtrue); // prevent sweeping
+ rb_funcall2(rself,SI(initialize),argc,argv);
+ return rself;
+}
+
+Ruby FObject_s_install(Ruby rself, Ruby name, Ruby inlets2, Ruby outlets2) {
+ int inlets, outlets;
+ Ruby name2;
+ if (SYMBOL_P(name)) {
+ name2 = rb_funcall(name,SI(to_str),0);
+ } else if (TYPE(name) == T_STRING) {
+ name2 = rb_funcall(name,SI(dup),0);
+ } else {
+ RAISE("expect symbol or string");
+ }
+ inlets = INT(inlets2); if ( inlets<0 || inlets>9) RAISE("...");
+ outlets = INT(outlets2); if (outlets<0 || outlets>9) RAISE("...");
+ rb_ivar_set(rself,SI(@ninlets),INT2NUM(inlets));
+ rb_ivar_set(rself,SI(@noutlets),INT2NUM(outlets));
+ rb_ivar_set(rself,SI(@foreign_name),name2);
+ rb_hash_aset(rb_ivar_get(mGridFlow,SI(@fclasses)), name2, rself);
+ rb_funcall(rself, SI(install2), 1, name2);
+ return Qnil;
+}
+
+\def Ruby total_time_get () {return gf_ull2num(total_time);}
+
+\def Ruby total_time_set (Ruby x) {
+ if (argc<1) RAISE("muh");
+ total_time = TO(uint64,x);
+ return argv[0];
+}
+
+\def void delete_m () {
+ Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects));
+ rb_funcall(keep,SI(delete),1,rself);
+}
+
+\classinfo
+\end class FObject
+
+/* ---------------------------------------------------------------- */
+/* C++<->Ruby bridge for classes/functions in base/number.c */
+
+static Ruby String_swap32_f (Ruby rself) {
+ int n = rb_str_len(rself)/4;
+ swap32(n,Pt<uint32>((uint32 *)rb_str_ptr(rself),n));
+ return rself;
+}
+
+static Ruby String_swap16_f (Ruby rself) {
+ int n = rb_str_len(rself)/2;
+ swap16(n,Pt<uint16>((uint16 *)rb_str_ptr(rself),n));
+ return rself;
+}
+
+NumberTypeE NumberTypeE_find (Ruby sym) {
+ if (TYPE(sym)!=T_SYMBOL) RAISE("expected symbol (not %s)",
+ rb_str_ptr(rb_inspect(rb_obj_class(sym))));
+ Ruby nt_dict = rb_ivar_get(mGridFlow,SI(@number_type_dict));
+ Ruby v = rb_hash_aref(nt_dict,sym);
+ if (v!=Qnil) return FIX2PTR(NumberType,v)->index;
+ RAISE("unknown number type \"%s\"", rb_sym_name(sym));
+}
+
+/* **************************************************************** */
+\class BitPacking < CObject
+
+\def void initialize(Ruby foo1, Ruby foo2, Ruby foo3) {}
+
+// !@#$ doesn't support number types
+\def String pack2 (String ins, String outs=Qnil) {
+ int n = rb_str_len(ins) / sizeof(int32) / size;
+ Pt<int32> in = Pt<int32>((int32 *)rb_str_ptr(ins),rb_str_len(ins));
+ int bytes2 = n*bytes;
+ Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2);
+ rb_str_modify(out);
+ pack(n,Pt<int32>(in,n),Pt<uint8>((uint8 *)rb_str_ptr(out),bytes2));
+ return out;
+}
+
+// !@#$ doesn't support number types
+\def String unpack2 (String ins, String outs=Qnil) {
+ int n = rb_str_len(argv[0]) / bytes;
+ Pt<uint8> in = Pt<uint8>((uint8 *)rb_str_ptr(ins),rb_str_len(ins));
+ int bytes2 = n*size*sizeof(int32);
+ Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2);
+ rb_str_modify(out);
+ unpack(n,Pt<uint8>((uint8 *)in,bytes2),Pt<int32>((int32 *)rb_str_ptr(out),n));
+ return out;
+}
+
+static Ruby BitPacking_s_new(Ruby argc, Ruby *argv, Ruby qlass) {
+ Ruby keep = rb_ivar_get(mGridFlow, rb_intern("@fobjects"));
+ if (argc!=3) RAISE("bad args");
+ if (TYPE(argv[2])!=T_ARRAY) RAISE("bad mask");
+ int endian = INT(argv[0]);
+ int bytes = INT(argv[1]);
+ Ruby *masks = rb_ary_ptr(argv[2]);
+ uint32 masks2[4];
+ int size = rb_ary_len(argv[2]);
+ if (size<1) RAISE("not enough masks");
+ if (size>4) RAISE("too many masks (%d)",size);
+ for (int i=0; i<size; i++) masks2[i] = NUM2UINT(masks[i]);
+ BitPacking *self = new BitPacking(endian,bytes,size,masks2);
+ Ruby rself = Data_Wrap_Struct(qlass, 0, CObject_free, self);
+ self->rself = rself;
+ rb_hash_aset(keep,rself,Qtrue); // prevent sweeping (leak) (!@#$ WHAT???)
+ rb_funcall2(rself,SI(initialize),argc,argv);
+ return rself;
+}
+
+\classinfo
+\end class BitPacking
+
+void gfpost(const char *fmt, ...) {
+ va_list args;
+ int length;
+ va_start(args,fmt);
+ const int n=256;
+ char post_s[n];
+ length = vsnprintf(post_s,n,fmt,args);
+ if (length<0 || length>=n) sprintf(post_s+n-6,"[...]"); /* safety */
+ va_end(args);
+ rb_funcall(mGridFlow,SI(gfpost2),2,rb_str_new2(fmt),rb_str_new2(post_s));
+}
+
+void define_many_methods(Ruby rself, int n, MethodDecl *methods) {
+ for (int i=0; i<n; i++) {
+ MethodDecl *md = &methods[i];
+ char *buf = strdup(md->selector);
+ if (strlen(buf)>2 && strcmp(buf+strlen(buf)-2,"_m")==0)
+ buf[strlen(buf)-2]=0;
+ rb_define_method(rself,buf,(RMethod)md->method,-1);
+ rb_enable_super(rself,buf);
+ free(buf);
+ }
+}
+
+static Ruby GridFlow_fclass_install(Ruby rself_, Ruby fc_, Ruby super) {
+ FClass *fc = FIX2PTR(FClass,fc_);
+ Ruby rself = super!=Qnil ?
+ rb_define_class_under(mGridFlow, fc->name, super) :
+ rb_funcall(mGridFlow,SI(const_get),1,rb_str_new2(fc->name));
+ define_many_methods(rself,fc->methodsn,fc->methods);
+ rb_ivar_set(rself,SI(@allocator),PTR2FIX((void*)(fc->allocator))); //#!@$??
+ if (fc->startup) fc->startup(rself);
+ return Qnil;
+}
+
+//----------------------------------------------------------------
+// GridFlow.class
+//\class GridFlow_s < patate
+
+typedef void (*Callback)(void*);
+static Ruby GridFlow_exec (Ruby rself, Ruby data, Ruby func) {
+ void *data2 = FIX2PTR(void,data);
+ Callback func2 = (Callback) FIX2PTR(void,func);
+ func2(data2);
+ return Qnil;
+}
+
+static Ruby GridFlow_get_id (Ruby rself, Ruby arg) {
+ fprintf(stderr,"%ld\n",arg);
+ return INT2NUM((int)arg);
+}
+
+Ruby GridFlow_rdtsc (Ruby rself) { return gf_ull2num(rdtsc()); }
+
+/* This code handles nested lists because PureData (0.38) doesn't do it */
+static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv) {
+ int stack[16];
+ int stackn=0;
+ Ruby *av = rb_ary_ptr(argv);
+ int ac = rb_ary_len(argv);
+ int j=0;
+ for (int i=0; i<ac; ) {
+ int close=0;
+ if (SYMBOL_P(av[i])) {
+ const char *s = rb_sym_name(av[i]);
+ while (*s=='(' || *s=='{') {
+ if (stackn==16) RAISE("too many nested lists (>16)");
+ stack[stackn++]=j;
+ s++;
+ }
+ const char *se = s+strlen(s);
+ while (se[-1]==')' || se[-1]=='}') { se--; close++; }
+ if (s!=se) {
+ Ruby u = rb_str_new(s,se-s);
+ av[j++] = rb_funcall(rself,SI(FloatOrSymbol),1,u);
+ }
+ } else {
+ av[j++]=av[i];
+ }
+ i++;
+ while (close--) {
+ if (!stackn) RAISE("unbalanced '}' or ')'",av[i]);
+ Ruby a2 = rb_ary_new();
+ int j2 = stack[--stackn];
+ for (int k=j2; k<j; k++) rb_ary_push(a2,av[k]);
+ j=j2;
+ av[j++] = a2;
+ }
+ }
+ if (stackn) RAISE("unbalanced '{' or '(' (stackn=%d)",stackn);
+ RARRAY(argv)->len = j;
+ return rself;
+}
+
+/* ---------------------------------------------------------------- */
+
+static uint32 memcpy_calls = 0;
+static uint64 memcpy_bytes = 0;
+static uint64 memcpy_time = 0;
+static uint32 malloc_calls = 0; /* only new not delete */
+static uint64 malloc_bytes = 0; /* only new not delete */
+static uint64 malloc_time = 0; /* in cpu ticks */
+
+// don't touch.
+static void gfmemcopy32(int32 *as, int32 *bs, int n) {
+ int32 ba = bs-as;
+#define FOO(I) as[I] = (as+ba)[I];
+ UNROLL_8(FOO,n,as)
+#undef FOO
+
+}
+
+void gfmemcopy(uint8 *out, const uint8 *in, int n) {
+ uint64 t = rdtsc();
+ memcpy_calls++;
+ memcpy_bytes+=n;
+ for (; n>16; in+=16, out+=16, n-=16) {
+ ((int32*)out)[0] = ((int32*)in)[0];
+ ((int32*)out)[1] = ((int32*)in)[1];
+ ((int32*)out)[2] = ((int32*)in)[2];
+ ((int32*)out)[3] = ((int32*)in)[3];
+ }
+ for (; n>4; in+=4, out+=4, n-=4) { *(int32*)out = *(int32*)in; }
+ for (; n; in++, out++, n--) { *out = *in; }
+ t=rdtsc()-t;
+ memcpy_time+=t;
+}
+
+extern "C" {
+void *gfmalloc(size_t n) {
+ uint64 t = rdtsc();
+ void *p = malloc(n);
+ long align = (long)p & 7;
+ if (align) fprintf(stderr,"malloc alignment = %ld mod 8\n",align);
+ t=rdtsc()-t;
+ malloc_time+=t;
+ malloc_calls++;
+ malloc_bytes+=n;
+ return p;
+}
+void gffree(void *p) {
+ uint64 t = rdtsc();
+ free(p);
+ t=rdtsc()-t;
+ malloc_time+=t;
+}};
+
+Ruby GridFlow_memcpy_calls (Ruby rself) { return LONG2NUM(memcpy_calls); }
+Ruby GridFlow_memcpy_bytes (Ruby rself) { return gf_ull2num(memcpy_bytes); }
+Ruby GridFlow_memcpy_time (Ruby rself) { return gf_ull2num(memcpy_time); }
+Ruby GridFlow_malloc_calls (Ruby rself) { return LONG2NUM(malloc_calls); }
+Ruby GridFlow_malloc_bytes (Ruby rself) { return gf_ull2num(malloc_bytes); }
+Ruby GridFlow_malloc_time (Ruby rself) { return gf_ull2num(malloc_time); }
+
+Ruby GridFlow_profiler_reset2 (Ruby rself) {
+ memcpy_calls = memcpy_bytes = memcpy_time = 0;
+ malloc_calls = malloc_bytes = malloc_time = 0;
+ return Qnil;
+}
+
+/* ---------------------------------------------------------------- */
+
+void startup_number();
+void startup_grid();
+void startup_flow_objects();
+void startup_flow_objects_for_image();
+void startup_flow_objects_for_matrix();
+
+Ruby cFormat;
+
+#define SDEF(_class_,_name_,_argc_) \
+ rb_define_singleton_method(c##_class_,#_name_,(RMethod)_class_##_s_##_name_,_argc_)
+#define SDEF2(_name1_,_name2_,_argc_) \
+ rb_define_singleton_method(mGridFlow,_name1_,(RMethod)_name2_,_argc_)
+
+STARTUP_LIST(void)
+
+// Ruby's entrypoint.
+void Init_gridflow () {
+#define FOO(_sym_,_name_) bsym._sym_ = ID2SYM(rb_intern(_name_));
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+ signal(11,SIG_DFL); // paranoia
+ mGridFlow = EVAL("module GridFlow; CObject = ::Object; "
+ "class<<self; attr_reader :bridge_name; end; "
+ "def post_string(s) STDERR.puts s end; "
+ "self end");
+ SDEF2("exec",GridFlow_exec,2);
+ SDEF2("get_id",GridFlow_get_id,1);
+ SDEF2("rdtsc",GridFlow_rdtsc,0);
+ SDEF2("profiler_reset2",GridFlow_profiler_reset2,0);
+ SDEF2("memcpy_calls",GridFlow_memcpy_calls,0);
+ SDEF2("memcpy_bytes",GridFlow_memcpy_bytes,0);
+ SDEF2("memcpy_time", GridFlow_memcpy_time,0);
+ SDEF2("malloc_calls",GridFlow_malloc_calls,0);
+ SDEF2("malloc_bytes",GridFlow_malloc_bytes,0);
+ SDEF2("malloc_time", GridFlow_malloc_time,0);
+ SDEF2("handle_braces!",GridFlow_handle_braces,1);
+ SDEF2("fclass_install",GridFlow_fclass_install,2);
+
+//#define FOO(A) fprintf(stderr,"sizeof("#A")=%d\n",sizeof(A));
+//FOO(Dim) FOO(BitPacking) FOO(GridHandler) FOO(GridInlet) FOO(GridOutlet) FOO(GridObject)
+//#undef FOO
+
+ rb_ivar_set(mGridFlow, SI(@fobjects), rb_hash_new());
+ rb_ivar_set(mGridFlow, SI(@fclasses), rb_hash_new());
+ rb_ivar_set(mGridFlow, SI(@bsym), PTR2FIX(&bsym));
+ rb_define_const(mGridFlow, "GF_VERSION", rb_str_new2(GF_VERSION));
+ rb_define_const(mGridFlow, "GF_COMPILE_TIME", rb_str_new2(GF_COMPILE_TIME));
+
+ cFObject = rb_define_class_under(mGridFlow, "FObject", rb_cObject);
+ EVAL(
+\ruby
+ module GridFlow
+ class FObject
+ def send_out2(*) end
+ def self.install2(*) end
+ def self.add_creator(name)
+ name=name.to_str.dup
+ GridFlow.fclasses[name]=self
+ GridFlow.add_creator_2 name end
+ end
+ end
+\end ruby
+);
+ define_many_methods(cFObject,COUNT(FObject_methods),FObject_methods);
+ SDEF(FObject, install, 3);
+ SDEF(FObject, new, -1);
+ ID gbi = SI(gf_bridge_init);
+ if (rb_respond_to(rb_cData,gbi)) rb_funcall(rb_cData,gbi,0);
+ Ruby cBitPacking =
+ rb_define_class_under(mGridFlow, "BitPacking", rb_cObject);
+ define_many_methods(cBitPacking,
+ ciBitPacking.methodsn,
+ ciBitPacking.methods);
+ SDEF(BitPacking,new,-1);
+ rb_define_method(rb_cString, "swap32!", (RMethod)String_swap32_f, 0);
+ rb_define_method(rb_cString, "swap16!", (RMethod)String_swap16_f, 0);
+
+ startup_number();
+ startup_grid();
+ startup_flow_objects();
+ startup_flow_objects_for_image();
+ startup_flow_objects_for_matrix();
+ if (!EVAL("begin require 'gridflow/base/main.rb'; true\n"
+ "rescue Exception => e; "
+ "STDERR.puts \"can't load: #{$!}\n"
+ "backtrace: #{$!.backtrace.join\"\n\"}\n"
+ "$: = #{$:.inspect}\"\n; false end")) return;
+ cFormat = EVAL("GridFlow::Format");
+ STARTUP_LIST()
+ EVAL("h=GridFlow.fclasses; h['#io:window'] = h['#io:quartz']||h['#io:x11']||h['#io:sdl']");
+ EVAL("GridFlow.load_user_config");
+ signal(11,SIG_DFL); // paranoia
+}
+
+void GFStack::push (FObject *o) {
+ void *bp = &o; // really. just finding our position on the stack.
+ if (n>=GF_STACK_MAX)
+ RAISE("stack overflow (maximum %d FObject activations at once)", GF_STACK_MAX);
+ uint64 t = rdtsc();
+ if (n) s[n-1].time = t - s[n-1].time;
+ s[n].o = o;
+ s[n].bp = bp;
+ s[n].time = t;
+ n++;
+}
+
+void GFStack::pop () {
+ uint64 t = rdtsc();
+ if (!n) RAISE("stack underflow (WHAT?)");
+ n--;
+ if (s[n].o) s[n].o->total_time += t - s[n].time;
+ if (n) s[n-1].time = t - s[n-1].time;
+}
+
+uint64 gf_timeofday () {
+ timeval t;
+ gettimeofday(&t,0);
+ return t.tv_sec*1000000+t.tv_usec;
+}
diff --git a/externals/gridflow/base/main.rb b/externals/gridflow/base/main.rb
new file mode 100644
index 00000000..82976fb6
--- /dev/null
+++ b/externals/gridflow/base/main.rb
@@ -0,0 +1,369 @@
+=begin
+ $Id: main.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+# ENV["RUBY_VERBOSE_GC"]="yes"
+
+# this file gets loaded by main.c upon startup
+# module GridFlow is supposed to be created by main.c
+# this includes GridFlow.post_string(s)
+
+# because Ruby1.6 has no #object_id and Ruby1.8 warns on #id
+unless Object.instance_methods(true).include? "object_id"
+ class Object; alias object_id id end
+end
+
+# in case of bug in Ruby ("Error: Success")
+module Errno; class E000 < StandardError; end; end
+
+#$post_log = File.open "/tmp/gridflow.log", "w"
+$post_log = nil
+
+class Array
+ def split(elem)
+ r=[]
+ j=0
+ for i in 0...length
+ (r<<self[j,i-j]; j=i+1) if self[i]==elem
+ end
+ r<<self[j,length-j]
+ end
+end
+
+module GridFlow #------------------
+
+def self.post(s,*a)
+ post_string(sprintf("%s"+s,post_header,*a))
+ ($post_log << sprintf(s,*a); $post_log.flush) if $post_log
+end
+
+class<<self
+ attr_accessor :data_path
+ attr_accessor :post_header
+ attr_accessor :verbose
+ attr_reader :fobjects
+ attr_reader :fclasses
+ attr_reader :cpu_hertz
+ attr_reader :subprocesses
+ attr_reader :bridge_name
+ alias gfpost post
+end
+
+@subprocesses={}
+@verbose=false
+@data_path=[]
+if GridFlow.respond_to? :config then
+ @data_path << GridFlow.config["PUREDATA_PATH"]+"/extra/gridflow/images"
+end
+
+def self.hunt_zombies
+ #STDERR.puts "GridFlow.hunt_zombies"
+ # the $$ value is bogus
+ begin
+ died = []
+ subprocesses.each {|x,v|
+ Process.waitpid2(x,Process::WNOHANG) and died<<x
+ }
+ rescue Errno::ECHILD
+ end
+ #STDERR.puts died.inspect
+ died.each {|x| subprocesses.delete x }
+end
+
+def self.packstring_for_nt(nt)
+ case nt
+ when :u, :u8, :uint8; "C*"
+ when :s, :i16, :int16; "s*"
+ when :i, :i32, :int32; "l*"
+ when :f, :f32, :float32; "f*"
+ when :d, :f64, :float64; "d*"
+ else raise "no decoder for #{nt.inspect}"
+ end
+end
+
+self.post_header = "[gf] "
+
+def self.gfpost2(fmt,s); post("%s",s) end
+
+if GridFlow.bridge_name then
+ post "This is GridFlow #{GridFlow::GF_VERSION} within Ruby version #{RUBY_VERSION}"
+ post "base/main.c was compiled on #{GridFlow::GF_COMPILE_TIME}"
+ post "Please use at least 1.6.6 if you plan to use sockets" if RUBY_VERSION<"1.6.6"
+end
+
+if not GridFlow.bridge_name then
+ require "gridflow/bridge/placebo"
+end
+
+Brace1 = "{".intern
+Brace2 = "}".intern
+Paren1 = "(".intern
+Paren2 = ")".intern
+
+def self.parse(m)
+ m = m.gsub(/(\{|\})/," \\1 ").split(/\s+/)
+ m.map! {|x| case x
+ when Integer, Symbol; x
+ when /^[+\-]?[0-9]+$/; x.to_i
+ when String; x.intern
+ end
+ }
+ m
+end
+
+def self.stringify_list(argv)
+ argv.map {|x| stringify x }.join(" ")
+end
+
+def self.stringify(arg)
+ case arg
+ when Integer, Float, Symbol; arg.to_s
+ when Array; "{#{stringify_list arg}}"
+ end
+end
+
+::Object.module_eval do def FloatOrSymbol(x) Float(x) rescue x.intern end end
+
+# adding some functionality to that:
+class FObject
+ @broken_ok = false
+ @do_loadbangs = true
+ class<<self
+ # global
+ attr_accessor :broken_ok
+ # per-class
+ attr_reader :ninlets
+ attr_reader :noutlets
+ attr_accessor :do_loadbangs
+ attr_accessor :comment
+ def foreign_name; @foreign_name if defined? @foreign_name end
+ end
+ def post(*a) GridFlow.post(*a) end
+ def self.subclass(*args,&b)
+ qlass = Class.new self
+ qlass.install(*args)
+ qlass.module_eval(&b)
+ end
+ alias :total_time :total_time_get
+ alias :total_time= :total_time_set
+ attr_writer :args # String
+ attr_accessor :argv # Array
+ attr_reader :outlets
+ attr_accessor :parent_patcher
+ attr_accessor :properties
+ attr_accessor :classname
+ def initialize2; end
+ def args
+ if defined? @args
+ @args
+ else
+ "[#{self.class} ...]"
+ end
+ end
+ alias info args
+ def connect outlet, object, inlet
+ @outlets ||= []
+ @outlets[outlet] ||= []
+ @outlets[outlet].push [object, inlet]
+ end
+ def self.name_lookup sym
+ qlasses = GridFlow.fclasses
+ qlass = qlasses[sym.to_s]
+ if not qlass
+ return qlasses['broken'] if @broken_ok
+ raise "object class '#{sym}' not found"
+ end
+ qlass
+ end
+ def self.[](*m)
+ o=nil
+ if m.length==1 and m[0] =~ / /
+ o="[#{m[0]}]"
+ m=GridFlow.parse(m[0])
+ else
+ o=m.inspect
+ end
+ GridFlow.handle_braces!(m)
+ ms = m.split ','.intern
+ m = ms.shift
+ qlass = m.shift
+ qlassname = qlass.to_s
+ qlass = name_lookup qlass.to_s unless Class===qlass
+ r = qlass.new(*m)
+ r.classname = qlassname
+ GridFlow.post "%s",r.args if GridFlow.verbose
+ for x in ms do r.send_in(-2, *x) end if FObject.do_loadbangs
+ r
+ end
+ def inspect
+ if args then "#<#{self.class} #{args}>" else super end
+ end
+ def initialize(*argv)
+ s = GridFlow.stringify_list argv
+ @argv = argv
+ @args = "["
+ @args << (self.class.foreign_name || self.to_s)
+ @args << " " if s.length>0
+ @args << s << "]"
+ @parent_patcher = nil
+ @properties = {}
+ @init_messages = []
+ end
+end
+
+class FPatcher < FObject
+ class << self
+ attr_reader :fobjects
+ attr_reader :wires
+ end
+ def initialize(*)
+ super
+ fobjects = self.class.fobjects
+ wires = self.class.wires
+ @fobjects = fobjects.map {|x| if String===x then FObject[x] else x.call end }
+ @inlets = []
+ @ninlets = self.class.ninlets or raise "oops"
+ i=0
+ @fobjects << self
+ while i<wires.length do
+ a,b,c,d = wires[i,4]
+ if a==-1 then
+ a=self
+ @inlets[b]||=[]
+ @inlets[b] << [@fobjects[c],d]
+ else
+ if c==-1 then
+ @fobjects[a].connect b,self,d+@ninlets
+ else
+ @fobjects[a].connect b,@fobjects[c],d
+ end
+ end
+ i+=4
+ end
+ end
+ def method_missing(sym,*args)
+ sym=sym.to_s
+ if sym =~ /^_(\d)_(.*)/ then
+ inl = Integer $1
+ sym = $2.intern
+ if inl<@ninlets then
+ raise "#{inspect} has not @inlets[#{inl}]" if not @inlets[inl]
+ for x in @inlets[inl] do
+ x[0].send_in x[1],sym,*args end
+ else
+ send_out(inl-@ninlets,sym,*args)
+ end
+ else super end
+ end
+end
+
+def GridFlow.estimate_cpu_clock
+ u0,t0=GridFlow.rdtsc,Time.new.to_f; sleep 0.01
+ u1,t1=GridFlow.rdtsc,Time.new.to_f; (u1-u0)/(t1-t0)
+end
+
+begin
+ @cpu_hertz = (0...3).map {
+ GridFlow.estimate_cpu_clock
+ }.sort[1] # median of three tries
+rescue
+ GridFlow.post $!
+end
+
+def GridFlow.find_file s
+ s=s.to_s
+ if s==File.basename(s) then
+ dir = GridFlow.data_path.find {|x| File.exist? "#{x}/#{s}" }
+ if dir then "#{dir}/#{s}" else s end
+ elsif GridFlow.respond_to? :find_file_2
+ GridFlow.find_file_2 s
+ else
+ s
+ end
+end
+
+def GridFlow.macerr(i)
+ begin
+ f=File.open("/System/Library/Frameworks/CoreServices.framework/"+
+ "Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/"+
+ "MacErrors.h")
+ while f.gets
+ m = /^\s*(\w+)\s*=\s*(-\d+),\s*\/\*\s*(.*)\s*\*\/$/.match $_
+ next if not m
+ if m[2].to_i == i then return "#{m[2]}: \"#{m[3]}\"" end
+ end
+ return "no error message available for this error number"
+ rescue FileError
+ return "Can't find Apple's precious copyrighted list of error messages on this system."
+ ensure
+ f.close if f
+ end
+end
+
+end # module GridFlow
+
+class IO
+ def nonblock= flag
+ bit = Fcntl::O_NONBLOCK
+ state = fcntl(Fcntl::F_GETFL, 0)
+ fcntl(Fcntl::F_SETFL, (state & ~bit) |
+ (if flag; bit else 0 end))
+ end
+end
+
+def protect
+ yield
+rescue Exception => e
+ STDERR.puts "#{e.class}: #{e}"
+ STDERR.puts e.backtrace
+end
+
+def GridFlow.load_user_config
+ require "gridflow/bridge/puredata.rb" if GridFlow.bridge_name == "puredata"
+ user_config_file = ENV["HOME"] + "/.gridflow_startup"
+ begin
+ load user_config_file if File.exist? user_config_file
+ rescue Exception => e
+ GridFlow.post "#{e.class}: #{e}:\n" + e.backtrace.join("\n")
+ GridFlow.post "while loading ~/.gridflow_startup"
+ end
+end
+
+require "gridflow/base/flow_objects.rb"
+require "gridflow/format/main.rb"
+
+%w(
+ # #for #finished #type #dim #transpose #perspective #store #outer
+ #grade #redim #import #export #export_list #cast
+ #scale_by #downscale_by #draw_polygon #draw_image #layer
+ #print #pack #export_symbol #rotate
+ #in #out
+).each {|k|
+ GridFlow::FObject.name_lookup(k).add_creator k.gsub(/#/,"@")
+}
+
+END {
+ GridFlow.fobjects.each {|k,v| k.delete if k.respond_to? :delete }
+ GridFlow.fobjects.clear
+ GC.start
+}
+
diff --git a/externals/gridflow/base/number.c b/externals/gridflow/base/number.c
new file mode 100644
index 00000000..c483cf0b
--- /dev/null
+++ b/externals/gridflow/base/number.c
@@ -0,0 +1,365 @@
+/*
+ $Id: number.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "grid.h.fcs"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#ifdef PASS1
+NumberType number_type_table[] = {
+#define FOO(_sym_,_size_,_flags_,args...) NumberType( #_sym_, _size_, _flags_, args ),
+NUMBER_TYPES(FOO)
+#undef FOO
+};
+const long number_type_table_n = COUNT(number_type_table);
+#endif
+
+// those are bogus class-templates in the sense that you don't create
+// objects from those, you just call static functions. The same kind
+// of pattern is present in STL to overcome some limitations of C++.
+
+template <class T> class Op {
+public:
+ // I call abort() on those because I can't say they're purevirtual.
+ static T f(T a, T b) {abort();}
+ static bool is_neutral(T x, LeftRight side) {assert(!"Op::is_neutral called?");}
+ static bool is_absorbent(T x, LeftRight side) {assert(!"Op::is_absorbent called?");}
+};
+
+template <class O> class OpLoops {
+public:
+ template <class T> static void op_map (int n, T *as, T b) {
+ if (!n) return;
+#define FOO(I) as[I]=O::f(as[I],b);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+ template <class T> static void op_zip (int n, T *as, T *bs) {
+ if (!n) return;
+ int ba=bs-as; // really!
+#define FOO(I) as[I]=O::f(as[I],as[ba+I]);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+ // disabled
+ template <class T> static void op_zip2 (int n, T *as, T *bs, T *cs) {
+ if (!n) return;
+ int ba=bs-as, ca=cs-as;
+#define FOO(I) as[ca+I]=O::f(as[I],as[ba+I]);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+#define W(i) as[i]=O::f(as[i],bs[i]);
+#define Z(i,j) as[i]=O::f(O::f(O::f(O::f(as[i],bs[i]),bs[i+j]),bs[i+j+j]),bs[i+j+j+j]);
+ template <class T> static void op_fold (int an, int n, T *as, T *bs) {
+ switch (an) {
+ case 1: for (; (n&3)!=0; bs++, n--) W(0);
+ for (; n; bs+=4, n-=4) { Z(0,1); } break;
+ case 2: for (; (n&3)!=0; bs+=2, n--) { W(0); W(1); }
+ for (; n; bs+=8, n-=4) { Z(0,2); Z(1,2); } break;
+ case 3: for (; (n&3)!=0; bs+=3, n--) { W(0); W(1); W(2); }
+ for (; n; bs+=12, n-=4) { Z(0,3); Z(1,3); Z(2,3); } break;
+ case 4: for (; (n&3)!=0; bs+=4, n--) { W(0); W(1); W(2); W(3); }
+ for (; n; bs+=16, n-=4) { Z(0,4); Z(1,4); Z(2,4); Z(3,4); } break;
+ default:for (; n--; ) {
+ int i=0;
+ for (; i<(an&-4); i+=4, bs+=4) {
+ as[i+0]=O::f(as[i+0],bs[0]);
+ as[i+1]=O::f(as[i+1],bs[1]);
+ as[i+2]=O::f(as[i+2],bs[2]);
+ as[i+3]=O::f(as[i+3],bs[3]);
+ }
+ for (; i<an; i++, bs++) as[i] = O::f(as[i],*bs);
+ }
+ }
+ }
+ template <class T> static void op_scan (int an, int n, T *as, T *bs) {
+ for (; n--; as=bs-an) {
+ for (int i=0; i<an; i++, as++, bs++) *bs=O::f(*as,*bs);
+ }
+ }
+};
+
+template <class T>
+static void quick_mod_map (int n, T *as, T b) {
+ if (!b) return;
+#define FOO(I) as[I]=mod(as[I],b);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+template <class T> static void quick_ign_map (int n, T *as, T b) {}
+template <class T> static void quick_ign_zip (int n, T *as, T *bs) {}
+template <class T> static void quick_put_map (int n, T *as, T b) {
+#define FOO(I) as[I]=b;
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+#ifdef PASS1
+void quick_put_map (int n, int16 *as, int16 b) {
+ if (n&1!=0 && (long)as&4!=0) { *as++=b; n--; }
+ quick_put_map (n>>1, (int32 *)as, (int32)(b<<16)+b);
+ if (n&1!=0) *as++=b;
+}
+void quick_put_map (int n, uint8 *as, uint8 b) {
+ while (n&3!=0 && (long)as&4!=0) { *as++=b; n--; }
+ int32 c=(b<<8)+b; c+=c<<16;
+ quick_put_map (n>>2, (int32 *)as, c);
+ while (n&3!=0) *as++=b;
+}
+#endif
+template <class T> static void quick_put_zip (int n, T *as, T *bs) {
+ gfmemcopy((uint8 *)as, (uint8 *)bs, n*sizeof(T));
+}
+
+// classic two-input operator
+#define DEF_OP(op,expr,neutral,absorbent) \
+ template <class T> class Y##op : Op<T> { public: \
+ inline static T f(T a, T b) { return expr; } \
+ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \
+ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } };
+#define DEF_OPFT(op,expr,neutral,absorbent,T) \
+ template <> class Y##op<T> : Op<T> { public: \
+ inline static T f(T a, T b) { return expr; } \
+ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \
+ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } }; \
+// this macro is for operators that have different code for the float version
+#define DEF_OPF(op,expr,expr2,neutral,absorbent) \
+ DEF_OP( op,expr, neutral,absorbent) \
+ DEF_OPFT(op,expr2,neutral,absorbent,float32) \
+ DEF_OPFT(op,expr2,neutral,absorbent,float64)
+
+#define DECL_OPON(base,op,T) NumopOn<T>( \
+ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, \
+ &base<Y##op<T> >::op_fold, &base<Y##op<T> >::op_scan, \
+ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent)
+#define DECL_OPON_NOFOLD(base,op,T) NumopOn<T>( \
+ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, 0,0, \
+ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent)
+#define DECL_OP(op,sym,flags) Numop(0, sym, \
+ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \
+ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \
+ DECL_OPON(OpLoops,op,float32), DECL_OPON(OpLoops,op,float64)), flags)
+#define DECL_OP_NOFLOAT(op,sym,flags) Numop(0, sym, \
+ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \
+ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \
+ NumopOn<float32>(0,0,0,0,0,0), NumopOn<float64>(0,0,0,0,0,0)), flags)
+#define DECL_OP_NOFOLD(op,sym,flags) Numop(0, sym, \
+ DECL_OPON_NOFOLD(OpLoops,op,uint8), DECL_OPON_NOFOLD(OpLoops,op,int16), \
+ DECL_OPON_NOFOLD(OpLoops,op,int32) NONLITE(, DECL_OPON_NOFOLD(OpLoops,op,int64), \
+ DECL_OPON_NOFOLD(OpLoops,op,float32), DECL_OPON_NOFOLD(OpLoops,op,float64)), flags)
+
+template <class T> static inline T gf_floor (T a) {
+ return (T) floor((double)a); }
+template <class T> static inline T gf_trunc (T a) {
+ return (T) floor(abs((double)a)) * (a<0?-1:1); }
+
+/*
+uint8 clipadd(uint8 a, uint8 b) { int32 c=a+b; return c<0?0:c>255?255:c; }
+int16 clipadd(int16 a, int16 b) { int32 c=a+b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipadd(int32 a, int32 b) { int64 c=a+b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipadd(int64 a, int64 b) { int64 c=(a>>1)+(b>>1)+(a&b&1);
+ return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a+b; }
+uint8 clipsub(uint8 a, uint8 b) { int32 c=a-b; return c<0?0:c>255?255:c; }
+int16 clipsub(int16 a, int16 b) { int32 c=a-b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipsub(int32 a, int32 b) { int64 c=a-b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipsub(int64 a, int64 b) { int64 c=(a>>1)-(b>>1); //???
+ return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a-b; }
+*/
+
+#ifdef PASS1
+DEF_OP(ignore, a, side==at_right, side==at_left)
+DEF_OP(put, b, side==at_left, side==at_right)
+DEF_OP(add, a+b, x==0, false)
+DEF_OP(sub, a-b, side==at_right && x==0, false)
+DEF_OP(bus, b-a, side==at_left && x==0, false)
+DEF_OP(mul, a*b, x==1, x==0)
+DEF_OP(mulshr8, ((int32)a*(int32)b)>>8, (int64)x==256, x==0) //!@#$ bug with int64
+DEF_OP(div, b==0 ? 0 : a/b, side==at_right && x==1, false)
+DEF_OP(div2, b==0 ? 0 : div2(a,b), side==at_right && x==1, false)
+DEF_OP(vid, a==0 ? 0 : b/a, side==at_left && x==1, false)
+DEF_OP(vid2, a==0 ? 0 : div2(b,a), side==at_left && x==1, false)
+DEF_OPF(mod, b==0 ? 0 : mod(a,b), b==0 ? 0 : a-b*gf_floor(a/b),
+ false, side==at_left && x==0 || side==at_right && x==1)
+DEF_OPF(dom, a==0 ? 0 : mod(b,a), a==0 ? 0 : b-a*gf_floor(b/a),
+ false, side==at_left && x==0 || side==at_right && x==1)
+//DEF_OPF(rem, b==0 ? 0 : a%b, b==0 ? 0 : a-b*gf_trunc(a/b))
+//DEF_OPF(mer, a==0 ? 0 : b%a, a==0 ? 0 : b-a*gf_trunc(b/a))
+DEF_OP(rem, b==0?0:a%b, false, side==at_left&&x==0 || side==at_right&&x==1)
+DEF_OP(mer, a==0?0:b%a, false, side==at_left&&x==0 || side==at_right&&x==1)
+#endif
+#ifdef PASS2
+DEF_OP(gcd, gcd(a,b), x==0, x==1)
+DEF_OP(gcd2, gcd2(a,b), x==0, x==1) // should test those and pick one of the two
+DEF_OP(lcm, a==0 || b==0 ? 0 : lcm(a,b), x==1, x==0)
+DEF_OPF(or , a|b, (float32)((int32)a | (int32)b), x==0, x==nt_all_ones(&x))
+DEF_OPF(xor, a^b, (float32)((int32)a ^ (int32)b), x==0, false)
+DEF_OPF(and, a&b, (float32)((int32)a & (int32)b), x==nt_all_ones(&x), x==0)
+DEF_OPF(shl, a<<b, a*pow(2.0,+b), side==at_right && x==0, false)
+DEF_OPF(shr, a>>b, a*pow(2.0,-b), side==at_right && x==0, false)
+DEF_OP(sc_and, a ? b : a, side==at_left && x!=0, side==at_left && x==0)
+DEF_OP(sc_or, a ? a : b, side==at_left && x==0, side==at_left && x!=0)
+DEF_OP(min, min(a,b), x==nt_greatest(&x), x==nt_smallest(&x))
+DEF_OP(max, max(a,b), x==nt_smallest(&x), x==nt_greatest(&x))
+#endif
+#ifdef PASS3
+DEF_OP(cmp, cmp(a,b), false, false)
+DEF_OP(eq, a == b, false, false)
+DEF_OP(ne, a != b, false, false)
+DEF_OP(gt, a > b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x))
+DEF_OP(le, a <= b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x))
+DEF_OP(lt, a < b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x))
+DEF_OP(ge, a >= b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x))
+DEF_OP(sin, (T)(b * sin(a * (M_PI / 18000))), false, false) // "LN=9000+36000n RA=0 LA=..."
+DEF_OP(cos, (T)(b * cos(a * (M_PI / 18000))), false, false) // "LN=36000n RA=0 LA=..."
+DEF_OP(atan, (T)(atan2(a,b) * (18000 / M_PI)), false, false) // "LA=0"
+DEF_OP(tanh, (T)(b * tanh(a * (M_PI / 18000))), false, x==0)
+DEF_OP(gamma, b<=0 ? 0 : (T)(0+floor(pow(a/256.0,256.0/b)*256.0)), false, false) // "RN=256"
+DEF_OP(pow, ipow(a,b), false, false) // "RN=1"
+DEF_OP(log, (T)(a==0 ? 0 : b * log(gf_abs(a))), false, false) // "RA=0"
+// 0.7.8
+//DEF_OPF(clipadd, clipadd(a,b), a+b, x==0, false)
+//DEF_OPF(clipsub, clipsub(a,b), a-b, side==at_right && x==0, false)
+DEF_OP(abssub, gf_abs(a-b), false, false)
+DEF_OP(sqsub, (a-b)*(a-b), false, false)
+DEF_OP(avg, (a+b)/2, false, false)
+DEF_OP(hypot, (T)(0+floor(sqrt(a*a+b*b))), false, false)
+DEF_OP(sqrt, (T)(0+floor(sqrt(a))), false, false)
+DEF_OP(rand, a==0 ? 0 : random()%(int32)a, false, false)
+//DEF_OP(erf,"erf*", 0)
+#endif
+
+extern Numop op_table1[], op_table2[], op_table3[];
+extern const long op_table1_n, op_table2_n, op_table3_n;
+
+#ifdef PASS1
+Numop op_table1[] = {
+ DECL_OP(ignore, "ignore", OP_ASSOC),
+ DECL_OP(put, "put", OP_ASSOC),
+ DECL_OP(add, "+", OP_ASSOC|OP_COMM), // "LINV=sub"
+ DECL_OP(sub, "-", 0),
+ DECL_OP(bus, "inv+", 0),
+ DECL_OP(mul, "*", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(mulshr8, "*>>8", OP_ASSOC|OP_COMM),
+ DECL_OP(div, "/", 0),
+ DECL_OP_NOFLOAT(div2, "div", 0),
+ DECL_OP(vid, "inv*", 0),
+ DECL_OP_NOFLOAT(vid2, "swapdiv", 0),
+ DECL_OP_NOFLOAT(mod, "%", 0),
+ DECL_OP_NOFLOAT(dom, "swap%", 0),
+ DECL_OP_NOFLOAT(rem, "rem", 0),
+ DECL_OP_NOFLOAT(mer, "swaprem", 0),
+};
+const long op_table1_n = COUNT(op_table1);
+#endif
+#ifdef PASS2
+Numop op_table2[] = {
+ DECL_OP_NOFLOAT(gcd, "gcd", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(gcd2, "gcd2", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(lcm, "lcm", OP_ASSOC|OP_COMM),
+ DECL_OP(or , "|", OP_ASSOC|OP_COMM),
+ DECL_OP(xor, "^", OP_ASSOC|OP_COMM),
+ DECL_OP(and, "&", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFOLD(shl, "<<", 0),
+ DECL_OP_NOFOLD(shr, ">>", 0),
+ DECL_OP_NOFOLD(sc_and,"&&", 0),
+ DECL_OP_NOFOLD(sc_or, "||", 0),
+ DECL_OP(min, "min", OP_ASSOC|OP_COMM),
+ DECL_OP(max, "max", OP_ASSOC|OP_COMM),
+};
+const long op_table2_n = COUNT(op_table2);
+#endif
+#ifdef PASS3
+Numop op_table3[] = {
+ DECL_OP_NOFOLD(eq, "==", OP_COMM),
+ DECL_OP_NOFOLD(ne, "!=", OP_COMM),
+ DECL_OP_NOFOLD(gt, ">", 0),
+ DECL_OP_NOFOLD(le, "<=", 0),
+ DECL_OP_NOFOLD(lt, "<", 0),
+ DECL_OP_NOFOLD(ge, ">=", 0),
+ DECL_OP_NOFOLD(cmp, "cmp", 0),
+ DECL_OP_NOFOLD(sin, "sin*", 0),
+ DECL_OP_NOFOLD(cos, "cos*", 0),
+ DECL_OP_NOFOLD(atan, "atan", 0),
+ DECL_OP_NOFOLD(tanh, "tanh*", 0),
+ DECL_OP_NOFOLD(gamma, "gamma", 0),
+ DECL_OP_NOFOLD(pow, "**", 0),
+ DECL_OP_NOFOLD(log, "log*", 0),
+// 0.7.8
+// DECL_OP(clipadd,"clip+", OP_ASSOC|OP_COMM),
+// DECL_OP(clipsub,"clip-", 0),
+ DECL_OP_NOFOLD(abssub,"abs-", OP_COMM),
+ DECL_OP_NOFOLD(sqsub,"sq-", OP_COMM),
+ DECL_OP_NOFOLD(avg,"avg", OP_COMM),
+ DECL_OP_NOFOLD(hypot,"hypot", OP_COMM),
+ DECL_OP_NOFOLD(sqrt,"sqrt", 0),
+ DECL_OP_NOFOLD(rand,"rand", 0),
+ //DECL_OP_NOFOLD(erf,"erf*", 0),
+};
+const long op_table3_n = COUNT(op_table3);
+#endif
+
+// D=dictionary, A=table, A##_n=table count.
+#define INIT_TABLE(D,A) { D=IEVAL(mGridFlow,"@"#D" ||= {}"); \
+ for(int i=0; i<A##_n; i++) { \
+ A[i].sym = ID2SYM(rb_intern(A[i].name)); \
+ rb_hash_aset(D,A[i].sym,PTR2FIX((A+i)));}}
+
+#ifdef PASS1
+Ruby op_dict = Qnil;
+Ruby number_type_dict = Qnil;
+void startup_number () {
+ INIT_TABLE(op_dict,op_table1)
+ INIT_TABLE(op_dict,op_table2)
+ INIT_TABLE(op_dict,op_table3)
+ INIT_TABLE(number_type_dict,number_type_table)
+
+ for (int i=0; i<COUNT(number_type_table); i++) {
+ number_type_table[i].index = (NumberTypeE) i;
+ char a[64];
+ strcpy(a,number_type_table[i].aliases);
+ char *b = strchr(a,',');
+ if (b) {
+ *b=0;
+ rb_hash_aset(number_type_dict, ID2SYM(rb_intern(b+1)),
+ PTR2FIX(&number_type_table[i]));
+ }
+ rb_hash_aset(number_type_dict, ID2SYM(rb_intern(a)),
+ PTR2FIX(&number_type_table[i]));
+ }
+// S:name; M:mode; F:replacement function;
+#define OVERRIDE_INT(S,M,F) { \
+ Numop *foo = FIX2PTR(Numop,rb_hash_aref(op_dict,SYM(S))); \
+ foo->on_uint8.op_##M=F; \
+ foo->on_int16.op_##M=F; \
+ foo->on_int32.op_##M=F; }
+ OVERRIDE_INT(ignore,map,quick_ign_map);
+ OVERRIDE_INT(ignore,zip,quick_ign_zip);
+ //OVERRIDE_INT(put,map,quick_put_map);
+ //OVERRIDE_INT(put,zip,quick_put_zip);
+ //OVERRIDE_INT(%,map,quick_mod_map); // !@#$ does that make an improvement at all?
+}
+#endif
diff --git a/externals/gridflow/base/source_filter.rb b/externals/gridflow/base/source_filter.rb
new file mode 100644
index 00000000..59e24867
--- /dev/null
+++ b/externals/gridflow/base/source_filter.rb
@@ -0,0 +1,274 @@
+$keywords = %w(class decl def end grdecl)
+$stack = []
+$classes = []
+
+ClassDecl = Struct.new(:name,:supername,:methods,:grins,:attrs,:info)
+MethodDecl = Struct.new(:rettype,:selector,:arglist,:minargs,:maxargs,:where)
+Arg = Struct.new(:type,:name,:default)
+
+class MethodDecl
+ def ==(o)
+ return false unless rettype==o.rettype &&
+ maxargs==o.maxargs # && minargs==o.minargs
+ arglist.each_index{|i| arglist[i] == o.arglist[i] or return false }
+ return true
+ end
+end
+
+class Arg
+ def ==(o)
+ type==o.type && name==o.name # && default==o.default
+ end
+end
+
+In = File.open ARGV[0], "r"
+Out = File.open ARGV[1], "w"
+
+def handle_class(line)
+ raise "already in class #{where}" if $stack[-1] and ClassDecl===$stack[-1]
+ #STDERR.puts "class: #{line}"
+ /^(\w+)(?:\s*<\s*(\w+))?$/.match line or raise "syntax error #{where}"
+ q=ClassDecl.new($1,$2,{},{},{},false)
+ $stack << q
+ $classes << q
+ Out.puts ""
+end
+
+def parse_methoddecl(line,term)
+ /^(\w+)\s+(\w+)\s*\(([^\)]*)\)\s*#{term}/.match line or
+ raise "syntax error #{where} #{line}"
+ rettype,selector,arglist = $1,$2,$3
+ arglist,minargs,maxargs = parse_arglist arglist
+ MethodDecl.new(rettype,selector,arglist,minargs,maxargs,where)
+end
+
+def parse_arglist(arglist)
+ arglist = arglist.split(/,/)
+ maxargs = arglist.length
+ args = arglist.map {|arg|
+ if /^\s*\.\.\.\s*$/.match arg then maxargs=-1; next end
+ /^\s*([\w\s\*<>]+)\s*\b(\w+)\s*(?:\=(.*))?/.match arg or
+ raise "syntax error in \"#{arg}\" #{where}"
+ type,name,default=$1,$2,$3
+ Arg.new(type.sub(/\s+$/,""),name,default)
+ }.compact
+ minargs = args.length
+ minargs-=1 while minargs>0 and args[minargs-1].default
+ [args,minargs,maxargs]
+end
+
+def unparse_arglist(arglist,with_default=true)
+ arglist.map {|arg|
+ x="#{arg.type} #{arg.name} "
+ x<<'='<<arg.default if with_default and arg.default
+ x
+ }.join(", ")
+end
+
+def where
+ "[#{ARGV[0]}:#{$linenumber}]"
+end
+
+def handle_attr(line)
+ type = line.gsub(%r"//.*$","").gsub(%r"/\*.*\*/","").gsub(%r";?\s*$","")
+ name = type.slice!(/\w+$/)
+ raise "missing \\class #{where}" if
+ not $stack[-1] or not ClassDecl===$stack[-1]
+ $stack[-1].attrs[name]=Arg.new(type,name,nil)
+ Out.print line
+ Out.puts "//FCS"
+ handle_decl "void _0_#{name}_m (#{type} #{name});"
+ Out.puts "# #{$linenumber}"
+end
+
+def handle_decl(line)
+ raise "missing \\class #{where}" if
+ not $stack[-1] or not ClassDecl===$stack[-1]
+ classname = $stack[-1].name
+ m = parse_methoddecl(line,";\s*$")
+ $stack[-1].methods[m.selector] = m
+
+ Out.print "#{m.rettype} #{m.selector}(int argc, Ruby *argv"
+ Out.print "," if m.arglist.length>0
+ Out.print "#{unparse_arglist m.arglist});"
+ Out.puts "static Ruby #{m.selector}_wrap"+
+ "(int argc, Ruby *argv, Ruby rself);//FCS"
+ Out.puts "# #{$linenumber+1}"
+end
+
+def handle_def(line)
+ m = parse_methoddecl(line,"\{?.*$")
+ term = line[/\{.*/]
+ qlass = $stack[-1]
+ raise "missing \\class #{where}" if not qlass or not ClassDecl===qlass
+ classname = qlass.name
+ if qlass.methods[m.selector]
+ n = m; m = qlass.methods[m.selector]
+ if m!=n then
+ STDERR.puts "warning: def does not match decl:"
+ STDERR.puts "#{m.where}: \\decl #{m.inspect}"
+ STDERR.puts "#{n.where}: \\def #{n.inspect}"
+ end
+ else
+ qlass.methods[m.selector] = m
+ end
+
+ Out.print "Ruby #{classname}::#{m.selector}_wrap"+
+ "(int argc, Ruby *argv, Ruby rself) {"+
+ "static const char *methodspec = "+
+ "\"#{qlass.name}::#{m.selector}(#{unparse_arglist m.arglist,false})\";"+
+ "DGS(#{classname});"
+
+ Out.print "if (argc<#{m.minargs}"
+ Out.print "||argc>#{m.maxargs}" if m.maxargs!=-1
+ Out.print ") RAISE(\"got %d args instead of %d..%d in %s\""+
+ ",argc,#{m.minargs},#{m.maxargs},methodspec);"
+
+ error = proc {|x,y|
+ "RAISE(\"got %s instead of #{x} in %s\","+
+ "rb_str_ptr(rb_inspect(rb_obj_class(#{y}))),methodspec)"
+ }
+
+ m.arglist.each_with_index{|arg,i|
+ case arg.type
+ when "Symbol"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_SYMBOL) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ when "Array"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_ARRAY) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ when "String"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])==T_SYMBOL) "+
+ "argv[#{i}]=rb_funcall(argv[#{i}],SI(to_s),0);"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_STRING) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ end
+ }
+
+# Out.print "return " if m.rettype!="void"
+ Out.print "VALUE foo = " if m.rettype!="void" ###
+
+ Out.print " self->#{m.selector}(argc,argv"
+ m.arglist.each_with_index{|arg,i|
+ if arg.default then
+ Out.print ",argc<#{i+1}?#{arg.default}:convert(argv[#{i}],(#{arg.type}*)0)"
+ else
+ Out.print ",convert(argv[#{i}],(#{arg.type}*)0)"
+ end
+ }
+ Out.print ");"
+ Out.print "self->check_magic();"
+ Out.print "return Qnil;" if m.rettype=="void"
+ Out.print "return foo;" if m.rettype!="void" ###
+ Out.print "} #{m.rettype} #{classname}::#{m.selector}(int argc, Ruby *argv"
+ Out.print "," if m.arglist.length>0
+ Out.puts "#{unparse_arglist m.arglist, false})#{term}//FCS"
+end
+
+def handle_classinfo(line)
+ frame = $stack[-1]
+ cl = frame.name
+ line="{}" if /^\s*$/ =~ line
+ Out.puts "static void #{cl}_startup (Ruby rself);"
+ Out.puts "static void *#{cl}_allocator () {return new #{cl};}"
+ Out.puts "static MethodDecl #{cl}_methods[] = {"
+ Out.puts frame.methods.map {|foo,method|
+ c,s = frame.name,method.selector
+ "{ \"#{s}\",(RMethod)#{c}::#{s}_wrap }"
+ }.join(",")
+ Out.puts "}; FClass ci#{cl} = { #{cl}_allocator, #{cl}_startup,"
+ Out.puts "#{cl.inspect}, COUNT(#{cl}_methods), #{cl}_methods };"
+ Out.puts "void #{frame.name}_startup (Ruby rself) "+line
+end
+
+def handle_grin(line)
+ fields = line.split(/\s+/)
+ i = fields[0].to_i
+ c = $stack[-1].name
+ Out.print "template <class T> void grin_#{i}(GridInlet *in, int n, Pt<T> data);"
+ Out.print "template <class T> static void grinw_#{i} (GridInlet *in, int n, Pt<T> data);"
+ Out.print "static GridHandler grid_#{i}_hand;"
+ handle_decl "Ruby _#{i}_grid(...);"
+ $stack[-1].grins[i] = fields.dup
+end
+
+def handle_end(line)
+ frame = $stack.pop
+ fields = line.split(/\s+/)
+ n = fields.length
+ if ClassDecl===frame then
+ #handle_classinfo if not frame.info
+ cl = frame.name
+ if fields[0]!="class" or
+ (n>1 and fields[1]!=cl)
+ then raise "end not matching #{where}" end
+ $stack.push frame
+ frame.attrs.each {|name,attr|
+ type,name,default = attr.to_a
+ #STDERR.puts "type=#{type} name=#{name} default=#{default}"
+ handle_def "void _0_#{name}_m (#{type} #{name}) { this->#{name}=#{name}; }"
+ }
+ frame.grins.each {|i,v|
+ k = case v[1]
+ when nil; '4'
+ when 'int32'; '1'
+ when 'int'; '2'
+ when 'float'; 'F'
+ else raise 'BORK BORK BORK' end
+ Out.print "static GridHandler #{cl}_grid_#{i}_hand = GRIN#{k}(#{cl},#{i});"
+ handle_def "Ruby _#{i}_grid(...) {"+
+ "if (in.size()<=#{i}) in.resize(#{i}+1);"+
+ "if (!in[#{i}]) in[#{i}]=new GridInlet((GridObject *)this,&#{cl}_grid_#{i}_hand);"+
+ "return in[#{i}]->begin(argc,argv);}"
+
+ }
+ $stack.pop
+ Out.puts "# #{$linenumber}"
+ end
+ if :ruby==frame then
+ if fields[0]!="ruby" then raise "expected \\end ruby" end
+ end
+ Out.puts ""
+end
+
+def handle_startall(line)
+ $classes.each {|q|
+ Out.print "rb_funcall(EVAL(\"GridFlow\"),SI(fclass_install),2,PTR2FIX(&ci#{q.name}),"
+ if q.supername then
+ Out.print "EVAL(\"GridFlow::#{q.supername}\"));"
+ else
+ Out.print "Qnil);"
+ end
+ }
+ Out.puts ""
+end
+
+def handle_ruby(line)
+ Out.puts ""
+ $stack.push :ruby
+end
+
+$rubymode=false
+$linenumber=1
+loop{
+ x = In.gets
+ break if not x
+ if /^\s*\\(\w+)\s*(.*)$/.match x then
+ begin
+ send("handle_#{$1}",$2)
+ rescue StandardError => e
+ STDERR.puts e.inspect
+ STDERR.puts "at line #{$linenumber}"
+ STDERR.puts e.backtrace
+ File.unlink ARGV[1]
+ exit 1
+ end
+ else
+ if $stack[-1]==:ruby then
+ x.gsub!(/([\\\"])/) { "\\"+$1 }
+ x="\"#{x.chomp}\\n\"\n"
+ end
+ Out.puts x
+ end
+ $linenumber+=1
+}
diff --git a/externals/gridflow/base/test.rb b/externals/gridflow/base/test.rb
new file mode 100644
index 00000000..1d44f918
--- /dev/null
+++ b/externals/gridflow/base/test.rb
@@ -0,0 +1,1086 @@
+# $Id: test.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+$:.delete_if {|x| x=='.' }
+require "gridflow"
+
+include GridFlow
+GridFlow.verbose=true
+
+$imdir = "./images"
+$animdir = "/opt/mex"
+srand Time.new.to_i
+$port = 4200+rand(100)
+
+def pressakey; puts "press return to continue."; readline; end
+
+class Expect < FObject
+ def praise(*a)
+ #raise(*a)
+ puts a
+ end
+ def expect(*v)
+ @count=0
+ @v=v
+ @expecting=true
+ yield
+ @expecting=false
+ praise "wrong number of messages (#{@count}), expecting #{@v.inspect}" if @count!=@v.length
+ end
+ def _0_list(*l)
+ return if not @expecting
+ praise "wrong number of messages (#{@count})" if @count==@v.length
+ praise "got #{l.inspect} expecting #{@v.inspect}" if @v[@count]!=l
+ @count+=1
+ end
+ def method_missing(s,*a)
+ praise "stray message: #{s}: #{a.inspect}"
+ end
+ install "expect", 1, 0
+end
+
+def test_bitpacking
+ #!@#$ WRITE ME
+end
+
+def test_operators
+ #!@#$ WRITE ME
+end
+
+def cast value, type
+ case type
+ when :uint8; value & 0xff
+ when :int16; (value & 0x7fff) - (value & 0x8000)
+ when :int32; value
+ when :int64; value
+ when :float32; value.to_f
+ when :float64; value.to_f
+ else raise "hell"
+ end
+end
+
+def test_math
+ hm = "#".intern
+#for nt in [:uint8, :int16, :int32, :int64, :float32, :float64] do
+for nt in [:uint8, :int16, :int32, :float32] do
+
+ #GridFlow.verbose = false
+ GridFlow.gfpost "starting test for #{nt}"
+ e = FObject["@export_list"]
+ x = Expect.new
+ e.connect 0,x,0
+
+ x.expect([1,2,3,11,12,13,21,22,23]) {
+ e.send_in 0, 3,3,nt,hm,1,2,3,11,12,13,21,22,23 }
+
+ a = FObject["fork"]
+ b = FObject["@ +"]
+ a.connect 0,b,0
+ a.connect 1,b,1
+ b.connect 0,e,0
+ x.expect([4]) { a.send_in 0, 2 }
+
+ x.expect([2,3,5,7]) { e.send_in 0,"list #{nt} 2 3 5 7" }
+ a = FObject["@fold + {#{nt} # 0}"]
+ a.connect 0,e,0
+ x.expect([cast(420000,nt)]) { a.send_in 0,"10000 #{nt} # 42" }
+
+ (a = FObject["@ + {#{nt} 0 10}"]).connect 0,e,0
+ x.expect([1,12,4,18,16,42,64]) {
+ a.send_in 0,:list,nt, 1,2,4,8,16,32,64 }
+
+ a = FObject["@ + {#{nt} 2 3 5}"]
+ b = FObject["@fold + {#{nt} # 0}"]
+ a.connect 0,b,0
+ b.connect 0,e,0
+ x.expect([cast(45332,nt)]) { a.send_in 0, 1000,nt,hm,42 }
+
+
+ (a = FObject["@ + {#{nt} # 42}"]).connect 0,e,0
+ x.expect((43..169).to_a) {
+ a.send_in 0,:list,nt, *(1..127).to_a }
+
+ x.expect([3,5,9,15]) {
+ a.send_in 1,:list,4,nt,hm, 2,3,5,7
+ a.send_in 0,:list,4,nt,hm, 1,2,4,8 }
+ x.expect([11,12,14,18]) {
+ a.send_in 1, "list #{nt} # 10"
+ a.send_in 0,:list,nt, 1,2,4,8 }
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64
+ (a = FObject["@ / {#{nt} # 3}"]).connect 0,e,0
+ x.expect([-2,-1,-1,-1,0,0,0,0,0,1,1,1,2]) {
+ a.send_in(0,:list,nt, *(-6..6).to_a) }
+
+ (a = FObject["@ div {#{nt} # 3}"]).connect 0,e,0
+ x.expect([-2,-2,-2,-1,-1,-1,0,0,0,1,1,1,2]) {
+ a.send_in(0, :list, nt, *(-6..6).to_a) }
+end
+
+ (a = FObject["@ ignore {#{nt} # 42}"]).connect 0,e,0
+ x.expect((42..52).to_a) { a.send_in(0, :list, nt, *(42..52).to_a) }
+
+ (a = FObject["@ put {#{nt} # 42}"]).connect 0,e,0
+ x.expect([42]*13) { a.send_in(0, :list, nt, *(-6..6).to_a) }
+
+if nt!=:uint8
+ (a = FObject["@! abs"]).connect 0,e,0
+ x.expect([2,3,5,7]) {
+ a.send_in 0,:list,nt, -2,3,-5,7 }
+end
+
+ (a = FObject["@fold * {#{nt} # 1}"]).connect 0,e,0
+ x.expect([210]) { a.send_in 0,:list,nt, 2,3,5,7 }
+ x.expect([128]) { a.send_in 0,:list,nt, 1,1,2,1,2,2,2,1,1,2,1,2,2 }
+
+ (a = FObject["@fold + {#{nt} 0 0}"]).connect 0,e,0
+ x.expect([18,23]) { a.send_in 0, 3,2,nt,hm,2,3,5,7,11,13 }
+
+ (a = FObject["@scan + {#{nt} 0 0}"]).connect 0,e,0
+ x.expect([2,3,7,10,18,23]) { a.send_in 0, 3,2,nt,hm,2,3,5,7,11,13 }
+
+ (a = FObject["@scan * {#{nt} # 1}"]).connect 0,e,0
+ x.expect([2,6,30,210]) { a.send_in 0,:list,nt, 2,3,5,7 }
+ x.expect([1,1,2,2,4,8,16,16,16,32,32,64,128]) {
+ a.send_in 0,:list,nt, 1,1,2,1,2,2,2,1,1,2,1,2,2 }
+
+ (a = FObject["@scan + {#{nt} 0 0 0}"]).connect 0,e,0
+ x.expect([1,2,3,5,7,9,12,15,18]) {
+ a.send_in 0,:list,3,3,nt,hm,*(1..9).to_a }
+
+ (a = FObject["@scan + {#{nt} # 0}"]).connect 0,e,0
+ x.expect([1,3,6, 4,9,15, 7,15,24]) {
+ a.send_in 0,:list,3,3,nt,hm,*(1..9).to_a }
+
+ (a = FObject["@outer +"]).connect 0,e,0
+ x.expect([9,10,12,17,18,20,33,34,36]) {
+ a.send_in 1,:list,nt, 1,2,4
+ a.send_in 0,:list,nt, 8,16,32 }
+
+ x.expect((0...100).to_a) {
+ a.send_in 1,(0...10).to_a
+ a.send_in 0,(0...10).map{|i| 10*i }}
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64
+ (a = FObject["@outer",:%,[nt,3,-3]]).connect 0,e,0
+ x.expect([0,0,1,-2,2,-1,0,0,1,-2,2,-1,0,0]) {
+ a.send_in 0,:list,nt, -30,-20,-10,0,+10,+20,+30 }
+
+ (a = FObject["@outer","swap%".intern,[nt,3,-3]]).connect 0,e,0
+ x.expect([-27,-3,-17,-3,-7,-3,0,0,3,7,3,17,3,27]) {
+ a.send_in 0,:list,nt, -30,-20,-10,0,+10,+20,+30 }
+end
+
+ (a = FObject["@import {3}"]).connect 0,e,0
+ x.expect([2,3,5]) { [2,3,5].each {|v| a.send_in 0,:list,nt,hm,v }}
+
+ (a = FObject["@import {3}"]).connect 0,e,0
+ x.expect([2,3,5]) { [2,3,5].each {|v| a.send_in 0,:list,nt,v }}
+
+ (a = FObject["@redim {5}"]).connect 0,e,0
+ x.expect([2,3,5,2,3]) { a.send_in 0,:list,2,3,5 }
+
+ (a = FObject["@redim {5}"]).connect 0,e,0
+ x.expect([0,0,0,0,0]) { a.send_in 0,:list }
+
+ (a = FObject["@redim {0}"]).connect 0,e,0
+ x.expect([]) { a.send_in 0,:list,42,37,69 }
+
+ (a = FObject["@inner * + {#{nt} # 0} {2 2 #{nt} # 2 3 5 7}"]).connect 0,e,0
+ (i0 = FObject["@redim {2 2}"]).connect 0,a,0
+ x.expect([12,17,48,68]) { i0.send_in 0,:list,nt, 1,2,4,8 }
+
+#if nt!=:int64
+if false
+ a = FObject["@print"]
+ a.send_in 0, "3 3 #{nt} # 1 0 0 0"
+ a.send_in 0, "3 3 3 #{nt} # 1 2 3 4"
+ a.send_in 0, "base 16"
+ a.send_in 0, "3 3 3 #{nt} # 255 0 0 0"
+end
+
+ (a = FObject["@outer * {3 2 #{nt} # 1 2 3}"]).connect 0,e,0
+ b = FObject["@dim"]
+ c = FObject["@export_list"]
+ a.connect 0,b,0
+ y = Expect.new
+ b.connect 0,c,0
+ c.connect 0,y,0
+
+ y.expect([2,3,2]) {
+ x.expect([1,2,3,1,2,3,10,20,30,10,20,30]) {
+ a.send_in 0,:list,nt, 1, 10 }}
+
+ #pr=GridPrint.new
+ (b = FObject["@redim {5 5}"]).connect 0,e,0
+ (a = FObject["@convolve * + {#{nt} # 0}"]).connect 0,b,0
+ (i0 = FObject["@redim {5 5 1}"]).connect 0,a,0
+ (i1 = FObject["@redim {3 1}"]).connect 0,a,1
+ i1.send_in 1, 3,3
+ x.expect([5,6,5,4,4,4,6,7,6,4,3,3,6,7,5,4,2,3,6,6,5,4,3,4,5]) {
+ a.send_in 1,:list,3,3,nt,hm, 1,1,1,1,1,1,1,1,1
+ i0.send_in 0,:list,nt, 1,1,1,0,0,0 }
+
+ (a = FObject["@convolve * + {#{nt} # 0}"]).connect 0,e,0
+ x.expect([1,3,6,4,0]) {
+ a.send_in 1, 1,2,nt,hm, 1,1
+ a.send_in 0, 1,5,nt,hm, 0,1,2,4,0 }
+
+ (a = FObject["@import {4}"]).connect 0,e,0
+ x.expect([2,3,5,7]) {
+ [2,3,5,7].each {|v| a.send_in 0,v }}
+ x.expect([1,2,3],[4,5,6],[7,8,9]) {
+ a.send_in 1, :list, 3
+ a.send_in 0, :list, *(1..9).to_a}
+
+ for o in ["@store"]
+ (a = FObject[o]).connect 0,e,0
+ a.send_in 1, 5, 4, nt, hm, 1,2,3,4,5
+ x.expect([1,2,3,4,4,5,1,2,2,3,4,5]) {
+ a.send_in 0, 3,1, hm, 0,2,4 }
+ x.expect([1,2,3,4,5]*24) { a.send_in 0, 2,3,0,hm }
+ x.expect([1,2,3,4,5]*4) { a.send_in 0, 0,hm }
+ x.expect([1,2,3,4,5]*4) { a.send_in 0 }
+ x.expect([1,2,3,4]) { a.send_in 0,[0] }
+ a.send_in 1,:put_at,[0,0]
+ a.send_in 1,2,2,nt,hm,6,7,8,9
+ x.expect([6,7,3,4, 8,9,2,3, 4,5,1,2, 3,4,5,1, 2,3,4,5]) { a.send_in 0 }
+ x.expect([6,7,3,4]) { a.send_in 0,[0] }
+ x.expect([8,9,2,3]) { a.send_in 0,[1] }
+ a.send_in 1,:put_at,[1,1]
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([6,7,3,4, 8,11,13,3, 4,17,19,2, 3,4,5,1, 2,3,4,5]) { a.send_in 0 }
+ end
+
+ b = FObject["@dim"]
+ c = FObject["@export_list"]
+ a.connect 0,b,0
+ y = Expect.new
+ b.connect 0,c,0
+ c.connect 0,y,0
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64 and nt!=:int64
+ (a = FObject["@for {#{nt} # 0} {#{nt} # 10} {#{nt} # 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10]) {
+ x.expect((0...10).to_a) {
+ a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} # 0} {#{nt} # -10} {#{nt} # 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([0]) { x.expect([]) { a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} # 0} {#{nt} # -10} {#{nt} # -1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10]) { x.expect([0,-1,-2,-3,-4,-5,-6,-7,-8,-9]) { a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} 0} {#{nt} 10} {#{nt} 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10,1]) {
+ x.expect((0...10).to_a) {
+ a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} 2 3} {#{nt} 5 7} {#{nt} 1 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([3,4,2]) {
+ x.expect([2,3,2,4,2,5,2,6,3,3,3,4,3,5,3,6,4,3,4,4,4,5,4,6]) {
+ a.send_in 0 } }
+end
+
+ (a = FObject["@complex_sq"]).connect 0,e,0
+ x.expect([8,0]) { a.send_in 0, 2, 2 }
+ x.expect([0,9]) { a.send_in 0, 0, 3 }
+
+ (a = FObject["@rotate 3000 {1 2 5}"]).connect 0,e,0
+# pr = GridPrint.new
+# a.connect 0,pr,0
+# x.expect([]) {
+ a.send_in 0, "5 5 # 1000 0 0 0 0 0"
+# }
+
+#if nt==:float32 or nt==:float64
+# (a = FObject["@matrix_solve"]).connect 0,e,0
+# x.expect([1,0,0,0,1,0,0,0,1]) { a.send_in 0, 3, 3, nt, hm, 1,0,0,0,1,0,0,0,1 }
+#end
+ GridFlow.gfpost "ending test for #{nt}"
+end # for nt
+
+ (a = FObject["@two"]).connect 0,e,0
+ x.expect([42,0]) { a.send_in 0,42 }
+ x.expect([42,28]) { a.send_in 1,28 }
+ x.expect([1313,28]) { a.send_in 0,1313 }
+
+ (a = FObject["@three"]).connect 0,e,0
+ x.expect([42,0,0]) { a.send_in 0,42 }
+ x.expect([42,28,0]) { a.send_in 1,28 }
+ x.expect([42,28,-1]) { a.send_in 2,-1 }
+
+ (a = FObject["@four"]).connect 0,e,0
+ x.expect([42,0,0,0]) { a.send_in 0,42 }
+ x.expect([42,0,0,-42]) { a.send_in 3,-42 }
+
+# glob = FObject["@global"]
+# glob.send_in 0, "profiler_dump"
+
+ e = FObject["@export_list"]
+ e.connect 0,x,0
+
+ a = FObject["@import per_message"]
+ a.connect 0,e,0
+ x.expect([1,2,3]) { a.send_in 0,1,2,3 }
+ x.expect([102,111,111]) { a.send_in 0,:symbol,:foo }
+ x.expect([ 70, 79, 79]) { a.send_in 0,:symbol,:FOO }
+
+ a = FObject["@join 1"]
+ a.connect 0,e,0
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([2,3,11,13,5,7,17,19]) { a.send_in 0,2,2,nt,hm,2,3,5,7 }
+
+if nt!=:float64; #!@#$
+ a.send_in 1, 5,1,nt,hm,42
+ y.expect([5,4]) {
+ x.expect([2,3,5,42,7,11,13,42,17,19,23,42,29,31,37,42,41,43,47,42]) {
+ a.send_in 0, 5,3,nt,hm,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 }}
+end
+ a = FObject["@join 0"]
+ a.connect 0,e,0
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([2,3,5,7,11,13,17,19]) { a.send_in 0,2,2,nt,hm,2,3,5,7 }
+
+ a = FObject["@join 0 {2 2 2 #{nt} # 1 2 3}"]
+ a.connect 0,e,0
+ a.connect 0,b,0
+ y.expect([2,2,2]) { x.expect([1,2,3,1,2,3,1,2]) { a.send_in 0,0,2,2,nt,hm }}
+
+ a = FObject["@ravel"]
+ b = FObject["@dim"]
+ be = FObject["@export_list"]
+ bx = Expect.new
+ a.connect 0,e,0
+ a.connect 0,b,0
+ b.connect 0,be,0
+ be.connect 0,bx,0
+ bx.expect([9]) {
+ x.expect([1,2,3,2,4,6,3,6,9]) {
+ o = FObject["@outer *"]
+ o.connect 0,a,0
+ o.send_in 1,1,2,3
+ o.send_in 0,1,2,3
+ }
+ }
+
+ a = FObject["@grade"]
+ a.connect 0,e,0
+ x.expect([0,2,4,6,8,9,7,5,3,1]) { a.send_in 0, 0,9,1,8,2,7,3,6,4,5 }
+ x.expect([0,9,1,8,2,7,3,6,4,5]) { a.send_in 0, 0,2,4,6,8,9,7,5,3,1 }
+ x.expect([7,6,5,4,3,2,1,0]) { a.send_in 0, 7,6,5,4,3,2,1,0 }
+
+ a = FObject["@grade"]
+ b = FObject["@fold +"]
+ a.connect 0,b,0
+ b.connect 0,e,0
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { (rand*0x10000).to_i }}
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { (rand*0x10).to_i }}
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { 0 }}
+
+ a = FObject["@perspective"]
+ a.connect 0,e,0
+ c = []
+ 8.times {|v|
+ 3.times {|i|
+ c << (v[i] * 1000 - 500) + (if i==2 then 2000 else 0 end)
+ }
+ }
+ x.expect([
+ -85,-85,85,-85,-85,85,85,85,
+ -51,-51,51,-51,-51,51,51,51]) {
+ a.send_in 0, 8,3,hm,*c }
+
+# regressiontests for past bugs
+ a = FObject["@inner"] # that's it.
+end
+
+def test_rtmetro
+ rt = FObject["rtmetro 1000"]
+ pr = FObject["rubyprint"]
+ rt.connect 0,pr,0
+ GridFlow.post "trying to start the rtmetro"
+ rt.send_in 0,1
+ $mainloop.timers.after(10.0) {
+ GridFlow.post "trying to stop the rtmetro (after 10 sec delay)"
+ rt.send_in 0,0
+ }
+ $mainloop.loop
+end
+
+def test_print
+ i = FObject["@redim {3}"]
+ pr = FObject["@print"]
+# pr = GridFlow::RubyPrint.new
+ i.connect 0,pr,0
+ i.send_in 0, 85, 170, 255
+ i.send_in 1, 3, 3
+ i.send_in 0, 1, 0, 0, 0
+ i.send_in 1, 2, 2, 2
+ i.send_in 0, 2, 3, 5, 7, 11, 13, 17, 19
+end
+
+class Barf < GridObject
+ def _0_rgrid_begin
+ raise "barf"
+ end
+ install_rgrid 0
+ install "barf", 1, 0
+end
+
+def test_nonsense
+# (a = FObject["@! abs"]).connect 0,e,0
+# x.expect_error {
+# a.send_in 1, 42,42 }
+
+ a = FObject["@import {3}"]
+ b = Barf.new
+ a.connect 0,b,0
+ begin
+ a.send_in 0, 1, 2, 3
+ rescue StandardError
+ nil
+ else
+ raise "Expected StandardError"
+ end
+ p b.inlet_dim(0)
+end
+
+def test_store
+ a = FObject["@in file #{$imdir}/teapot.png"]
+ b = FObject["@store"]
+ c = FObject["@out x11"]
+ a.connect 0,b,1
+ a.send_in 0,"cast uint8"
+ a.send_in 0
+ b.connect 0,c,0
+ d = FObject["@for {0 0} {256 256} {1 1}"]
+ e = FObject["@ ^ 85"]
+ d.connect 0,e,0
+ e.connect 0,b,0
+ f = FObject["fps detailed"]
+ c.connect 0,f,0
+ pr = FObject["rubyprint"]
+ f.connect 0,pr,0
+ GridFlow.verbose = false
+ 256.times {|t|
+ e.send_in 1,t
+ d.send_in 0
+ }
+end
+
+# generates recursive checkerboard pattern (munchies) in bluish colours.
+class Munchies < FPatcher
+ @fobjects = ["fork","fork","@for 0 64 1","@for 0 64 1","@for 2 5 1",
+ "@outer ^","@outer *"]
+ @wires = [-1,0,0,0, 0,0,1,0, 1,1,4,0, 4,0,6,1,
+ 1,0,3,0, 3,0,5,1, 0,0,2,0, 2,0,5,0, 5,0,6,0, 6,0,-1,0 ]
+ def initialize() super end
+ install "munchies",1,1
+end
+
+def test_munchies
+ m=Munchies.new
+ gout = FObject["@out quartz"]
+ m.connect 0,gout,0
+ m.send_in 0
+ $mainloop.loop
+end
+
+def test_image command
+ gin = FObject["@in"]
+ gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+# 31.times {
+ 3.times {
+ gin.send_in 0,"open #{command}"
+ gout.send_in 0,"timelog 1"
+ gin.send_in 0
+ }
+ FObject["@global"].send_in 0, "profiler_dump"
+ $mainloop.loop
+end
+
+def test_ppm2
+ gin = FObject["@in"]
+ store = FObject["@store"]
+ pa = FObject["@convolve << + 0"]
+ pb = FObject["@ / 9"]
+ ra = FObject["@redim {3 3}"]
+ gout = FObject["@out x11"]
+ gin.connect 0,store,1
+ store.connect 0,pa,0
+ pa.connect 0,pb,0
+ pb.connect 0,gout,0
+ ra.connect 0,pa,1
+ ra.send_in 0,"0 0"
+ gout.send_in 0,"timelog 1"
+ gin.send_in 0,"open file #{$imdir}/teapot.png"
+# gin.send_in 0,"open file #{$imdir}/g001.jpg"
+ gin.send_in 0
+# 40.times { store.send_in 0 }
+ loop { store.send_in 0 }
+ v4j = FObject["@global"]
+ v4j.send_in 0,"profiler_dump"
+# $mainloop.loop
+end
+
+def test_foo
+ foo = FObject["@for {0 0} {64 64} {1 1}"]
+ che = FObject["@checkers"]
+ sca = FObject["@scale_by {5 3}"]
+ out = FObject["@out x11"]
+ foo.connect 0,che,0
+ che.connect 0,sca,0
+ sca.connect 0,out,0
+ foo.send_in 0
+ $mainloop.loop
+end
+
+def test_anim(*msgs)
+ GridFlow.verbose = false
+ gin = FObject["@in"]
+ #gout1 = FObject["@out x11"]
+ gout1 = FObject["@out sdl"]
+ #gout1 = FObject["@out quicktime file test.mov"]
+ #gout1.send_in 0, :codec, :jpeg
+ fps = FObject["fps detailed"]
+ rpr = FObject["rubyprint"]
+ gout1.connect 0,fps,0
+ #fps.connect 0,rpr,0
+=begin
+ gout1 = FObject["@downscale_by {3 2}"]
+ gout2 = FObject["@rgb_to_greyscale"]
+ gout3 = FObject["@out aalib X11 -height 60 -width 132"]
+ gout1.connect 0,gout2,0
+ gout2.connect 0,gout3,0
+=end
+
+ rpr = FObject["rubyprint"]
+# gin.connect 1,rpr,0
+
+ gin.connect 0,gout1,0
+=begin
+ layer=FObject["@layer"]
+ gin.connect 0,layer,0
+ layer.connect 0,gout1,0
+ check=FObject["@checkers"]
+ phor=FObject["@for {0 0} {256 256} {1 1}"]
+ phor.connect 0,check,0
+ check.connect 0,layer,1
+ phor.send_in 0
+=end
+
+# scale = FObject["@scale_by 3"]
+# gin.connect 0,scale,0
+# scale.connect 0,gout1,0
+
+# pr = FObject["rubyprint time"]; gout.connect 0,pr,0
+ msgs.each {|m| gin.send_in 0,m }
+ gin.send_in 0, "cast uint8"
+# gout.send_in 0,"timelog 1"
+ d=Time.new
+ frames=2000
+ frames.times {|n|
+ #GridFlow.post "%d", n
+ gin.send_in 0
+ #gin.send_in 0, rand(1000)
+ }
+# loop { gin.send_in 0 }
+# metro = FObject["rtmetro 80"]
+# metro.connect 0,gin,0
+# metro.send_in 0,1
+# $mainloop.loop
+
+ d=Time.new-d
+ printf "%d frames in %.6f seconds (avg %.6f ms, %.6f fps)\n",
+ frames, d, 1000*d/frames, frames/d
+# global.send_in 0,"dfgdfgdkfjgl"
+ gout1.send_in 0, :close
+ global = FObject["@global"]
+ global.send_in 0,"profiler_dump"
+end
+
+class TestTCP < FObject
+ attr_accessor :idle
+ def initialize
+ @idle = true
+ end
+ def _0_bang
+ # GridFlow.gfpost "tick"
+ # avoid recursion
+ $mainloop.timers.after(0) {
+ ($in_client.send_in 0; @idle=false) if @idle
+ }
+ end
+ install "tcptest",1,1
+end
+
+def test_tcp
+ if fork
+ # client (is receiving)
+ GridFlow.post_header = "[client] "
+ $in_client = in1 = FObject["@in"]
+ out = FObject["@out x11"]
+ in1.connect 0,out,0
+ out.send_in 0,"timelog 1"
+ out.send_in 0,"autodraw 2"
+ GridFlow.post "test: waiting 1 second"
+ sleep 1
+ p "HI"
+ #in1.send_in 0,"open grid tcp localhost #{$port}"
+ in1.send_in 0,"open grid tcp 127.0.0.1 #{$port}"
+ p "HI"
+
+ test_tcp = TestTCP.new
+
+ out.connect 0,test_tcp,0
+ test_tcp.connect 0,in1,0
+
+ GridFlow.post "entering mainloop..."
+ $mainloop.loop
+ else
+ # server (is sending)
+ GridFlow.post_header = "[server] "
+ $in1_server = in1 = FObject["@in"]
+ $in2_server = in2 = FObject["@in"]
+ $out = out = FObject["@out"]
+ toggle = 0
+ in1.connect 0,out,0
+ in2.connect 0,out,0
+ in1.send_in 0,"open #{$imdir}/r001.jpg"
+ in2.send_in 0,"open #{$imdir}/b001.jpg"
+ out.send_in 0,"open grid tcpserver #{$port}"
+ out.send_in 0,"type uint8"
+ test_tcp = GridFlow::FObject.new
+ def test_tcp._0_bang
+ # GridFlow.post "tick"
+ @toggle ||= 0
+ # avoid recursion
+ $mainloop.timers.after(0.01) {
+ if $out.format.stream
+ if @toggle==0; $in1_server else $in2_server end.send_in 0
+ @toggle ^= 1
+ end
+ _0_bang
+ }
+ end
+ out.connect 0,test_tcp,0
+ test_tcp.send_in 0
+ GridFlow.post "entering mainloop..."
+ $mainloop.loop
+ end
+end
+
+def test_layer
+
+ gin = FObject["@in png file ShaunaKennedy/atmosphere-bleu.png"]
+# gin1 = FObject["@in file #{$imdir}/r001.jpg"]
+# gin = FObject["@join 2 {240 320 1 # 128}"]
+# gin1.connect 0,gin,0
+
+# gfor = FObject["@for {0 0} {120 160} {1 1}"]
+# gfor = FObject["@for {0 0} {240 320} {1 1}"]
+ gfor = FObject["@for {0 0} {480 640} {1 1}"]
+ gche = FObject["@checkers"]
+
+ gove = FObject["@layer"]
+# gove = FObject["@fold + {0 0 0 0}"]
+# gout = FObject["@print"]
+# gove = FObject["@inner2 * + 0 {3 4 # 1 0 0 0 0}"]
+# gout = FObject["@out sdl"]
+ gout = FObject["@out x11"]
+
+ gin.connect 0,gove,0
+ gfor.connect 0,gche,0
+ gche.connect 0,gove,1
+ gove.connect 0,gout,0
+
+ gfor.send_in 0
+
+ fps = FObject["fps detailed"]
+ pr = FObject["rubyprint"]
+ gout.connect 0,fps,0
+ fps.connect 0,pr,0
+
+ loop{gin.send_in 0}
+# gin.send_in 0
+# gin1.send_in 0
+ $mainloop.loop
+end
+
+Images = [
+ "png file opensource.png",
+# "png file ShaunaKennedy/atmosphere.png",
+# "targa file #{$imdir}/tux.tga",
+# "png file #{$imdir}/lena.png",
+ "file #{$imdir}/ruby0216.jpg",
+ "file #{$imdir}/g001.jpg",
+ "file #{$imdir}/teapot.tga",
+ "grid gzfile #{$imdir}/foo.grid.gz",
+ "grid gzfile #{$imdir}/foo2.grid.gz",
+# "videodev /dev/video0",
+]
+
+def test_formats_speed
+ gin = FObject["@in"]
+ gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+ GridFlow.verbose=false
+ t1=[]
+ Images.each {|command|
+ gin.send_in 0,"open #{command}"
+ t0 = Time.new
+ 10.times {gin.send_in 0}
+ t1 << (Time.new - t0)
+ sleep 1
+ }
+ p t1
+end
+
+def test_formats
+ gin = FObject["@in"]
+ gout = FObject["@out window"]
+ gs = FObject["@ + {int16 # 0}"]
+ gin.connect 0,gs,0
+ gs.connect 0,gout,0
+# GridFlow.verbose=false
+ t1=[]
+ Images.each {|command|
+ gin.send_in 0,"open #{command}"
+ gin.send_in 0,"cast int16"
+ # test for load, rewind, load
+ 5.times {|x| gs.send_in 1, [:int16, '#'.intern, x*128]; gin.send_in 0}
+ # test for filehandle leak
+ #1000.times { gin.send_in 0,"open #{command}" }
+ sleep 1
+ }
+ p t1
+end
+
+def test_rewind
+ gin = FObject["@in videodev /dev/video1 noinit"]
+ gin.send_in 0, "transfer read"
+ gout = FObject["@out ppm file /tmp/foo.ppm"]
+# gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+ loop {
+ gin.send_in 0
+ gout.send_in 0, "rewind"
+ }
+end
+
+def test_formats_write
+ # read files, store and save them, reload, compare, expect identical
+ a = FObject["@in"]
+ b = FObject["@out"]; a.connect 0,b,0
+ c = FObject["@ -"]; a.connect 0,c,1
+ d = FObject["@in"]; d.connect 0,c,0
+ e = FObject["@fold +"]; c.connect 0,e,0
+ f = FObject["@fold +"]; e.connect 0,f,0
+ g = FObject["@fold +"]; f.connect 0,g,0
+ h = FObject["@ / 15000"]; g.connect 0,h,0
+ i = FObject["@export_list"]; h.connect 0,i,0
+ x = Expect.new; i.connect 0,x,0
+ [
+ ["ppm file", "#{$imdir}/g001.jpg"],
+ ["targa file", "#{$imdir}/teapot.tga"],
+ ["targa file", "#{$imdir}/tux.tga"],
+ ["jpeg file", "#{$imdir}/ruby0216.jpg"],
+ ["grid gzfile", "#{$imdir}/foo.grid.gz", "endian little"],
+ ["grid gzfile", "#{$imdir}/foo.grid.gz", "endian big"],
+ ].each {|type,file,*rest|
+ a.send_in 0, "open #{type} #{file}"
+ b.send_in 0, "open #{type} /tmp/patate"
+ rest.each {|r| b.send_in 0,r }
+ a.send_in 0
+ b.send_in 0, "close"
+ raise "written file does not exist" if not File.exist? "/tmp/patate"
+ d.send_in 0, "open #{type} /tmp/patate"
+ x.expect([0]) { d.send_in 0 }
+# d.send_in 0
+ }
+end
+
+def test_mpeg_write
+ a = FObject["@in ppm file /opt/mex/r.ppm.cat"]
+ b = FObject["@out x11"]
+ a.connect 0,b,0
+ loop{a.send_in 0}
+end
+
+def test_headerless
+ gout = FObject["@out"]
+ gout.send_in 0, "open grid file #{$imdir}/hello.txt"
+ gout.send_in 0, "headerless"
+ gout.send_in 0, "type uint8"
+ gout.send_in 0, "104 101 108 108 111 32 119 111 114 108 100 33 10"
+ gout.send_in 0, "close"
+ gin = FObject["@in"]
+ pr = FObject["@print"]
+ gin.connect 0,pr,0
+ gin.send_in 0, "open grid file #{$imdir}/hello.txt"
+ gin.send_in 0, "headerless 13"
+ gin.send_in 0, "type uint8"
+ gin.send_in 0
+end
+
+
+def test_sound
+# o2 = FObject["@ * 359"] # @ 439.775 Hz
+# o1 = FObject["@for 0 44100 1"] # 1 sec @ 1.225 Hz ?
+ o1 = FObject["@for 0 4500 1"]
+ o2 = FObject["@ * 1600"] # @ 439.775 Hz
+ o3 = FObject["@ sin* 255"]
+ o4 = FObject["@ gamma 400"]
+ o5 = FObject["@ << 7"]
+ out = FObject["@out"]
+ o1.connect 0,o2,0
+ o2.connect 0,o3,0
+ o3.connect 0,o4,0
+ o4.connect 0,o5,0
+ o5.connect 0,out,0
+# out.send_in 0,"open raw file /dev/dsp"
+ out.send_in 0,"open grid file /dev/dsp"
+ out.send_in 0,"type int16"
+ x=0
+ loop {
+ o4.send_in 1, x
+ o1.send_in 0
+ x+=10
+ }
+end
+
+include Math
+def test_polygon
+ o1 = FObject["@for 0 5 1"]
+ o2 = FObject["@ * 14400"]
+ o3 = FObject["@outer + {0 9000}"]
+ o4 = FObject["@ +"]
+ o5 = FObject["@ cos* 112"]
+ o6 = FObject["@ + {120 160}"]
+ poly = FObject["@draw_polygon + {3 uint8 # 255}"]
+if false
+ out1 = FObject["@cast int32"]
+ out2 = FObject["@solarize"]
+# out1 = FObject["@downscale_by 2 smoothly"]
+ out3 = FObject["@out x11"]
+ out1.connect 0,out2,0
+ out2.connect 0,out3,0
+else
+ out1 = FObject["@out x11"]
+ fps = FObject["fps detailed cpu"]
+ out1.connect 0,fps,0
+ pr = FObject["rubyprint"]
+ fps.connect 0,pr,0
+end
+ store = FObject["@store"]; store.send_in 1, "240 320 3 uint8 # 0"
+# store2 = FObject["@store"]
+ store.connect 0,poly,0
+ poly.connect 0,store,1
+# store2.connect 0,store,1
+ o1.connect 0,o2,0
+ o2.connect 0,o3,0
+ o3.connect 0,o4,0
+ o4.connect 0,o5,0
+ o5.connect 0,o6,0
+ o6.connect 0,poly,2
+# cast = FObject["@cast int32"]
+# poly.connect 0,cast,0
+# cast.connect 0,out1,0
+ poly.connect 0,out1,0
+ x=0
+ GridFlow.verbose=false
+ task=proc {
+ o4.send_in 1, 5000*x
+ o5.send_in 1, 200+200*sin(x)
+ poly.send_in 1,:list,:uint8, *(0..2).map{|i| 4+4*cos(0.2*x+i*PI*2/3) }
+ o1.send_in 0
+ store.send_in 0
+# store2.send_in 0
+ x+=1
+ if x<1000 then $mainloop.timers.after(0.0) {task[]}
+ else GridGlobal.new.send_in 0,"profiler_dump"; exit end
+ }
+ task[]
+ $mainloop.loop
+end
+
+class FRoute2 < FObject
+ def initialize(selector)
+ @selector = selector.to_s
+ end
+ def method_missing(sym,*a)
+ sym=sym.to_s
+ if sym =~ /^_0_(.*)/
+ send_out((if $1==@selector then 0 else 1 end), $1.intern, *a)
+ else super end
+ end
+ install "route2", 1, 2
+end
+
+def test_aalib
+# gin = FObject["@in ppm file #{$imdir}/r001.jpg"]
+ gin = FObject["@in ppm file #{$animdir}/b.jpg.cat"]
+ grey = FObject["@rgb_to_greyscale"]
+ cont = FObject["@ << 1"]
+ clip = FObject["@ min 255"]
+ gout = FObject["@out aalib X11"]
+ sto = FObject["@store"]
+ op = FObject["aa_fill_with_text {localhost 4242}"]
+ filt = FObject["route2 grid"]
+ gin.connect 0,grey,0
+ grey.connect 0,cont,0
+ cont.connect 0,clip,0
+ clip.connect 0,gout,0
+ gout.connect 0,filt,0
+ filt.connect 0,sto,1
+ sto.connect 0,op,0
+ op.connect 0,gout,0
+ gout.send_in 0, :autodraw, 0
+ GridFlow.verbose = false
+ task=proc{
+ gin.send_in 0
+ gout.send_in 0, :dump
+ sto.send_in 0
+ gout.send_in 0, :draw
+ $mainloop.timers.after(0.1,&task)
+ }
+ task[]
+ $mainloop.loop
+end
+
+def test_store2
+ o = [
+ FObject["@for {0 0} {240 320} {1 1}"],
+ FObject["@ / 2"],
+ FObject["@ + 0"],
+ FObject["@ + 0"],
+ FObject["@store"],
+ FObject["@out x11"]]
+ (0..4).each {|x| o[x].connect 0,o[x+1],0 }
+ q = FObject["@in ppm file images/r001.jpg"]
+ q.connect 0,o[4],1
+ q.send_in 0
+ o[0].send_in 0
+ $mainloop.loop
+end
+
+def test_remap
+ rem = FObject["@remap_image"]
+ rot = FObject["@rotate 4000"]
+ rem.connect 1,rot,0
+ rot.connect 0,rem,1
+ gin = FObject["@in ppm file #{$imdir}/teapot.png"]
+ gout = FObject["@out x11"]
+ gin.connect 0,rem,0
+ rem.connect 0,gout,0
+ gin.send_in 0
+ $mainloop.loop
+end
+
+def test_asm
+ GridFlow.verbose=false
+ a = FObject["@in ppm file images/r001.jpg"]
+ aa = FObject["@cast uint8"]
+ b = FObject["@store"]
+ d = FObject["@store"]
+ a.connect 0,aa,0
+ aa.connect 0,b,1
+ aa.connect 0,d,1
+ a.send_in 0
+ c = FObject["@ + {uint8 # 0}"]
+ t0 = Time.new; 1000.times {b.send_in 0}; t1 = Time.new-t0
+ t1 *= 1
+ b.connect 0,c,0
+ stuff=proc{
+ 3.times{
+ t0 = Time.new; 1000.times {b.send_in 0}; t2 = Time.new-t0
+ t2 *= 1
+ GridFlow.post " %f %f %f", t1, t2, t2-t1
+ }
+ }
+ puts "map:"
+ stuff[]
+ d.connect 0,c,1 # for zip
+ d.send_in 0
+ puts "zip:"
+ stuff[]
+end
+
+def test_metro
+ o1 = RtMetro.new(1000,:geiger)
+ o2 = RubyPrint.new(:time)
+ o1.connect 0,o2,0
+ o1.send_in 0,1
+ $mainloop.loop
+end
+
+def test_outer
+ o = FObject["@outer + {0}"]
+ o.send_in 0, 25, 240, 320, 3, "#".intern, 42
+ g = FObject["@global"]
+ g.send_in 0, :profiler_dump
+end
+
+def test_jmax_to_pd filename
+ require "gridflow/extra/jmax_format.rb"
+ require "gridflow/extra/puredata_format.rb"
+ jfr = JMaxFileReader.new(File.open(filename),FObject)
+ FObject.broken_ok = true
+ my_patcher = jfr.parse
+# my_patcher.subobjects.each {|x,| x.trigger if LoadBang===x }
+# $mainloop.loop
+# $p=my_patcher; ARGV.clear; load "/home/matju/bin/iruby"
+ filename = File.basename filename
+ filename[File.extname(filename)]=".pd"
+ filename[0,0]="pd_examples/"
+ pfw = PureDataFileWriter.new(filename)
+ pfw.write_patcher my_patcher
+ pfw.close
+end
+
+def test_error
+ x = FObject["@store"]
+ x.send_in 0
+end
+
+if ARGV[0] then
+ name = ARGV.shift
+ send "test_#{name}", *ARGV
+# ARGV.each {|a| send "test_#{a}" }
+ exit 0
+end
+
+#test_polygon
+#test_math
+#test_munchies
+#test_image "grid file #{$imdir}/foo.grid"
+#test_image "grid gzfile #{$imdir}/foo.grid.gz"
+#test_print
+#test_nonsense
+#test_ppm2
+#test_anim "open file #{$imdir}/g001.jpg"#,"loop 0"
+#test_anim "open ppm file #{$animdir}/b.ppm.cat"
+#test_anim "open jpeg file #{$imdir}/rgb.jpeg.cat"
+#test_anim "open quicktime file BLAH"
+#test_anim "open quicktime file #{$imdir}/rgb_uncompressed.mov"
+#test_anim "open quicktime file #{$imdir}/test_mjpega.mov"
+#test_anim "open ppm gzfile motion_tracking.ppm.cat.gz"
+#test_anim "open videodev /dev/video","channel 1","size 480 640"
+#test_anim "open videodev /dev/video1 noinit","transfer read"
+#test_anim "open videodev /dev/video","channel 1","size 120 160"
+#test_anim "open mpeg file /home/matju/net/Animations/washington_zoom_in.mpeg"
+#test_anim "open quicktime file /home/matju/Shauna/part_1.mov"
+#test_anim "open quicktime file #{$imdir}/gt.mov"
+#test_anim "open quicktime file /home/matju/pics/domopers_hi.mov"
+#test_anim "open quicktime file /home/matju/net/c.mov"
+#test_formats
+#test_tcp
+#test_sound
+#test_metro
+#$mainloop.loop
diff --git a/externals/gridflow/bridge/placebo.rb b/externals/gridflow/bridge/placebo.rb
new file mode 100644
index 00000000..3d712d1d
--- /dev/null
+++ b/externals/gridflow/bridge/placebo.rb
@@ -0,0 +1,43 @@
+=begin
+ $Id: placebo.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004,2005 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+class Object
+ def self.dummy(sel)
+ self.module_eval "def #{sel}(*args) GridFlow.post \"dummy #{sel}: %s\", args.inspect end"
+ end
+end
+
+module GridFlow
+ class<<self
+ # def add_creator_2(*args) post "dummy add_creator_2: %s", args.inspect end
+ dummy :add_creator_2
+ def post_string(s) STDERR.puts s end
+ end
+ class Clock
+ def initialize(victim) @victim=victim end
+ dummy :delay
+ end
+ class Pointer
+ dummy :initialize
+ end
+end
diff --git a/externals/gridflow/bridge/puredata.c b/externals/gridflow/bridge/puredata.c
new file mode 100644
index 00000000..dd6fd88f
--- /dev/null
+++ b/externals/gridflow/bridge/puredata.c
@@ -0,0 +1,772 @@
+/*
+ $Id: puredata.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004,2005 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+/*
+'bridge_puredata.c' becomes 'gridflow.pd_linux' which loads Ruby and tells
+Ruby to load the other 95% of Gridflow. GridFlow creates the FObject class
+and then subclasses it many times, each time calling FObject.install(),
+which tells the bridge to register a class in puredata. Puredata
+objects have proxies for each of the non-first inlets so that any inlet
+may receive any distinguished message. All inlets are typed as 'anything',
+and the 'anything' method translates the whole message to Ruby objects and
+tries to call a Ruby method of the proper name.
+*/
+
+#define IS_BRIDGE
+#include "../base/grid.h.fcs"
+/* resolving conflict: T_OBJECT will be PD's, not Ruby's */
+#undef T_OBJECT
+#undef EXTERN
+#include <m_pd.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include "g_canvas.h"
+
+/* **************************************************************** */
+struct BFObject;
+struct FMessage {
+ BFObject *self;
+ int winlet;
+ t_symbol *selector;
+ int ac;
+ const t_atom *at;
+ bool is_init;
+};
+#include <sys/time.h>
+#include <signal.h>
+#include <setjmp.h>
+#define rb_sym_name rb_sym_name_r4j
+static const char *rb_sym_name(Ruby sym) {return rb_id2name(SYM2ID(sym));}
+
+static BuiltinSymbols *syms;
+
+void CObject_freeee (void *victim) {
+ CObject *self = (CObject *)victim;
+ self->check_magic();
+ if (!self->rself) {
+ fprintf(stderr,"attempt to free object that has no rself\n");
+ abort();
+ }
+ self->rself = 0; /* paranoia */
+ delete self;
+}
+
+
+/* can't even refer to the other mGridFlow because we don't link
+ statically to the other gridflow.so */
+static Ruby mGridFlow2=0;
+static Ruby mPointer=0;
+
+\class Pointer < CObject
+struct Pointer : CObject {
+ void *p;
+ Pointer() { assert(!"DYING HORRIBLY"); }
+ Pointer(void *_p) : p(_p) {}
+ \decl Ruby ptr ();
+};
+\def Ruby ptr () { return LONG2NUM(((long)p)); }
+\classinfo {
+ IEVAL(rself,
+"self.module_eval{"
+"def inspect; p=('%08x'%ptr).gsub(/^\\.\\.f/,''); \"#<Pointer:#{p}>\" % ptr; end;"
+"alias to_s inspect }"
+);}
+\end class Pointer
+Ruby Pointer_s_new (void *ptr) {
+ return Data_Wrap_Struct(EVAL("GridFlow::Pointer"), 0, 0, new Pointer(ptr));
+}
+void *Pointer_get (Ruby rself) {
+ DGS(Pointer);
+ return self->p;
+}
+
+static Ruby make_error_message () {
+ char buf[1000];
+ sprintf(buf,"%s: %s",rb_class2name(rb_obj_class(ruby_errinfo)),
+ rb_str_ptr(rb_funcall(ruby_errinfo,SI(to_s),0)));
+ Ruby ary = rb_ary_new2(2);
+ Ruby backtrace = rb_funcall(ruby_errinfo,SI(backtrace),0);
+ rb_ary_push(ary,rb_str_new2(buf));
+ for (int i=0; i<2 && i<rb_ary_len(backtrace); i++)
+ rb_ary_push(ary,rb_funcall(backtrace,SI([]),1,INT2NUM(i)));
+// rb_ary_push(ary,rb_funcall(rb_funcall(backtrace,SI(length),0),SI(to_s),0));
+ return ary;
+}
+
+static int ninlets_of (Ruby qlass) {
+ if (!rb_ivar_defined(qlass,SYM2ID(syms->iv_ninlets))) RAISE("no inlet count");
+ return INT(rb_ivar_get(qlass,SYM2ID(syms->iv_ninlets)));
+}
+
+static int noutlets_of (Ruby qlass) {
+ if (!rb_ivar_defined(qlass,SYM2ID(syms->iv_noutlets))) RAISE("no outlet count");
+ return INT(rb_ivar_get(qlass,SYM2ID(syms->iv_noutlets)));
+}
+
+#ifndef STACK_GROW_DIRECTION
+#define STACK_GROW_DIRECTION -1
+#endif
+extern "C" void Init_stack(VALUE *addr);
+static VALUE *localize_sysstack () {
+ long bp;
+ sscanf(RUBY_STACK_END,"0x%08lx",&bp);
+ //fprintf(stderr,"old RUBY_STACK_END = %08lx\n",bp);
+ // HACK (2004.08.29: alx has a problem; i hope it doesn't get worse)
+ // this rounds to the last word of a 4k block
+ // cross fingers that no other OS does it too different
+ // !@#$ doesn't use STACK_GROW_DIRECTION
+ // bp=((bp+0xfff)&~0xfff)-sizeof(void*);
+ // GAAAH
+ bp=((bp+0xffff)&~0xffff)-sizeof(void*);
+ //fprintf(stderr,"new RUBY_STACK_END = %08lx\n",bp);
+ return (VALUE *)bp;
+}
+
+//****************************************************************
+// BFObject
+
+struct BFObject : t_object {
+ int32 magic; // paranoia
+ Ruby rself;
+ int nin; // per object settings (not class)
+ int nout; // per object settings (not class)
+ t_outlet **out;
+
+ void check_magic () {
+ if (magic != OBJECT_MAGIC) {
+ fprintf(stderr,"Object memory corruption! (ask the debugger)\n");
+ for (int i=-1; i<=1; i++) {
+ fprintf(stderr,"this[0]=0x%08x\n",((int*)this)[i]);
+ }
+ raise(11);
+ }
+ }
+};
+
+static t_class *find_bfclass (t_symbol *sym) {
+ t_atom a[1];
+ SETSYMBOL(a,sym);
+ char buf[4096];
+ if (sym==&s_list) strcpy(buf,"list"); else atom_string(a,buf,sizeof(buf));
+ Ruby v = rb_hash_aref(rb_ivar_get(mGridFlow2, SI(@fclasses)), rb_str_new2(buf));
+ if (v==Qnil) {
+ post("GF: class not found: '%s'",buf);
+ return 0;
+ }
+ if (Qnil==rb_ivar_get(v,SI(@bfclass))) {
+ post("@bfclass missing for '%s'",buf);
+ return 0;
+ }
+ return FIX2PTR(t_class,rb_ivar_get(v,SI(@bfclass)));
+}
+
+static t_class *BFProxy_class;
+
+struct BFProxy : t_object {
+ BFObject *parent;
+ int inlet;
+};
+
+static void Bridge_export_value(Ruby arg, t_atom *at) {
+ if (INTEGER_P(arg)) {
+ SETFLOAT(at,NUM2INT(arg));
+ } else if (SYMBOL_P(arg)) {
+ const char *name = rb_sym_name(arg);
+ SETSYMBOL(at,gensym((char *)name));
+ } else if (FLOAT_P(arg)) {
+ SETFLOAT(at,RFLOAT(arg)->value);
+ } else if (rb_obj_class(arg)==mPointer) {
+ SETPOINTER(at,(t_gpointer*)Pointer_get(arg));
+ } else {
+ RAISE("cannot convert argument of class '%s'",
+ rb_str_ptr(rb_funcall(rb_funcall(arg,SI(class),0),SI(inspect),0)));
+ }
+}
+
+static Ruby Bridge_import_value(const t_atom *at) {
+ t_atomtype t = at->a_type;
+ if (t==A_SYMBOL) {
+ return ID2SYM(rb_intern(at->a_w.w_symbol->s_name));
+ } else if (t==A_FLOAT) {
+ return rb_float_new(at->a_w.w_float);
+ } else if (t==A_POINTER) {
+ return Pointer_s_new(at->a_w.w_gpointer);
+ } else {
+ return Qnil; /* unknown */
+ }
+}
+
+static Ruby BFObject_method_missing_1 (FMessage *fm) {
+ Ruby argv[fm->ac+2];
+ argv[0] = INT2NUM(fm->winlet);
+ argv[1] = ID2SYM(rb_intern(fm->selector->s_name));
+ for (int i=0; i<fm->ac; i++) argv[2+i] = Bridge_import_value(fm->at+i);
+ fm->self->check_magic();
+ rb_funcall2(fm->self->rself,SI(send_in),fm->ac+2,argv);
+ return Qnil;
+}
+
+static Ruby BFObject_rescue (FMessage *fm) {
+ Ruby error_array = make_error_message();
+// for (int i=0; i<rb_ary_len(error_array); i++)
+// post("%s\n",rb_str_ptr(rb_ary_ptr(error_array)[i]));
+ if (fm->self) pd_error(fm->self,"%s",rb_str_ptr(
+ rb_funcall(error_array,SI(join),1,rb_str_new2("\n"))));
+ if (fm->self && fm->is_init) fm->self = 0;
+ return Qnil;
+}
+
+static void BFObject_method_missing (BFObject *bself,
+int winlet, t_symbol *selector, int ac, t_atom *at) {
+ FMessage fm = { bself, winlet, selector, ac, at, false };
+ if (!bself->rself) {
+ pd_error(bself,"message to a dead object. (supposed to be impossible)");
+ return;
+ }
+ rb_rescue2(
+ (RMethod)BFObject_method_missing_1,(Ruby)&fm,
+ (RMethod)BFObject_rescue,(Ruby)&fm,
+ rb_eException,0);
+}
+
+static void BFObject_method_missing0 (BFObject *self,
+t_symbol *s, int argc, t_atom *argv) {
+ BFObject_method_missing(self,0,s,argc,argv);
+}
+
+static void BFProxy_method_missing(BFProxy *self,
+t_symbol *s, int argc, t_atom *argv) {
+ BFObject_method_missing(self->parent,self->inlet,s,argc,argv);
+}
+
+static Ruby BFObject_init_1 (FMessage *fm) {
+ Ruby argv[fm->ac+1];
+ for (int i=0; i<fm->ac; i++) argv[i+1] = Bridge_import_value(fm->at+i);
+
+ if (fm->selector==&s_list) {
+ argv[0] = rb_str_new2("list"); // pd is slightly broken here
+ } else {
+ argv[0] = rb_str_new2(fm->selector->s_name);
+ }
+
+ Ruby rself = rb_funcall2(rb_const_get(mGridFlow2,SI(FObject)),SI([]),fm->ac+1,argv);
+ DGS(FObject);
+ self->bself = fm->self;
+ self->bself->rself = rself;
+
+ int ninlets = self->bself->nin = ninlets_of(rb_funcall(rself,SI(class),0));
+ int noutlets = self->bself->nout = noutlets_of(rb_funcall(rself,SI(class),0));
+
+ for (int i=1; i<ninlets; i++) {
+ BFProxy *p = (BFProxy *)pd_new(BFProxy_class);
+ p->parent = self->bself;
+ p->inlet = i;
+ inlet_new(self->bself, &p->ob_pd, 0,0);
+ }
+ self->bself->out = new t_outlet*[noutlets];
+ for (int i=0; i<noutlets; i++) {
+ self->bself->out[i] = outlet_new(self->bself,&s_anything);
+ }
+ rb_funcall(rself,SI(initialize2),0);
+ return rself;
+}
+
+static void *BFObject_init (t_symbol *classsym, int ac, t_atom *at) {
+ t_class *qlass = find_bfclass(classsym);
+ if (!qlass) return 0;
+ BFObject *bself = (BFObject *)pd_new(qlass);
+ bself->magic = OBJECT_MAGIC;
+ bself->check_magic();
+ FMessage fm = { self: bself, winlet:-1, selector: classsym,
+ ac: ac, at: at, is_init: true };
+ long r = rb_rescue2(
+ (RMethod)BFObject_init_1,(Ruby)&fm,
+ (RMethod)BFObject_rescue,(Ruby)&fm,
+ rb_eException,0);
+ return r==Qnil ? 0 : (void *)bself; // return NULL if broken object
+}
+
+static void BFObject_delete_1 (FMessage *fm) {
+ fm->self->check_magic();
+ if (fm->self->rself) {
+ rb_funcall(fm->self->rself,SI(delete),0);
+ } else {
+ post("BFObject_delete is NOT handling BROKEN object at %08x",(int)fm);
+ }
+}
+
+static void BFObject_delete (BFObject *bself) {
+ bself->check_magic();
+ FMessage fm = { self: bself, winlet:-1, selector: gensym("delete"),
+ ac: 0, at: 0, is_init: false };
+ rb_rescue2(
+ (RMethod)BFObject_delete_1,(Ruby)&fm,
+ (RMethod)BFObject_rescue,(Ruby)&fm,
+ rb_eException,0);
+ bself->magic = 0xDeadBeef;
+}
+
+/* **************************************************************** */
+
+struct RMessage {
+ VALUE rself;
+ ID sel;
+ int argc;
+ VALUE *argv;
+};
+
+// this was called rb_funcall_rescue[...] but recently (ruby 1.8.2)
+// got a conflict with a new function in ruby.
+
+VALUE rb_funcall_myrescue_1(RMessage *rm) {
+ return rb_funcall2(rm->rself,rm->sel,rm->argc,rm->argv);
+}
+
+static Ruby rb_funcall_myrescue_2 (RMessage *rm) {
+ Ruby error_array = make_error_message();
+// for (int i=0; i<rb_ary_len(error_array); i++)
+// post("%s\n",rb_str_ptr(rb_ary_ptr(error_array)[i]));
+ post("%s",rb_str_ptr(rb_funcall(error_array,SI(join),1,rb_str_new2("\n"))));
+ return Qnil;
+}
+
+VALUE rb_funcall_myrescue(VALUE rself, ID sel, int argc, ...) {
+ va_list foo;
+ va_start(foo,argc);
+ VALUE argv[argc];
+ for (int i=0; i<argc; i++) argv[i] = va_arg(foo,VALUE);
+ RMessage rm = { rself, sel, argc, argv };
+ va_end(foo);
+ return rb_rescue2(
+ (RMethod)rb_funcall_myrescue_1,(Ruby)&rm,
+ (RMethod)rb_funcall_myrescue_2,(Ruby)&rm,
+ rb_eException,0);
+}
+
+/* Call this to get a gobj's bounding rectangle in pixels */
+void bf_getrectfn(t_gobj *x, struct _glist *glist,
+int *x1, int *y1, int *x2, int *y2) {
+ BFObject *bself = (BFObject*)x;
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ Ruby a = rb_funcall_myrescue(bself->rself,SI(pd_getrect),1,can);
+ if (TYPE(a)!=T_ARRAY || rb_ary_len(a)<4) {
+ post("bf_getrectfn: return value should be 4-element array");
+ *x1=*y1=*x2=*y2=0;
+ return;
+ }
+ *x1 = INT(rb_ary_ptr(a)[0]);
+ *y1 = INT(rb_ary_ptr(a)[1]);
+ *x2 = INT(rb_ary_ptr(a)[2]);
+ *y2 = INT(rb_ary_ptr(a)[3]);
+}
+
+/* and this to displace a gobj: */
+void bf_displacefn(t_gobj *x, struct _glist *glist, int dx, int dy) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ BFObject *bself = (BFObject *)x;
+ bself->te_xpix+=dx;
+ bself->te_ypix+=dy;
+ rb_funcall_myrescue(bself->rself,SI(pd_displace),3,can,INT2NUM(dx),INT2NUM(dy));
+ canvas_fixlinesfor(glist, (t_text *)x);
+}
+
+/* change color to show selection: */
+void bf_selectfn(t_gobj *x, struct _glist *glist, int state) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_select),2,can,INT2NUM(state));
+}
+
+/* change appearance to show activation/deactivation: */
+void bf_activatefn(t_gobj *x, struct _glist *glist, int state) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_activate),2,can,INT2NUM(state));
+}
+
+/* warn a gobj it's about to be deleted */
+void bf_deletefn(t_gobj *x, struct _glist *glist) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_delete),1,can);
+ canvas_deletelinesfor(glist, (t_text *)x);
+}
+
+/* making visible or invisible */
+void bf_visfn(t_gobj *x, struct _glist *glist, int flag) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ Ruby rself = ((BFObject*)x)->rself;
+ DGS(FObject);
+ self->check_magic();
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_vis),2,can,INT2NUM(flag));
+}
+
+/* field a mouse click (when not in "edit" mode) */
+int bf_clickfn(t_gobj *x, struct _glist *glist,
+int xpix, int ypix, int shift, int alt, int dbl, int doit) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ Ruby ret = rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_click),7,can,
+ INT2NUM(xpix),INT2NUM(ypix),
+ INT2NUM(shift),INT2NUM(alt),
+ INT2NUM(dbl),INT2NUM(doit));
+ if (TYPE(ret) == T_FIXNUM) return INT(ret);
+ post("bf_clickfn: expected Fixnum");
+ return 0;
+}
+
+/* save to a binbuf */
+void bf_savefn(t_gobj *x, t_binbuf *b) {
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_save),1,Qnil);
+}
+
+/* open properties dialog */
+void bf_propertiesfn(t_gobj *x, struct _glist *glist) {
+ Ruby can = PTR2FIX(glist_getcanvas(glist));
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_properties),1,can);
+}
+
+/* get keypresses during focus */
+void bf_keyfn(void *x, t_floatarg fkey) {
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_key),1,INT2NUM((int)fkey));
+}
+
+/* get motion diff during focus */
+void bf_motionfn(void *x, t_floatarg dx, t_floatarg dy) {
+ rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_motion),2,
+ INT2NUM((int)dx), INT2NUM((int)dy));
+}
+
+/* **************************************************************** */
+
+static void BFObject_class_init_1 (t_class *qlass) {
+ class_addanything(qlass,(t_method)BFObject_method_missing0);
+}
+
+\class FObject
+
+static Ruby FObject_s_install2(Ruby rself, Ruby name) {
+ if (TYPE(name)!=T_STRING) RAISE("name must be String");
+ t_class *qlass = class_new(gensym(rb_str_ptr(name)),
+ (t_newmethod)BFObject_init, (t_method)BFObject_delete,
+ sizeof(BFObject), CLASS_DEFAULT, A_GIMME,0);
+ rb_ivar_set(rself, SI(@bfclass), PTR2FIX(qlass));
+ FMessage fm = {0, -1, 0, 0, 0, false};
+ rb_rescue2(
+ (RMethod)BFObject_class_init_1,(Ruby)qlass,
+ (RMethod)BFObject_rescue,(Ruby)&fm,
+ rb_eException,0);
+ return Qnil;
+}
+
+static Ruby FObject_send_out2(int argc, Ruby *argv, Ruby rself) {
+//\def Ruby send_out2(...) {
+ DGS(FObject);
+ BFObject *bself = self->bself;
+ if (!bself) {
+ //post("FObject#send_out2 : bself is NULL, rself=%08x",rself);
+ return Qnil;
+ }
+ bself->check_magic();
+ Ruby qlass = rb_funcall(rself,SI(class),0);
+ int outlet = INT(argv[0]);
+ Ruby sym = argv[1];
+ argc-=2;
+ argv+=2;
+ t_atom sel, at[argc];
+ Bridge_export_value(sym,&sel);
+ for (int i=0; i<argc; i++) Bridge_export_value(argv[i],at+i);
+ t_outlet *out = bself->te_outlet;
+ outlet_anything(bself->out[outlet],atom_getsymbol(&sel),argc,at);
+ return Qnil;
+}
+
+static Ruby FObject_s_set_help (Ruby rself, Ruby path) {
+ path = rb_funcall(path,SI(to_s),0);
+ Ruby qlassid = rb_ivar_get(rself,SI(@bfclass));
+ if (qlassid==Qnil) RAISE("@bfclass==nil ???");
+ t_class *qlass = FIX2PTR(t_class,qlassid);
+ class_sethelpsymbol(qlass,gensym(rb_str_ptr(path)));
+ return Qnil;
+}
+
+static Ruby GridFlow_s_gui (int argc, Ruby *argv, Ruby rself) {
+ if (argc!=1) RAISE("bad args");
+ Ruby command = rb_funcall(argv[0],SI(to_s),0);
+ sys_gui(rb_str_ptr(command));
+ return Qnil;
+}
+
+static Ruby GridFlow_s_bind (Ruby rself, Ruby argv0, Ruby argv1) {
+ if (TYPE(argv0)==T_STRING) {
+#if PD_VERSION_INT < 37
+ RAISE("requires Pd 0.37");
+#else
+ Ruby name = rb_funcall(argv0,SI(to_s),0);
+ Ruby qlassid = rb_ivar_get(rb_hash_aref(rb_ivar_get(mGridFlow2,SI(@fclasses)),name),SI(@bfclass));
+ if (qlassid==Qnil) RAISE("no such class: %s",rb_str_ptr(name));
+ pd_typedmess(&pd_objectmaker,gensym(rb_str_ptr(name)),0,0);
+ t_pd *o = pd_newest();
+ pd_bind(o,gensym(rb_str_ptr(argv1)));
+#endif
+ } else {
+ Ruby rself = argv0;
+ DGS(FObject);
+ t_symbol *s = gensym(rb_str_ptr(argv1));
+ t_pd *o = (t_pd *)(self->bself);
+ //fprintf(stderr,"binding %08x to: \"%s\" (%08x %s)\n",o,rb_str_ptr(argv[1]),s,s->s_name);
+ pd_bind(o,s);
+ }
+ return Qnil;
+}
+
+static Ruby FObject_s_gui_enable (Ruby rself) {
+ Ruby qlassid = rb_ivar_get(rself,SI(@bfclass));
+ if (qlassid==Qnil) RAISE("no class id ?");
+ t_widgetbehavior *wb = new t_widgetbehavior;
+ wb->w_getrectfn = bf_getrectfn;
+ wb->w_displacefn = bf_displacefn;
+ wb->w_selectfn = bf_selectfn;
+ wb->w_activatefn = bf_activatefn;
+ wb->w_deletefn = bf_deletefn;
+ wb->w_visfn = bf_visfn;
+ wb->w_clickfn = bf_clickfn;
+ //wb->w_savefn = bf_savefn;
+ //wb->w_propertiesfn = bf_propertiesfn;
+ class_setwidget(FIX2PTR(t_class,qlassid),wb);
+ return Qnil;
+}
+
+static Ruby FObject_focus (Ruby rself, Ruby canvas_, Ruby x_, Ruby y_) {
+ DGS(FObject);
+ t_glist *canvas = FIX2PTR(t_glist,canvas_);
+ t_gobj *bself = (t_gobj *)(self->bself);
+ int x = INT(x_);
+ int y = INT(y_);
+ glist_grab(canvas,bself, bf_motionfn, bf_keyfn, x,y);
+ return Qnil;
+}
+
+// doesn't use rself but still is aside FObject_focus for symmetry reasons.
+static Ruby FObject_unfocus (Ruby rself, Ruby canvas_) {
+ DGS(FObject);
+ t_glist *canvas = FIX2PTR(t_glist,canvas_);
+ glist_grab(canvas,0,0,0,0,0);
+ return Qnil;
+}
+
+static Ruby FObject_add_inlets (Ruby rself, Ruby n_) {
+ DGS(FObject);
+ if (!self->bself) RAISE("there is no bself");
+ int n = INT(n_);
+ for (int i=self->bself->nin; i<self->bself->nin+n; i++) {
+ BFProxy *p = (BFProxy *)pd_new(BFProxy_class);
+ p->parent = self->bself;
+ p->inlet = i;
+ inlet_new(self->bself, &p->ob_pd, 0,0);
+ }
+ self->bself->nin+=n;
+ return Qnil;
+}
+
+static Ruby FObject_add_outlets (Ruby rself, Ruby n_) {
+ DGS(FObject);
+ if (!self->bself) RAISE("there is no bself");
+ int n = INT(n_);
+ t_outlet **oldouts = self->bself->out;
+ self->bself->out = new t_outlet*[self->bself->nout+n];
+ memcpy(self->bself->out,oldouts,self->bself->nout*sizeof(t_outlet*));
+ for (int i=self->bself->nout; i<self->bself->nout+n; i++) {
+ self->bself->out[i] = outlet_new(self->bself,&s_anything);
+ }
+ self->bself->nout+=n;
+ return Qnil;
+}
+
+static Ruby bridge_add_to_menu (int argc, Ruby *argv, Ruby rself) {
+ if (argc!=1) RAISE("bad args");
+ Ruby name = rb_funcall(argv[0],SI(to_s),0);
+ Ruby qlassid = rb_ivar_get(rb_hash_aref(rb_ivar_get(mGridFlow2,SI(@fclasses)),name),SI(@bfclass));
+ if (qlassid==Qnil) RAISE("no such class: %s",rb_str_ptr(name));
+ //!@#$
+ return Qnil;
+}
+
+static Ruby GridFlow_s_add_creator_2 (Ruby rself, Ruby name_) {
+ t_symbol *name = gensym(rb_str_ptr(rb_funcall(name_,SI(to_s),0)));
+ class_addcreator((t_newmethod)BFObject_init,name,A_GIMME,0);
+ return Qnil;
+}
+
+static Ruby FObject_get_position (Ruby rself, Ruby canvas) {
+ DGS(FObject);
+ t_text *bself = (t_text *)(self->bself);
+ t_glist *c = FIX2PTR(t_glist,canvas);
+ float x0,y0;
+ if (c->gl_havewindow || !c->gl_isgraph) {
+ x0=bself->te_xpix;
+ y0=bself->te_ypix;
+ }
+ else { // graph-on-parent: have to zoom
+ float zx = float(c->gl_x2 - c->gl_x1) / (c->gl_screenx2 - c->gl_screenx1);
+ float zy = float(c->gl_y2 - c->gl_y1) / (c->gl_screeny2 - c->gl_screeny1);
+ x0 = glist_xtopixels(c, c->gl_x1 + bself->te_xpix*zx);
+ y0 = glist_ytopixels(c, c->gl_y1 + bself->te_ypix*zy);
+ }
+ Ruby a = rb_ary_new();
+ rb_ary_push(a,INT2NUM((int)x0));
+ rb_ary_push(a,INT2NUM((int)y0));
+ return a;
+}
+
+\classinfo {}
+\end class FObject
+
+//****************************************************************
+
+\class Clock < CObject
+struct Clock : CObject {
+ t_clock *serf;
+ Ruby owner; /* copy of ptr that serf already has, for marking */
+ \decl void set (double systime);
+ \decl void delay(double delaytime);
+ \decl void unset();
+};
+
+void Clock_fn (Ruby rself) { rb_funcall_myrescue(rself,SI(call),0); }
+void Clock_mark (Clock *self) { rb_gc_mark(self->owner); }
+void Clock_free (Clock *self) { clock_free(self->serf); CObject_freeee(self); }
+
+Ruby Clock_s_new (Ruby qlass, Ruby owner) {
+ Clock *self = new Clock();
+ self->rself = Data_Wrap_Struct(qlass, Clock_mark, Clock_free, self);
+ self->serf = clock_new((void*)owner,(t_method)Clock_fn);
+ self->owner = owner;
+ return self->rself;
+}
+
+\def void set (double systime) { clock_set(serf, systime); }
+\def void delay(double delaytime) { clock_delay(serf,delaytime); }
+\def void unset() { clock_unset(serf); }
+
+\classinfo {}
+\end class Clock
+
+//****************************************************************
+
+Ruby GridFlow_s_post_string (Ruby rself, Ruby string) {
+ if (TYPE(string)!=T_STRING) RAISE("not a string!");
+ post("%s",rb_str_ptr(string));
+ return Qnil;
+}
+
+#define SDEF(_name1_,_name2_,_argc_) \
+ rb_define_singleton_method(mGridFlow2,_name1_,(RMethod)GridFlow_s_##_name2_,_argc_)
+
+Ruby gf_bridge_init (Ruby rself) {
+ Ruby ver = EVAL("GridFlow::GF_VERSION");
+ if (strcmp(rb_str_ptr(ver), GF_VERSION) != 0) {
+ RAISE("GridFlow version mismatch: "
+ "main library is '%s'; bridge is '%s'",
+ rb_str_ptr(ver), GF_VERSION);
+ }
+ syms = FIX2PTR(BuiltinSymbols,rb_ivar_get(mGridFlow2,SI(@bsym)));
+ Ruby fo = EVAL("GridFlow::FObject");
+ rb_define_singleton_method(fo,"install2",(RMethod)FObject_s_install2,1);
+ rb_define_singleton_method(fo,"gui_enable", (RMethod)FObject_s_gui_enable, 0);
+ rb_define_singleton_method(fo,"set_help", (RMethod)FObject_s_set_help, 1);
+ rb_define_method(fo,"get_position",(RMethod)FObject_get_position,1);
+ rb_define_method(fo,"send_out2", (RMethod)FObject_send_out2,-1);
+ rb_define_method(fo,"send_out2", (RMethod)FObject_send_out2,-1);
+ rb_define_method(fo,"add_inlets", (RMethod)FObject_add_inlets, 1);
+ rb_define_method(fo,"add_outlets", (RMethod)FObject_add_outlets, 1);
+ rb_define_method(fo,"unfocus", (RMethod)FObject_unfocus, 1);
+ rb_define_method(fo, "focus", (RMethod)FObject_focus, 3);
+
+ SDEF("post_string",post_string,1);
+ SDEF("add_creator_2",add_creator_2,1);
+ SDEF("gui",gui,-1);
+ SDEF("bind",bind,2);
+ // SDEF("add_to_menu",add_to_menu,-1);
+
+ \startall
+ rb_define_singleton_method(EVAL("GridFlow::Clock" ),"new", (RMethod)Clock_s_new, 1);
+ rb_define_singleton_method(EVAL("GridFlow::Pointer"),"new", (RMethod)Pointer_s_new, 1);
+ mPointer = EVAL("GridFlow::Pointer");
+ EVAL("class<<GridFlow;attr_accessor :config; end");
+ EVAL("GridFlow.config = {'PUREDATA_PATH' => %{"PUREDATA_PATH"}}");
+ return Qnil;
+}
+
+struct BFGridFlow : t_object {};
+
+t_class *bindpatcher;
+static void *bindpatcher_init (t_symbol *classsym, int ac, t_atom *at) {
+ t_pd *bself = pd_new(bindpatcher);
+ if (ac!=1 || at->a_type != A_SYMBOL) {
+ post("bindpatcher: oops");
+ } else {
+ t_symbol *s = atom_getsymbol(at);
+ post("binding patcher to: %s",s->s_name);
+ pd_bind((t_pd *)canvas_getcurrent(),s);
+ }
+ return bself;
+}
+
+extern "C" void gridflow_setup () {
+ char *foo[] = {"Ruby-for-PureData","-w","-e",";"};
+ post("setting up Ruby-for-PureData...");
+/*
+ post("pd_getfilename() = %s", pd_getfilename()->s_name);
+ post("pd_getdirname() = %s", pd_getdirname()->s_name);
+ post("canvas_getcurrentdir() = %p", canvas_getcurrentdir());
+ char *dirresult = new char[242];
+ char *nameresult;
+ int fd = open_via_path("","gridflow",".so",dirresult,&nameresult,242,1);
+ post("open_via_path: fd=%d dirresult=\"%s\" nameresult=\"%s\"",fd,dirresult,nameresult);
+ delete[] dirresult;
+*/
+ ruby_init();
+ Init_stack(localize_sysstack());
+ ruby_options(COUNT(foo),foo);
+ post("we are using Ruby version %s",rb_str_ptr(EVAL("RUBY_VERSION")));
+ Ruby cData = rb_const_get(rb_cObject,SI(Data));
+ BFProxy_class = class_new(gensym("ruby_proxy"),
+ NULL,NULL,sizeof(BFProxy),CLASS_PD|CLASS_NOINLET, A_NULL);
+ class_addanything(BFProxy_class,BFProxy_method_missing);
+ rb_define_singleton_method(cData,"gf_bridge_init",
+ (RMethod)gf_bridge_init,0);
+
+ mGridFlow2 = EVAL(
+ "module GridFlow; class<<self; attr_reader :bridge_name; end; "
+ "@bridge_name = 'puredata'; self end");
+ post("(done)");
+ if (!
+ EVAL("begin require 'gridflow'; true; rescue Exception => e;\
+ STDERR.puts \"[#{e.class}] [#{e.message}]:\n#{e.backtrace.join'\n'}\"; false; end"))
+ {
+ post("ERROR: Cannot load GridFlow-for-Ruby (gridflow.so)\n");
+ return;
+ }
+ bindpatcher = class_new(gensym("bindpatcher"),
+ (t_newmethod)bindpatcher_init, 0, sizeof(t_object),CLASS_DEFAULT,A_GIMME,0);
+}
+
+
diff --git a/externals/gridflow/bridge/puredata.rb b/externals/gridflow/bridge/puredata.rb
new file mode 100644
index 00000000..fffda1c2
--- /dev/null
+++ b/externals/gridflow/bridge/puredata.rb
@@ -0,0 +1,194 @@
+=begin
+ $Id: puredata.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+# <matju> alx1: in puredata.rb, just after the header, you have a %w() block,
+# and in it you write the name of your object, and if your helpfile is not
+# named like your object, then you add an equal sign and the filename
+
+#!@#$ DON'T PUT ABSTRACTIONS IN THE %w() !!!
+# @mouse=help_mouse @motion_detection=help_motion_detect @fade=help_fade
+# @apply_colormap_channelwise @checkers @complex_sq @contrast
+# @posterize @ravel @greyscale_to_rgb @rgb_to_greyscale @solarize @spread
+#rgb_to_yuv=#rgb_to_yuv_and_#yuv_to_rgb
+#yuv_to_rgb=#rgb_to_yuv_and_#yuv_to_rgb
+#clip #contrast #fade #numop #remap_image
+
+# NEW help files
+#!@#$ (what's #+-help.pd ? #print-help2.pd ?)
+%w(
+ # #cast #dim #reverse
+ #pack=#unpack-#pack
+ #unpack=#unpack-#pack
+ renamefile
+ #in plotter_control
+ listelement exec ls #print unix_time
+).each {|name|
+ if name =~ /=/ then name,file = name.split(/=/) else file = name end
+ begin
+ x = GridFlow.fclasses[name]
+ x.set_help "gridflow/flow_classes/#{file}-help.pd"
+ rescue Exception => e
+ GridFlow.post "for [#{name}], #{e.class}: #{e}" # + ":\n" + e.backtrace.join("\n")
+ end
+}
+
+# OLD help files
+%w(
+ @cast
+ @convolve @downscale_by @draw_polygon @export=@importexport
+ @finished @fold @for @global @grade
+ @import=@importexport @inner=@foldinnerouter
+ @in=@inout @join @layer @outer=@foldinnerouter @out=@inout
+ @! @perspective printargs @print @redim
+ rubyprint @scale_by @scale_to @scan
+ @store
+).each {|name|
+ if name =~ /=/ then name,file = name.split(/=/) else file = name end
+ begin
+ GridFlow.fclasses[name].set_help "gridflow/#{file}.pd"
+ rescue Exception => e
+ GridFlow.post "ruby #{e.class}: #{e}:\n" + e.backtrace.join("\n")
+ end
+}
+
+#GridFlow.gui "frame .controls.gridflow -relief ridge -borderwidth 2\n"
+#GridFlow.gui "button .controls.gridflow.button -text FOO\n"
+#GridFlow.gui "pack .controls.gridflow.button -side left\n"
+#GridFlow.gui "pack .controls.gridflow -side right\n"
+
+GridFlow.gui %q{
+
+if {[catch {
+ # pd 0.37
+ menu .mbar.gridflow -tearoff $pd_tearoff
+ .mbar add cascade -label "GridFlow" -menu .mbar.gridflow
+ set gfmenu .mbar.gridflow
+}]} {
+ # pd 0.36
+ ###the problem is that GridFlow.bind requires 0.37
+}
+catch {
+$gfmenu add command -label "profiler_dump" -command {pd "gridflow profiler_dump;"}
+$gfmenu add command -label "profiler_reset" -command {pd "gridflow profiler_reset;"}
+$gfmenu add command -label "formats" -command {pd "gridflow formats;"}
+}
+
+if {[string length [info command post]] == 0} {
+ proc post {x} {puts $x}
+}
+
+# if not running Impd:
+if {[string length [info command listener_new]] == 0} {
+# start of part duplicated from Impd
+proc listener_new {self name} {
+ global _
+ set _($self:hist) {}
+ set _($self:histi) 0
+ frame $self
+ label $self.label -text "$name: "
+ entry $self.entry -width 40
+# entry $self.count -width 5
+ pack $self.label -side left
+ pack $self.entry -side left -fill x -expand yes
+# pack $self.count -side left
+ pack $self -fill x -expand no
+ bind $self.entry <Up> "listener_up $self"
+ bind $self.entry <Down> "listener_down $self"
+}
+
+proc listener_up {self} {
+ global _
+ if {$_($self:histi) > 0} {set _($self:histi) [expr -1+$_($self:histi)]}
+ $self.entry delete 0 end
+ $self.entry insert 0 [lindex $_($self:hist) $_($self:histi)]
+ $self.entry icursor end
+# $self.count delete 0 end
+# $self.count insert 0 "$_($self:histi)/[llength $_($self:hist)]"
+}
+
+proc listener_down {self} {
+ global _
+ if {$_($self:histi) < [llength $_($self:hist)]} {incr _($self:histi)}
+ $self.entry delete 0 end
+ $self.entry insert 0 [lindex $_($self:hist) $_($self:histi)]
+ $self.entry icursor end
+# $self.count delete 0 end
+# $self.count insert 0 "$_($self:histi)/[llength $_($self:hist)]"
+}
+
+proc listener_append {self v} {
+ global _
+ lappend _($self:hist) $v
+ set _($self:histi) [llength $_($self:hist)]
+}
+
+proc tcl_eval {} {
+ set l [.tcl.entry get]
+ post "tcl: $l"
+ post "returns: [eval $l]"
+ listener_append .tcl [.tcl.entry get]
+ .tcl.entry delete 0 end
+
+}
+if {[catch {
+ listener_new .tcl "Tcl"
+ bind .tcl.entry <Return> {tcl_eval}
+}]} {
+ listener_new .tcl "Tcl" {tcl_eval}
+}
+
+
+}
+# end of part duplicated from Impd
+
+proc ruby_eval {} {
+ set l {}
+ foreach c [split [.ruby.entry get] ""] {lappend l [scan $c %c]}
+ pd "gridflow eval $l;"
+ listener_append .ruby [.ruby.entry get]
+ .ruby.entry delete 0 end
+}
+
+if {[catch {
+ listener_new .ruby "Ruby"
+ bind .ruby.entry <Return> {ruby_eval}
+}]} {
+ listener_new .ruby "Ruby" {ruby_eval}
+}
+
+} # GridFlow.gui
+
+if false
+GridFlow.gui %q{
+catch {
+ if {[file exists ${pd_guidir}/lib/gridflow/icons/peephole.gif]} {
+ global pd_guidir
+ image create photo icon_peephole -file ${pd_guidir}/lib/gridflow/icons/peephole.gif
+ global butt
+ button_bar_add peephole {guiext peephole}
+ } {
+ puts $stderr GAAAH
+ }
+}
+} # GridFlow.gui
+end
diff --git a/externals/gridflow/cpu/mmx.rb b/externals/gridflow/cpu/mmx.rb
new file mode 100644
index 00000000..1a3b15d3
--- /dev/null
+++ b/externals/gridflow/cpu/mmx.rb
@@ -0,0 +1,225 @@
+=begin
+ $Id: mmx.rb,v 1.1 2005-10-04 02:02:14 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+STDOUT.reopen ARGV[0], "w"
+$loader = File.open ARGV[1], "w"
+$count = 0
+$lines = 0
+
+puts "; generated by/for GridFlow 0.8.0"
+$loader.puts "#include \"../base/grid.h.fcs\"\nextern \"C\" {"
+
+# this class is not really used yet (only self.make)
+class AsmFunction
+ def initialize(name)
+ @name = name
+ @label_count = 1
+ end
+ def self.make(name)
+ puts "", "GLOBAL #{name}", "#{name}:"
+ puts "push ebp", "mov ebp,esp", "push esi", "push edi"
+ yield AsmFunction.new(name)
+ puts "pop edi", "pop esi", "leave", "ret", ""
+ end
+ def make_until(*ops)
+ a = @label_count
+ b = @label_count+1
+ @label_count+=2
+ ops[-1]<<" #{@name}_#{b}"
+ puts "#{@name}_#{a}: ", *ops
+ yield
+ puts "jmp #{@name}_#{a}"
+ puts "#{@name}_#{b}:"
+ end
+end
+
+$sizeof = {
+ :uint8 => 1,
+ :int16 => 2,
+ :int32 => 4,
+ :int64 => 8,
+ :float32 => 4,
+ :float64 => 8,
+}
+
+$accum = {
+ :uint8 => "al",
+ :int16 => "ax",
+ :int32 => "eax",
+}
+
+$asm_type = {
+ :uint8 => "byte",
+ :int16 => "word",
+ :int32 => "dword",
+ :int64 => "qword",
+}
+
+# in the following, the opcode "_" means no such thing seems available.
+# also >> for x86 ought to be shr in the uint8 case.
+# btw, i got all of the MMX information from the NASM manual, Appendix B.
+$opcodes = {
+# [--GF--|--x86--|--mmx-et-al----------------------------------------]
+# [ | |-uint8-|-int16-|-int32-|-int64-|-float32-|-float64-]
+ :add => %w[ + add paddb paddw paddd paddq ],
+ :sub => %w[ - sub psubb psubw psubd psubq ],
+ :and => %w[ & and pand pand pand pand ],
+ :xor => %w[ ^ xor pxor pxor pxor pxor ],
+ :or => %w[ | or por por por por ],
+# :max => %w[ max _ pmaxub pmaxsw _ _ ], # not plain MMX !!! (req.Katmai)
+# :min => %w[ min _ pminub pminsw _ _ ], # not plain MMX !!! (req.Katmai)
+# :eq => %w[ == _ pcmpeqb pcmpeqw pcmpeqd _ ],
+# :gt => %w[ > _ pcmpgtb pcmpgtw pcmpgtd _ ],
+# :shl => %w[ << shl _ psllw pslld psllq ], # noncommutative
+# :shr => %w[ >> sar _ psraw psrad _ ], # noncommutative
+# :clipadd => %w[ clip+ _ paddusb paddsw _ _ ], # future use
+# :clipsub => %w[ clip- _ psubusb psubsw _ _ ], # future use
+# :andnot => %w[ &not _ pandn pandn pandn pandn ], # not planned
+}
+
+$opcodes.each {|k,op|
+ op.map! {|x| if x=="_" then nil else x end }
+ STDERR.puts op.inspect
+}
+
+$decls = ""
+$install = ""
+
+def make_fun_map(op,type)
+ s="mmx_#{type}_map_#{op}"
+ size = $sizeof[type]
+ accum = $accum[type]
+ sym = $opcodes[op][0]
+ opcode = $opcodes[op][1]
+ mopcode = $opcodes[op][size+(size<4 ? 1 : 0)]
+ return if not mopcode
+ AsmFunction.make(s) {|a|
+ puts "mov ecx,[ebp+8]", "mov esi,[ebp+12]", "mov eax,[ebp+16]"
+ puts "mov dx,ax", "shl eax,8", "mov al,dl" if size==1
+ puts "mov edx,eax", "shl eax,16", "mov ax,dx" if size<=2
+ puts "push eax", "push eax", "movq mm7,[esp]", "add esp,8"
+ foo = proc {|n|
+ a.make_until("cmp ecx,#{8/size*n}","jb near") {
+ 0.step(n,4) {|k|
+ nn=[n-k,4].min
+ o=(0..3).map{|x| 8*(x+k) }
+ for i in 0...nn do puts "movq mm#{i},[esi+#{o[i]}]" end
+ for i in 0...nn do puts "#{mopcode} mm#{i},mm7" end
+ for i in 0...nn do puts "movq [esi+#{o[i]}],mm#{i}" end
+ }
+ puts "lea esi,[esi+#{8*n}]", "lea ecx,[ecx-#{8/size*n}]"
+ }
+ }
+ foo.call 4
+ foo.call 1
+ a.make_until("test ecx,ecx", "jz") {
+ puts "#{opcode} #{$asm_type[type]} [esi],#{accum}", "lea esi,[esi+#{size}]"
+ puts "dec ecx"
+ }
+ puts "emms"
+ }
+ $decls << "void #{s}(int,#{type}*,#{type});\n"
+ $install << "FIX2PTR(Numop,rb_hash_aref(op_dict,SYM(#{sym})))"
+ $install << "->on_#{type}.op_map = #{s};\n"
+ $count += 1
+end
+
+def make_fun_zip(op,type)
+s="mmx_#{type}_zip_#{op}"
+ size = $sizeof[type]
+ accum = $accum[type]
+ sym = $opcodes[op][0]
+ opcode = $opcodes[op][1]
+ mopcode = $opcodes[op][size+(size<4 ? 1 : 0)]
+ return if not mopcode
+ AsmFunction.make(s) {|a|
+ puts "mov ecx,[ebp+8]", "mov edi,[ebp+12]",
+ "mov esi,[ebp+16]"#, "mov ebx,[ebp+20]"
+ foo = proc {|n|
+ a.make_until("cmp ecx,#{8/size*n}","jb near") {
+ 0.step(n,4) {|k|
+ nn=[n-k,4].min
+ o=(0..3).map{|x| 8*(x+k) }
+ for i in 0...nn do puts "movq mm#{i},[edi+#{o[i]}]" end
+ for i in 0...nn do puts "movq mm#{i+4},[esi+#{o[i]}]" end
+ for i in 0...nn do puts "#{mopcode} mm#{i},mm#{i+4}" end
+ for i in 0...nn do puts "movq [edi+#{o[i]}],mm#{i}" end
+ }
+ #for i in 0...n do puts "movq [ebx+#{8*i}],mm#{i}" end
+ puts "lea edi,[edi+#{8*n}]"
+ puts "lea esi,[esi+#{8*n}]"
+ #puts "lea ebx,[ebx+#{8*n}]"
+ puts "lea ecx,[ecx-#{8/size*n}]"
+ }
+ }
+ foo.call 4
+ foo.call 1
+ a.make_until("test ecx,ecx", "jz") {
+ # requires commutativity ??? fails with shl, shr
+ puts "mov #{accum},[esi]"
+ puts "#{opcode} #{$asm_type[type]} [edi],#{accum}"
+ #puts "mov #{accum},[edi]"
+ #puts "#{opcode} #{accum},[esi]"
+ #puts "mov [ebx],#{accum}"
+ puts "lea edi,[edi+#{size}]"
+ puts "lea esi,[esi+#{size}]"
+ #puts "lea ebx,[ebx+#{size}]"
+ puts "dec ecx"
+ }
+ puts "emms"
+ }
+ #$decls << "void #{s}(int,#{type}*,#{type}*,#{type}*);\n"
+ $decls << "void #{s}(int,#{type}*,#{type}*);\n"
+ $install << "FIX2PTR(Numop,rb_hash_aref(op_dict,SYM(#{sym})))"
+ $install << "->on_#{type}.op_zip = #{s};\n"
+ $count += 1
+end
+
+for op in $opcodes.keys do
+ for type in [:uint8, :int16#, :int32
+ ] do
+ make_fun_map(op,type)
+ make_fun_zip(op,type)
+ end
+end
+
+$loader.puts $decls
+$loader.puts %`
+}; /* extern */
+#include <stdlib.h>
+void startup_mmx_loader () {/*bogus*/}
+void startup_mmx () {
+ if (getenv("NO_MMX")) return;
+ if (EVAL(\"GridFlow.bridge_name\")!=Qnil) gfpost(\"startup_cpu: using MMX optimisations\");
+ #{$install}
+}`
+
+STDERR.puts "automatically generated #{$count} MMX asm functions"
+
+=begin notes:
+CPUID has a bit for detecting MMX
+PACKSSDW PACKSSWB PACKUSWB = saturation-casting
+PCMPxx: Compare Packed Integers
+PMULHW, PMULLW: Multiply Packed _unsigned_ 16-bit Integers, and Store
+PUNPCKxxx: Unpack and Interleave Data
+=end
diff --git a/externals/gridflow/devices4ruby/ParallelPort.rb b/externals/gridflow/devices4ruby/ParallelPort.rb
new file mode 100644
index 00000000..10188592
--- /dev/null
+++ b/externals/gridflow/devices4ruby/ParallelPort.rb
@@ -0,0 +1,72 @@
+require "linux/ioctl"
+# Copyright (c) 2001, 2003 by Mathieu Bouchard
+# this is published under the Ruby license
+
+=begin
+ if using a DB-25 female connector as found on a PC,
+ then the pin numbering is like:
+ 13 _____ 1
+ 25 \___/ 14
+
+ 1 = STROBE = the clock line is a square wave, often at 9600 Hz,
+ which determines the data rate in usual circumstances.
+ 2..9 = D0..D7 = the eight ordinary data bits
+ 10 = -ACK (status bit 6 ?)
+ 11 = BUSY (status bit 7)
+ 12 = PAPER_END (status bit 5)
+ 13 = SELECT (status bit 4 ?)
+ 14 = -AUTOFD
+ 15 = -ERROR (status bit 3 ?)
+ 16 = -INIT
+ 17 = -SELECT_IN
+ 18..25 = GROUND
+=end
+
+module Linux; module ParallelPort
+ extend IoctlClass
+
+ @port_flags = %w[
+ LP_EXIST
+ LP_SELEC
+ LP_BUSY
+ LP_OFFL
+ LP_NOPA
+ LP_ERR
+ LP_ABORT
+ LP_CAREFUL
+ LP_ABORTOPEN
+ LP_TRUST_IRQ
+ ]
+
+ @port_status = %w[
+ nil,
+ nil,
+ nil,
+ LP_PERRORP # unchanged input, active low
+ LP_PSELECD # unchanged input, active high
+ LP_POUTPA # unchanged input, active high
+ LP_PACK # unchanged input, active low
+ LP_PBUSY # inverted input, active high
+ ]
+
+ LPCHAR = 0x0601
+ LPTIME = 0x0602
+ LPABORT = 0x0604
+ LPSETIRQ = 0x0605
+ LPGETIRQ = 0x0606
+ LPWAIT = 0x0608
+ LPCAREFUL = 0x0609 # obsoleted??? wtf?
+ LPABORTOPEN = 0x060a
+ LPGETSTATUS = 0x060b # return LP_S(minor)
+ LPRESET = 0x060c # reset printer
+ LPGETSTATS = 0x060d # struct lp_stats (most likely turned off)
+ LPGETFLAGS = 0x060e # get status flags
+ LPTRUSTIRQ = 0x060f # set/unset the LP_TRUST_IRQ flag
+
+ ioctl_reader :port_flags , :LPGETFLAGS
+ ioctl_reader :port_status, :LPGETSTATUS
+ ioctl_writer :port_careful,:LPCAREFUL
+ ioctl_writer :port_char, :LPCHAR
+
+end end
+
diff --git a/externals/gridflow/devices4ruby/SoundMixer.rb b/externals/gridflow/devices4ruby/SoundMixer.rb
new file mode 100644
index 00000000..0ae50b60
--- /dev/null
+++ b/externals/gridflow/devices4ruby/SoundMixer.rb
@@ -0,0 +1,152 @@
+require "linux/ioctl"
+
+module Linux; module SoundMixer
+ extend IoctlClass
+
+ MIXER_NRDEVICES = 0x00000019
+ MIXER_VOLUME = 0x00000000
+ MIXER_BASS = 0x00000001
+ MIXER_TREBLE = 0x00000002
+ MIXER_SYNTH = 0x00000003
+ MIXER_PCM = 0x00000004
+ MIXER_SPEAKER = 0x00000005
+ MIXER_LINE = 0x00000006
+ MIXER_MIC = 0x00000007
+ MIXER_CD = 0x00000008
+ MIXER_IMIX = 0x00000009
+ MIXER_ALTPCM = 0x0000000a
+ MIXER_RECLEV = 0x0000000b
+ MIXER_IGAIN = 0x0000000c
+ MIXER_OGAIN = 0x0000000d
+ MIXER_LINE1 = 0x0000000e
+ MIXER_LINE2 = 0x0000000f
+ MIXER_LINE3 = 0x00000010
+ MIXER_DIGITAL1 = 0x00000011
+ MIXER_DIGITAL2 = 0x00000012
+ MIXER_DIGITAL3 = 0x00000013
+ MIXER_PHONEIN = 0x00000014
+ MIXER_PHONEOUT = 0x00000015
+ MIXER_VIDEO = 0x00000016
+ MIXER_RADIO = 0x00000017
+ MIXER_MONITOR = 0x00000018
+ ONOFF_MIN = 0x0000001c
+ ONOFF_MAX = 0x0000001e
+ MIXER_NONE = 0x0000001f
+ MIXER_ENHANCE = 0x0000001f
+ MIXER_MUTE = 0x0000001f
+ MIXER_LOUD = 0x0000001f
+ MIXER_RECSRC = 0x000000ff
+ MIXER_DEVMASK = 0x000000fe
+ MIXER_RECMASK = 0x000000fd
+ MIXER_CAPS = 0x000000fc
+ MIXER_STEREODEVS = 0x000000fb
+ MIXER_OUTSRC = 0x000000fa
+ MIXER_OUTMASK = 0x000000f9
+ MASK_VOLUME = 0x00000001
+ MASK_BASS = 0x00000002
+ MASK_TREBLE = 0x00000004
+ MASK_SYNTH = 0x00000008
+ MASK_PCM = 0x00000010
+ MASK_SPEAKER = 0x00000020
+ MASK_LINE = 0x00000040
+ MASK_MIC = 0x00000080
+ MASK_CD = 0x00000100
+ MASK_IMIX = 0x00000200
+ MASK_ALTPCM = 0x00000400
+ MASK_RECLEV = 0x00000800
+ MASK_IGAIN = 0x00001000
+ MASK_OGAIN = 0x00002000
+ MASK_LINE1 = 0x00004000
+ MASK_LINE2 = 0x00008000
+ MASK_LINE3 = 0x00010000
+ MASK_DIGITAL1 = 0x00020000
+ MASK_DIGITAL2 = 0x00040000
+ MASK_DIGITAL3 = 0x00080000
+ MASK_PHONEIN = 0x00100000
+ MASK_PHONEOUT = 0x00200000
+ MASK_RADIO = 0x00800000
+ MASK_VIDEO = 0x00400000
+ MASK_MONITOR = 0x01000000
+ MASK_MUTE = 0x80000000
+ MASK_ENHANCE = 0x80000000
+ MASK_LOUD = 0x80000000
+ MIXER_READ_VOLUME = 0x80044d00
+ MIXER_READ_BASS = 0x80044d01
+ MIXER_READ_TREBLE = 0x80044d02
+ MIXER_READ_SYNTH = 0x80044d03
+ MIXER_READ_PCM = 0x80044d04
+ MIXER_READ_SPEAKER = 0x80044d05
+ MIXER_READ_LINE = 0x80044d06
+ MIXER_READ_MIC = 0x80044d07
+ MIXER_READ_CD = 0x80044d08
+ MIXER_READ_IMIX = 0x80044d09
+ MIXER_READ_ALTPCM = 0x80044d0a
+ MIXER_READ_RECLEV = 0x80044d0b
+ MIXER_READ_IGAIN = 0x80044d0c
+ MIXER_READ_OGAIN = 0x80044d0d
+ MIXER_READ_LINE1 = 0x80044d0e
+ MIXER_READ_LINE2 = 0x80044d0f
+ MIXER_READ_LINE3 = 0x80044d10
+ MIXER_READ_MUTE = 0x80044d1f
+ MIXER_READ_ENHANCE = 0x80044d1f
+ MIXER_READ_LOUD = 0x80044d1f
+ MIXER_READ_RECSRC = 0x80044dff
+ MIXER_READ_DEVMASK = 0x80044dfe
+ MIXER_READ_RECMASK = 0x80044dfd
+ MIXER_READ_STEREODEVS = 0x80044dfb
+ MIXER_READ_CAPS = 0x80044dfc
+ MIXER_WRITE_VOLUME = 0xc0044d00
+ MIXER_WRITE_BASS = 0xc0044d01
+ MIXER_WRITE_TREBLE = 0xc0044d02
+ MIXER_WRITE_SYNTH = 0xc0044d03
+ MIXER_WRITE_PCM = 0xc0044d04
+ MIXER_WRITE_SPEAKER = 0xc0044d05
+ MIXER_WRITE_LINE = 0xc0044d06
+ MIXER_WRITE_MIC = 0xc0044d07
+ MIXER_WRITE_CD = 0xc0044d08
+ MIXER_WRITE_IMIX = 0xc0044d09
+ MIXER_WRITE_ALTPCM = 0xc0044d0a
+ MIXER_WRITE_RECLEV = 0xc0044d0b
+ MIXER_WRITE_IGAIN = 0xc0044d0c
+ MIXER_WRITE_OGAIN = 0xc0044d0d
+ MIXER_WRITE_LINE1 = 0xc0044d0e
+ MIXER_WRITE_LINE2 = 0xc0044d0f
+ MIXER_WRITE_LINE3 = 0xc0044d10
+ MIXER_WRITE_MUTE = 0xc0044d1f
+ MIXER_WRITE_ENHANCE = 0xc0044d1f
+ MIXER_WRITE_LOUD = 0xc0044d1f
+ MIXER_WRITE_RECSRC = 0xc0044dff
+ MIXER_INFO = 0x805c4d65
+ MIXER_ACCESS = 0xc0804d66
+ MIXER_AGC = 0xc0044d67
+ MIXER_3DSE = 0xc0044d68
+ MIXER_PRIVATE1 = 0xc0044d6f
+ MIXER_PRIVATE2 = 0xc0044d70
+ MIXER_PRIVATE3 = 0xc0044d71
+ MIXER_PRIVATE4 = 0xc0044d72
+ MIXER_PRIVATE5 = 0xc0044d73
+ MIXER_GETLEVELS = 0xc0a44d74
+ MIXER_SETLEVELS = 0xc0a44d75
+
+ DEVICE_LABELS = [
+ "Vol ", "Bass ", "Trebl", "Synth", "Pcm ",
+ "Spkr ","Line ", "Mic ", "CD ", "Mix ",
+ "Pcm2 ","Rec ", "IGain", "OGain",
+ "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3",
+ "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"
+ ]
+
+ DEVICE_NAMES = [
+ "vol", "bass", "treble", "synth", "pcm", "speaker", "line",
+ "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain",
+ "line1", "line2", "line3", "dig1", "dig2", "dig3",
+ "phin", "phout", "video", "radio", "monitor"
+ ]
+
+ DEVICE_NAMES.each_with_index {|name,i|
+ ioctl_accessor name,
+ MIXER_READ_VOLUME+i,
+ MIXER_WRITE_VOLUME+i
+ }
+
+end end
diff --git a/externals/gridflow/devices4ruby/SoundPCM.rb b/externals/gridflow/devices4ruby/SoundPCM.rb
new file mode 100644
index 00000000..d1b97159
--- /dev/null
+++ b/externals/gridflow/devices4ruby/SoundPCM.rb
@@ -0,0 +1,102 @@
+# $Id: SoundPCM.rb,v 1.1 2005-10-04 02:02:14 matju Exp $
+require "linux/ioctl"
+
+module Linux
+
+module SoundPCM
+ extend IoctlClass
+
+ # SNDCTL Kernel Procedure Numbers
+
+ SEQ_RESET = 0x00005100
+ SEQ_SYNC = 0x00005101
+ SEQ_CTRLRATE = 0xc0045103
+ SEQ_GETOUTCOUNT = 0x80045104
+ SEQ_GETINCOUNT = 0x80045105
+ SEQ_PERCMODE = 0x40045106
+ SEQ_TESTMIDI = 0x40045108
+ SEQ_RESETSAMPLES = 0x40045109
+ SEQ_NRSYNTHS = 0x8004510a
+ SEQ_NRMIDIS = 0x8004510b
+ SEQ_THRESHOLD = 0x4004510d
+ SEQ_PANIC = 0x00005111
+ SEQ_OUTOFBAND = 0x40085112
+ SEQ_GETTIME = 0x80045113
+
+ SYNTH_INFO = 0xc08c5102
+ SYNTH_MEMAVL = 0xc004510e
+ SYNTH_ID = 0xc08c5114
+ SYNTH_CONTROL = 0xcfa45115
+ SYNTH_REMOVESAMPLE = 0xc00c5116
+
+ FM_LOAD_INSTR = 0x40285107
+ FM_4OP_ENABLE = 0x4004510f
+
+ TMR_TIMEBASE = 0xc0045401
+ TMR_START = 0x00005402
+ TMR_STOP = 0x00005403
+ TMR_CONTINUE = 0x00005404
+ TMR_TEMPO = 0xc0045405
+ TMR_SOURCE = 0xc0045406
+ TMR_METRONOME = 0x40045407
+ TMR_SELECT = 0x40045408
+
+ MIDI_INFO = 0xc074510c
+ MIDI_PRETIME = 0xc0046d00
+ MIDI_MPUMODE = 0xc0046d01
+ MIDI_MPUCMD = 0xc0216d02
+
+ # DSP_* names are obsolete ?
+ DSP_STEREO = 0xc0045003
+ DSP_GETBLKSIZE = 0xc0045004
+ DSP_SETDUPLEX = 0x00005016
+ DSP_GETODELAY = 0x80045017
+ DSP_PROFILE = 0x40045017
+
+ # what is this?
+ COPR_RESET = 0x00004300
+ COPR_LOAD = 0xcfb04301
+ COPR_RDATA = 0xc0144302
+ COPR_RCODE = 0xc0144303
+ COPR_WDATA = 0x40144304
+ COPR_WCODE = 0x40144305
+ COPR_RUN = 0xc0144306
+ COPR_HALT = 0xc0144307
+ COPR_SENDMSG = 0xcfa44308
+ COPR_RCVMSG = 0x8fa44309
+
+ # SOUND_PCM Kernel Procedure Numbers
+
+ PCM_READ_BITS , PCM_WRITE_BITS = 0x80045005, 0xc0045005
+ PCM_READ_CHANNELS , PCM_WRITE_CHANNELS = 0x80045006, 0xc0045006
+ PCM_READ_FILTER , PCM_WRITE_FILTER = 0x80045007, 0xc0045007
+ PCM_READ_RATE , PCM_WRITE_RATE = 0x80045002, 0xc0045002
+
+ PCM_RESET = 0x00005000
+ PCM_SYNC = 0x00005001
+ PCM_POST = 0x00005008
+ PCM_SUBDIVIDE = 0xc0045009
+ PCM_SETFRAGMENT = 0xc004500a
+ PCM_GETFMTS = 0x8004500b
+ PCM_SETFMT = 0xc0045005
+ PCM_GETOSPACE = 0x8010500c
+ PCM_GETISPACE = 0x8010500d
+ PCM_NONBLOCK = 0x0000500e
+ PCM_GETCAPS = 0x8004500f
+ PCM_GETTRIGGER , PCM_SETTRIGGER = 0x80045010, 0x40045010
+ PCM_SETSYNCRO = 0x00005015
+ PCM_GETIPTR = 0x800c5011
+ PCM_GETOPTR = 0x800c5012
+ PCM_MAPINBUF = 0x80085013
+ PCM_MAPOUTBUF = 0x80085014
+
+ ioctl_accessor :bits , :PCM_READ_BITS , :PCM_WRITE_BITS
+ ioctl_accessor :channels, :PCM_READ_CHANNELS, :PCM_WRITE_CHANNELS
+ ioctl_accessor :filter , :PCM_READ_FILTER , :PCM_WRITE_FILTER
+ ioctl_accessor :rate , :PCM_READ_RATE , :PCM_WRITE_RATE
+end
+
+# backward compatibility
+SoundDSP = SoundPCM
+
+end # Linux
diff --git a/externals/gridflow/devices4ruby/extconf.rb b/externals/gridflow/devices4ruby/extconf.rb
new file mode 100644
index 00000000..9e49cbdc
--- /dev/null
+++ b/externals/gridflow/devices4ruby/extconf.rb
@@ -0,0 +1,112 @@
+#!/usr/bin/env ruby
+# $Id: extconf.rb,v 1.1 2005-10-04 02:02:14 matju Exp $
+# installer for RubyX11 / MetaRuby / etc
+# by Mathieu Bouchard
+
+require "rbconfig"
+require "ftools"
+include Config
+
+$DESTDIR = "#{CONFIG["sitedir"]}/#{CONFIG["MAJOR"]}.#{CONFIG["MINOR"]}"
+#$DESTDIR = "/home/matju/lib/ruby/#{RUBY_VERSION[0,3]}"
+$RUBY = "ruby"
+
+while ARGV.length>0
+ arg=ARGV.shift
+ case arg
+ when /=/
+ i=arg.index '='
+ ARGV.unshift arg[0..i-1], arg[i+1..-1]
+ when "--prefix"
+ $DESTDIR = ARGV.shift + "/lib/ruby/#{CONFIG["MAJOR"]}.#{CONFIG["MINOR"]}"
+ end
+end
+
+
+
+def install_files(f,base,entries)
+ entries.each {|type,name,*rest|
+ case type
+ when :ruby
+ f.puts "\tinstall -m644 #{base+name} $(DESTDIR)/#{base+name}"
+ when :directory
+ f.puts "\t@mkdir $(DESTDIR)/#{base+name} || true"
+ install_files(f,base+name,rest)
+ end
+ }
+end
+
+def uninstall_files(f,base,entries)
+ entries.each {|type,name,*rest|
+ case type
+ when :ruby
+ f.puts "\trm $(DESTDIR)/#{base+name}"
+ when :directory
+ uninstall_files(f,base+name,rest)
+ end
+ }
+end
+
+def make_makefile
+ File.open("Makefile","w") {|f|
+ f.puts "# Warning: this file is GENERATED by ./extconf.rb", ""
+ f.puts "DESTDIR = #{$DESTDIR}", ""
+ f.puts "RUBY = #{$RUBY}"
+ f.puts "all::", ""
+ f.puts "Makefile: extconf.rb"
+ f.puts "\t$(RUBY) extconf.rb", ""
+
+ f.puts "install::"
+ f.puts "\t@mkdir -p $(DESTDIR)"
+ install_files(f,"",FILES)
+ f.puts
+ f.puts "uninstall::"
+ uninstall_files(f,"",FILES)
+ f.puts
+ }
+ #FILES.each {|name|
+ # File.install "lib/#{name}", "#{DSTPATH}/#{name}", 0644, true
+ #end
+end
+
+#----------------------------------------------------------------#
+
+$DESTDIR += "/linux/" #(HACK!)
+
+FILES = [
+# [:directory, "linux/",
+ [:ruby, "ioctl.rb"],
+ [:ruby, "SoundPCM.rb"],
+ [:ruby, "ParallelPort.rb"],
+ [:ruby, "SoundMixer.rb"],
+# ]
+]
+
+make_makefile
+
+
+__END__
+### the following is discarded (just a test)
+
+require "mkmf"
+
+srcs = %w(
+ termios
+)
+
+#have_library("m")
+#have_func("sincos")
+#have_func("asinh")
+
+#if have_header("fftw.h")
+# if have_library("fftw", "fftwnd_create_plan")
+# srcs.push "na_fftw"
+# else
+# $defs.delete "-DHAVE_FFTW_H"
+# end
+#end
+
+$objs = srcs.map {|i| i+".o"}
+
+#dir_config("linux")
+create_makefile("linux")
diff --git a/externals/gridflow/devices4ruby/ioctl.rb b/externals/gridflow/devices4ruby/ioctl.rb
new file mode 100644
index 00000000..100d138d
--- /dev/null
+++ b/externals/gridflow/devices4ruby/ioctl.rb
@@ -0,0 +1,66 @@
+# general-purpose code for performing
+# less-than-trivial IOCTL operations.
+# note that this is quite hackish
+# but is still better than writing actual C code.
+
+module Linux; DEVICES_VERSION = "0.1.1"; end
+
+module IoctlClass
+ def ioctl_reader(sym,cmd_in)
+ module_eval %{def #{sym}
+ ioctl_intp_in(#{cmd_in})
+ end}
+ end
+ def ioctl_writer(sym,cmd_out)
+ module_eval %{def #{sym}=(v)
+ ioctl_intp_out(#{cmd_out},v)
+ #{sym} if respond_to? :#{sym}
+ end}
+ end
+ def ioctl_accessor(sym,cmd_in,cmd_out)
+ ioctl_reader(sym,cmd_in)
+ ioctl_writer(sym,cmd_out)
+ end
+end
+
+module Ioctl
+ # this method is not used anymore
+ def int_from_4(foo)
+ # if it crashes, just insert foo=foo.reverse here.
+ (foo[0]+0x100*foo[1])+0x10000*(foo[2]+0x100*foo[3])
+ end
+
+# this was a big hack (from hell) that i used until I actually
+# learned the other feature of ioctl().
+=begin
+ def ioctl_intp_out(arg1,arg2)
+ tmp = arg2 + 2**32
+ foo = [2*tmp.id + 16].pack("l").unpack("P4")[0]
+ tmp_ptr = int_from_4(foo)
+# STDOUT.printf "tmp_ptr=%x\n", tmp_ptr
+ ioctl(arg1,tmp_ptr)
+ end
+
+ def ioctl_intp_in(arg1)
+ tmp = 0xdeadbeef + 2**32
+ foo = [2*tmp.id + 16].pack("l").unpack("P4")[0]
+ tmp_ptr = int_from_4(foo)
+# tmp_ptr = foo.unpack("l")[0]
+# STDOUT.printf "tmp_ptr=%x\n", tmp_ptr
+ ioctl(arg1,tmp_ptr)
+ tmp & (2**32-1)
+ end
+=end
+
+ def ioctl_intp_out(arg1,arg2)
+ ioctl(arg1,[arg2].pack("l"))
+ end
+
+ def ioctl_intp_in(arg1)
+ ioctl(arg1,s="blah")
+ return s.unpack("l")[0]
+ end
+
+end
+
+class IO; include Ioctl; end
diff --git a/externals/gridflow/doc/moulinette.rb b/externals/gridflow/doc/moulinette.rb
new file mode 100644
index 00000000..03dbfa86
--- /dev/null
+++ b/externals/gridflow/doc/moulinette.rb
@@ -0,0 +1,612 @@
+=begin
+ $Id: moulinette.rb,v 1.1 2005-10-04 02:02:14 matju Exp $
+ convert GridFlow Documentation XML to HTML with special formatting.
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004,2005 by Mathieu Bouchard
+
+ 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 ../../COPYING 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.
+=end
+
+GF_VERSION = "0.8.0"
+
+#$use_rexml = true
+$use_rexml = false
+
+#require "gridflow"
+
+if $use_rexml
+ # this is a pure ruby xml-parser
+ begin
+ require "rexml/sax2parser"
+ rescue LoadError
+ require "rexml/parsers/sax2parser"
+ include REXML::Parsers
+ end
+ include REXML
+else
+ # this uses libexpat.so
+ require "xmlparser"
+end
+
+=begin todo
+
+ [ ] make it use the mk() function as much as possible.
+ [ ] make it validate
+ [ ] make it find the size of the pictures (and insert width/height attrs)
+ [ ] tune the output
+ [ ] fix the header of the page
+
+=end
+
+if nil
+ alias real_print print
+ alias real_puts puts
+ def print(*x); real_print "[#{caller[0]}]"; real_print *x; end
+ def puts (*x); real_print "[#{caller[0]}]"; real_puts *x; end
+end
+
+def warn(text)
+ STDERR.print "\e[1;031mWARNING:\e[0m "
+ STDERR.puts text
+end
+
+$escape_map={
+ "<" => "&lt;",
+ ">" => "&gt;",
+ "&" => "&amp;",
+}
+
+# hackish transcoding from unicode to iso-8859-1
+def multicode(text); text.gsub(/\xc2(.)/) { $1 } end
+
+def html_quote(text)
+ return nil if not text
+ text = text.gsub(/[<>&]/) {|x| $escape_map[x] }
+ text = multicode(text) if /\xc2/ =~ text
+ text
+end
+
+def mk(tag,*values,&block)
+ raise "value-list's length must be even" if values.length % 2 != 0
+ print "<#{tag}"
+ i=0
+ while i<values.length
+ print " #{values[i]}=\"#{values[i+1]}\""
+ i+=2
+ end
+ print ">"
+ (block[]; mke tag) if block
+end
+def mke(tag)
+ print "</#{tag}>"
+end
+
+def mkimg(parent,alt=nil,prefix=nil)
+ #STDERR.puts parent.to_s
+ icon = parent.contents.find {|x| XNode===x and x.tag == 'icon' }
+ name = parent.att["name"]
+ url = prefix+"/"+name+"-icon.png"
+ if icon and icon.att["src"]
+ url = icon.att["src"]
+ STDERR.puts "overriding #{url} with #{icon.att["src"]}"
+ end
+ url = url.sub(/,.*$/,"") # what's this for again?
+ warn "icon #{url} not found" if not File.exist? url
+ url = url.gsub(%r"#") {|x| sprintf "%%%02x", x[0] }
+ alt = icon.att["text"] if icon and not alt
+ alt = "[#{name}]"
+ mk(:img, :src, url, :alt, alt, :border, 0)
+end
+
+class XString < String
+ def show
+ print html_quote(gsub(/[\r\n\t ]+$/," "))
+ end
+end
+
+module HasOwnLayout; end
+
+class XNode
+ # subclass interface:
+ # #show_index : print as html in index
+ # #show : print as html in main part of the doc
+
+ @valid_tags = {}
+ class<<self
+ attr_reader :valid_tags
+ def register(*args,&b)
+ qlass = (if b then Class.new self else self end)
+ qlass.class_eval(&b) if b
+ for k in args do XNode.valid_tags[k]=qlass end
+ #qlass.class_eval {
+ # public :show
+ # public :show_index
+ #}
+ end
+ end
+
+ def initialize tag, att, *contents
+ @tag,@att,@contents =
+ tag, att, contents
+ contents.each {|c| c.parent = self if XNode===c }
+ end
+
+ attr_reader :tag, :att, :contents
+ attr_accessor :parent
+ def [] i; contents[i] end
+
+ def show_index
+ contents.each {|x| next unless XNode===x; x.show_index }
+ end
+
+ # this method segfaults in ruby 1.8
+ # because of method lookup on Qundef or whatever.
+ def show
+ #STDERR.puts GridFlow.get_id(contents)
+ #STDERR.puts self
+ contents.each {|x|
+ # STDERR.puts GridFlow.get_id(x)
+ x.show
+ }
+ end
+ def inspect; "#<XNode #{tag}>"; end
+ def to_s; inspect; end
+end
+
+XNode.register("documentation") {}
+
+XNode.register(*%w( icon help arg rest )) {public
+ def show; end
+}
+
+XNode.register("section") {public
+ def show
+ write_black_ruler
+ mk(:tr) { mk(:td,:colspan,4) {
+ mk(:a,:name,att["name"].gsub(/ /,'_')) {}
+ mk(:h4) { print att["name"] }}}
+
+ contents.each {|x|
+ if HasOwnLayout===x then
+ x.show
+ else
+ mk(:tr) { mk(:td) {}; mk(:td) {}; mk(:td) { x.show }}
+ puts ""
+ end
+ }
+
+ mk(:tr) { mk(:td) { print "&nbsp;" }}
+ puts ""
+ end
+ def show_index
+ mk(:h4) {
+ mk(:a,:href,"#"+att["name"].gsub(/ /,'_')) {
+ print att["name"] }}
+ print "<ul>\n"
+ super
+ print "</ul>\n"
+ end
+}
+
+# basic text formatting nodes.
+XNode.register(*%w( p i u b sup )) {public
+ def show
+ print "<#{tag}>"
+ super
+ print "</#{tag}>"
+ end
+}
+
+XNode.register("k") {public
+ def show
+ print "<kbd><font color=\"#007777\">" # oughta be in stylesheet?
+ super
+ print "</font></kbd>"
+ end
+}
+
+# explicit hyperlink on the web.
+XNode.register("link") {public
+ def show
+ STDERR.puts "att = #{att.inspect}"
+ raise if not att['to']
+ print "<a href='#{att['to']}'>"
+ super
+ print att[:to] if contents.length==0
+ print "</a>"
+ end
+}
+
+XNode.register("list") {public
+ attr_accessor :counter
+ def show
+ self.counter = att.fetch("start"){"1"}.to_i
+ mk(:ul) {
+ super # method call on Qundef ???
+ }
+ end
+}
+
+XNode.register("li") {public
+ def show
+ mk(:li) {
+ print "<b>#{parent.counter}</b>", " : "
+ parent.counter += 1
+ super
+ }
+ end
+}
+
+# and "macro", "enum", "type", "use"
+XNode.register("class") {public
+ include HasOwnLayout
+ def show
+ tag = self.tag
+ name = att['name'] or raise
+ mk(:tr) {
+ mk(:td,:colspan,4,:bgcolor,"#ffb080") {
+ mk(:b) { print "&nbsp;"*2, "#{tag} " }
+ mk(:a,:name,name) { print name }
+ }
+ }
+ mk(:tr) {
+ mk(:td) {}
+ mk(:td,:valign,:top) {
+ print "<br>\n"
+ help = contents.find {|x| XNode===x and x.tag == 'help' }
+ mkimg(self,nil,"flow_classes") if /reference|format/ =~ $file
+ mk(:br,:clear,"left")
+ 2.times { mk(:br) }
+ if help
+ big = help.att['image'] || att['name']
+ if big[0]==?@ then big="images/help_#{big}.png" end
+ warn "help #{big} not found" if not File.exist?(big)
+ #small = big.gsub(/png$/, 'jpg').gsub(/\//, '/ic_')
+ mk(:a,:href,big) {
+ #mk(:img,:src,small,:border,0)
+ mk(:img,:src,"images/see_screenshot.png",:border,0)
+ }
+ end
+ mk(:br,:clear,"left")
+ mk(:br)
+ }#/td
+ mk(:td) {
+ print "<br>\n"
+ super
+ print "<br>"
+ }#/td
+ }#/tr
+ end
+ def show_index
+ icon = contents.find {|x| XNode===x && x.tag == "icon" }
+ if not att["name"] then
+ raise "name tag missing?"
+ end
+ mk(:li) { mk(:a,:href,"\#"+att["name"]) {
+ mkimg(self,att["cname"],"flow_classes")
+ }}
+ puts
+ super
+ end
+}
+
+def nice_table
+ mk(:table,:border,0,:bgcolor,:black,:cellspacing,1) {
+ mk(:tr) {
+ mk(:td,:valign,:top,:align,:left) {
+ mk(:table,:bgcolor,:white,:border,0,
+ :cellpadding,4,:cellspacing,1) {
+ yield }}}}
+end
+
+XNode.register("attr") {public
+ def show
+ print "<br>"
+ if parent.tag == "inlet" or parent.tag == "outlet"
+ mk(:b) {
+ print "#{parent.tag}&nbsp;#{parent.att['id']} "
+
+ }
+ end
+ print "<b>#{tag}</b>&nbsp;"
+ print "#{html_quote att['name']} <b>(</b>"
+ s=html_quote(att["name"])
+ s="<i>#{att['type']}</i> #{s}" if att['type']
+ print "<b>#{s}</b>"
+ print "<b>)</b> "
+ end
+}
+
+XNode.register("method") {public
+if true #
+ def show
+ print "<br>"
+ if parent.tag == "inlet" or parent.tag == "outlet"
+ mk(:b) {
+ print "#{parent.tag}&nbsp;#{parent.att['id']} "
+
+ }
+ end
+ print "<b>#{tag}</b>&nbsp;"
+ print "#{html_quote att['name']} <b>(</b>"
+ print contents.map {|x|
+ next unless XNode===x
+ case x.tag
+ when "arg"
+ s=html_quote(x.att["name"])
+ s="<i>#{x.att['type']}</i> #{s}" if x.att['type']
+ s
+ when "rest"
+ (x.att["name"]||"") + "..."
+ end
+ }.compact.join("<b>, </b>")
+ print "<b>)</b> "
+ super
+ print "<br>\n"
+ end
+else #
+ def show
+ print "<br>"
+ mk(:table) { mk(:tr) { mk(:td) {
+ name = ""
+ name << "#{parent.tag} #{parent.att['id']} " if \
+ parent.tag == "inlet" or parent.tag == "outlet"
+ name << tag.to_s
+ mk(:b) { print name.gsub(/ /,"&nbsp;") }
+ }; mk(:td) {
+ nice_table { mk(:tr) {
+ mk(:td,:width,1) { print html_quote(att['name']) }
+ contents.each {|x|
+ next unless XNode===x
+ case x.tag
+ when "arg"
+ mk(:td,:bgcolor,:pink) {
+ s = ""
+ if x.att["type"]
+ s << "<i>" << html_quote(x.att["type"]) << "</i>"
+ end
+ if x.att["name"]
+ s << " " << html_quote(x.att["name"])
+ end
+ s<<"&nbsp;" if s.length==0
+ mk(:b) { puts s }
+ }
+ when "rest"
+ mk(:td,:bgcolor,:pink) {
+ mk(:b) { print html_quote(x.att["name"]), "..."}
+ }
+ end
+ }
+ }}
+ }; mk(:td) {
+ super
+ }}}
+ end
+end #
+}
+
+XNode.register("table") {public
+ def show
+ colors = ["#ffffff","#f0f8ff",]
+ rows = contents.find_all {|x| XNode===x && x.tag=="row" }
+ rows.each_with_index {|x,i| x.bgcolor = colors[i%2] }
+ mk(:tr) {
+ 2.times { mk(:td) {} }
+ mk(:td) {
+ nice_table {
+ mk(:tr) {
+ columns = contents.find_all {|x| XNode===x && x.tag=="column" }
+ columns.each {|x| mk(:td,:bgcolor,"#808080") {
+ mk(:font,:color,"#ffffff") {
+ mk(:b) {
+ x.contents.each {|y| y.show }}}}}
+ }
+ super
+ }}}
+ end
+}
+
+XNode.register("column") {public
+ def show; end
+}
+
+XNode.register("row") {public
+ attr_accessor :bgcolor
+ def show
+ columns = parent.contents.find_all {|x| XNode===x && x.tag=="column" }
+ mk(:tr) { columns.each {|x| mk(:td,:bgcolor,bgcolor) {
+ id = x.att["id"]
+ case x.att["type"]
+ when "icon" # should fix this for non-op icons
+ x = "op/#{att['cname']}-icon.png"
+ if not File.exist? x
+ warn "no icon for #{att['name']} (#{x})\n"
+ end
+ mk(:img,:src,x,:border,0,:alt,att["name"])
+ else
+ if id==""
+ then contents.each {|x| x.show }
+ else
+# print html_quote(att[id] || "--")
+ print multicode(att[id] || "--")
+ end
+ end
+ }}}
+ end
+}
+
+XNode.register("inlet","outlet") {}
+
+#----------------------------------------------------------------#
+
+if $use_rexml
+ class GFDocParser
+ def initialize(file)
+ @sax = SAX2Parser.new(File.open(file))
+ @xml_lists = []
+ @stack = [[]]
+ @sax.listen(:start_element) {|a,b,c,d| startElement(b,d) }
+ @sax.listen( :end_element) {|a,b,c| endElement(b) }
+ @sax.listen( :characters) {|a| @gfdoc.character(a) }
+ end
+ def do_it; @sax.parse; end
+ end
+else
+ class GFDocParser
+ def initialize(file)
+ @xml = XMLParser.new("ISO-8859-1")
+ foo=self; @xml.instance_eval { @gfdoc=foo }
+ def @xml.startElement(tag,attrs) @gfdoc.startElement(tag,attrs) end
+ def @xml.endElement(tag) @gfdoc.endElement(tag) end
+ def @xml.character(text) @gfdoc.character(text) end
+ @file = File.open file
+ @xml_lists = []
+ @stack = [[]]
+ end
+ def do_it; @xml.parse(@file.readlines.join("\n"), true) end
+ def method_missing(sel,*args) @xml.send(sel,*args) end
+ end
+end
+
+class GFDocParser
+ attr_reader :stack
+ def startElement(tag,attrs)
+ if not XNode.valid_tags[tag] then
+ raise XMLParserError, "unknown tag #{tag}"
+ end
+ @stack<<[tag,attrs]
+ end
+ def endElement(tag)
+ node = XNode.valid_tags[tag].new(*@stack.pop)
+ @stack.last << node
+ end
+ def character(text)
+ if not String===@stack.last.last then
+ @stack.last << XString.new("")
+ end
+ @stack.last.last << text
+ end
+end
+
+#----------------------------------------------------------------#
+
+def write_header(tree)
+puts <<EOF
+<html><head>
+<!-- #{"$"}Id#{"$"} -->
+<title>GridFlow #{GF_VERSION} - #{tree.att['title']}</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<link rel="stylesheet" href="gridflow.css" type="text/css">
+</head>
+<body bgcolor="#FFFFFF"
+ leftmargin="0" topmargin="0"
+ marginwidth="0" marginheight="0">
+<table width="100%" bgcolor="white" border="0" cellspacing="2">
+<tr><td colspan="4" bgcolor="#082069">
+<img src="images/titre_gridflow.png" width="253" height="23">
+</td></tr><tr><td>&nbsp;</td></tr>
+EOF
+write_black_ruler
+puts <<EOF
+<tr><td colspan="4" height="16">
+ <h4>GridFlow #{GF_VERSION} - #{tree.att['title']}</h4>
+</td></tr>
+<tr>
+ <td width="5%" rowspan="2">&nbsp;</td>
+ <td width="15%" height="23">&nbsp;</td>
+ <td width="80%" height="23">&nbsp;</td>
+ <td width="5%" height="23">&nbsp;</td>
+</tr>
+EOF
+end
+
+def write_black_ruler
+puts <<EOF
+<tr><td colspan="4" bgcolor="black">
+<img src="images/black.png" width="1" height="2"></td></tr>
+EOF
+end
+
+def write_footer
+puts <<EOF
+<td colspan="4" bgcolor="black">
+<img src="images/black.png" width="1" height="2"></td></tr>
+<tr><td colspan="4">
+<p><font size="-1">
+GridFlow #{GF_VERSION} Documentation<br>
+Copyright &copy; 2001,2002,2003,2004,2005 by Mathieu Bouchard
+<a href="mailto:matju@sympatico.ca">matju@artengine.ca</a>
+</font></p>
+</td></tr></table></body></html>
+EOF
+end
+
+#----------------------------------------------------------------#
+
+$nodes = {}
+XMLParserError = Exception if $use_rexml
+
+def read_one_page file
+ begin
+ STDERR.puts "reading #{file}"
+ parser = GFDocParser.new(file)
+ parser.do_it
+ $nodes[file] = parser.stack[0][0]
+ rescue Exception => e
+ puts ""
+ puts ""
+ STDERR.puts e.inspect
+ i = parser.stack.length-1
+ (STDERR.puts "\tinside <#{parser.stack[i][0]}>"; i-=1) until i<1
+ # strange that line numbers are doubled.
+ # also the byte count is offset by the line count !?!?!?
+ STDERR.puts "\tinside #{file}:#{parser.line/2 + 1}" +
+ " (column #{parser.column}," +
+ " byte #{parser.byteIndex - parser.line/2})"
+ raise "why don't you fix the documentation"
+ end
+end
+
+def write_one_page file
+ begin
+ $file = file
+ output_name = file.sub(/\.xml/,".html")
+ STDERR.puts "writing #{output_name}"
+ STDOUT.reopen output_name, "w"
+ tree = $nodes[file]
+ write_header(tree)
+ mk(:tr) { mk(:td,:colspan,2) { mk(:div,:cols,tree.att["indexcols"]||1) {
+ tree.show_index
+ puts "<br><br>"
+ }}}
+ tree.show
+ write_footer
+ puts ""
+ puts ""
+ rescue Exception => e
+ STDERR.puts "#{e.class}: #{e.message}"
+ STDERR.puts e.backtrace
+ end
+end
+
+$files = %w(
+ install.xml project_policy.xml
+ reference.xml format.xml internals.xml architecture.xml)
+
+$files.each {|input_name| read_one_page input_name }
+$files.each {|input_name| write_one_page input_name }
diff --git a/externals/gridflow/extra/jmax_format.rb b/externals/gridflow/extra/jmax_format.rb
new file mode 100644
index 00000000..c1b93ce5
--- /dev/null
+++ b/externals/gridflow/extra/jmax_format.rb
@@ -0,0 +1,167 @@
+=begin
+ $Id: jmax_format.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+
+class JMaxFileHandler
+ Size = [4,1,2,4]
+ Packer = ["N","c","n","N"]
+ OpTable = [
+ [0, :return],
+ [1, :push, :int],
+ [2, :push, :float],
+ [3, :push, :symbol],
+ [5, :set, :int],
+ [6, :set, :float],
+ [7, :set, :symbol],
+ [9, :pop_args, :int],
+ [10, :push_obj, :int],
+ [11, :mv_obj, :int],
+ [12, :pop_objs, :int],
+ [13, :make_obj, :int],
+ [14, :put_prop, :symbol],
+ [16, :obj_mess, :int, :symbol, :int],
+ [18, :push_obj_table, :int],
+ [19, :pop_obj_table],
+ [20, :connect],
+ [21, :make_top_obj, :int],
+ ]
+ OpTableById = {}
+ OpTableByName = {}
+ OpTable.each {|entry|
+ id,name,arg = entry
+ OpTableById[id] = entry
+ OpTableByName[name] = entry
+ }
+end
+
+class JObject
+ attr_reader :properties
+ attr_accessor :parent_patcher
+ attr_reader :init_messages
+ attr_reader :connections
+ def self.[](*args) new(*args) end
+ def initialize(*args)
+ @args = args
+ @properties = {}
+ @init_messages = []
+ end
+ def send_in(inlet,*args)
+ @init_messages << [inlet,*args]
+ end
+ def connect(inlet,target,outlet)
+ end
+ def subobjects
+ @subobjects={} if not defined? @subobjects
+ @subobjects
+ end
+end
+
+class JMaxFileReader < JMaxFileHandler
+ def initialize(f,factory=JObject)
+ @f = f
+ @symbols = []
+ @estack = []
+ @ostack = []
+ @tstack = []
+ @factory = factory
+ end
+ def parse
+ magic, code_size, n_symbols = @f.read(12).unpack("a4NN")
+ case magic
+ when "bMax"; #ok
+ when "bMa2"; raise "bMa2 format (jMax 4) is not supported yet"
+ else raise "not a jMax file"
+ end
+ @code = @f.read code_size
+ @symbols = @f.read.split(/\0/).map {|x| x.intern }
+ @index = 0
+ while @index < @code.size
+ read_opcode
+ end
+ raise "@estack.size!=0" if @estack.size!=0
+ raise "@ostack.size!=1" if @ostack.size!=1
+ raise "@tstack.size!=0" if @tstack.size!=0
+ @ostack[0]
+ end
+ def read_opcode
+ #puts "#{@index} of #{@code.size}"
+ op = @code[@index]; @index+=1
+ op1,op2 = op&0x3f,op>>6
+ entry = OpTableById[op1]
+ if not entry
+ puts "skipping unknown opcode #{op1},#{op2}"
+ return
+ end
+ args = []
+ (entry.length-2).times {|i|
+ args << (case entry[2+i]
+ when :int
+ n=Size[op2]; v=@code[@index,n].unpack(Packer[op2])[0]
+ x = if v[8*n-1]!=0 then ~(~v&((1<<(8*n-1))-1)) else v end
+ #STDERR.puts "WARNING: #{v} -> #{x}" if x<0
+ x
+ when :float
+ n=4; @code[@index,4].unpack("g")[0]
+ when :symbol
+ n=Size[op2]; @symbols[@code[@index,n].unpack(Packer[op2])[0]]
+ when nil
+ end)
+ @index+=n
+ }
+ #text = sprintf "%05d: %2d,%1d: %s", @index, op1, op2, entry[1]
+ #text << "(" << args.map{|x|x.inspect}.join(",") << ")"
+ #puts text
+ send entry[1], *args
+ end
+ def push x; @estack << x end
+ def set x
+ if @estack.size>0 then @estack[-1]=x else @estack << x end
+ end
+ def put_prop x; @ostack[-1].properties[x] = @estack[-1] end
+ def make_obj x
+ patcher = @ostack[-1] if @ostack.size>0
+ baby = @factory[*(@estack[-x,x].reverse)]
+ @ostack << baby
+ @ostack[-1].parent_patcher = patcher
+ patcher.subobjects[baby]=true if patcher
+ end
+ alias :make_top_obj :make_obj
+ def pop_args x; @estack[-x,x]=[] end
+ def push_obj_table x; @tstack<<[] end
+ def mv_obj x; @tstack[-1][x]=@ostack[-1] end
+ def pop_objs x; @ostack[-x,x]=[] end
+ def obj_mess i,s,n
+ o = @ostack[-1]
+ m = @estack[-n,n].reverse
+ if i<0 then o.send s,*m else o.send_in i,s,*m end
+ end
+ def push_obj x; @ostack<<@tstack[-1][x] end
+ def connect; @ostack[-1].connect @estack[-1],@ostack[-2],@estack[-2] end
+ def pop_obj_table; @tstack.pop end
+ def return; end
+end
+
+if $0 == __FILE__
+ jff = JMaxFileReader.new File.open("samples/fire.jmax")
+ jff.parse
+end
diff --git a/externals/gridflow/extra/puredata_format.rb b/externals/gridflow/extra/puredata_format.rb
new file mode 100644
index 00000000..508f545e
--- /dev/null
+++ b/externals/gridflow/extra/puredata_format.rb
@@ -0,0 +1,129 @@
+=begin
+ $Id: puredata_format.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+class PureDataFileWriter
+ def initialize filename
+ @f = File.open filename, "w"
+ end
+ def close; @f.close end
+ def write_patcher o
+ pr = o.properties
+ @f.puts "#N canvas #{pr[:wx]} #{pr[:wy]} #{pr[:ww]} #{pr[:wh]} 10;"
+ ol = o.subobjects.keys
+ x=0
+ ol.find_all {|a| a.classname=="inlet"}.sort {|a,b| a.argv[0] <=> b.argv[0] }.each {|a|
+ if x>a.properties[:x]
+ a.properties[:x]=x+16
+ STDERR.puts "warning: moving inlet #{a.argv[0]} to the right"
+ end
+ x=a.properties[:x]
+ }
+ x=0
+ ol.find_all {|a| a.classname=="outlet"}.sort {|a,b| a.argv[0] <=> b.argv[0] }.each {|a|
+ if x>a.properties[:x]
+ a.properties[:x]=x+16
+ STDERR.puts "warning: moving outlet #{a.argv[0]} to the right"
+ end
+ x=a.properties[:x]
+ }
+ ol.each {|so| write_object so }
+ ol.each_with_index {|so,i|
+ next if not so.instance_eval{defined? @outlets}
+ so.outlets.each_with_index {|conns,outlet|
+ next if not conns
+ conns.each {|target,inlet|
+ @f.puts "#X connect #{i} #{outlet} #{ol.index target} #{inlet};"
+ }
+ }
+ }
+ end
+
+ def list_to_s l
+ l.map {|x|
+ if Array===x then "( " + list_to_s(x) + " )" else escape(x.to_s) end
+ }.join " "
+ end
+
+ # what am i supposed to do?
+ def escape x; x.gsub(/[;,]/) {|x| " \\#{x}" }.gsub(/\n/) {"\\\n"} end
+ def escape2 x; x.gsub(/[,]/) {|x| " \\#{x} " }.gsub(/[;\$\"]/) {|x| "\\#{x}" }.gsub(/\n/) {"\\\n"} end
+
+ def write_object o
+ pr = o.properties
+ #classname = o.class.instance_eval{@foreign_name}
+ classname = o.classname
+ if classname=="jpatcher"
+ #@f.print "#N canvas 0 0 "
+ write_patcher o
+ end
+
+ case classname
+ when "display"; classname="print"
+ when "list"; classname="listmake"
+ end
+
+ t = case classname
+ when "jcomment"; "text"
+ when "messbox"; "msg"
+ when "jpatcher"; "restore"
+ when "intbox"; "floatatom"
+ else "obj"
+ end
+ @f.print "#X #{t} #{pr[:x]} #{pr[:y]} "
+
+ case classname
+ when "button"
+ @f.print "bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1"
+ when "jcomment"
+ @f.print escape2(pr[:comment].to_s)
+ when "messbox"
+ av=o.argv[0]
+ i=0
+ dollar="$".intern
+ while i<av.length
+ if av[i]==dollar then av[i,2]=("\\$"+av[i+1].to_s).intern else i+=1 end
+ end
+ @f.print(list_to_s(av))
+ when "slider"
+ #doradio = pr[:maxValue]-pr[:minValue]<=10
+ doradio = false
+ #p doradio
+ name = case pr[:orientation]
+ when 1; if doradio then "hradio" else "hsl" end
+ when 2,nil; if doradio then "vradio" else "vsl" end
+ else raise "bogus slider orientation?" end
+ @f.print "#{name} "+
+ "#{pr[:w]} #{pr[:h]} #{pr[:minValue]} #{pr[:maxValue]} 0 0 "+
+ "empty empty empty -2 -6 0 8 -262144 -1 -1 0 1"
+ when "intbox"
+ @f.print "5 0 0 0 - - -;"
+ when "inlet"; @f.print "inlet"
+ when "outlet"; @f.print "outlet"
+ when "jpatcher"
+ @f.print("pd ",list_to_s(o.argv))
+ else
+ @f.print(classname," ",list_to_s(o.argv))
+ end
+ @f.puts ";"
+ end
+end
diff --git a/externals/gridflow/extra/server_1_grid.rb b/externals/gridflow/extra/server_1_grid.rb
new file mode 100644
index 00000000..f1f4a9fc
--- /dev/null
+++ b/externals/gridflow/extra/server_1_grid.rb
@@ -0,0 +1,26 @@
+# $Id: server_1_grid.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+require "socket"
+require "smpte" # in this folder
+
+picture = "\x7fGRID \000\003"
+picture << [240,320,3].pack("N*")
+make_smpte(picture) {|*rgb| rgb.pack "N*" }
+
+# File.open("blah.grid","w") {|f| f.write picture }
+
+serv = TCPServer.new 4242
+loop {
+ puts "waiting for connection (port 4242)"
+ sock = serv.accept
+ puts "incoming connection"
+ begin
+ loop {
+ sock.write picture
+ puts "wrote one picture"
+ }
+ rescue Errno::EPIPE # Broken Pipe
+ puts "connection closed (by client)"
+ # it's ok, go back to waiting.
+ end
+}
diff --git a/externals/gridflow/extra/server_1_ppm.rb b/externals/gridflow/extra/server_1_ppm.rb
new file mode 100644
index 00000000..dad6d3f7
--- /dev/null
+++ b/externals/gridflow/extra/server_1_ppm.rb
@@ -0,0 +1,20 @@
+# $Id: server_1_ppm.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+require "socket"
+
+picture = File.open("../images/teapot.ppm") {|x| x.read }
+
+serv = TCPServer.new 4242
+loop {
+ puts "waiting for connection (port 4242)"
+ sock = serv.accept
+ begin
+ loop {
+ sock.write picture
+ puts "wrote one picture"
+ }
+ rescue Errno::EPIPE # Broken Pipe
+ puts "connection closed (by client)"
+ # it's ok, go back to waiting.
+ end
+}
diff --git a/externals/gridflow/extra/server_2.rb b/externals/gridflow/extra/server_2.rb
new file mode 100644
index 00000000..4807d502
--- /dev/null
+++ b/externals/gridflow/extra/server_2.rb
@@ -0,0 +1,65 @@
+# a server program to connect 2 or more clients together.
+# by Mathieu Bouchard
+
+require "fcntl"
+require "socket"
+
+class IO
+ def nonblock=flag
+ bit = Fcntl::O_NONBLOCK
+ fcntl(Fcntl::F_SETFL, (fcntl(Fcntl::F_GETFL) & ~bit) |
+ if flag then bit else 0 end)
+ end
+ # does not work with any ruby version, due to a bug. see below.
+ def read_at_most n
+ s=""
+ k=1<<(Math.log(n)/Math.log(2)).to_i
+ while k>0
+ unless k+s.length>n
+ puts "trying #{k}"
+ (s << read(k)) rescue Errno::EWOULDBLOCK
+ end
+ k>>=1
+ end
+ s
+ end
+ # this one works but is slow.
+ def bugfree_read_at_most n
+ s=""
+ (s << (read 1) while s.length<n) rescue Errno::EWOULDBLOCK
+ s
+ end
+end
+
+serv = TCPServer.new 4242
+socks = [serv]
+
+loop {
+ puts "waiting for connection (port 4242)"
+ begin
+ loop {
+ puts "waiting"
+ ready,blah,crap = IO.select socks, [], socks, 1
+ (ready||[]).each {|s|
+ if s==serv then
+ sock = serv.accept
+ sock.nonblock=true
+ socks << sock
+ puts "incoming connection (total: #{socks.length-1})"
+ else
+ other = socks.find_all{|x|not TCPServer===x} - [s]
+ stuff = s.bugfree_read_at_most 1024
+ p stuff
+ (s.close; socks.delete s) if not stuff or stuff.length==0
+ other.each {|x|
+ p x
+ x.write stuff
+ }
+ end
+ }
+ }
+ rescue Errno::EPIPE # Broken Pipe
+ puts "connection closed (by client)"
+ # it's ok, go back to waiting.
+ end
+}
diff --git a/externals/gridflow/extra/smpte.rb b/externals/gridflow/extra/smpte.rb
new file mode 100644
index 00000000..c14c936e
--- /dev/null
+++ b/externals/gridflow/extra/smpte.rb
@@ -0,0 +1,23 @@
+# Copyright 2001 by Mathieu Bouchard
+# $Id: smpte.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+# The standard SMPTE color test pattern.
+# AS SEEN ON TV !!! (but this is a cheap plastic imitation)
+
+def make_smpte(picture="")
+ row_1 = ""
+ row_2 = ""
+ row_3 = ""
+ row_3_c = [[0,63,105],[255,255,255],[64,0,119]]
+ (0...320).each {|x|
+ n_barre_1 = 7 - x*7/320
+ n_barre_2 = if n_barre_1&1==0 then 0 else 8 - n_barre_1 end
+ row_1 << yield (*([1,2,0].map{|c| 255 * ((n_barre_1 >> c)&1) }))
+ row_2 << yield (*([1,2,0].map{|c| 255 * ((n_barre_2 >> c)&1) }))
+ row_3 << yield (*(row_3_c[x/57] || [0,0,0]))
+ }
+ 160.times { picture << row_1 }
+ 20 .times { picture << row_2 }
+ 60 .times { picture << row_3 }
+ picture
+end
diff --git a/externals/gridflow/format/aalib.c b/externals/gridflow/format/aalib.c
new file mode 100644
index 00000000..f014a632
--- /dev/null
+++ b/externals/gridflow/format/aalib.c
@@ -0,0 +1,168 @@
+/*
+ $Id: aalib.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "../base/grid.h.fcs"
+#define aa_hardwareparams aa_hardware_params
+#include <aalib.h>
+
+/* MINNOR is a typo in aalib.h, sorry */
+typedef
+#if AA_LIB_MINNOR == 2
+ int
+#else
+ enum aa_attribute
+#endif
+AAAttr;
+
+\class FormatAALib < Format
+struct FormatAALib : Format {
+ aa_context *context;
+ aa_renderparams *rparams;
+ int autodraw; /* as for X11 */
+ bool raw_mode;
+
+ FormatAALib () : context(0), autodraw(1) {}
+
+ \decl void initialize (Symbol mode, Symbol target);
+ \decl void close ();
+ \decl void _0_hidecursor ();
+ \decl void _0_print (int y, int x, int a, Symbol text);
+ \decl void _0_draw ();
+ \decl void _0_autodraw (int autodraw);
+ \decl void _0_dump ();
+ \grin 0 int
+};
+
+GRID_INLET(FormatAALib,0) {
+ if (!context) RAISE("boo");
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ switch (in->dim->get(2)) {
+ case 1: raw_mode = false; break;
+ case 2: raw_mode = true; break;
+ default:
+ RAISE("expecting 1 greyscale channel (got %d)",in->dim->get(2));
+ }
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+} GRID_FLOW {
+ int f = in->factor();
+ if (raw_mode) {
+ int sx = min(f,aa_scrwidth(context));
+ int y = in->dex/f;
+ while (n) {
+ if (y>=aa_scrheight(context)) return;
+ for (int x=0; x<sx; x++) {
+ context->textbuffer[y*aa_scrwidth(context)+x]=data[x*2+0];
+ context->attrbuffer[y*aa_scrwidth(context)+x]=data[x*2+1];
+ }
+ y++;
+ n-=f;
+ data+=f;
+ }
+ } else {
+ int sx = min(f,context->imgwidth);
+ int y = in->dex/f;
+ while (n) {
+ if (y>=context->imgheight) return;
+ for (int x=0; x<sx; x++) aa_putpixel(context,x,y,data[x]);
+ y++;
+ n-=f;
+ data+=f;
+ }
+ }
+} GRID_FINISH {
+ if (!raw_mode) {
+ aa_palette pal;
+ for (int i=0; i<256; i++) aa_setpalette(pal,i,i,i,i);
+ aa_renderpalette(context,pal,rparams,0,0,
+ aa_scrwidth(context),aa_scrheight(context));
+ }
+ if (autodraw==1) aa_flush(context);
+} GRID_END
+
+\def void close () {
+ if (context) {
+ aa_close(context);
+ context=0;
+ }
+}
+
+\def void _0_hidecursor () { aa_hidemouse(context); }
+\def void _0_draw () { aa_flush(context); }
+\def void _0_print (int y, int x, int a, Symbol text) {
+ aa_puts(context,x,y,(AAAttr)a,(char *)rb_sym_name(text));
+ if (autodraw==1) aa_flush(context);
+}
+\def void _0_autodraw (int autodraw) {
+ if (autodraw<0 || autodraw>1)
+ RAISE("autodraw=%d is out of range",autodraw);
+ this->autodraw = autodraw;
+}
+\def void _0_dump () {
+ int32 v[] = {aa_scrheight(context), aa_scrwidth(context), 2};
+ GridOutlet out(this,0,new Dim(3,v));
+ for (int y=0; y<aa_scrheight(context); y++) {
+ for (int x=0; x<aa_scrwidth(context); x++) {
+ STACK_ARRAY(int32,data,2);
+ data[0] = context->textbuffer[y*aa_scrwidth(context)+x];
+ data[1] = context->attrbuffer[y*aa_scrwidth(context)+x];
+ out.send(2,data);
+ }
+ }
+}
+
+/* !@#$ varargs missing here */
+\def void initialize (Symbol mode, Symbol target) {
+ rb_call_super(argc,argv);
+ argc-=2; argv+=2;
+ char *argv2[argc];
+ for (int i=0; i<argc; i++)
+ argv2[i] = strdup(rb_str_ptr(rb_funcall(argv[i],SI(to_s),0)));
+ if (mode!=SYM(out)) RAISE("write-only, sorry");
+ aa_parseoptions(0,0,&argc,argv2);
+ for (int i=0; i<argc; i++) free(argv2[i]);
+ Ruby drivers = rb_ivar_get(rb_obj_class(rself),SI(@drivers));
+ Ruby driver_address = rb_hash_aref(drivers,target);
+ if (driver_address==Qnil)
+ RAISE("unknown aalib driver '%s'",rb_sym_name(target));
+ aa_driver *driver = FIX2PTR(aa_driver,driver_address);
+ context = aa_init(driver,&aa_defparams,0);
+ rparams = aa_getrenderparams();
+ if (!context) RAISE("opening aalib didn't work");
+ int32 v[]={context->imgheight,context->imgwidth,1};
+ gfpost("aalib image size: %s",(new Dim(3,v))->to_s());
+}
+
+\classinfo {
+ Ruby drivers = rb_ivar_set(rself,SI(@drivers),rb_hash_new());
+ const aa_driver *const *p = aa_drivers;
+ for (; *p; p++) {
+ rb_hash_aset(drivers,ID2SYM(rb_intern((*p)->shortname)), PTR2FIX(*p));
+ }
+// IEVAL(rself,"GridFlow.post('aalib supports: %s', @drivers.keys.join(', '))");
+ IEVAL(rself,"install '#in:aalib',1,1;@flags=2;@comment='Ascii Art Library'");
+}
+\end class FormatAALib
+void startup_aalib () {
+ \startall
+}
diff --git a/externals/gridflow/format/dc1394.c b/externals/gridflow/format/dc1394.c
new file mode 100644
index 00000000..1632bef7
--- /dev/null
+++ b/externals/gridflow/format/dc1394.c
@@ -0,0 +1,346 @@
+/*
+ $Id: dc1394.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <libraw1394/raw1394.h>
+#include <libdc1394/dc1394_control.h>
+#include "../base/grid.h.fcs"
+
+typedef raw1394handle_t RH;
+typedef nodeid_t NID;
+
+static const int ruby_lineno = __LINE__;
+static const char *ruby_code =
+\ruby
+
+def choice(*)end
+class CStruct
+ def initialize(*) end
+end
+
+choice :name,:Speed,:start,0,:values,%w(SPEED_100 SPEED_200 SPEED_400)
+choice :name,:Framerate,:start,32,:values,
+%w(FRAMERATE_1_875 FRAMERATE_3_75 FRAMERATE_7_5 FRAMERATE_15
+ FRAMERATE_30 FRAMERATE_60)
+
+choice :name,:Format0Mode,:start,64,:values,%w(
+ MODE_160x120_YUV444 MODE_320x240_YUV422
+ MODE_640x480_YUV411 MODE_640x480_YUV422
+ MODE_640x480_RGB MODE_640x480_MONO
+ MODE_640x480_MONO16)
+
+choice :name,:Format1Mode,:start,96,:values,%w(
+ MODE_800x600_YUV422 MODE_800x600_RGB
+ MODE_800x600_MONO MODE_1024x768_YUV422
+ MODE_1024x768_RGB MODE_1024x768_MONO
+ MODE_800x600_MONO16 MODE_1024x768_MONO16)
+
+choice :name,:Format2Mode,:start,128,:values,%w(
+ MODE_1280x960_YUV422 MODE_1280x960_RGB
+ MODE_1280x960_MONO MODE_1600x1200_YUV422
+ MODE_1600x1200_RGB MODE_1600x1200_MONO
+ MODE_1280x960_MONO16 MODE_1600x1200_MONO16)
+
+choice :name,:Format6Mode,:start,256,:values,%w(MODE_EXIF)
+
+choice :name,:Format7Mode,:start,288,:values,%w(
+ MODE_FORMAT7_0 MODE_FORMAT7_1 MODE_FORMAT7_2 MODE_FORMAT7_3
+ MODE_FORMAT7_4 MODE_FORMAT7_5 MODE_FORMAT7_6 MODE_FORMAT7_7)
+
+choice :name,:Format7ColorMode,:start,320,:values,%w(
+ COLOR_FORMAT7_MONO8 COLOR_FORMAT7_YUV411
+ COLOR_FORMAT7_YUV422 COLOR_FORMAT7_YUV444
+ COLOR_FORMAT7_RGB8 COLOR_FORMAT7_MONO16
+ COLOR_FORMAT7_RGB16)
+
+choice :name,:TriggerMode,:start,352,:values,%w(
+ TRIGGER_MODE_0 TRIGGER_MODE_1 TRIGGER_MODE_2 TRIGGER_MODE_3)
+
+choice :name,:CameraImageFormat,:start,384,:values,%w(
+ FORMAT_VGA_NONCOMPRESSED FORMAT_SVGA_NONCOMPRESSED_1
+ FORMAT_SVGA_NONCOMPRESSED_2 skip 3
+ FORMAT_STILL_IMAGE FORMAT_SCALABLE_IMAGE_SIZE)
+
+choice :name,:CameraFeatures,:start,416,:values,%w(
+ FEATURE_BRIGHTNESS FEATURE_EXPOSURE
+ FEATURE_SHARPNESS FEATURE_WHITE_BALANCE
+ FEATURE_HUE FEATURE_SATURATION
+ FEATURE_GAMMA FEATURE_SHUTTER
+ FEATURE_GAIN FEATURE_IRIS
+ FEATURE_FOCUS FEATURE_TEMPERATURE
+ FEATURE_TRIGGER skip 19
+ FEATURE_ZOOM FEATURE_PAN
+ FEATURE_TILT FEATURE_OPTICAL_FILTER
+ skip 12 FEATURE_CAPTURE_SIZE
+ FEATURE_CAPTURE_QUALITY skip 14)
+
+choice :name,:DCBool,:start,0,:values,%w(False True)
+
+#define MAX_CHARS 32
+#define SUCCESS 1
+#define FAILURE -1
+#define NO_CAMERA 0xffff
+
+# Parameter flags for setup_format7_capture()
+#define QUERY_FROM_CAMERA -1
+#define USE_MAX_AVAIL -2
+#define USE_RECOMMENDED -3
+
+# all dc1394_ prefixes removed
+# raw1394handle_t = RH
+# nodeid_t = NID
+# RH+NID = RN
+
+CameraInfo = CStruct.new %{
+ RH rh;
+ NID id;
+ octlet_t ccr_offset;
+ u_int64_t euid_64;
+ char vendor[MAX_CHARS + 1];
+ char model[MAX_CHARS + 1];
+}
+
+CameraCapture = CStruct.new %{
+ NID node;
+ int channel, frame_rate, frame_width, frame_height;
+ int * capture_buffer;
+ int quadlets_per_frame, quadlets_per_packet;
+ const unsigned char * dma_ring_buffer;
+ int dma_buffer_size, dma_frame_size, num_dma_buffers, dma_last_buffer;
+ const char * dma_device_file;
+ int dma_fd, port;
+ struct timeval filltime;
+ int dma_extra_count;
+ unsigned char * dma_extra_buffer;
+ int drop_frames;
+}
+
+MiscInfo = CStruct.new %{
+ int format, mode, framerate;
+ bool is_iso_on;
+ int iso_channel, iso_speed, mem_channel_number;
+ int save_channel, load_channel;
+}
+
+FeatureInfo = CStruct.new %{
+ uint feature_id;
+ bool available, one_push, readout_capable, on_off_capable;
+ bool auto_capable, manual_capable, polarity_capable, one_push_active;
+ bool is_on, auto_active;
+ char trigger_mode_capable_mask;
+ int trigger_mode;
+ bool trigger_polarity;
+ int min, max, value, BU_value, RV_value, target_value;
+}
+
+FeatureSet = CStruct.new %{
+ FeatureInfo feature[NUM_FEATURES];
+}
+#void print_feature_set(FeatureSet *features);
+#extern const char *feature_desc[NUM_FEATURES];
+#void print_feature(FeatureInfo *feature);
+#RawFire create_handle(int port);
+#destroy_handle(RH rh);
+#void print_camera_info(camerainfo *info);
+
+class RH; %{
+ # those two:
+ # Return -1 in numCameras and NULL from the call if there is a problem
+ # otherwise the number of cameras and the NID array from the call
+ NID* get_camera_nodes(int *numCameras, int showCameras);
+ NID* get_sorted_camera_nodes(int numids, int *ids, int *numCameras, int showCameras);
+ release_camera(CameraCapture *camera);
+ single_capture(CameraCapture *camera);
+# this one returns FAILURE or SUCCESS
+ multi_capture(CameraCapture *cams, int num);
+}end
+
+class RN; %{
+ init_camera();
+ is_camera(bool *value);
+ get_camera_feature_set(FeatureSetFeatureInfo *features);
+ get_camera_feature(FeatureInfo *feature);
+ get_camera_misc_info(miscinfo *info);
+ get_sw_version(quadlet_t *value);
+ get_camera_info(camerainfo *info);
+ query_supported_formats(quadlet_t *value);
+ query_supported_modes(uint format, quadlet_t *value);
+ query_supported_framerates(uint format, uint mode, quadlet_t *value);
+ query_revision(int mode, quadlet_t *value);
+ query_basic_functionality(quadlet_t *value);
+ query_advanced_feature_offset(quadlet_t *value);
+ attr set_video_framerate(uint framerate);
+ attr video_mode(uint mode);
+ attr video_format(uint format);
+ double_attr iso_channel_and_speed(uint channel, uint speed);
+ camera_on();
+ camera_off();
+ start_iso_transmission();
+ stop_iso_transmission();
+ get_iso_status(bool *is_on);
+ set_one_shot();
+ unset_one_shot();
+ set_multi_shot(uint numFrames);
+ unset_multi_shot();
+# attributes :
+# those are get_/set_ methods where the get has an input parameter
+# and the set has an output parameter
+ attr uint brightness
+ attr uint exposure
+ attr uint sharpness
+ double_attr set_white_balance(uint u_b_value, uint v_r_value);
+ attr uint hue
+ attr uint saturation(uint saturation);
+ attr uint gamma(uint gamma);
+ attr shutter(uint shutter);
+ attr uint gain
+ attr uint iris
+ attr uint focus
+ attr uint trigger_mode
+ attr uint zoom
+ attr uint pan
+ attr uint tilt
+ attr uint optical_filter
+ attr uint capture_size
+ attr uint capture_quality
+ int get_temperature(uint *target_temperature, uint *temperature);
+ int set_temperature(uint target_temperature);
+
+ get_memory_load_ch(uint *channel);
+ get_memory_save_ch(uint *channel);
+ is_memory_save_in_operation(bool *value);
+ set_memory_save_ch(uint channel);
+ memory_save();
+ memory_load(uint channel);
+ attr bool trigger_polarity
+ trigger_has_polarity(bool *polarity);
+ attr bool set_trigger_on_off
+
+# this one returns SUCCESS on success, FAILURE otherwise
+ setup_capture(
+ int channel, int format, int mode, int speed, int frame_rate,
+ CameraCapture *camera);
+ dma_setup_capture(
+ int channel, int format, int mode, int speed, int frame_rate,
+ int num_dma_buffers, int drop_frames, const char *dma_device_file,
+ CameraCapture *camera);
+ setup_format7_capture(
+ int channel, int mode, int speed, int bytes_per_packet,
+ uint left, uint top, uint width, uint height, CameraCapture *camera);
+ dma_setup_format7_capture(
+ int channel, int mode, int speed, int bytes_per_packet,
+ uint left, uint top, uint width, uint height,
+ int num_dma_buffers, CameraCapture *camera);
+}end
+
+#RNF = RN+uint feature
+class RNF; %{
+ query_feature_control(uint *availability);
+ query_feature_characteristics(quadlet_t *value);
+ attr uint feature_value
+ is_feature_present(bool *value);
+ has_one_push_auto(bool *value);
+ is_one_push_in_operation(bool *value);
+ start_one_push_operation();
+ can_read_out(bool *value);
+ can_turn_on_off(bool *value);
+ is_feature_on(bool *value);
+ feature_on_off(uint value);
+ has_auto_mode(bool *value);
+ has_manual_mode(bool *value);
+ is_feature_auto(bool *value);
+ auto_on_off(uint value);
+ get_min_value(uint *value);
+ get_max_value(uint *value);
+}end
+
+# DMA Capture Functions
+#dma_release_camera(RH rh, CameraCapture *camera);
+#dma_unlisten(RH rh, CameraCapture *camera);
+#dma_single_capture(CameraCapture *camera);
+#dma_multi_capture(CameraCapture *cams,int num);
+#dma_done_with_buffer(CameraCapture * camera);
+
+# default return type is int, prolly means SUCCESS/FAILURE
+
+#RNM = RN+uint mode
+class RNM; %{
+ query_format7_max_image_size(uint *horizontal_size, uint *vertical_size);
+ query_format7_unit_size(uint *horizontal_unit, uint *vertical_unit);
+ query_format7_color_coding(quadlet_t *value);
+ query_format7_pixel_number(uint *pixnum);
+ query_format7_total_bytes(uint *total_bytes);
+ query_format7_packet_para(uint *min_bytes, uint *max_bytes);
+ query_format7_recommended_byte_per_packet(uint *bpp);
+ query_format7_packet_per_frame(uint *ppf);
+ query_format7_unit_position(uint *horizontal_pos, uint *vertical_pos);
+ # those were query/set pairs.
+ double_qattr format7_image_position(uint left, uint top);
+ double_qattr format7_image_size(uint width, uint height);
+ qattr uint format7_color_coding_id
+ qattr uint format7_byte_per_packet
+ query_format7_value_setting(uint *present, uint *setting1, uint *err_flag1, uint *err_flag2);
+ set_format7_value_setting(); //huh?
+}end
+
+\end ruby
+;
+
+\class FormatDC1394 < Format
+struct FormatDC1394 : Format {
+ \decl void initialize (Symbol mode);
+ \decl void frame ();
+};
+
+\def void initialize(Symbol mode) {
+ gfpost("DC1394: hello world");
+ RH rh = raw1394_new_handle();
+ int numPorts = raw1394_get_port_info(rh,0,0);
+ raw1394_destroy_handle(rh);
+ gfpost("there are %d Feuerweuer ports",numPorts);
+ if (mode!=SYM(in)) RAISE("sorry, read-only");
+ for(int port=0; port<numPorts; port++) {
+ gfpost("trying port #%d...",port);
+ RH rh = dc1394_create_handle(port);
+ int numCameras=0xDEADBEEF;
+ NID *nodes = dc1394_get_camera_nodes(rh,&numCameras,0);
+ gfpost("port #%d has %d cameras",port,numCameras);
+ for (int i=0; i<numCameras; i++) gfpost("camera at node #%d",nodes[i]);
+ // I'm stuck here, can't find that iSight camera. -- matju
+ }
+ dc1394_destroy_handle(rh);
+}
+
+\def void frame () {
+ gfpost("i'd like to get a frame from the cam, but how?");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:dc1394',1,1;@flags=4;@comment='Video4linux 1.x'");
+ //IEVAL(rself,ruby_code);
+ rb_funcall(rself,SI(instance_eval),3,rb_str_new2(ruby_code),
+ rb_str_new2(__FILE__),INT2NUM(ruby_lineno+3));
+}
+\end class FormatDC1394
+void startup_dc1394 () {
+ \startall
+}
diff --git a/externals/gridflow/format/jpeg.c b/externals/gridflow/format/jpeg.c
new file mode 100644
index 00000000..e59d8e06
--- /dev/null
+++ b/externals/gridflow/format/jpeg.c
@@ -0,0 +1,130 @@
+/*
+ $Id: jpeg.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+//!@#$ not handling abort on compress
+//!@#$ not handling abort on decompress
+
+#include "../base/grid.h.fcs"
+/* removing macros (removing warnings) */
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef EXTERN
+extern "C" {
+#include <jpeglib.h>
+};
+
+\class FormatJPEG < Format
+struct FormatJPEG : Format {
+ P<BitPacking> bit_packing;
+ struct jpeg_compress_struct cjpeg;
+ struct jpeg_decompress_struct djpeg;
+ struct jpeg_error_mgr jerr;
+ int fd;
+ FILE *f;
+ \decl Ruby frame ();
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \grin 0 int
+};
+
+GRID_INLET(FormatJPEG,0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+ cjpeg.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cjpeg);
+ jpeg_stdio_dest(&cjpeg,f);
+ cjpeg.image_width = in->dim->get(1);
+ cjpeg.image_height = in->dim->get(0);
+ cjpeg.input_components = 3;
+ cjpeg.in_color_space = JCS_RGB;
+ jpeg_set_defaults(&cjpeg);
+ jpeg_start_compress(&cjpeg,TRUE);
+} GRID_FLOW {
+ int rowsize = in->dim->get(1)*in->dim->get(2);
+ int rowsize2 = in->dim->get(1)*3;
+ uint8 row[rowsize2];
+ uint8 *rows[1] = { row };
+ while (n) {
+ bit_packing->pack(in->dim->get(1),data,Pt<uint8>(row,rowsize));
+ jpeg_write_scanlines(&cjpeg,rows,1);
+ n-=rowsize; data+=rowsize;
+ }
+} GRID_FINISH {
+ jpeg_finish_compress(&cjpeg);
+ jpeg_destroy_compress(&cjpeg);
+} GRID_END
+
+static bool gfeof(FILE *f) {
+ off_t cur,end;
+ cur = ftell(f);
+ fseek(f,0,SEEK_END);
+ end = ftell(f);
+ fseek(f,cur,SEEK_SET);
+ return cur==end;
+}
+
+\def Ruby frame () {
+ off_t off = NUM2LONG(rb_funcall(rb_ivar_get(rself,SI(@stream)),SI(tell),0));
+ fseek(f,off,SEEK_SET);
+ if (gfeof(f)) return Qfalse;
+ djpeg.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&djpeg);
+ jpeg_stdio_src(&djpeg,f);
+ jpeg_read_header(&djpeg,TRUE);
+ int sx=djpeg.image_width, sy=djpeg.image_height, chans=djpeg.num_components;
+ GridOutlet out(this,0,new Dim(sy, sx, chans),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ jpeg_start_decompress(&djpeg);
+ uint8 row[sx*chans];
+ uint8 *rows[1] = { row };
+ for (int n=0; n<sy; n++) {
+ jpeg_read_scanlines(&djpeg,rows,1);
+ out.send(sx*chans,Pt<uint8>(row,sx*chans));
+ }
+ jpeg_finish_decompress(&djpeg);
+ jpeg_destroy_decompress(&djpeg);
+ return Qnil;
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: jpeg file <filename>");
+ rb_funcall(rself,SI(raw_open),3,mode,source,filename);
+ Ruby stream = rb_ivar_get(rself,SI(@stream));
+ fd = NUM2INT(rb_funcall(stream,SI(fileno),0));
+ f = fdopen(fd,mode==SYM(in)?"r":"w");
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+ "install '#in:jpeg',1,1;@mode=6;"
+ "include GridFlow::EventIO; suffixes_are'jpeg','jpg'");
+}
+\end class FormatJPEG
+void startup_jpeg () {
+ \startall
+}
diff --git a/externals/gridflow/format/main.rb b/externals/gridflow/format/main.rb
new file mode 100644
index 00000000..6e31be75
--- /dev/null
+++ b/externals/gridflow/format/main.rb
@@ -0,0 +1,788 @@
+=begin
+ $Id: main.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+=end
+
+require "socket"
+require "fcntl"
+
+module GridFlow
+
+class<<self
+ def max_rank; 16; end
+ def max_size; 64*1024**2; end
+ def max_packet; 1024*2; end
+end
+
+ENDIAN_BIG,ENDIAN_LITTLE,ENDIAN_SAME,ENDIAN_DIFF = 0,1,2,3
+
+OurByteOrder = case [1].pack("L")
+ when "\0\0\0\1"; ENDIAN_BIG # Mac, Sun, SiliconGraphics
+ when "\1\0\0\0"; ENDIAN_LITTLE # Intel
+ else raise "Cannot determine byte order" end
+
+class Format < GridObject
+ FF_R,FF_W = 4,2 # flags indicating support of :in and :out respectively.
+ attr_accessor :parent
+=begin API (version 0.8)
+ mode is :in or :out
+ def initialize(mode,*args) :
+ open a file handler (do it via .new of class)
+ attr_reader :description :
+ a _literal_ (constant) string describing the format handler
+ def self.info() optional :
+ return a string describing the format handler differently
+ than self.description(). in particular, it can list
+ compile-time options and similar things. for example,
+ quicktime returns a list of codecs.
+ def frame() :
+ read one frame, send through outlet 0
+ return values :
+ Integer >= 0 : frame number of frame read.
+ false : no frame was read : end of sequence.
+ nil : a frame was read, but can't say its number.
+ note that trying to read a nonexistent frame should no longer
+ rewind automatically (@in handles that part), nor re-read the
+ last frame (mpeg/quicktime used to do this)
+ def seek(Integer i) : select one frame to be read next (by number)
+ def length() : ^Integer returns number of frames (never implemented ?)
+ def close() : close a handler
+ inlet 0 :
+ grid : frame to write
+ other : special options
+ outlet 0 : grid : frame just read
+ outlet 1 : everything else
+=end
+
+ def initialize(mode,*)
+ super
+ @cast = :int32
+ @colorspace = :rgb
+ @mode = mode
+ @frame = 0
+ @parent = nil
+ @stream = nil
+ flags = self.class.instance_eval{if defined?@flags then @flags else 6 end}
+ # FF_W, FF_R, FF_RW
+ case mode
+ when :in; flags[2]==1
+ when :out; flags[1]==1
+ else raise "Format opening mode is incorrect"
+ end or raise \
+ "Format '#{self.class.instance_eval{@symbol_name}}'"\
+ " does not support mode '#{mode}'"
+ end
+
+ def close
+ @stream.close if defined? @stream and @stream
+ end
+
+ def self.suffixes_are(*suffixes)
+ suffixes.map{|s|s.split(/[, ]/)}.flatten.each {|suffix|
+ Format.suffixes[suffix] = self
+ }
+ end
+
+ class<<self
+ attr_reader :symbol_name
+ attr_reader :description
+ attr_reader :flags
+ attr_reader :suffixes
+ end
+ @suffixes = {}
+ def seek frame
+ (rewind; return) if frame == 0
+ raise "don't know how to seek for frame other than # 0"
+ end
+
+ # this is what you should use to rewind
+ # different file-sources may redefine this as something else
+ # (eg: gzip)
+ def rewind
+ raise "Nothing to rewind about..." if not @stream
+ @stream.seek 0,IO::SEEK_SET
+ @frame = 0
+ end
+
+ # This is different from IO#eof, which waits until a read has failed
+ # doesn't work in nonblocking mode? (I don't recall why)
+ def eof?
+ thispos = (@stream.seek 0,IO::SEEK_CUR; @stream.tell)
+ lastpos = (@stream.seek 0,IO::SEEK_END; @stream.tell)
+ @stream.seek thispos,IO::SEEK_SET
+ return thispos == lastpos
+ rescue Errno::ESPIPE # just ignore if seek is not possible
+ return false
+ end
+
+ # "ideal" buffer size or something
+ # the buffer may be bigger than this but usually not by much.
+ def self.buffersize; 16384 end
+
+ def _0_headerless(*args) #!@#$ goes in FormatGrid ?
+ args=args[0] if Array===args[0]
+ #raise "expecting dimension list..."
+ args.map! {|a|
+ Numeric===a or raise "expecting dimension list..."
+ a.to_i
+ }
+ @headerless = args
+ end
+ def _0_headerful #!@#$ goes in FormatGrid ?
+ @headerless = nil
+ end
+ def _0_type arg
+ #!@#$ goes in FormatGrid ?
+ #!@#$ bug: should not be able to modify this _during_ a transfer
+ case arg
+ when :uint8; @bpv=8; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xff])
+ when :int16; @bpv=16; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xffff])
+ when :int32; @bpv=32; @bp=nil
+ else raise "unsupported number type: #{arg}"
+ end
+ end
+ def _0_cast arg
+ case arg
+ when :uint8, :int16, :int32, :int64, :float32, :float64
+ @cast = arg
+ else raise "unsupported number type: #{arg}"
+ end
+ end
+ def frame; @frame+=1; @frame-1 end
+end
+
+# common parts between GridIn and GridOut
+module GridIO
+ def check_file_open; if not @format then raise "can't do that: file not open" end end
+ def _0_close; check_file_open; @format.close; @format = nil end
+ def delete; @format.close if @format; @format = nil; super end
+ attr_reader :format
+
+ def _0_open(sym,*a)
+ sym = sym.intern if String===sym
+ if a.length==0 and /\./ =~ sym.to_s then a=[sym]; sym=:file end
+ qlass = GridFlow.fclasses["\#io:#{sym}"]
+ if not qlass then raise "unknown file format identifier: #{sym}" end
+ _0_close if @format
+ @format = qlass.new @mode, *a
+ @format.connect 0,self,1
+ @format.connect 1,self,2
+ @format.parent = self
+ @loop = true
+ end
+
+ def _0_timelog flag; @timelog = Integer(flag)!=0 end
+ def _0_loop flag; @loop = Integer(flag)!=0 end
+ def method_missing(*message)
+ sel = message[0].to_s
+ if sel =~ /^_0_/
+ message[0] = sel.sub(/^_0_/,"").intern
+ @format.send_in 0, *message
+ elsif sel =~ /^_2_/
+ sel = sel.sub(/^_2_/,"").intern
+ message.shift
+ send_out 1, sel, *message
+ else
+ return super
+ end
+ end
+end
+
+GridObject.subclass("#in",1,2) {
+ install_rgrid 0
+ include GridIO
+ def initialize(*a)
+ super
+ @format = nil
+ @timelog = false
+ @framecount = 0
+ @time = Time.new
+ @mode = :in
+ return if a.length==0
+ _0_open(*a)
+ end
+ def _0_bang
+ check_file_open
+ framenum = @format.frame
+ if framenum == false
+ send_out 1
+ return if not @loop
+ @format.seek 0
+ framenum = @format.frame
+ if framenum == false
+ raise "can't read frame: the end is at the beginning???"
+ end
+ end
+ send_out 1, framenum if framenum
+ end
+ def _0_float frame; _0_set frame; _0_bang end
+ def _0_set frame; check_file_open; @format.seek frame end
+ def _0_reset; check_file_open; @format.seek 0; end
+ def _1_grid(*a) send_out 0,:grid,*a end
+ def _0_load name; _0_open name; _0_bang; _0_close end
+}
+
+GridObject.subclass("#out",1,1) {
+ include GridIO
+ def initialize(*a)
+ super
+ @format = nil
+ @timelog = false
+ @framecount = 0
+ @time = Time.new
+ @mode = :out
+ return if a.length==0
+ if Integer===a[0] or Float===a[0]
+ _0_open :x11,:here
+ _0_out_size a[0],a[1]
+ else
+ _0_open(*a)
+ end
+ end
+
+ def _0_list(*a) @format._0_list(*a) end
+
+ # hacks
+ def _1_grid(*a) send_out 0,:grid,*a end # for aalib
+ def _1_position(*a) send_out 0,:position,*a end
+ def _1_keypress(*a) send_out 0,:keypress,*a end
+ def _1_keyrelease(*a) send_out 0,:keyrelease,*a end
+
+ def _0_grid(*a)
+ check_file_open
+ @format._0_grid(*a)
+ send_out 0,:bang
+ log if @timelog
+ @framecount+=1
+ end
+
+ def log
+ time = Time.new
+ post("\#out: frame#%04d time: %10.3f s; diff: %5d ms",
+ @framecount, time, ((time-@time)*1000).to_i)
+ @time = time
+ end
+ install_rgrid 0
+}
+
+class BitPacking
+ alias pack pack2
+ alias unpack unpack2
+end
+
+# adding event-driven IO to a Format class
+module EventIO
+ def read_wait?; !!@action; end
+
+ def initialize(*)
+ @acceptor = nil
+ @buffer = nil
+ @action = nil
+ @chunksize = nil
+ @rewind_redefined = false
+ @clock = Clock.new self
+ @delay = 100 # ms
+ super
+ end
+
+ def call() try_read end
+
+ def on_read(n,&action)
+ @action = action
+ @chunksize = n
+ end
+
+ def try_accept
+ #!@#$ use setsockopt(SO_REUSEADDR) here???
+ TCPSocket.do_not_reverse_lookup = true # hack
+ @acceptor.nonblock = true
+ @stream = @acceptor.accept
+ @stream.nonblock = true
+ @stream.sync = true
+ @clock.unset
+# send_out 0, :accept # does not work
+ rescue Errno::EAGAIN
+ end
+
+ def try_read(dummy=nil)
+ n = @chunksize-(if @buffer then @buffer.length else 0 end)
+ t = @stream.read(n) # or raise EOFError
+ if not t
+ raise "heck" if not @stream.eof?
+ rewind
+ t = @stream.read(n) or raise "can't read any of #{n} bytes?"
+ end
+ if @buffer then @buffer << t else @buffer = t end
+ if @buffer.length == @chunksize
+ action,buffer = @action,@buffer
+ @action,@buffer = nil,""
+ @clock.unset
+ action.call buffer
+ end
+ rescue Errno::EAGAIN
+ post "read would block"
+ end
+
+ def raw_open_gzip_in(filename)
+ r,w = IO.pipe
+ if pid=fork
+ GridFlow.subprocesses[pid]=true
+ w.close
+ @stream = r
+ else
+ r.close
+ STDOUT.reopen w
+ STDIN.reopen @stream
+ @stream = File.open filename, "r"
+ exec "gzip", "-dc"
+ end
+ end
+ def raw_open_gzip_out(filename)
+ r,w = IO.pipe
+ if pid=fork
+ GridFlow.subprocesses[pid]=true
+ r.close
+ @stream = w
+ else
+ w.close
+ STDIN.reopen r
+ STDOUT.reopen @stream
+ @stream = File.open filename, "w"
+ exec "gzip", "-c"
+ end
+ end
+ def raw_open(mode,source,*args)
+ @raw_open_args = mode,source,*args
+ fmode = case mode
+ when :in; "r"
+ when :out; "w"
+ else raise "bad mode" end
+ close
+ case source
+ when :file
+ filename = args[0].to_s
+ filename = GridFlow.find_file filename if mode==:in
+ @stream = File.open filename, fmode
+ when :gzfile
+ filename = args[0].to_s
+ filename = GridFlow.find_file filename if mode==:in
+ if mode==:in then
+ raw_open_gzip_in filename
+ else
+ raw_open_gzip_out filename
+ end
+ def self.rewind
+ raw_open(*@raw_open_args)
+ @frame = 0
+ end unless @rewind_redefined
+ @rewind_redefined = true
+ when :tcp
+ if RUBY_VERSION < "1.6.6"
+ raise "use at least 1.6.6 (reason: bug in socket code)"
+ end
+ post "-----------"
+ time = Time.new
+ TCPSocket.do_not_reverse_lookup = true # hack
+ @stream = TCPSocket.open(args[0].to_s,args[1].to_i)
+ post "----------- #{Time.new-time}"
+ @stream.nonblock = true
+ @stream.sync = true
+ @clock.delay @delay
+ when :tcpserver
+ TCPSocket.do_not_reverse_lookup = true # hack
+ TCPServer.do_not_reverse_lookup = true # hack
+ post "-----------"
+ time = Time.new
+ @acceptor = TCPServer.open(args[0].to_s)
+ post "----------- #{Time.new-time}"
+ @acceptor.nonblock = true
+ #$tasks[self] = proc {self.try_accept} #!!!!!
+ else
+ raise "unknown access method '#{source}'"
+ end
+ end
+ def close
+ @acceptor.close if @acceptor
+ @stream.close if @stream
+ GridFlow.hunt_zombies
+ end
+end
+
+Format.subclass("#io:file",1,1) {
+ def self.new(mode,file)
+ file=file.to_s
+ a = [mode,:file,file]
+ if not /\./=~file then raise "no filename suffix?" end
+ suf=file.split(/\./)[-1]
+ h=Format.suffixes[suf]
+ if not h then raise "unknown suffix '.#{suf}'" end
+ h.new(*a)
+ end
+ @comment="format autodetection proxy"
+}
+
+Format.subclass("#io:grid",1,1) {
+ include EventIO
+ install_rgrid 0
+ @comment = "GridFlow file format"
+ suffixes_are "grid"
+=begin
+ This is the Grid format I defined:
+ 1 uint8: 0x7f
+ 4 uint8: "GRID" big endian | "grid" little endian
+ 1 uint8: type {
+ number of bits in 8,16,32,64, plus one of: 1:unsigned 2:float
+ but float8,float16 are not allowed (!)
+ }
+ 1 uint8: reserved (supported: 0)
+ 1 uint8: number of dimensions N (supported: at least 0..4)
+ N uint32: number of elements per dimension D[0]..D[N-1]
+ raw data goes there.
+=end
+ # bits per value: 32 only
+ attr_accessor :bpv # Fixnum: bits-per-value
+ # endianness
+ # attr_accessor :endian # ENDIAN_LITTLE or ENDIAN_BIG
+ # IO or File or TCPSocket
+ attr_reader :stream
+ # nil=headerful; array=assumed dimensions of received grids
+ #attr_accessor :headerless
+
+ def initialize(mode,source,*args)
+ super
+ @bpv = 32
+ @headerless = nil
+ @endian = OurByteOrder
+ raw_open mode,source,*args
+ end
+
+ def post(*s)
+ # because i'm using miller_0_38 and it can't disable the console
+ # i am using fprintf stderr instead of post.
+ ### STDERR.puts(sprintf(*s))
+ # disabled because i don't need it now
+ end
+
+ # rewinding and starting
+ def frame
+ raise "can't get frame when there is no connection" if not @stream
+ raise "already waiting for input" if read_wait?
+ return false if eof?
+ post "----- 1"
+ if @headerless then
+ @n_dim=@headerless.length
+ @dim = @headerless
+ @dex = 0
+ set_bufsize
+ send_out_grid_begin 0, @dim
+ on_read(bufsize) {|data| frame3 data }
+ else
+ on_read(8) {|data| frame1 data }
+ end
+ post "----- 2"
+ (try_read nil while read_wait?) if not TCPSocket===@stream
+ post "----- 3"
+ super
+ post "----- 4"
+ end
+
+ def set_bufsize
+ @prod = 1
+ @dim.each {|x| @prod *= x }
+ n = @prod/@dim[0]
+ k = GridFlow.max_packet / n
+ k=1 if k<1
+ @bufsize = k*n*@bpv/8
+ @bufsize = @prod if @bufsize > @prod
+ end
+
+ # the header
+ def frame1 data
+ post "----- frame1"
+ head,@bpv,reserved,@n_dim = data.unpack "a5ccc"
+ @endian = case head
+ when "\x7fGRID"; ENDIAN_BIG
+ when "\x7fgrid"; ENDIAN_LITTLE
+ else raise "grid header: invalid (#{data.inspect})" end
+ case bpv
+ when 8, 16, 32; # ok
+ else raise "unsupported bpv (#{@bpv})"
+ end
+ if reserved!=0
+ raise "reserved field is not zero"
+ end
+ if @n_dim > GridFlow.max_rank
+ raise "too many dimensions (#{@n_dim})"
+ end
+ on_read(4*@n_dim) {|data| frame2 data }
+ end
+
+ # the dimension list
+ def frame2 data
+ post "----- frame2"
+ @dim = data.unpack(if @endian==ENDIAN_LITTLE then "V*" else "N*" end)
+ set_bufsize
+ if @prod > GridFlow.max_size
+ raise "dimension list: invalid prod (#{@prod})"
+ end
+ send_out_grid_begin 0, @dim, @cast
+
+ on_read(bufsize) {|data| frame3 data }
+ @dex = 0
+ end
+
+ attr_reader :bufsize
+
+ # for each slice of the body
+ def frame3 data
+ post "----- frame3 with dex=#{@dex.inspect}, prod=#{@prod.inspect}"
+ n = data.length
+ nn = n*8/@bpv
+ # is/was there a problem with the size of the data being read?
+ case @bpv
+ when 8
+ @bp = BitPacking.new(@endian,1,[0xff])
+ send_out_grid_flow(0, @bp.unpack(data))
+ @dex += data.length
+ when 16
+ @bp = BitPacking.new(@endian,2,[0xffff])
+ send_out_grid_flow(0, @bp.unpack(data))
+ @dex += data.length/2
+ when 32
+ data.swap32! if @endian!=OurByteOrder
+ send_out_grid_flow 0, data
+ @dex += data.length/4
+ end
+ if @dex >= @prod
+ @clock.unset
+ else
+ on_read(bufsize) {|data| frame3 data }
+ end
+ end
+
+ def _0_rgrid_begin
+ if not @stream
+ raise "can't send frame when there is no connection"
+ end
+ @dim = inlet_dim 0
+ post "@dim=#{@dim.inspect}"
+ return if @headerless
+ # header
+ @stream.write(
+ [if @endian==ENDIAN_LITTLE then "\x7fgrid" else "\x7fGRID" end,
+ @bpv,0,@dim.length].pack("a5ccc"))
+ # dimension list
+ @stream.write(
+ @dim.to_a.pack(if @endian==ENDIAN_LITTLE then "V*" else "N*" end))
+ end
+
+ def _0_rgrid_flow data
+ case @bpv
+ when 8, 16
+ @stream.write @bp.pack(data)
+ when 32
+ data.swap32! if GridFlow::OurByteOrder != @endian
+ @stream.write data
+ end
+ end
+
+ def _0_rgrid_end; @stream.flush end
+
+ def endian(a)
+ @endian = case a
+ when :little; ENDIAN_LITTLE
+ when :big; ENDIAN_BIG
+ when :same; ENDIAN_SAME
+ else raise "argh"
+ end
+ end
+
+ def headerless(*args)
+ args=args[0] if Array===args[0]
+ args.map! {|a|
+ Numeric===a or raise "expecting dimension list..."
+ a.to_i
+ }
+ @headerless = args
+ end
+
+ def headerful; @headerless = nil end
+
+ #!@#$ method name conflict ?
+ def type(nt)
+ #!@#$ bug: should not be able to modify this _during_ a transfer
+ case nt
+ when :uint8; @bpv= 8; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xff])
+ when :int16; @bpv=16; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xffff])
+ when :int32; @bpv=32; @bp=nil
+ else raise "unsupported number type"
+ end
+ end
+}
+
+module PPMandTarga
+ # "and false" disables features that may cause crashes and don't
+ # accelerate gridflow that much.
+ def frame_read_body height, width, channels
+ bs = width*channels
+ n = bs*height
+ bs = (self.class.buffersize/bs)*bs+bs # smallest multiple of bs over BufferSize
+ buf = ""
+ if RUBY_VERSION >= "1.8.0" and false
+ data = "x"*bs # must preallocate (bug in 1.8.0.pre1-3)
+ while n>0 do
+ bs=n if bs>n
+ @stream.read(bs,data) or raise EOFError
+ if @bp then
+ send_out_grid_flow 0, @bp.unpack(data,buf)
+ else
+ send_out_grid_flow 0, data, :uint8
+ end
+ n-=bs
+ end
+ else
+ nothing = ""
+ while n>0 do
+ bs=n if bs>n
+ data = @stream.read(bs) or raise EOFError
+ if @bp then
+ send_out_grid_flow 0, @bp.unpack(data,buf)
+ else
+ send_out_grid_flow 0, data, :uint8
+ end
+ data.replace nothing and false # prevent clogging memory
+ n-=bs
+ end
+ end
+ end
+end
+
+Format.subclass("#io:ppm",1,1) {
+ install_rgrid 0
+ @comment = "Portable PixMap (PPM) File Format"
+ suffixes_are "ppm"
+ include EventIO, PPMandTarga
+
+ def initialize(mode,source,*args)
+ @bp = if mode==:out
+ BitPacking.new(ENDIAN_LITTLE,3,[0x0000ff,0x00ff00,0xff0000])
+ else nil end
+ super
+ raw_open mode,source,*args
+ end
+ def frame
+ #@stream.sync = false
+ metrics=[]
+ return false if eof?
+ line = @stream.gets
+ (rewind; line = @stream.gets) if not line # hack
+ line.chomp!
+ if line != "P6" then raise "Wrong format (needing PPM P6)" end
+ while metrics.length<3
+ line = @stream.gets
+ next if line =~ /^#/
+ metrics.push(*(line.split(/\s+/).map{|x| Integer x }))
+ end
+ metrics[2]==255 or
+ raise "Wrong color depth (max_value=#{metrics[2]} instead of 255)"
+
+ send_out_grid_begin 0, [metrics[1], metrics[0], 3], @cast
+ frame_read_body metrics[1], metrics[0], 3
+ super
+ end
+
+ def _0_rgrid_begin
+ dim = inlet_dim 0
+ raise "expecting (rows,columns,channels)" if dim.length!=3
+ raise "expecting channels=3" if dim[2]!=3
+ @stream.write "P6\n"
+ @stream.write "# generated using GridFlow #{GF_VERSION}\n"
+ @stream.write "#{dim[1]} #{dim[0]}\n255\n"
+ @stream.flush
+ inlet_set_factor 0, 3
+ end
+ def _0_rgrid_flow(data) @stream.write @bp.pack(data) end
+ def _0_rgrid_end; @stream.flush end
+}
+
+Format.subclass("#io:targa",1,1) {
+ install_rgrid 0
+ @comment = "TrueVision Targa"
+ suffixes_are "tga"
+ include EventIO, PPMandTarga
+=begin
+targa header is like:
+ [:comment, Uint8, :length],
+ [:colortype, Uint8],
+ [:colors, Uint8], 5,
+ [:origin_x, Int16],
+ [:origin_y, Int16],
+ [:w, Uint16],
+ [:h, Uint16],
+ [:depth, Uint8], 1,
+ [:comment, String8Unpadded, :data],
+=end
+ def initialize(mode,source,*args)
+ super
+ raw_open mode,source,*args
+ end
+
+ def set_bitpacking depth
+ @bp = case depth
+ #!@#$ endian here doesn't seem to be changing much ?
+ when 24; BitPacking.new(ENDIAN_LITTLE,3,[0xff0000,0x00ff00,0x0000ff])
+ when 32; BitPacking.new(ENDIAN_LITTLE,4,
+ [0x00ff0000,0x0000ff00,0x000000ff,0xff000000])
+ else
+ raise "tga: unsupported colour depth: #{depth}\n"
+ end
+ end
+
+ def frame
+ return false if eof?
+ head = @stream.read(18)
+ comment_length,colortype,colors,w,h,depth = head.unpack("cccx9vvcx")
+ comment = @stream.read(comment_length)
+ raise "unsupported color format: #{colors}" if colors != 2
+# post "tga: size y=#{h} x=#{w} depth=#{depth} colortype=#{colortype}"
+# post "tga: comment: \"#{comment}\""
+ set_bitpacking depth
+ send_out_grid_begin 0, [ h, w, depth/8 ], @cast
+ frame_read_body h, w, depth/8
+ super
+ end
+
+ def _0_rgrid_begin
+ dim = inlet_dim 0
+ raise "expecting (rows,columns,channels)" if dim.length!=3
+ raise "expecting channels=3 or 4" if dim[2]!=3 and dim[2]!=4
+ # comment = "created using GridFlow"
+ #!@#$ why did i use that comment again?
+ comment = "generated using GridFlow #{GF_VERSION}"
+ @stream.write [comment.length,colortype=0,colors=2,"\0"*9,
+ dim[1],dim[0],8*dim[2],(8*(dim[2]-3))|32,comment].pack("ccca9vvcca*")
+ set_bitpacking 8*dim[2]
+ inlet_set_factor 0, dim[2]
+ end
+ def _0_rgrid_flow data; @stream.write @bp.pack(data) end
+ def _0_rgrid_end; @stream.flush end
+}
+end # module GridFlow
diff --git a/externals/gridflow/format/mpeg3.c b/externals/gridflow/format/mpeg3.c
new file mode 100644
index 00000000..d6820ccd
--- /dev/null
+++ b/externals/gridflow/format/mpeg3.c
@@ -0,0 +1,98 @@
+/*
+ $Id: mpeg3.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#define LIBMPEG_INCLUDE_HERE
+#include "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+\class FormatMPEG3 < Format
+struct FormatMPEG3 : Format {
+ mpeg3_t *mpeg;
+ P<BitPacking> bit_packing;
+ int track;
+ FormatMPEG3 () : track(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void seek (int frame);
+ \decl Ruby frame ();
+ \decl void close ();
+};
+
+\def void seek (int frame) { mpeg3_set_frame(mpeg,frame,track); }
+
+\def Ruby frame () {
+ int nframe = mpeg3_get_frame(mpeg,track);
+ if (nframe >= mpeg3_video_frames(mpeg,track)) return Qfalse;
+
+ int sx = mpeg3_video_width(mpeg,track);
+ int sy = mpeg3_video_height(mpeg,track);
+ int npixels = sx*sy;
+ int channels = 3;
+ Pt<uint8> buf = ARRAY_NEW(uint8,sy*sx*channels+16);
+ uint8 *rows[sy];
+ for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ int result = mpeg3_read_frame(mpeg,rows,0,0,sx,sy,sx,sy,MPEG3_RGB888,track);
+
+ GridOutlet out(this,0,new Dim(sy, sx, channels),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int bs = out.dim->prod(1);
+ STACK_ARRAY(int32,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> row = buf+channels*sx*y;
+ /* bit_packing->unpack(sx,row,b2); out.send(bs,b2); */
+ out.send(bs,row);
+ }
+ delete[] (uint8 *)buf;
+ return INT2NUM(nframe);
+}
+
+\def void close () {
+// fprintf(stderr, "begin mpeg3_close...\n");
+ if (mpeg) { mpeg3_close(mpeg); mpeg=0; }
+ rb_call_super(argc,argv);
+// fprintf(stderr, "end mpeg3_close...\n");
+}
+
+// libmpeg3 may be nice, but it won't take a filehandle, only filename
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (mode!=SYM(in)) RAISE("read-only, sorry");
+ if (source!=SYM(file)) RAISE("usage: mpeg file <filename>");
+ if (TYPE(filename)!=T_STRING) RAISE("PATATE POILUE");
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ mpeg = mpeg3_open(rb_str_ptr(filename));
+ if (!mpeg) RAISE("IO Error: can't open file `%s': %s", filename, strerror(errno));
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,"install '#in:mpeg',1,1;@flags=4;"
+ "@comment='Motion Picture Expert Group Format"
+ " (using HeroineWarrior\\'s)';suffixes_are'mpg,mpeg'");
+}
+\end class FormatMPEG3
+void startup_mpeg3 () {
+ \startall
+}
diff --git a/externals/gridflow/format/png.c b/externals/gridflow/format/png.c
new file mode 100644
index 00000000..69a0254e
--- /dev/null
+++ b/externals/gridflow/format/png.c
@@ -0,0 +1,138 @@
+/*
+ $Id: png.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+/* !@#$ not handling abort on compress */
+/* !@#$ not handling abort on decompress */
+
+#include "../base/grid.h.fcs"
+extern "C" {
+#include <libpng12/png.h>
+};
+
+\class FormatPNG < Format
+struct FormatPNG : Format {
+ P<BitPacking> bit_packing;
+ png_structp png;
+ png_infop info;
+ int fd;
+ FILE *f;
+ FormatPNG () : bit_packing(0), png(0), f(0) {}
+ \decl Ruby frame ();
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \grin 0 int
+};
+
+GRID_INLET(FormatPNG,0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+ RAISE("bother, said pooh, as the PNG encoding was found unimplemented");
+} GRID_FLOW {
+ int rowsize = in->dim->get(1)*in->dim->get(2);
+ int rowsize2 = in->dim->get(1)*3;
+ uint8 row[rowsize2];
+ while (n) {
+ bit_packing->pack(in->dim->get(1),data,Pt<uint8>(row,rowsize));
+ n-=rowsize; data+=rowsize;
+ }
+} GRID_FINISH {
+} GRID_END
+
+\def Ruby frame () {
+ uint8 sig[8];
+ if (!fread(sig, 1, 8, f)) return Qfalse;
+ if (!png_check_sig(sig, 8)) RAISE("bad signature");
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png) RAISE("!png");
+ info = png_create_info_struct(png);
+ if (!info) {
+ png_destroy_read_struct(&png, NULL, NULL); RAISE("!info");
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ png_destroy_read_struct(&png, &info, NULL); RAISE("png read error");
+ }
+ png_init_io(png, f);
+ png_set_sig_bytes(png, 8); // we already read the 8 signature bytes
+ png_read_info(png, info); // read all PNG info up to image data
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, 0,0,0);
+
+ png_bytepp row_pointers = 0;
+ if (color_type == PNG_COLOR_TYPE_PALETTE
+ || (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ || png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_expand(png);
+ // 16bpp y, 32bpp ya, 48bpp rgb, 64bpp rgba...
+ if (bit_depth == 16) png_set_strip_16(png);
+
+ double display_gamma = 2.2;
+ double gamma;
+ if (png_get_gAMA(png, info, &gamma))
+ png_set_gamma(png, display_gamma, gamma);
+ png_read_update_info(png, info);
+
+ int rowbytes = png_get_rowbytes(png, info);
+ int channels = (int)png_get_channels(png, info);
+ Pt<uint8> image_data = ARRAY_NEW(uint8,rowbytes*height);
+ row_pointers = new png_bytep[height];
+ //gfpost("png: color_type=%d channels=%d, width=%d, rowbytes=%ld, height=%ld, gamma=%f",
+ // color_type, channels, width, rowbytes, height, gamma);
+ for (int i=0; i<(int)height; i++) row_pointers[i] = image_data + i*rowbytes;
+ if ((uint32)rowbytes != width*channels)
+ RAISE("rowbytes mismatch: %d is not %d*%d=%d",
+ rowbytes, width, channels, width*channels);
+ png_read_image(png, row_pointers);
+ delete row_pointers;
+ row_pointers = 0;
+ png_read_end(png, 0);
+
+ GridOutlet out(this,0,new Dim(height, width, channels),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ out.send(rowbytes*height,image_data);
+ free(image_data);
+ png_destroy_read_struct(&png, &info, NULL);
+ return Qnil;
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: png file <filename>");
+ rb_funcall(rself,SI(raw_open),3,mode,source,filename);
+ Ruby stream = rb_ivar_get(rself,SI(@stream));
+ fd = NUM2INT(rb_funcall(stream,SI(fileno),0));
+ f = fdopen(fd,mode==SYM(in)?"r":"w");
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+ "install '#io:png',1,1;@mode=4;include GridFlow::EventIO; suffixes_are'png'");
+}
+\end class FormatPNG
+void startup_png () {
+ \startall
+}
diff --git a/externals/gridflow/format/quicktimeapple.c b/externals/gridflow/format/quicktimeapple.c
new file mode 100644
index 00000000..0664322e
--- /dev/null
+++ b/externals/gridflow/format/quicktimeapple.c
@@ -0,0 +1,500 @@
+/*
+ $Id: quicktimeapple.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#define T_DATA T_COCOA_DATA
+#include <Quicktime/Quicktime.h>
+#include <Quicktime/Movies.h>
+#include <Quicktime/QuickTimeComponents.h>
+#undef T_DATA
+#include "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <CoreServices/CoreServices.h>
+
+typedef ComponentInstance VideoDigitizerComponent, VDC;
+typedef ComponentResult VideoDigitizerError, VDE;
+
+//enum {VDCType='vdig', vdigInterfaceRev=2 };
+//enum {ntscIn=0, currentIn=0, palIn, secamIn, ntscReallyIn };
+//enum {compositeIn, sVideoIn, rgbComponentIn, rgbComponentSyncIn, yuvComponentIn, yuvComponentSyncIn, tvTunerIn, sdiIn};
+//enum {vdPlayThruOff, vdPlayThruOn};
+//enum {vdDigitizerBW, vdDigitizerRGB};
+//enum {vdBroadcastMode, vdVTRMode};
+//enum {vdUseAnyField, vdUseOddField, vdUseEvenField};
+//enum {vdTypeBasic, vdTypeAlpha, vdTypeMask, vdTypeKey};
+/*enum {digiInDoesNTSC, digiInDoesPAL, digiInDoesSECAM, skip 4,
+ digiInDoesGenLock, digiInDoesComposite, digiInDoesSVideo, digiInDoesComponent,
+ digiInVTR_Broadcast, digiInDoesColor, digiInDoesBW, skip 17,
+ digiInSignalLock};*/
+/*bitset {digiOutDoes1, digiOutDoes2, digiOutDoes4,
+ digiOutDoes8, digiOutDoes16, digiOutDoes32,
+ digiOutDoesDither, digiOutDoesStretch, digiOutDoesShrink,
+ digiOutDoesMask, skip 1,
+ digiOutDoesDouble, digiOutDoesQuad, digiOutDoesQuarter, digiOutDoesSixteenth,
+ digiOutDoesRotate, digiOutDoesHorizFlip, digiOutDoesVertFlip, digiOutDoesSkew,
+ digiOutDoesBlend, digiOutDoesWarp, digiOutDoesHW_DMA,
+ digiOutDoesHWPlayThru, digiOutDoesILUT, digiOutDoesKeyColor,
+ digiOutDoesAsyncGrabs, digiOutDoesUnreadableScreenBits,
+ digiOutDoesCompress, digiOutDoesCompressOnly,
+ digiOutDoesPlayThruDuringCompress, digiOutDoesCompressPartiallyVisible,
+ digiOutDoesNotNeedCopyOfCompressData};*/
+/*struct DigitizerInfo {
+ short vdigType;
+ long inputCapabilityFlags, outputCapabilityFlags;
+ long inputCurrentFlags, outputCurrentFlags;
+ short slot;
+ GDHandle gdh, maskgdh;
+ short minDestHeight, minDestWidth;
+ short maxDestHeight, maxDestWidth;
+ short blendLevels;
+ long reserved;};*/
+/*struct VdigType { long digType, reserved;};*/
+/*struct VdigTypeList { short count; VdigType list[1];};*/
+/*struct VdigBufferRec { PixMapHandle dest; Point location; long reserved;};*/
+/*struct VdigBufferRecList {
+ short count; MatrixRecordPtr matrix; RgnHandle mask; VdigBufferRec list[1];};*/
+//typedef VdigBufferRecList *VdigBufferRecListPtr;
+//typedef VdigBufferRecListPtr *VdigBufferRecListHandle;
+//typedef CALLBACK_API(void,VdigIntProcPtr)(long flags, long refcon);
+//typedef STACK_UPP_TYPE(VdigIntProcPtr);
+/*struct VDCompressionList {
+ CodecComponent codec; CodecType cType; Str63 typeName, name;
+ long formatFlags, compressFlags, reserved;};*/
+//typedef VDCompressionList * VDCompressionListPtr;
+//typedef VDCompressionListPtr *VDCompressionListHandle;
+/*bitset {
+ dmaDepth1, dmaDepth2, dmaDepth4, dmaDepth8, dmaDepth16, dmaDepth32,
+ dmaDepth2Gray, dmaDepth4Gray, dmaDepth8Gray};*/
+//enum {kVDIGControlledFrameRate=-1};
+//bitset {vdDeviceFlagShowInputsAsDevices, vdDeviceFlagHideDevice};
+/*bitset {
+ vdFlagCaptureStarting, vdFlagCaptureStopping,
+ vdFlagCaptureIsForPreview, vdFlagCaptureIsForRecord,
+ vdFlagCaptureLowLatency, vdFlagCaptureAlwaysUseTimeBase,
+ vdFlagCaptureSetSettingsBegin, vdFlagCaptureSetSettingsEnd};*/
+/*\class VDC
+VDE VDGetMaxSrcRect (short inputStd, Rect *maxSrcRect)
+VDE VDGetActiveSrcRect(short inputStd, Rect *activeSrcRect)
+VDE VD[GS]etDigitizerRect(Rect *digitizerRect)
+VDE VDGetVBlankRect(short inputStd, Rect *vBlankRect)
+VDE VDGetMaskPixMap(PixMapHandlemaskPixMap)
+VDE VDGetPlayThruDestination(PixMapHandle * dest, Rect *destRect, MatrixRecord * m, RgnHandle *mask)
+VDE VDUseThisCLUT(CTabHandle colorTableHandle)
+VDE VD[SG*]etInputGammaValue(Fixed channel1, Fixed channel2, Fixed channel3)
+VDE VD[GS]etBrightness(uint16 *)
+VDE VD[GS]etContrast(uint16 *)
+VDE VD[GS]etHue(uint16 *)
+VDE VD[GS]etSharpness(uint16 *)
+VDE VD[GS]etSaturation(uint16 *)
+VDE VDGrabOneFrame(VDC ci)
+VDE VDGetMaxAuxBuffer(PixMapHandle *pm, Rect *r)
+VDE VDGetDigitizerInfo(DigitizerInfo *info)
+VDE VDGetCurrentFlags(long *inputCurrentFlag, long *outputCurrentFlag)
+VDE VD[SG*]etKeyColor(long index)
+VDE VDAddKeyColor(long *index)
+VDE VDGetNextKeyColor(long index)
+VDE VD[GS]etKeyColorRange(RGBColor minRGB, RGBColor maxRGB)
+VDE VDSetDigitizerUserInterrupt(long flags, VdigIntUPP userInterruptProc, long refcon)
+VDE VD[SG*]etInputColorSpaceMode(short colorSpaceMode)
+VDE VD[SG*]etClipState(short clipEnable)
+VDE VDSetClipRgn(RgnHandle clipRegion)
+VDE VDClearClipRgn(RgnHandle clipRegion)
+VDE VDGetCLUTInUse(CTabHandle *colorTableHandle)
+VDE VD[SG*]etPLLFilterType(short pllType)
+VDE VDGetMaskandValue(uint16 blendLevel, long *mask, long *value)
+VDE VDSetMasterBlendLevel(uint16 *blendLevel)
+VDE VDSetPlayThruDestination(PixMapHandledest, RectPtr destRect, MatrixRecordPtr m, RgnHandle mask)
+VDE VDSetPlayThruOnOff(short state)
+VDE VD[SG*]etFieldPreference(short fieldFlag)
+VDE VDPreflightDestination(Rect *digitizerRect, PixMap **dest, RectPtr destRect, MatrixRecordPtr m)
+VDE VDPreflightGlobalRect(GrafPtr theWindow, Rect *globalRect)
+VDE VDSetPlayThruGlobalRect(GrafPtr theWindow, Rect *globalRect)
+VDE VDSetInputGammaRecord(VDGamRecPtrinputGammaPtr)
+VDE VDGetInputGammaRecord(VDGamRecPtr *inputGammaPtr)
+VDE VD[SG]etBlackLevelValue(uint16 *)
+VDE VD[SG]etWhiteLevelValue(uint16 *)
+VDE VDGetVideoDefaults(uint16 *blackLevel, uint16 *whiteLevel, uint16 *brightness, uint16 *hue, uint16 *saturation, uint16 *contrast, uint16 *sharpness)
+VDE VDGetNumberOfInputs(short *inputs)
+VDE VDGetInputFormat(short input, short *format)
+VDE VD[SG*]etInput(short input)
+VDE VDSetInputStandard(short inputStandard)
+VDE VDSetupBuffers(VdigBufferRecListHandle bufferList)
+VDE VDGrabOneFrameAsync(short buffer)
+VDE VDDone(short buffer)
+VDE VDSetCompression(OSTypecompressType, short depth, Rect *bounds, CodecQspatialQuality, CodecQtemporalQuality, long keyFrameRate)
+VDE VDCompressOneFrameAsync(VDC ci)
+VDE VDCompressDone(UInt8 *queuedFrameCount, Ptr *theData, long *dataSize, UInt8 *similarity, TimeRecord *t)
+VDE VDReleaseCompressBuffer(Ptr bufferAddr)
+VDE VDGetImageDescription(ImageDescriptionHandle desc)
+VDE VDResetCompressSequence(VDC ci)
+VDE VDSetCompressionOnOff(Boolean)
+VDE VDGetCompressionTypes(VDCompressionListHandle h)
+VDE VDSetTimeBase(TimeBase t)
+VDE VDSetFrameRate(Fixed framesPerSecond)
+VDE VDGetDataRate(long *milliSecPerFrame, Fixed *framesPerSecond, long *bytesPerSecond)
+VDE VDGetSoundInputDriver(Str255 soundDriverName)
+VDE VDGetDMADepths(long *depthArray, long *preferredDepth)
+VDE VDGetPreferredTimeScale(TimeScale *preferred)
+VDE VDReleaseAsyncBuffers(VDC ci)
+VDE VDSetDataRate(long bytesPerSecond)
+VDE VDGetTimeCode(TimeRecord *atTime, void *timeCodeFormat, void *timeCodeTime)
+VDE VDUseSafeBuffers(Boolean useSafeBuffers)
+VDE VDGetSoundInputSource(long videoInput, long *soundInput)
+VDE VDGetCompressionTime(OSTypecompressionType, short depth, Rect *srcRect, CodecQ *spatialQuality, CodecQ *temporalQuality, ulong *compressTime)
+VDE VDSetPreferredPacketSize(long preferredPacketSizeInBytes)
+VDE VD[SG*]etPreferredImageDimensions(long width, long height)
+VDE VDGetInputName(long videoInput, Str255 name)
+VDE VDSetDestinationPort(CGrafPtr destPort)
+VDE VDGetDeviceNameAndFlags(Str255 outName, UInt32 *outNameFlags)
+VDE VDCaptureStateChanging(UInt32inStateFlags)
+VDE VDGetUniqueIDs(UInt64 *outDeviceID, UInt64 *outInputID)
+VDE VDSelectUniqueIDs(const UInt64 *inDeviceID, const UInt64 *inInputID)
+\end class VDC
+*/
+
+\class FormatQuickTimeCamera < Format
+struct FormatQuickTimeCamera : Format {
+ P<Dim> dim;
+ Pt<uint8> buf;
+ VDC vdc;
+ int m_newFrame;
+ SeqGrabComponent m_sg;
+ SGChannel m_vc;
+ short m_pixelDepth;
+ Rect rect;
+ GWorldPtr m_srcGWorld;
+ PixMapHandle m_pixMap;
+ Ptr m_baseAddr;
+ long m_rowBytes;
+ int m_quality;
+//int m_colorspace;
+ FormatQuickTimeCamera() : vdc(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void frame ();
+ \decl void close ();
+ \grin 0 int
+};
+
+// /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Components.h
+
+static int nn(int c) {return c?c:' ';}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ L
+//vdc = SGGetVideoDigitizerComponent(c);
+ rb_call_super(argc,argv);
+ dim = new Dim(240,320,4);
+ OSErr e;
+ rect.top=rect.left=0;
+ rect.bottom=dim->v[0]; rect.right=dim->v[1];
+ int n=0;
+ Component c = 0;
+ ComponentDescription cd;
+ cd.componentType = SeqGrabComponentType;
+ cd.componentSubType = 0;
+ cd.componentManufacturer = 0;
+ cd.componentFlags = 0;
+ cd.componentFlagsMask = 0;
+ for(;;) {
+ c = FindNextComponent(c, &cd);
+ if (!c) break;
+ ComponentDescription cd2;
+ Ptr name=0,info=0,icon=0;
+ GetComponentInfo(c,&cd2,&name,&info,&icon);
+ gfpost("Component #%d",n);
+ char *t = (char *)&cd.componentType;
+ gfpost(" type='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0]));
+ t = (char *)&cd.componentSubType;
+ gfpost(" subtype='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0]));
+ gfpost(" name=%08x, *name='%*s'",name, *name, name+1);
+ gfpost(" info=%08x, *info='%*s'",info, *name, info+1);
+ n++;
+ }
+ gfpost("number of components: %d",n);
+ m_sg = OpenDefaultComponent(SeqGrabComponentType, 0);
+ if(!m_sg) RAISE("could not open default component");
+ e=SGInitialize(m_sg);
+ if(e!=noErr) RAISE("could not initialize SG");
+ e=SGSetDataRef(m_sg, 0, 0, seqGrabDontMakeMovie);
+ if (e!=noErr) RAISE("dataref failed");
+ e=SGNewChannel(m_sg, VideoMediaType, &m_vc);
+ if(e!=noErr) gfpost("could not make new SG channel");
+ e=SGSetChannelBounds(m_vc, &rect);
+ if(e!=noErr) gfpost("could not set SG ChannelBounds");
+ e=SGSetChannelUsage(m_vc, seqGrabPreview);
+ if(e!=noErr) gfpost("could not set SG ChannelUsage");
+// m_rowBytes = m_vidXSize*4;
+ switch (3) {
+ case 0: e=SGSetChannelPlayFlags(m_vc, channelPlayNormal); break;
+ case 1: e=SGSetChannelPlayFlags(m_vc, channelPlayHighQuality); break;
+ case 2: e=SGSetChannelPlayFlags(m_vc, channelPlayFast); break;
+ case 3: e=SGSetChannelPlayFlags(m_vc, channelPlayAllData); break;
+ }
+ int dataSize = dim->prod();
+ buf = ARRAY_NEW(uint8,dataSize);
+ m_rowBytes = dim->prod(1);
+ e=QTNewGWorldFromPtr (&m_srcGWorld,k32ARGBPixelFormat,
+ &rect,NULL,NULL,0,buf,m_rowBytes);
+ if (0/*yuv*/) {
+ int dataSize = dim->prod()*2/4;
+ buf = ARRAY_NEW(uint8,dataSize);
+ m_rowBytes = dim->prod(1)*2/4;
+ e=QTNewGWorldFromPtr (&m_srcGWorld,k422YpCbCr8CodecType,
+ &rect,NULL,NULL,0,buf,m_rowBytes);
+ }
+ if (e!=noErr) RAISE("error #%d at QTNewGWorldFromPtr",e);
+ if (!m_srcGWorld) RAISE("Could not allocate off screen");
+ SGSetGWorld(m_sg,(CGrafPtr)m_srcGWorld, NULL);
+ SGStartPreview(m_sg);
+}
+
+/*pascal Boolean pix_videoDarwin :: SeqGrabberModalFilterProc (DialogPtr theDialog, const EventRecord *theEvent, short *itemHit, long refCon){
+ Boolean handled = false;
+ if ((theEvent->what == updateEvt) &&
+ ((WindowPtr) theEvent->message == (WindowPtr) refCon)) {
+ BeginUpdate ((WindowPtr) refCon);
+ EndUpdate ((WindowPtr) refCon);
+ handled = true;
+ }
+ WindowRef awin = GetDialogWindow(theDialog);
+ ShowWindow (awin);
+ SetWindowClass(awin,kUtilityWindowClass);
+ //ChangeWindowAttributes(awin,kWindowStandardHandlerAttribute,0); SGPanelEvent(m_sg,m_vc,theDialog,0,theEvent,itemHit,&handled);
+ // AEProcessAppleEvent (theEvent);
+ return handled;
+}
+void pix_videoDarwin :: DoVideoSettings(){
+ Rect newActiveVideoRect;
+ Rect curBounds, curVideoRect, newVideoRect;
+ ComponentResult err;
+ SGModalFilterUPP seqGragModalFilterUPP;
+ err = SGGetChannelBounds (m_vc, &curBounds);
+ err = SGGetVideoRect (m_vc, &curVideoRect);
+ err = SGPause (m_sg, true);
+ seqGragModalFilterUPP = (SGModalFilterUPP)NewSGModalFilterUPP(SeqGrabberModalFilterProc);
+ err = SGSettingsDialog(m_sg, m_vc, 0,
+ NULL, seqGrabSettingsPreviewOnly, seqGragModalFilterUPP, (long)m_srcGWorld);
+ DisposeSGModalFilterUPP(seqGragModalFilterUPP);
+ err = SGGetVideoRect (m_vc, &newVideoRect);
+ err = SGGetSrcVideoBounds (m_vc, &newActiveVideoRect);
+ err = SGPause (m_sg, false);
+}
+*/
+
+\def void frame () {
+ GridOutlet out(this,0,dim);
+ out.send(dim->prod(),buf);
+L}
+
+\def void close () {
+ L
+ if (m_vc) {
+ if (::SGDisposeChannel(m_sg, m_vc)) RAISE("SGDisposeChannel");
+ m_vc=0;
+ }
+ if (m_sg) {
+ if (::CloseComponent(m_sg)) RAISE("CloseComponent");
+ m_sg = NULL;
+ if (m_srcGWorld) {
+ ::DisposeGWorld(m_srcGWorld);
+ m_pixMap = NULL;
+ m_srcGWorld = NULL;
+ m_baseAddr = NULL;
+ }
+ }
+}
+
+GRID_INLET(FormatQuickTimeCamera,0) {
+ RAISE("Unimplemented. Sorry.");
+//!@#$
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_factor(in->dim->prod());
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\classinfo {
+ IEVAL(rself,
+\ruby
+ install '#io:quicktimecamera',1,1
+ @comment="Apple Quicktime (CAMERA MODULE)"
+ @flags=4
+\end ruby
+);}
+\end class FormatQuickTimeCamera
+
+\class FormatQuickTimeApple < Format
+struct FormatQuickTimeApple : Format {
+ Movie movie;
+ TimeValue time;
+ short movie_file;
+ GWorldPtr gw; /* just like an X11 Image or Pixmap, maybe. */
+ Pt<uint8> buffer;
+ P<Dim> dim;
+ int nframe, nframes;
+
+ FormatQuickTimeApple() : movie(0), time(0), movie_file(0), gw(0),
+ buffer(), dim(0), nframe(0), nframes(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void close ();
+ \decl void codec_m (String c);
+ \decl void colorspace_m (Symbol c);
+ \decl Ruby frame ();
+ \decl void seek (int frame);
+ \grin 0
+};
+
+\def void seek (int frame) {
+ nframe=frame;
+}
+
+\def Ruby frame () {
+ CGrafPtr savedPort;
+ GDHandle savedDevice;
+ SetMovieGWorld(movie,gw,GetGWorldDevice(gw));
+ Rect r;
+ GetMovieBox(movie,&r);
+ PixMapHandle pixmap = GetGWorldPixMap(gw);
+ short flags = nextTimeStep;
+ if (nframe>=nframes) return Qfalse;
+ if (nframe==0) flags |= nextTimeEdgeOK;
+ TimeValue duration;
+ OSType mediaType = VisualMediaCharacteristic;
+ GetMovieNextInterestingTime(movie,
+ flags,1,&mediaType,time,0,&time,&duration);
+ if (time<0) {
+ time=0;
+ return Qfalse;
+ }
+// gfpost("quicktime frame #%d; time=%d duration=%d", nframe, (long)time, (long)duration);
+ SetMovieTimeValue(movie,nframe*duration);
+ MoviesTask(movie,0);
+ GridOutlet out(this,0,dim);
+ Pt<uint32> bufu32 = Pt<uint32>((uint32 *)buffer.p,dim->prod()/4);
+ int n = dim->prod()/4;
+ int i;
+ for (i=0; i<n&-4; i+=4) {
+ bufu32[i+0]=(bufu32[i+0]<<8)+(bufu32[i+0]>>24);
+ bufu32[i+1]=(bufu32[i+1]<<8)+(bufu32[i+1]>>24);
+ bufu32[i+2]=(bufu32[i+2]<<8)+(bufu32[i+2]>>24);
+ bufu32[i+3]=(bufu32[i+3]<<8)+(bufu32[i+3]>>24);
+ }
+ for (; i<n; i++) {
+ bufu32[i+0]=(bufu32[i+0]<<8)+(bufu32[i+0]>>24);
+ }
+
+ out.send(dim->prod(),buffer);
+ int nf=nframe;
+ nframe++;
+ return INT2NUM(nf);
+}
+
+GRID_INLET(FormatQuickTimeApple,0) {
+ RAISE("Unimplemented. Sorry.");
+//!@#$
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_factor(in->dim->prod());
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def void codec_m (String c) { RAISE("Unimplemented. Sorry."); }
+\def void colorspace_m (Symbol c) { RAISE("Unimplemented. Sorry."); }
+
+\def void close () {
+//!@#$
+ if (movie) {
+ DisposeMovie(movie);
+ DisposeGWorld(gw);
+ CloseMovieFile(movie_file);
+ movie_file=0;
+ }
+ rb_call_super(argc,argv);
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ int err;
+ rb_call_super(argc,argv);
+ if (source==SYM(file)) {
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ FSSpec fss;
+ FSRef fsr;
+ err = FSPathMakeRef((const UInt8 *)rb_str_ptr(filename), &fsr, NULL);
+ if (err) goto err;
+ err = FSGetCatalogInfo(&fsr, kFSCatInfoNone, NULL, NULL, &fss, NULL);
+ if (err) goto err;
+ err = OpenMovieFile(&fss,&movie_file,fsRdPerm);
+ if (err) goto err;
+ } else {
+ RAISE("usage: quicktime [file <filename> | camera bleh]");
+ }
+ NewMovieFromFile(&movie, movie_file, NULL, NULL, newMovieActive, NULL);
+ Rect r;
+ GetMovieBox(movie, &r);
+ gfpost("handle=%d movie=%d tracks=%d",
+ movie_file, movie, GetMovieTrackCount(movie));
+ gfpost("duration=%d; timescale=%d cHz",
+ (long)GetMovieDuration(movie),
+ (long)GetMovieTimeScale(movie));
+ nframes = GetMovieDuration(movie); /* i don't think so */
+ gfpost("rect=((%d..%d),(%d..%d))",
+ r.top, r.bottom, r.left, r.right);
+ OffsetRect(&r, -r.left, -r.top);
+ SetMovieBox(movie, &r);
+ dim = new Dim(r.bottom-r.top, r.right-r.left, 4);
+ SetMoviePlayHints(movie, hintsHighQuality, hintsHighQuality);
+ buffer = ARRAY_NEW(uint8,dim->prod());
+ err = QTNewGWorldFromPtr(&gw, k32ARGBPixelFormat, &r,
+ NULL, NULL, 0, buffer, dim->prod(1));
+ if (err) goto err;
+ return;
+err:
+ RAISE("can't open file `%s': error #%d (%s)", rb_str_ptr(filename),
+ err,
+ rb_str_ptr(rb_funcall(mGridFlow,SI(macerr),1,INT2NUM(err))));
+}
+
+\classinfo {
+ EnterMovies();
+IEVAL(rself,
+\ruby
+ install '#io:quicktime',1,1
+ @comment="Apple Quicktime (using Apple's)"
+ @flags=4
+ suffixes_are'mov'
+ def self.new(mode,source,filename)
+ if source==:camera then FormatQuickTimeCamera.new(mode,source,filename) else super end
+ end
+\end ruby
+);}
+\end class FormatQuickTimeApple
+void startup_quicktimeapple () {
+ \startall
+}
diff --git a/externals/gridflow/format/quicktimehw.c b/externals/gridflow/format/quicktimehw.c
new file mode 100644
index 00000000..4ae77209
--- /dev/null
+++ b/externals/gridflow/format/quicktimehw.c
@@ -0,0 +1,244 @@
+/*
+ $Id: quicktimehw.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <quicktime/quicktime.h>
+#include <quicktime/colormodels.h>
+
+#include <quicktime/lqt_version.h>
+#ifdef LQT_VERSION
+#include <quicktime/lqt.h>
+#include <quicktime/lqt_codecinfo.h>
+#endif
+
+\class FormatQuickTimeHW < Format
+struct FormatQuickTimeHW : Format {
+ quicktime_t *anim;
+ int track;
+ P<Dim> dim;
+ char *codec;
+ int colorspace;
+ int channels;
+ bool started;
+ P<Dim> force;
+ int length; // in frames
+ float64 framerate;
+ P<BitPacking> bit_packing;
+ FormatQuickTimeHW() : track(0), dim(0), codec(QUICKTIME_RAW),
+ started(false), force(0), framerate(29.97), bit_packing(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void close ();
+ \decl Ruby frame ();
+ \decl void seek (int frame);
+
+ \decl void _0_force_size (int32 height, int32 width);
+ \decl void _0_codec (String c);
+ \decl void _0_colorspace (Symbol c);
+ \decl void _0_parameter (Symbol name, int32 value);
+ \decl void _0_framerate (float64 f);
+ \decl void _0_size (int32 height, int32 width);
+ \grin 0 int
+};
+
+\def void _0_force_size (int32 height, int32 width) { force = new Dim(height, width); }
+\def void seek (int frame) {quicktime_set_video_position(anim,frame,track);}
+
+\def Ruby frame () {
+ int nframe = quicktime_video_position(anim,track);
+ int length2 = quicktime_video_length(anim,track);
+ if (nframe >= length) {
+// gfpost("nframe=%d length=%d length2=%d",nframe,length,length2);
+ return Qfalse;
+ }
+ /* if it works, only do it once, to avoid silly stderr messages forgotten in LQT */
+ if (!quicktime_reads_cmodel(anim,colorspace,0) && !started) {
+ RAISE("LQT says this video cannot be decoded into the chosen colorspace");
+ }
+ int sx = quicktime_video_width(anim,track);
+ int sy = quicktime_video_height(anim,track);
+ int sz = quicktime_video_depth(anim,track);
+ channels = sz/8; // hack. how do i get the video's native colormodel ?
+ switch (sz) {
+ case 24: colorspace=BC_RGB888; break;
+ case 32: colorspace=BC_RGBA8888; break;
+ default: gfpost("strange quicktime. ask matju."); break;
+ }
+ if (force) {
+ sy = force->get(0);
+ sx = force->get(1);
+ }
+ Pt<uint8> buf = ARRAY_NEW(uint8,sy*sx*channels);
+ uint8 *rows[sy]; for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ int result = quicktime_decode_scaled(anim,0,0,sx,sy,sx,sy,colorspace,rows,track);
+ GridOutlet out(this,0,new Dim(sy, sx, channels),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int bs = out.dim->prod(1);
+ out.give(sy*sx*channels,buf);
+ started=true;
+ return INT2NUM(nframe);
+}
+
+//!@#$ should also support symbol values (how?)
+\def void _0_parameter (Symbol name, int32 value) {
+ quicktime_set_parameter(anim, (char*)rb_sym_name(name), &value);
+}
+
+\def void _0_framerate (float64 f) {
+ framerate=f;
+ quicktime_set_framerate(anim, f);
+}
+
+\def void _0_size (int32 height, int32 width) {
+ if (dim) RAISE("video size already set!");
+ // first frame: have to do setup
+ dim = new Dim(height, width, 3);
+ quicktime_set_video(anim,1,dim->get(1),dim->get(0),framerate,codec);
+ quicktime_set_cmodel(anim,colorspace);
+}
+
+GRID_INLET(FormatQuickTimeHW,0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2)!=channels) RAISE("expecting %d channels (got %d)",channels,in->dim->get(2));
+ in->set_factor(in->dim->prod());
+ if (dim) {
+ if (!dim->equal(in->dim)) RAISE("all frames should be same size");
+ } else {
+ // first frame: have to do setup
+ dim = in->dim;
+ quicktime_set_video(anim,1,dim->get(1),dim->get(0),framerate,codec);
+ quicktime_set_cmodel(anim,colorspace);
+ quicktime_set_depth(anim,8*channels,track);
+ }
+} GRID_FLOW {
+ int sx = quicktime_video_width(anim,track);
+ int sy = quicktime_video_height(anim,track);
+ uint8 *rows[sy];
+ if (sizeof(T)>1) {
+ uint8 data2[n];
+ bit_packing->pack(sx*sy,data,Pt<uint8>(data2,n));
+ for (int i=0; i<sy; i++) rows[i]=data2+i*sx*channels;
+ quicktime_encode_video(anim,rows,track);
+ } else {
+ for (int i=0; i<sy; i++) rows[i]=data+i*sx*channels;
+ quicktime_encode_video(anim,rows,track);
+ }
+} GRID_FINISH {
+} GRID_END
+
+\def void _0_codec (String c) {
+ //fprintf(stderr,"codec = %s\n",rb_str_ptr(rb_inspect(c)));
+#ifdef LQT_VERSION
+ char buf[5];
+ strncpy(buf,rb_str_ptr(c),4);
+ for (int i=rb_str_len(c); i<4; i++) buf[i]=' ';
+ buf[4]=0;
+ Ruby fourccs = rb_ivar_get(rb_obj_class(rself),SI(@fourccs));
+ if (Qnil==rb_hash_aref(fourccs,rb_str_new2(buf)))
+ RAISE("warning: unknown fourcc '%s' (%s)",
+ buf, rb_str_ptr(rb_inspect(rb_funcall(fourccs,SI(keys),0))));
+#endif
+ codec = strdup(buf);
+}
+
+\def void _0_colorspace (Symbol c) {
+ if (0) {
+ } else if (c==SYM(rgb)) { channels=3; colorspace=BC_RGB888;
+ } else if (c==SYM(rgba)) { channels=4; colorspace=BC_RGBA8888;
+ } else if (c==SYM(bgr)) { channels=3; colorspace=BC_BGR888;
+ } else if (c==SYM(bgrn)) { channels=4; colorspace=BC_BGR8888;
+ } else if (c==SYM(yuv)) { channels=3; colorspace=BC_YUV888;
+ } else if (c==SYM(yuva)) { channels=4; colorspace=BC_YUVA8888;
+ } else if (c==SYM(YUV420P)) { channels=3; colorspace=BC_YUV420P;
+ } else RAISE("unknown colorspace '%s' (supported: rgb, rgba, bgr, bgrn, yuv, yuva)",rb_sym_name(c));
+}
+
+\def void close () {
+ if (anim) { quicktime_close(anim); anim=0; }
+ rb_call_super(argc,argv);
+}
+
+// libquicktime may be nice, but it won't take a filehandle, only filename
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: quicktime file <filename>");
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ anim = quicktime_open(rb_str_ptr(filename),mode==SYM(in),mode==SYM(out));
+ if (!anim) RAISE("can't open file `%s': %s", rb_str_ptr(filename), strerror(errno));
+ if (mode==SYM(in)) {
+ length = quicktime_video_length(anim,track);
+ gfpost("quicktime: codec=%s height=%d width=%d depth=%d framerate=%f",
+ quicktime_video_compressor(anim,track),
+ quicktime_video_height(anim,track),
+ quicktime_video_width(anim,track),
+ quicktime_video_depth(anim,track),
+ quicktime_frame_rate(anim,track));
+/* This doesn't really work: (is it just for encoding?)
+ if (!quicktime_supported_video(anim,track))
+ RAISE("quicktime: unsupported codec: %s",
+ quicktime_video_compressor(anim,track));
+*/
+ }
+ _0_colorspace(0,0,SYM(rgb));
+ quicktime_set_cpus(anim,1);
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+\ruby
+ install '#io:quicktime',1,1
+ @comment=%[Burkhard Plaum's (or HeroineWarrior's) libquicktime]
+ suffixes_are 'mov'
+ @flags=6
+ def self.info; %[codecs: #{@codecs.keys.join' '}] end
+\end ruby
+);
+
+#ifdef LQT_VERSION
+ lqt_registry_init();
+ int n = lqt_get_num_video_codecs();
+ Ruby codecs = rb_hash_new();
+ Ruby fourccs = rb_hash_new();
+ for (int i=0; i<n; i++) {
+ const lqt_codec_info_t *s = lqt_get_video_codec_info(i);
+ Ruby name = rb_str_new2(s->name);
+ Ruby f = rb_ary_new2(s->num_fourccs);
+ for (int j=0; j<s->num_fourccs; j++) {
+ Ruby fn = rb_str_new2(s->fourccs[j]);
+ rb_ary_push(f,fn);
+ rb_hash_aset(fourccs,fn,name);
+ }
+ rb_hash_aset(codecs,name,f);
+ }
+ rb_ivar_set(rself,SI(@codecs),codecs);
+ rb_ivar_set(rself,SI(@fourccs),fourccs);
+#endif
+}
+\end class FormatQuickTimeHW
+void startup_quicktimehw () {
+ \startall
+}
diff --git a/externals/gridflow/format/sdl.c b/externals/gridflow/format/sdl.c
new file mode 100644
index 00000000..628f71ee
--- /dev/null
+++ b/externals/gridflow/format/sdl.c
@@ -0,0 +1,123 @@
+/*
+ $Id: sdl.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "../base/grid.h.fcs"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <SDL/SDL.h>
+
+static bool in_use = false;
+
+\class FormatSDL < Format
+struct FormatSDL : Format {
+ SDL_Surface *screen;
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ void resize_window (int sx, int sy);
+ void call ();
+ Pt<uint8> pixels () {
+ return Pt<uint8>((uint8 *)screen->pixels,
+ dim->prod(0,1)*bit_packing->bytes);
+ }
+ \decl void initialize (Symbol mode);
+ \decl void close ();
+ \grin 0 int
+};
+
+void FormatSDL::call() {
+ SDL_Event event;
+ while(SDL_PollEvent(&event)) {}
+ IEVAL(rself,"@clock.delay 20");
+}
+
+void FormatSDL::resize_window (int sx, int sy) {
+ dim = new Dim(sy,sx,3);
+ screen = SDL_SetVideoMode(sx,sy,0,SDL_SWSURFACE);
+ if (!screen)
+ RAISE("Can't switch to (%d,%d,%dbpp): %s", sy,sx,24, SDL_GetError());
+}
+
+GRID_INLET(FormatSDL,0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels: red,green,blue (got %d)",in->dim->get(2));
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_factor(sxc);
+ if (sx!=osx || sy!=osy) resize_window(sx,sy);
+} GRID_FLOW {
+ int bypl = screen->pitch;
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1);
+ int y = in->dex / sxc;
+ assert((in->dex % sxc) == 0);
+ assert((n % sxc) == 0);
+ if (SDL_MUSTLOCK(screen)) if (SDL_LockSurface(screen) < 0) return; //???
+ for (; n>0; y++, data+=sxc, n-=sxc) {
+ /* convert line */
+ bit_packing->pack(sx, data, pixels()+y*bypl);
+ }
+ if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
+} GRID_FINISH {
+ SDL_UpdateRect(screen,0,0,in->dim->get(1),in->dim->get(0));
+} GRID_END
+
+\def void close () {
+ IEVAL(rself,"@clock.unset");
+ in_use=false;
+}
+
+\def void initialize (Symbol mode) {
+ dim=0;screen=0;
+ rb_call_super(argc,argv);
+ if (in_use) RAISE("only one FormatSDL object at a time; sorry");
+ in_use=true;
+ if (SDL_Init(SDL_INIT_VIDEO)<0)
+ RAISE("SDL_Init() error: %s",SDL_GetError());
+ atexit(SDL_Quit);
+ resize_window(320,240);
+ SDL_PixelFormat *f = screen->format;
+ uint32 mask[3] = {f->Rmask,f->Gmask,f->Bmask};
+ switch (f->BytesPerPixel) {
+ case 1: RAISE("8 bpp not supported"); break;
+ case 2: case 3: case 4:
+ bit_packing = new BitPacking(is_le(),f->BytesPerPixel,3,mask);
+ break;
+ default: RAISE("%d bytes/pixel: how do I deal with that?",f->BytesPerPixel); break;
+ }
+ IEVAL(rself,"@clock = Clock.new self");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:sdl',1,1;@flags=2;@comment='Simple Directmedia Layer'");
+}
+\end class FormatSDL
+void startup_sdl () {
+ \startall
+}
diff --git a/externals/gridflow/format/videodev.c b/externals/gridflow/format/videodev.c
new file mode 100644
index 00000000..e907eaa9
--- /dev/null
+++ b/externals/gridflow/format/videodev.c
@@ -0,0 +1,544 @@
+/*
+ $Id: videodev.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "../base/grid.h.fcs"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/videodev.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+/* **************************************************************** */
+
+typedef video_capability VideoCapability;
+typedef video_channel VideoChannel ;
+typedef video_tuner VideoTuner ;
+typedef video_window VideoWindow ;
+typedef video_picture VideoPicture ;
+typedef video_mbuf VideoMbuf ;
+typedef video_mmap VideoMmap ;
+
+static const char *ruby_code =
+\ruby
+flags :name,:VideoTypeFlags,:start,0,:values,%w(
+ CAPTURE TUNER TELETEXT OVERLAY CHROMAKEY CLIPPING FRAMERAM
+ SCALES MONOCHROME SUBCAPTURE
+ MPEG_DECODER MPEG_ENCODER MJPEG_DECODER MJPEG_ENCODER
+)
+flags :name,:TunerFlags,:start,0,:values,%w(
+ PAL NTSC SECAM LOW NORM DUMMY5 DUMMY6 STEREO_ON RDS_ON MBS_ON
+)
+flags :name,:ChannelFlags,:start,0,:values,%w(
+ TUNER AUDIO NORM
+)
+choice :name,:VideoPaletteChoice,:start,0,:values,%w(
+ NIL GREY HI240
+ RGB565 RGB24 RGB32 RGB555
+ YUV422 YUYV UYVY YUV420 YUV411 RAW
+ YUV422P YUV411P YUV420P YUV410P
+)
+choice :name,:VideoModeChoice,:start,0,:values,%w(
+ PAL NTSC SECAM AUTO
+)
+\end ruby
+;
+
+/* **************************************************************** */
+
+/*
+#define WH(_field_,_spec_) \
+ sprintf(buf+strlen(buf), "%s: " _spec_ "; ", #_field_, self->_field_);
+#define WHYX(_name_,_fieldy_,_fieldx_) \
+ sprintf(buf+strlen(buf), "%s: y=%d, x=%d; ", #_name_, self->_fieldy_, self->_fieldx_);
+#define WHFLAGS(_field_,_table_) { \
+ char *foo; \
+ sprintf(buf+strlen(buf), "%s: %s; ", #_field_, \
+ foo=flags_to_s(self->_field_,COUNT(_table_),_table_)); \
+ delete[] foo;}
+#define WHCHOICE(_field_,_table_) { \
+ char *foo; \
+ sprintf(buf+strlen(buf), "%s: %s; ", #_field_, \
+ foo=choice_to_s(self->_field_,COUNT(_table_),_table_));\
+ delete[] foo;}
+static char *flags_to_s(int value, int n, named_int *table) {
+ char foo[256];
+ *foo = 0;
+ for(int i=0; i<n; i++) {
+ if ((value & (1<<i)) == 0) continue;
+ if (*foo) strcat(foo," | ");
+ strcat(foo,table[i].name);
+ }
+ if (!*foo) strcat(foo,"0");
+ return strdup(foo);
+}
+static char *choice_to_s(int value, int n, named_int *table) {
+ if (value < 0 || value >= n) {
+ char foo[64];
+ sprintf(foo,"(Unknown #%d)",value);
+ return strdup(foo);
+ } else {
+ return strdup(table[value].name);
+ }
+}
+*/
+
+class RStream {
+public:
+ Ruby a;
+ RStream(Ruby a) : a(a) {}
+ RStream &operator <<(/*Ruby*/ void *v) { rb_ary_push(a,(Ruby)v); return *this; }
+ RStream &operator <<(int v) { return *this<<(void *)INT2NUM(v); }
+};
+
+static void gfpost(VideoChannel *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoChannel) << self->channel
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->tuners << self->flags << self->type << self->norm;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoTuner *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoTuner) << self->tuner
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->rangelow << self->rangehigh
+ << self->flags << self->mode << self->signal;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoCapability *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoCapability)
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->type
+ << self->channels << self->audios
+ << self->maxheight << self->maxwidth
+ << self->minheight << self->minwidth;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoWindow *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoWindow)
+ << self->y << self->x
+ << self->height << self->width
+ << self->chromakey << self->flags << self->clipcount;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoPicture *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoPicture)
+ << self->brightness << self->contrast << self->colour
+ << self->hue << self->whiteness << self->depth << self->palette;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoMbuf *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoMBuf) << self->size << self->frames;
+ for (int i=0; i<4; i++) rs << self->offsets[i];
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoMmap *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoMMap) << self->frame
+ << self->height << self->width << self->format;
+ rb_p(rs.a);
+};
+
+/* **************************************************************** */
+
+\class FormatVideoDev < Format
+struct FormatVideoDev : Format {
+ VideoCapability vcaps;
+ VideoMbuf vmbuf;
+ VideoMmap vmmap;
+ Pt<uint8> image;
+ int palette;
+ int queue[8], queuesize, queuemax, next_frame;
+ int current_channel, current_tuner;
+ bool use_mmap;
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+
+ FormatVideoDev () : queuesize(0), queuemax(2), next_frame(0), use_mmap(true), bit_packing(0), dim(0) {}
+ void frame_finished (Pt<uint8> buf);
+
+ \decl void initialize (Symbol mode, String filename, Symbol option=Qnil);
+ \decl void initialize2 ();
+ \decl void close ();
+ \decl void alloc_image ();
+ \decl void dealloc_image ();
+ \decl void frame ();
+ \decl void frame_ask ();
+ \grin 0 int
+
+ \decl void _0_size (int sy, int sx);
+ \decl void _0_norm (int value);
+ \decl void _0_tuner (int value);
+ \decl void _0_channel (int value);
+ \decl void _0_frequency (int value);
+ \decl void _0_transfer (Symbol sym, int queuemax=2);
+ \decl void _0_colorspace (Symbol c);
+ \decl void _0_get (Symbol attr=0);
+ \decl void _0_brightness (uint16 value);
+ \decl void _0_hue (uint16 value);
+ \decl void _0_colour (uint16 value);
+ \decl void _0_contrast (uint16 value);
+ \decl void _0_whiteness (uint16 value);
+};
+
+#define DEBUG(args...) 42
+//#define DEBUG(args...) gfpost
+
+#define IOCTL(_f_,_name_,_arg_) \
+ (DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_))
+
+#define WIOCTL(_f_,_name_,_arg_) \
+ (DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_) < 0) && \
+ (gfpost("ioctl %s: %s",#_name_,strerror(errno)),1)
+
+#define WIOCTL2(_f_,_name_,_arg_) \
+ ((DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_) < 0) && \
+ (gfpost("ioctl %s: %s",#_name_,strerror(errno)), \
+ RAISE("ioctl error"), 0))
+
+#define GETFD NUM2INT(rb_funcall(rb_ivar_get(rself,SI(@stream)),SI(fileno),0))
+
+\def void _0_size (int sy, int sx) {
+ int fd = GETFD;
+ VideoWindow grab_win;
+ // !@#$ bug here: won't flush the frame queue
+ dim = new Dim(sy,sx,3);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ gfpost(&grab_win);
+ grab_win.clipcount = 0;
+ grab_win.flags = 0;
+ if (sy && sx) {
+ grab_win.height = sy;
+ grab_win.width = sx;
+ }
+ gfpost(&grab_win);
+ WIOCTL(fd, VIDIOCSWIN, &grab_win);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ gfpost(&grab_win);
+}
+
+\def void dealloc_image () {
+ if (!image) return;
+ if (!use_mmap) {
+ delete[] (uint8 *)image;
+ } else {
+ munmap(image, vmbuf.size);
+ image = Pt<uint8>();
+ }
+}
+
+\def void alloc_image () {
+ if (!use_mmap) {
+ image = ARRAY_NEW(uint8,dim->prod(0,1)*bit_packing->bytes);
+ return;
+ }
+ int fd = GETFD;
+ WIOCTL2(fd, VIDIOCGMBUF, &vmbuf);
+ image = Pt<uint8>((uint8 *)
+ mmap(0,vmbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0),
+ vmbuf.size);
+ if (((int)image)<=0) {
+ image=Pt<uint8>();
+ RAISE("mmap: %s", strerror(errno));
+ }
+}
+
+\def void frame_ask () {
+ int fd = GETFD;
+ if (queuesize>=queuemax) RAISE("queue is full (queuemax=%d)",queuemax);
+ if (queuesize>=vmbuf.frames) RAISE("queue is full (vmbuf.frames=%d)",vmbuf.frames);
+ vmmap.frame = queue[queuesize++] = next_frame;
+ vmmap.format = palette;
+ vmmap.width = dim->get(1);
+ vmmap.height = dim->get(0);
+ WIOCTL2(fd, VIDIOCMCAPTURE, &vmmap);
+ next_frame = (next_frame+1) % vmbuf.frames;
+}
+
+void FormatVideoDev::frame_finished (Pt<uint8> buf) {
+ GridOutlet out(this,0,dim,NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ /* picture is converted here. */
+ int sy = dim->get(0);
+ int sx = dim->get(1);
+ int bs = dim->prod(1);
+ if (palette==VIDEO_PALETTE_YUV420P) {
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> bufy = buf+sx*y;
+ Pt<uint8> bufu = buf+sx*sy +(sx/2)*(y/2);
+ Pt<uint8> bufv = buf+sx*sy*5/4+(sx/2)*(y/2);
+ for (int x=0; x<sx; x++) {
+ b2[x*3+0]=bufy[x];
+ b2[x*3+1]=bufu[x/2];
+ b2[x*3+2]=bufv[x/2];
+ }
+ out.send(bs,b2);
+ }
+ } else if (bit_packing) {
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> buf2 = buf+bit_packing->bytes*sx*y;
+ bit_packing->unpack(sx,buf2,b2);
+ out.send(bs,b2);
+ }
+ } else {
+ out.send(sy*bs,buf);
+ }
+}
+
+static int read2(int fd, uint8 *image, int n) {
+ int r=0;
+ for (; n>0; ) {
+ int rr=read(fd,image,n);
+ if (rr<0) return rr;
+ r+=rr, image+=rr, n-=rr;
+ }
+ return r;
+}
+
+static int read3(int fd, uint8 *image, int n) {
+ int r=read(fd,image,n);
+ if (r<0) return r;
+ return n;
+}
+
+\def void frame () {
+ if (!image) rb_funcall(rself,SI(alloc_image),0);
+ int fd = GETFD;
+ if (!use_mmap) {
+ /* picture is read at once by frame() to facilitate debugging. */
+ int tot = dim->prod(0,1) * bit_packing->bytes;
+ int n = (int) read3(fd,image,tot);
+ if (n==tot) frame_finished(image);
+ if (0> n) RAISE("error reading: %s", strerror(errno));
+ if (n < tot) RAISE("unexpectedly short picture: %d of %d",n,tot);
+ return;
+ }
+ while(queuesize<queuemax) rb_funcall(rself,SI(frame_ask),0);
+ vmmap.frame = queue[0];
+ uint64 t0 = gf_timeofday();
+ WIOCTL2(fd, VIDIOCSYNC, &vmmap);
+ uint64 t1 = gf_timeofday();
+ //if (t1-t0 > 100) gfpost("VIDIOCSYNC delay: %d us",t1-t0);
+ frame_finished(image+vmbuf.offsets[queue[0]]);
+ queuesize--;
+ for (int i=0; i<queuesize; i++) queue[i]=queue[i+1];
+ rb_funcall(rself,SI(frame_ask),0);
+}
+
+GRID_INLET(FormatVideoDev,0) {
+ RAISE("can't write.");
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def void _0_norm (int value) {
+ int fd = GETFD;
+ VideoTuner vtuner;
+ vtuner.tuner = current_tuner;
+ if (value<0 || value>3) RAISE("norm must be in range 0..3");
+ if (0> IOCTL(fd, VIDIOCGTUNER, &vtuner)) {
+ gfpost("no tuner #%d", value);
+ } else {
+ vtuner.mode = value;
+ gfpost(&vtuner);
+ WIOCTL(fd, VIDIOCSTUNER, &vtuner);
+ }
+}
+
+\def void _0_tuner (int value) {
+ int fd = GETFD;
+ VideoTuner vtuner;
+ vtuner.tuner = current_tuner = value;
+ if (0> IOCTL(fd, VIDIOCGTUNER, &vtuner)) RAISE("no tuner #%d", value);
+ vtuner.mode = VIDEO_MODE_NTSC; //???
+ gfpost(&vtuner);
+ WIOCTL(fd, VIDIOCSTUNER, &vtuner);
+}
+
+\def void _0_channel (int value) {
+ int fd = GETFD;
+ VideoChannel vchan;
+ vchan.channel = value;
+ current_channel = value;
+ if (0> IOCTL(fd, VIDIOCGCHAN, &vchan)) RAISE("no channel #%d", value);
+ gfpost(&vchan);
+ WIOCTL(fd, VIDIOCSCHAN, &vchan);
+ if (vcaps.type & VID_TYPE_TUNER) rb_funcall(rself,SI(_0_tuner),1,INT2NUM(0));
+}
+
+\def void _0_frequency (int value) {
+ int fd = GETFD;
+ if (0> IOCTL(fd, VIDIOCSFREQ, &value)) RAISE("can't set frequency to %d",value);
+}
+
+\def void _0_transfer (Symbol sym, int queuemax=2) {
+ if (sym == SYM(read)) {
+ rb_funcall(rself,SI(dealloc_image),0);
+ use_mmap = false;
+ gfpost("transfer read");
+ } else if (sym == SYM(mmap)) {
+ rb_funcall(rself,SI(dealloc_image),0);
+ use_mmap = true;
+ rb_funcall(rself,SI(alloc_image),0);
+ queuemax=min(queuemax,vmbuf.frames);
+ gfpost("transfer mmap with queuemax=%d (max max is vmbuf.frames=%d)"
+ ,queuemax,vmbuf.frames);
+ this->queuemax=queuemax;
+ } else RAISE("don't know that transfer mode");
+}
+
+#define PICTURE_ATTR(_name_) {\
+ int fd = GETFD; \
+ VideoPicture vp; \
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ vp._name_ = value; \
+ WIOCTL(fd, VIDIOCSPICT, &vp);}
+
+\def void _0_brightness (uint16 value) {PICTURE_ATTR(brightness)}
+\def void _0_hue (uint16 value) {PICTURE_ATTR(hue)}
+\def void _0_colour (uint16 value) {PICTURE_ATTR(colour)}
+\def void _0_contrast (uint16 value) {PICTURE_ATTR(contrast)}
+\def void _0_whiteness (uint16 value) {PICTURE_ATTR(whiteness)}
+
+#define PICTURE_ATTR_GET(_name_) { \
+ int fd = GETFD; \
+ VideoPicture vp; \
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ Ruby argv[3] = {INT2NUM(1), SYM(_name_), INT2NUM(vp._name_)}; \
+ send_out(COUNT(argv),argv);}
+
+\def void _0_get (Symbol attr) {
+ if (!attr) {
+ _0_get(0,0,SYM(brightness));
+ _0_get(0,0,SYM(hue ));
+ _0_get(0,0,SYM(colour ));
+ _0_get(0,0,SYM(contrast ));
+ _0_get(0,0,SYM(whiteness ));
+ _0_get(0,0,SYM(frequency ));
+ } else if (attr==SYM(brightness)) { PICTURE_ATTR_GET(brightness);
+ } else if (attr==SYM(hue )) { PICTURE_ATTR_GET(hue );
+ } else if (attr==SYM(colour )) { PICTURE_ATTR_GET(colour );
+ } else if (attr==SYM(contrast )) { PICTURE_ATTR_GET(contrast );
+ } else if (attr==SYM(whiteness )) { PICTURE_ATTR_GET(whiteness );
+ } else if (attr==SYM(frequency )) {
+ int fd = GETFD;
+ int value;
+ WIOCTL(fd, VIDIOCGFREQ, &value);
+ {Ruby argv[3] ={INT2NUM(1), SYM(frequency), INT2NUM(value)}; send_out(COUNT(argv),argv);}
+ } else { RAISE("What you say?"); }
+}
+
+\def void close () {
+ if (image) rb_funcall(rself,SI(dealloc_image),0);
+ rb_call_super(argc,argv);
+}
+
+\def void _0_colorspace (Symbol c) {
+ if (c==SYM(RGB24)) palette=VIDEO_PALETTE_RGB24;
+ else if (c==SYM(YUV420P)) palette=VIDEO_PALETTE_YUV420P;
+ else RAISE("supported: RGB24, YUV420P");
+
+ int fd = GETFD;
+ VideoPicture *gp = new VideoPicture;
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ gp->palette = palette;
+ WIOCTL(fd, VIDIOCSPICT, gp);
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ //if (bit_packing) { delete bit_packing; bit_packing=0; }
+ switch(palette) {
+ case VIDEO_PALETTE_RGB24:{
+ uint32 masks[3] = { 0xff0000,0x00ff00,0x0000ff };
+ bit_packing = new BitPacking(is_le(),3,3,masks);
+ }break;
+ case VIDEO_PALETTE_YUV420P:{
+ // woops, special case already, can't do that with bit_packing
+ }
+ default:
+ RAISE("can't handle palette %d", gp->palette);
+ }
+ delete gp;
+}
+
+\def void initialize2 () {
+ int fd = GETFD;
+ VideoPicture *gp = new VideoPicture;
+/* long flags;
+ fcntl(fd,F_GETFL,&flags);
+ flags |= O_NONBLOCK;
+ fcntl(fd,F_SETFL,&flags); */
+
+ WIOCTL(fd, VIDIOCGCAP, &vcaps);
+ gfpost(&vcaps);
+ rb_funcall(rself,SI(_0_size),2,INT2NUM(vcaps.maxheight),INT2NUM(vcaps.maxwidth));
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ gfpost(gp);
+ char buf[1024] = "";
+ int n = 17 /*COUNT(video_palette_choice)*/;
+ for (int i=0; i<n; i++) {
+ gp->palette = i;
+ ioctl(fd, VIDIOCSPICT, gp);
+ ioctl(fd, VIDIOCGPICT, gp);
+ if (gp->palette == i) {
+ if (*buf) strcpy(buf+strlen(buf),", ");
+ //strcpy(buf+strlen(buf),video_palette_choice[i].name);
+ sprintf(buf+strlen(buf),"%d",i);
+ }
+ }
+ gfpost("This card supports palettes: %s", buf);
+ _0_colorspace(0,0,SYM(RGB24));
+ rb_funcall(rself,SI(_0_channel),1,INT2NUM(0));
+ delete gp;
+}
+
+\def void initialize (Symbol mode, String filename, Symbol option=Qnil) {
+ rb_call_super(argc,argv);
+ image = Pt<uint8>();
+ rb_ivar_set(rself,SI(@stream),
+ rb_funcall(rb_cFile,SI(open),2,filename,rb_str_new2("r+")));
+ rb_funcall(rself,SI(initialize2),0);
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:videodev',1,1;@flags=4;@comment='Video4linux 1.x'");
+}
+\end class FormatVideoDev
+void startup_videodev () {
+ \startall
+}
diff --git a/externals/gridflow/format/x11.c b/externals/gridflow/format/x11.c
new file mode 100644
index 00000000..62041f06
--- /dev/null
+++ b/externals/gridflow/format/x11.c
@@ -0,0 +1,655 @@
+/*
+ $Id: x11.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include "../base/grid.h.fcs"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/StringDefs.h>
+
+/* X11 Error Handler type */
+typedef int (*XEH)(Display *, XErrorEvent *);
+
+#ifdef HAVE_X11_SHARED_MEMORY
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+
+\class FormatX11 < Format
+struct FormatX11 : Format {
+ template <class T> void frame_by_type (T bogus);
+/* at the Display/Screen level */
+ Display *display; /* connection to xserver */
+ Visual *visual; /* screen properties */
+ Window root_window;
+ Colormap colormap; /* for 256-color mode */
+ long black,white; /* color numbers in default colormap */
+ short depth;
+ bool use_shm; /* should use shared memory? */
+ bool use_stripes; /* use alternate conversion in 256-color mode */
+
+/* at the Window level */
+ int autodraw; /* how much to send to the display at once */
+ Window window; /* X11 window number */
+ Window parent; /* X11 window number of the parent */
+ GC imagegc; /* X11 graphics context (like java.awt.Graphics) */
+ XImage *ximage; /* X11 image descriptor */
+ char *name; /* window name (for use by window manager) */
+ Pt<uint8> image; /* the real data (that XImage binds to) */
+ bool is_owner;
+ int pos_x, pos_y;
+
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ bool lock_size;
+ bool override_redirect;
+
+#ifdef HAVE_X11_SHARED_MEMORY
+ XShmSegmentInfo *shm_info; /* to share memory with X11/Unix */
+#endif
+
+ Atom wmProtocolsAtom;
+ Atom wmDeleteAtom;
+
+ FormatX11 () : use_stripes(false),
+ autodraw(1), window(0), ximage(0), name(0), image(Pt<uint8>()), is_owner(true),
+ dim(0), lock_size(false), override_redirect(false)
+#ifdef HAVE_X11_SHARED_MEMORY
+ , shm_info(0)
+#endif
+ {}
+
+ void show_section(int x, int y, int sx, int sy);
+ void set_wm_hints (int sx, int sy);
+ void dealloc_image ();
+ bool alloc_image (int sx, int sy);
+ void resize_window (int sx, int sy);
+ void open_display(const char *disp_string);
+ void report_pointer(int y, int x, int state);
+ void prepare_colormap();
+ Window FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level=0);
+
+ \decl void initialize (...);
+ \decl void frame ();
+ \decl void close ();
+ \decl void call ();
+ \decl void _0_out_size (int sy, int sx);
+ \decl void _0_draw ();
+ \decl void _0_autodraw (int autodraw);
+ \decl void _0_setcursor (int shape);
+ \decl void _0_hidecursor ();
+ \decl void _0_set_geometry (int y, int x, int sy, int sx);
+ \decl void _0_fall_thru (int flag);
+ \grin 0 int
+};
+
+/* ---------------------------------------------------------------- */
+
+void FormatX11::show_section(int x, int y, int sx, int sy) {
+ if (y>dim->get(0)||x>dim->get(1)) return;
+ if (y+sy>dim->get(0)) sy=dim->get(0)-y;
+ if (x+sx>dim->get(1)) sx=dim->get(1)-x;
+#ifdef HAVE_X11_SHARED_MEMORY
+ if (use_shm) {
+ XSync(display,False);
+ XShmPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy,False);
+ /* should completion events be waited for? looks like a bug */
+ } else
+#endif
+ XPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy);
+ XFlush(display);
+}
+
+/* window manager hints, defines the window as non-resizable */
+void FormatX11::set_wm_hints (int sx, int sy) {
+ if (!is_owner) return;
+ XWMHints wmhints;
+ XTextProperty window_name, icon_name;
+ XSizeHints hints;
+ // enable recommended, maximum and minimum size
+ hints.flags=PSize|PMaxSize|PMinSize;
+ // set those
+ hints.min_width = hints.max_width = hints.width = sx;
+ hints.min_height = hints.max_height = hints.height = sy;
+
+ wmhints.input = True;
+ wmhints.flags = InputHint;
+ XStringListToTextProperty((char **)&name, 1, &window_name);
+ XStringListToTextProperty((char **)&name, 1, &icon_name);
+ XSetWMProperties(display, window,
+ &window_name, &icon_name, NULL, 0, &hints, &wmhints, NULL);
+}
+
+void FormatX11::report_pointer(int y, int x, int state) {
+ Ruby argv[5] = {
+ INT2NUM(0), SYM(position),
+ INT2NUM(y), INT2NUM(x), INT2NUM(state) };
+ send_out(COUNT(argv),argv);
+}
+
+\def void call() {
+ XEvent e;
+ for (;;) {
+ int xpending = XEventsQueued(display, QueuedAfterFlush);
+ if (!xpending) break;
+ XNextEvent(display,&e);
+ switch (e.type) {
+ case Expose:{
+ XExposeEvent *ex = (XExposeEvent *)&e;
+ if (rb_ivar_get(rself,SI(@mode)) == SYM(out)) {
+ show_section(ex->x,ex->y,ex->width,ex->height);
+ }
+ }break;
+ case ButtonPress:{
+ XButtonEvent *eb = (XButtonEvent *)&e;
+ eb->state |= 128<<eb->button;
+ report_pointer(eb->y,eb->x,eb->state);
+ }break;
+ case ButtonRelease:{
+ XButtonEvent *eb = (XButtonEvent *)&e;
+ eb->state &= ~(128<<eb->button);
+ report_pointer(eb->y,eb->x,eb->state);
+ }break;
+ case KeyPress:
+ case KeyRelease:{
+ XKeyEvent *ek = (XKeyEvent *)&e;
+ //XLookupString(ek, buf, 63, 0, 0);
+ char *kss = XKeysymToString(XLookupKeysym(ek, 0));
+ char buf[64];
+ if (!kss) return; /* unknown keys ignored */
+ if (isdigit(*kss)) sprintf(buf,"D%s",kss); else strcpy(buf,kss);
+ Ruby argv[6] = {
+ INT2NUM(0), e.type==KeyPress ? SYM(keypress) : SYM(keyrelease),
+ INT2NUM(ek->y), INT2NUM(ek->x), INT2NUM(ek->state),
+ rb_funcall(rb_str_new2(buf),SI(intern),0) };
+ send_out(COUNT(argv),argv);
+ //XFree(kss);
+ }break;
+ case MotionNotify:{
+ XMotionEvent *em = (XMotionEvent *)&e;
+ report_pointer(em->y,em->x,em->state);
+ }break;
+ case DestroyNotify:{
+ gfpost("This window is being closed, so this handler will close too!");
+ rb_funcall(rself,SI(close),0);
+ return;
+ }break;
+ case ConfigureNotify:break; // as if we cared
+ case ClientMessage:{
+ // tnx to vektor&walken
+ /*
+ if (e.xclient.message_type==wmProtocolsAtom
+ && e.xclient.format==32
+ && (Atom)(e.xclient.data.l[0])==wmDeleteAtom) {
+ gfpost("This window is being closed, so this handler will close too!");
+ rb_funcall(rself,SI(close),0);
+ return;
+ }
+ */
+ }break;
+ }
+ }
+ IEVAL(rself,"@clock.delay 20");
+}
+
+\def void frame () {
+ XGetSubImage(display, window,
+ 0, 0, dim->get(1), dim->get(0),
+ (unsigned)-1, ZPixmap, ximage, 0, 0);
+ GridOutlet out(this,0,dim,NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int sy=dim->get(0), sx=dim->get(1);
+ int bs=dim->prod(1);
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> b1 = Pt<uint8>(image,ximage->bytes_per_line*dim->get(0))
+ + ximage->bytes_per_line * y;
+ bit_packing->unpack(sx,b1,b2);
+ out.send(bs,b2);
+ }
+}
+
+/* loathe Xlib's error handlers */
+static FormatX11 *current_x11;
+static int FormatX11_error_handler (Display *d, XErrorEvent *xee) {
+ gfpost("X11 reports Error: display=0x%08x",(int)d);
+ gfpost("serial=0x%08x error=0x%08x request=0x%08lx minor=0x%08x",
+ xee->serial, xee->error_code, xee->request_code, xee->minor_code);
+ current_x11->use_shm = false;
+ return 42; /* it seems that the return value is ignored. */
+}
+
+void FormatX11::dealloc_image () {
+ if (!ximage) return;
+ if (use_shm) {
+ #ifdef HAVE_X11_SHARED_MEMORY
+ shmdt(ximage->data);
+ XShmDetach(display,shm_info);
+ if (shm_info) {delete shm_info; shm_info=0;}
+ //ximage->data = new char[1]; // bogus
+ //ximage->data = 0;
+ //XDestroyImage(ximage);
+ XFree(ximage);
+ ximage = 0;
+ image = Pt<uint8>();
+ #endif
+ } else {
+ //XDestroyImage(ximage);
+ XFree(ximage);
+ ximage = 0;
+ image = Pt<uint8>();
+ }
+}
+
+bool FormatX11::alloc_image (int sx, int sy) {
+ dim = new Dim(sy, sx, 3);
+ dealloc_image();
+ if (sx==0 || sy==0) return false;
+#ifdef HAVE_X11_SHARED_MEMORY
+ if (use_shm) {
+ shm_info = new XShmSegmentInfo;
+ ximage = XShmCreateImage(display,visual,depth,ZPixmap,0,shm_info,sx,sy);
+ if (!ximage) {
+ gfpost("shm got disabled (1), retrying...");
+ return alloc_image(sx,sy);}
+ shm_info->shmid = shmget(IPC_PRIVATE,
+ ximage->bytes_per_line*ximage->height, IPC_CREAT|0777);
+ if(shm_info->shmid < 0)
+ RAISE("ERROR: shmget failed: %s",strerror(errno));
+ ximage->data = shm_info->shmaddr =
+ (char *)shmat(shm_info->shmid,0,0);
+ image = Pt<uint8>((uint8 *)ximage->data,
+ ximage->bytes_per_line*sy);
+ shm_info->readOnly = False;
+ current_x11 = this;
+ //XSetErrorHandler(FormatX11_error_handler);
+ if (!XShmAttach(display, shm_info)) RAISE("ERROR: XShmAttach: big problem");
+ XSync(display,0); // make sure the server picks it up
+ //XSetErrorHandler(0);
+ /* yes, this can be done now. should cause auto-cleanup. */
+ shmctl(shm_info->shmid,IPC_RMID,0);
+ if (!use_shm) {
+ gfpost("shm got disabled (2), retrying...");
+ return alloc_image(sx,sy);}
+ } else
+#endif
+ ximage = XCreateImage(display,visual,depth,ZPixmap,0,0,sx,sy,8,0);
+ if (!ximage) RAISE("can't create image");
+ image = ARRAY_NEW(uint8,ximage->bytes_per_line*sy);
+ ximage->data = (int8 *)image;
+ int status = XInitImage(ximage);
+ if (status!=1) gfpost("XInitImage returned: %d", status);
+ return true;
+}
+
+void FormatX11::resize_window (int sx, int sy) {
+ if (sy<16) sy=16; if (sy>4096) RAISE("height too big");
+ if (sx<16) sx=16; if (sx>4096) RAISE("width too big");
+ alloc_image(sx,sy);
+ if (name) delete name;
+ name = new char[64];
+ sprintf(name,"GridFlow (%d,%d,3)",sy,sx);
+ if (window) {
+ if (is_owner && !lock_size) {
+ set_wm_hints(sx,sy);
+ XResizeWindow(display,window,sx,sy);
+ }
+ } else {
+ XSetWindowAttributes xswa;
+ xswa.do_not_propagate_mask = 0; //?
+ xswa.override_redirect = override_redirect; //#!@#$
+ window = XCreateWindow(display,
+ parent, pos_x, pos_y, sx, sy, 0,
+ CopyFromParent, InputOutput, CopyFromParent,
+ CWOverrideRedirect|CWDontPropagate, &xswa);
+ if(!window) RAISE("can't create window");
+ set_wm_hints(sx,sy);
+ if (is_owner) {
+ // fall_thru 0
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask | PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
+ KeyPressMask | KeyReleaseMask);
+ XMapRaised(display, window);
+ } else {
+ // fall_thru 1
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask);
+ }
+ imagegc = XCreateGC(display, window, 0, NULL);
+ if (visual->c_class == PseudoColor) prepare_colormap();
+ }
+ XSync(display,0);
+}
+
+GRID_INLET(FormatX11,0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2)!=3 && in->dim->get(2)!=4)
+ RAISE("expecting 3 or 4 channels: red,green,blue,ignored (got %d)",in->dim->get(2));
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_factor(sxc);
+ if (sx!=osx || sy!=osy) resize_window(sx,sy);
+ if (in->dim->get(2)!=bit_packing->size) {
+ bit_packing->mask[3]=0;
+ bit_packing = new BitPacking(bit_packing->endian,
+ bit_packing->bytes, in->dim->get(2), bit_packing->mask);
+ }
+} GRID_FLOW {
+ int bypl = ximage->bytes_per_line;
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1);
+ int y = in->dex/sxc;
+ int oy = y;
+ for (; n>0; y++, data+=sxc, n-=sxc) {
+ // convert line
+ if (use_stripes) {
+ int o=y*bypl;
+ for (int x=0, i=0, k=y%3; x<sx; x++, i+=3, k=(k+1)%3) {
+ image[o+x] = (k<<6) | data[i+k]>>2;
+ }
+ } else {
+ bit_packing->pack(sx, data, image+y*bypl);
+ }
+ }
+ if (autodraw==2) show_section(0,oy,sx,y-oy);
+} GRID_FINISH {
+ if (autodraw==1) show_section(0,0,in->dim->get(1),in->dim->get(0));
+} GRID_END
+
+\def void close () {
+ if (!this) RAISE("stupid error: trying to close display NULL. =)");
+ bit_packing=0;
+ IEVAL(rself,"@clock.unset");
+ if (is_owner) XDestroyWindow(display,window);
+ XSync(display,0);
+ dealloc_image();
+ XCloseDisplay(display);
+ display=0;
+ rb_call_super(argc,argv);
+}
+
+\def void _0_out_size (int sy, int sx) { resize_window(sx,sy); }
+\def void _0_draw () { show_section(0,0,dim->get(1),dim->get(0)); }
+
+\def void _0_autodraw (int autodraw) {
+ if (autodraw<0 || autodraw>2)
+ RAISE("autodraw=%d is out of range",autodraw);
+ this->autodraw = autodraw;
+}
+
+\def void _0_setcursor (int shape) {
+ shape = 2*(shape&63);
+ Cursor c = XCreateFontCursor(display,shape);
+ XDefineCursor(display,window,c);
+ XFlush(display);
+}
+
+\def void _0_hidecursor () {
+ Font font = XLoadFont(display,"fixed");
+ XColor color; /* bogus */
+ Cursor c = XCreateGlyphCursor(display,font,font,' ',' ',&color,&color);
+ XDefineCursor(display,window,c);
+ XFlush(display);
+}
+
+void FormatX11::prepare_colormap() {
+ Colormap colormap = XCreateColormap(display,window,visual,AllocAll);
+ XColor colors[256];
+ if (use_stripes) {
+ for (int i=0; i<192; i++) {
+ int k=(i&63)*0xffff/63;
+ colors[i].pixel = i;
+ colors[i].red = (i>>6)==0 ? k : 0;
+ colors[i].green = (i>>6)==1 ? k : 0;
+ colors[i].blue = (i>>6)==2 ? k : 0;
+ colors[i].flags = DoRed | DoGreen | DoBlue;
+ }
+ XStoreColors(display,colormap,colors,192);
+ } else {
+ for (int i=0; i<256; i++) {
+ colors[i].pixel = i;
+ colors[i].red = ((i>>0)&7)*0xffff/7;
+ colors[i].green = ((i>>3)&7)*0xffff/7;
+ colors[i].blue = ((i>>6)&3)*0xffff/3;
+ colors[i].flags = DoRed | DoGreen | DoBlue;
+ }
+ XStoreColors(display,colormap,colors,256);
+ }
+ XSetWindowColormap(display,window,colormap);
+}
+
+void FormatX11::open_display(const char *disp_string) {
+ int screen_num;
+ Screen *screen;
+
+ // Open an X11 connection
+ display = XOpenDisplay(disp_string);
+ if(!display) RAISE("ERROR: opening X11 display: %s",strerror(errno));
+
+ // btw don't expect too much from Xlib error handling.
+ // Xlib, you are so free of the ravages of intelligence...
+ XSetErrorHandler(FormatX11_error_handler);
+
+ screen = DefaultScreenOfDisplay(display);
+ screen_num = DefaultScreen(display);
+ visual = DefaultVisual(display, screen_num);
+ white = XWhitePixel(display,screen_num);
+ black = XBlackPixel(display,screen_num);
+ root_window = DefaultRootWindow(display);
+ depth = DefaultDepthOfScreen(screen);
+ colormap = 0;
+
+ switch(visual->c_class) {
+ case TrueColor: case DirectColor: /* without colormap */
+ break;
+ case PseudoColor: /* with colormap */
+ if (depth!=8)
+ RAISE("ERROR: with colormap, only supported depth is 8 (got %d)",
+ depth);
+ break;
+ default: RAISE("ERROR: visual type not supported (got %d)",
+ visual->c_class);
+ }
+
+#ifdef HAVE_X11_SHARED_MEMORY
+ use_shm = !! XShmQueryExtension(display);
+#else
+ use_shm = false;
+#endif
+}
+
+Window FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level) {
+ if (level>2) return 0xDeadBeef;
+ Window root_r, parent_r;
+ Window *children_r;
+ unsigned int nchildren_r;
+ XQueryTree(display,xid,&root_r,&parent_r,&children_r,&nchildren_r);
+ Window target = 0xDeadBeef;
+ for (int i=0; i<(int)nchildren_r; i++) {
+ Atom actual_type_r;
+ int actual_format_r;
+ unsigned long nitems_r, bytes_after_r;
+ unsigned char *prop_r;
+ XGetWindowProperty(display,children_r[i],key,0,666,0,AnyPropertyType,
+ &actual_type_r,&actual_format_r,&nitems_r,&bytes_after_r,&prop_r);
+ uint32 value_l = strlen(value);
+ bool match = prop_r && nitems_r>=value_l &&
+ strncmp((char *)prop_r+nitems_r-value_l,value,value_l)==0;
+ XFree(prop_r);
+ if (match) {target=children_r[i]; break;}
+ target = search_window_tree(children_r[i],key,value,level+1);
+ if (target != 0xDeadBeef) break;
+ }
+ if (children_r) XFree(children_r);
+ return target;
+}
+
+\def void _0_set_geometry (int y, int x, int sy, int sx) {
+ pos_x = x;
+ pos_y = y;
+ XMoveWindow(display,window,x,y);
+ resize_window(sx,sy);
+ XFlush(display);
+}
+
+\def void _0_fall_thru (int flag) {
+ if (flag) {
+ gfpost("falling through!");
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask);
+ } else {
+ gfpost("NOT falling through!");
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask |
+ PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
+ }
+ XFlush(display);
+}
+
+\def void initialize (...) {
+ int sy=240, sx=320; // defaults
+ rb_call_super(argc,argv);
+ argv++, argc--;
+ VALUE domain = argc<1 ? SYM(here) : argv[0];
+ int i;
+ if (domain==SYM(here)) {
+ open_display(0);
+ i=1;
+ } else if (domain==SYM(local)) {
+ if (argc<2) RAISE("open x11 local: not enough args");
+ char host[256];
+ int dispnum = NUM2INT(argv[1]);
+ sprintf(host,":%d",dispnum);
+ open_display(host);
+ i=2;
+ } else if (domain==SYM(remote)) {
+ if (argc<3) RAISE("open x11 remote: not enough args");
+ char host[256];
+ strcpy(host,rb_sym_name(argv[1]));
+ int dispnum = NUM2INT(argv[2]);
+ sprintf(host+strlen(host),":%d",dispnum);
+ open_display(host);
+ i=3;
+ } else if (domain==SYM(display)) {
+ if (argc<2) RAISE("open x11 display: not enough args");
+ char host[256];
+ strcpy(host,rb_sym_name(argv[1]));
+ for (int k=0; host[k]; k++) if (host[k]=='%') host[k]==':';
+ gfpost("mode `display', DISPLAY=`%s'",host);
+ open_display(host);
+ i=2;
+ } else {
+ RAISE("x11 destination syntax error");
+ }
+
+ for(;i<argc;i++) {
+ Ruby a=argv[i];
+ if (a==SYM(override_redirect)) {
+ override_redirect = true;
+ } else if (a==SYM(use_stripes)){
+ use_stripes = true;
+ } else {
+ break;
+ }
+ }
+
+ pos_x=pos_y=0;
+ parent = root_window;
+ if (i>=argc) {
+ } else {
+ VALUE winspec = argv[i];
+ if (winspec==SYM(root)) {
+ window = root_window;
+ is_owner = false;
+ } else if (winspec==SYM(embed)) {
+ Ruby title_s = rb_funcall(argv[i+1],SI(to_s),0);
+ char *title = strdup(rb_str_ptr(title_s));
+ sy = sx = pos_y = pos_x = 0;
+ parent = search_window_tree(root_window,XInternAtom(display,"WM_NAME",0),title);
+ free(title);
+ if (parent == 0xDeadBeef) RAISE("Window not found.");
+ } else if (winspec==SYM(embed_by_id)) {
+ const char *winspec2 = rb_sym_name(argv[i+1]);
+ if (strncmp(winspec2,"0x",2)==0) {
+ parent = strtol(winspec2+2,0,16);
+ } else {
+ parent = atoi(winspec2);
+ }
+ } else {
+ if (TYPE(winspec)==T_SYMBOL) {
+ const char *winspec2 = rb_sym_name(winspec);
+ if (strncmp(winspec2,"0x",2)==0) {
+ window = strtol(winspec2+2,0,16);
+ } else {
+ window = atoi(winspec2); // huh?
+ }
+ } else {
+ window = INT(winspec);
+ }
+ is_owner = false;
+ sy = sx = pos_y = pos_x = 0;
+ }
+ }
+
+ // "resize" also takes care of creation
+ resize_window(sx,sy);
+
+ if (is_owner) {
+ wmProtocolsAtom = XInternAtom(display, "WM_PROTOCOLS", False);
+ wmDeleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display,window,&wmDeleteAtom,1);
+ }
+
+ Visual *v = visual;
+ int disp_is_le = !ImageByteOrder(display);
+ int bpp = ximage->bits_per_pixel;
+ switch(visual->c_class) {
+ case TrueColor: case DirectColor: {
+ uint32 masks[3] = { v->red_mask, v->green_mask, v->blue_mask };
+ bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks);
+ } break;
+ case PseudoColor: {
+ uint32 masks[3] = { 0x07, 0x38, 0xC0 };
+ bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks);
+ } break;
+ }
+ IEVAL(rself,"@clock = Clock.new self; @clock.delay 0");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:x11',1,1;@mode=6;@comment='X Window System Version 11.5'");
+}
+\end class FormatX11
+void startup_x11 () {
+ \startall
+}
diff --git a/externals/gridflow/optional/usb.c b/externals/gridflow/optional/usb.c
new file mode 100644
index 00000000..1721d4c4
--- /dev/null
+++ b/externals/gridflow/optional/usb.c
@@ -0,0 +1,358 @@
+/*
+ $Id: usb.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ 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 ../COPYING 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.
+*/
+
+#include <usb.h>
+#include "../base/grid.h.fcs"
+
+static Ruby cUSB;
+
+struct named_int {const char *name; int v;};
+
+named_int usb_class_choice[] = {
+ {"USB_CLASS_PER_INTERFACE",0},
+ {"USB_CLASS_AUDIO",1},
+ {"USB_CLASS_COMM",2},
+ {"USB_CLASS_HID",3},
+ {"USB_CLASS_PRINTER",7},
+ {"USB_CLASS_MASS_STORAGE",8},
+ {"USB_CLASS_HUB",9},
+ {"USB_CLASS_DATA",10},
+ {"USB_CLASS_VENDOR_SPEC",0xff},
+ {0,0},
+};
+
+named_int usb_descriptor_types_choices[] = {
+ {"USB_DT_DEVICE",0x01},
+ {"USB_DT_CONFIG",0x02},
+ {"USB_DT_STRING",0x03},
+ {"USB_DT_INTERFACE",0x04},
+ {"USB_DT_ENDPOINT",0x05},
+ {"USB_DT_HID",0x21},
+ {"USB_DT_REPORT",0x22},
+ {"USB_DT_PHYSICAL",0x23},
+ {"USB_DT_HUB",0x29},
+ {0,0},
+};
+
+named_int usb_descriptor_types_sizes[] = {
+ {"USB_DT_DEVICE_SIZE",18},
+ {"USB_DT_CONFIG_SIZE",9},
+ {"USB_DT_INTERFACE_SIZE",9},
+ {"USB_DT_ENDPOINT_SIZE",7},
+ {"USB_DT_ENDPOINT_AUDIO_SIZE",9},/* Audio extension */
+ {"USB_DT_HUB_NONVAR_SIZE",7},
+ {0,0},
+};
+
+named_int usb_endpoints_choices[] = {
+ {"USB_ENDPOINT_ADDRESS_MASK",0x0f},/* in bEndpointAddress */
+ {"USB_ENDPOINT_DIR_MASK",0x80},
+ {"USB_ENDPOINT_TYPE_MASK",0x03},/* in bmAttributes */
+ {"USB_ENDPOINT_TYPE_CONTROL",0},
+ {"USB_ENDPOINT_TYPE_ISOCHRONOUS",1},
+ {"USB_ENDPOINT_TYPE_BULK",2},
+ {"USB_ENDPOINT_TYPE_INTERRUPT",3},
+ {0,0},
+};
+
+named_int usb_requests_choice[] = {
+ {"USB_REQ_GET_STATUS",0x00},
+ {"USB_REQ_CLEAR_FEATURE",0x01},
+ {"USB_REQ_SET_FEATURE",0x03},
+ {"USB_REQ_SET_ADDRESS",0x05},
+ {"USB_REQ_GET_DESCRIPTOR",0x06},
+ {"USB_REQ_SET_DESCRIPTOR",0x07},
+ {"USB_REQ_GET_CONFIGURATION",0x08},
+ {"USB_REQ_SET_CONFIGURATION",0x09},
+ {"USB_REQ_GET_INTERFACE",0x0A},
+ {"USB_REQ_SET_INTERFACE",0x0B},
+ {"USB_REQ_SYNCH_FRAME",0x0C},
+ {0,0},
+};
+
+named_int usb_type_choice[] = {
+ {"USB_TYPE_STANDARD",(0x00 << 5)},
+ {"USB_TYPE_CLASS",(0x01 << 5)},
+ {"USB_TYPE_VENDOR",(0x02 << 5)},
+ {"USB_TYPE_RESERVED",(0x03 << 5)},
+ {0,0},
+};
+
+named_int usb_recipient_choice[] = {
+ {"USB_RECIP_DEVICE",0x00},
+ {"USB_RECIP_INTERFACE",0x01},
+ {"USB_RECIP_ENDPOINT",0x02},
+ {"USB_RECIP_OTHER",0x03},
+ {0,0},
+};
+
+named_int usb_misc[] = {
+ {"USB_MAXENDPOINTS",32},
+ {"USB_MAXINTERFACES",32},
+ {"USB_MAXALTSETTING",128},
+ {"USB_MAXCONFIG",8},
+ {"USB_ENDPOINT_IN",0x80},
+ {"USB_ENDPOINT_OUT",0x00},
+ {"USB_ERROR_BEGIN",500000},
+ {0,0},
+};
+
+named_int* usb_all_defines[] = {
+ usb_class_choice,
+ usb_descriptor_types_choices,
+ usb_descriptor_types_sizes,
+ usb_endpoints_choices,
+ usb_requests_choice,
+ usb_type_choice,
+ usb_recipient_choice,
+ usb_misc,
+};
+
+#define COMMA ,
+
+//14
+#define USB_DEVICE_DESCRIPTOR(MANGLE,SEP) \
+ MANGLE(bLength)SEP\
+ MANGLE(bDescriptorType)SEP\
+ MANGLE(bcdUSB)SEP\
+ MANGLE(bDeviceClass)SEP\
+ MANGLE(bDeviceSubClass)SEP\
+ MANGLE(bDeviceProtocol)SEP\
+ MANGLE(bMaxPacketSize0)SEP\
+ MANGLE(idVendor)SEP\
+ MANGLE(idProduct)SEP\
+ MANGLE(bcdDevice)SEP\
+ MANGLE(iManufacturer)SEP\
+ MANGLE(iProduct)SEP\
+ MANGLE(iSerialNumber)SEP\
+ MANGLE(bNumConfigurations)
+
+//8
+#define USB_ENDPOINT_DESCRIPTOR(MANGLE,SEP) \
+ MANGLE(bLength)SEP\
+ MANGLE(bDescriptorType)SEP\
+ MANGLE(bEndpointAddress)SEP\
+ MANGLE(bmAttributes)SEP\
+ MANGLE(wMaxPacketSize)SEP\
+ MANGLE(bInterval)SEP\
+ MANGLE(bRefresh)SEP\
+ MANGLE(bSynchAddress)
+// MANGLE(extras)
+
+//9
+#define USB_INTERFACE_DESCRIPTOR(MANGLE,SEP) \
+ MANGLE(bLength)SEP\
+ MANGLE(bDescriptorType)SEP\
+ MANGLE(bInterfaceNumber)SEP\
+ MANGLE(bAlternateSetting)SEP\
+ MANGLE(bNumEndpoints)SEP\
+ MANGLE(bInterfaceClass)SEP\
+ MANGLE(bInterfaceSubClass)SEP\
+ MANGLE(bInterfaceProtocol)SEP\
+ MANGLE(iInterface)
+// MANGLE(endpoint)
+// MANGLE(extras)
+
+//8
+#define USB_CONFIG_DESCRIPTOR(MANGLE,SEP) \
+ MANGLE(bLength)SEP\
+ MANGLE(bDescriptorType)SEP\
+ MANGLE(wTotalLength)SEP\
+ MANGLE(bNumInterfaces)SEP\
+ MANGLE(bConfigurationValue)SEP\
+ MANGLE(iConfiguration)SEP\
+ MANGLE(bmAttributes)SEP\
+ MANGLE(MaxPower)
+// MANGLE(interface)
+// MANGLE(extras)
+
+static Ruby usb_get_endpoint (struct usb_endpoint_descriptor *self) {
+#define MANGLE(X) INT2NUM(self->X)
+ return rb_funcall(rb_const_get(cUSB,SI(Endpoint)),SI(new),8,
+ USB_ENDPOINT_DESCRIPTOR(MANGLE,COMMA));
+#undef MANGLE
+}
+
+static Ruby usb_get_interface (struct usb_interface_descriptor *self) {
+#define MANGLE(X) INT2NUM(self->X)
+ return rb_funcall(rb_const_get(cUSB,SI(Interface)),SI(new),9+1,
+ USB_INTERFACE_DESCRIPTOR(MANGLE,COMMA),
+ usb_get_endpoint(self->endpoint));
+#undef MANGLE
+}
+
+static Ruby usb_get_config (struct usb_config_descriptor *self) {
+ if (!self) {
+ fprintf(stderr,"warning: usb_get_config: null pointer\n");
+ return Qnil;
+ }
+#define MANGLE(X) INT2NUM(self->X)
+ Ruby interface = rb_ary_new();
+ for (int i=0; i<self->interface->num_altsetting; i++) {
+ rb_ary_push(interface, usb_get_interface(&self->interface->altsetting[i]));
+ }
+ return rb_funcall(rb_const_get(cUSB,SI(Config)),SI(new),8+1,
+ USB_CONFIG_DESCRIPTOR(MANGLE,COMMA), interface);
+#undef MANGLE
+}
+
+static Ruby usb_scan_bus (usb_bus *bus) {
+ Ruby rbus = rb_ary_new();
+ for (struct usb_device *dev=bus->devices; dev; dev=dev->next) {
+ struct usb_device_descriptor *devd = &dev->descriptor;
+ Ruby config = rb_ary_new();
+ for (int i=0; i<devd->bNumConfigurations; i++) {
+ rb_ary_push(config,usb_get_config(&dev->config[i]));
+ }
+#define MANGLE(X) INT2NUM(devd->X)
+ rb_ary_push(rbus, rb_funcall(rb_const_get(cUSB,SI(Device)),SI(new),14+3,
+ USB_DEVICE_DESCRIPTOR(MANGLE,COMMA),
+ rb_str_new2(dev->filename),
+ PTR2FIX(dev),
+ config));
+#undef MANGLE
+ }
+ return rbus;
+}
+
+\class USB < CObject
+class USB : public CObject {
+ usb_dev_handle *h;
+public:
+ \decl void initialize (Ruby dev);
+ \decl int close ();
+ \decl int bulk_write (int ep, String s, int timeout);
+ \decl int bulk_read (int ep, String s, int timeout);
+ \decl int claim_interface(int interface);
+ \decl int release_interface(int interface);
+ \decl int set_configuration(int configuration);
+ \decl int set_altinterface(int alternate);
+ \decl int control_msg(int requesttype, int request, int value, int index, String s, int timeout);
+ \decl int resetep(int ep);
+ \decl int clear_halt(int ep);
+ \decl int reset();
+ //\decl int get_string(int index, int langid, String s);
+ //\decl int get_string_simple(int index, String s);
+};
+
+\def void initialize (Ruby dev) {
+ Ruby ptr = rb_funcall(dev,SI(ptr),0);
+ rb_ivar_set(rself, SI(@dev), ptr);
+ h = usb_open(FIX2PTR(struct usb_device,ptr));
+ if (!h) RAISE("usb_open returned null handle");
+}
+
+\def int close () {
+ if (!h) RAISE("USB closed");
+ int r = usb_close(h);
+ h=0;
+ return r;
+}
+
+#define TRAP(stuff) int r=(stuff); if (r<0) RAISE("%s", usb_strerror()); else return r;
+
+\def int bulk_write (int ep, String s, int timeout) {
+ if (!h) RAISE("USB closed");
+ TRAP(usb_bulk_write(h, ep, rb_str_ptr(s), rb_str_len(s), timeout));}
+\def int bulk_read (int ep, String s, int timeout) {
+ if (!h) RAISE("USB closed");
+ gfpost("%d, '%s', %d",ep,rb_str_ptr(s),timeout);
+ TRAP(usb_bulk_read(h, ep, rb_str_ptr(s), rb_str_len(s), timeout));}
+\def int claim_interface(int interface) {
+ TRAP(usb_claim_interface(h, interface));}
+\def int release_interface(int interface) {
+ TRAP(usb_release_interface(h, interface));}
+\def int set_configuration(int configuration) {
+ TRAP(usb_set_configuration(h, configuration));}
+\def int set_altinterface(int alternate) {
+ TRAP(usb_set_altinterface(h, alternate));}
+\def int control_msg(int requesttype, int request, int value, int index, String s, int timeout) {
+ TRAP(usb_control_msg(h, requesttype, request, value, index, rb_str_ptr(s), rb_str_len(s), timeout));}
+\def int resetep(int ep) {
+ TRAP(usb_resetep(h, ep));}
+\def int clear_halt(int ep) {
+ TRAP(usb_clear_halt(h, ep));}
+\def int reset() {
+ TRAP(usb_reset(h));}
+/*\def int get_string(int index, int langid, String s) {
+ TRAP(usb_get_string(h, index, langid, rb_str_ptr(s), rb_str_len(s)));}*/
+/*\def int get_string_simple(int index, String s) {
+ TRAP(usb_get_string_simple(h, index, rb_str_ptr(s), rb_str_len(s)));}*/
+
+// not handled yet:
+// struct usb_string_descriptor
+// struct usb_hid_descriptor
+// void usb_set_debug(int level);
+// Device usb_device(USB dev);
+// get_string and get_string_simple (not present in libusb 0.1.4)
+
+\classinfo
+\end class USB
+
+static Ruby USB_s_new(Ruby argc, Ruby *argv, Ruby qlass) {
+ USB *self = new USB();
+ Ruby rself = Data_Wrap_Struct(qlass, 0, CObject_free, self);
+ self->rself = rself;
+ Ruby keep = rb_ivar_get(mGridFlow, rb_intern("@fobjects"));
+ rb_hash_aset(keep,rself,Qtrue); /* prevent sweeping (leak) (!@#$ WHAT??) */
+ rb_funcall2(rself,SI(initialize),argc,argv);
+ return rself;
+}
+
+#define SDEF(_class_,_name_,_argc_) \
+ rb_define_singleton_method(c##_class_,#_name_,(RMethod)_class_##_s_##_name_,_argc_)
+
+void startup_usb () {
+ cUSB = rb_define_class_under(mGridFlow, "USB", rb_cObject);
+ //rb_define_singleton_method(cUSB, "new", USB_new, 1);
+ EVAL("class Symbol; def decap; x=to_s; x[0..0]=x[0..0].downcase; x.intern end end");
+ SDEF(USB,new,-1);
+ define_many_methods(cUSB, ciUSB.methodsn, ciUSB.methods);
+#define MANGLE(X) rb_funcall(SYM(X),SI(decap),0)
+ rb_const_set(cUSB, SI(Device), rb_funcall(EVAL("Struct"),SI(new),14+3,
+ USB_DEVICE_DESCRIPTOR(MANGLE,COMMA), SYM(filename), SYM(ptr), SYM(config)));
+ rb_const_set(cUSB, SI(Endpoint), rb_funcall(EVAL("Struct"),SI(new),8,
+ USB_ENDPOINT_DESCRIPTOR(MANGLE,COMMA)));
+ rb_const_set(cUSB, SI(Interface), rb_funcall(EVAL("Struct"),SI(new),9+1,
+ USB_INTERFACE_DESCRIPTOR(MANGLE,COMMA), SYM(endpoint)));
+ rb_const_set(cUSB, SI(Config), rb_funcall(EVAL("Struct"),SI(new),8+1,
+ USB_CONFIG_DESCRIPTOR(MANGLE,COMMA), SYM(interface)));
+#undef MANGLE
+ for(int i=0; i<COUNT(usb_all_defines); i++) {
+ named_int *ud = usb_all_defines[i];
+ for(; ud->name; ud++) {
+ rb_const_set(cUSB, rb_intern(ud->name), INT2NUM(ud->v));
+ }
+ }
+ //usb_set_debug(42);
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ Ruby busses = rb_hash_new();
+ rb_ivar_set(cUSB, SI(@busses), busses);
+ for (usb_bus *bus=usb_get_busses(); bus; bus=bus->next) {
+ rb_hash_aset(busses,rb_str_new2(bus->dirname),usb_scan_bus(bus));
+ }
+ //IEVAL(cUSB,"STDERR.print '@busses = '; STDERR.puts @busses.inspect");
+}
+