aboutsummaryrefslogtreecommitdiff
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
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
-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");
+}
+