=begin $Id: flow_objects.rb,v 1.2 2006-03-15 04:37:28 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 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 /#io:/ =~ 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<=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; def initialize() super 2 end } GridPack.subclass("@three",3,1) { install_rgrid 0; def initialize() super 2 end } GridPack.subclass("@four", 4,1) { install_rgrid 0; def initialize() super 2 end } GridPack.subclass("@eight",8,1) { install_rgrid 0; def initialize() super 2 end } GridObject.subclass("#unpack",1,0) { install_rgrid 0, true def initialize(n=2) @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/86400, t.to_i%86400, ((t.to_f-t.to_f.floor)*1000000).to_i send_out 2, t.year, t.month, t.day, t.hour, t.min, t.day 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 def _0_glob (s) send_out 0, :list, *Dir[ 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 } # 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","# >> 8"] @wires = [-1,0,0,0, 0,0,1,0, 1,0,-1,0] def update_rotator n = @axis[2] rotator = (0...n).map {|i| (0...n).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| a = @axis[i].to_i b = @axis[j].to_i #GridFlow.post "(#{a},#{b}) #{rotator[a].inspect}" rotator[a][b] = (scale*Math.cos(th+(j-i)*Math::PI/2)).to_i }} @fobjects[0].send_in 1,n,n,"#".intern,*rotator.flatten end def _0_axis(from,to,total) total>=0 or raise "total-axis number incorrect" from>=0 and from=0 and to 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 } FObject.subclass("listfind",2,1) { def initialize(*a) _1_list(*a) end def _1_list(*a) @a = a end def _0_float(x) i=0 while i<@a.length (send_out 0,i; return) if @a[i]==x i+=1 end send_out 0,-1 end doc:_1_list,"list to search into" doc:_0_float,"float to find in that list" doc_out:_0_float,"position of the incoming float in the stored list" } #-------- 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 # 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 } # ASCII, useful for controlling pics FObject.subclass("ascii",1,1) { def puts(x) x.each_byte {|b| send_out 0, b } end def _0_float x; puts "#{x.to_i}" end } # System, similar to shell FObject.subclass("system",1,1) { def _0_system(*a) system(a.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) { class GFSoundMixer < FObject; install "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 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 } FObject.subclass("regsub",3,1) { def initialize(from,to) _1_symbol(from); _2_symbol(to) end def _0_symbol(s) send_out 0, :symbol, s.to_s.gsub(@from, @to).intern end def _1_symbol(from) @from = Regexp.new(from.to_s.gsub(/`/,"\\")) end def _2_symbol(to) @to = to.to_s.gsub(/`/,"\\") end doc:_0_symbol,"a string to transform" doc:_1_symbol,"a regexp pattern to be found inside of the string" doc:_2_symbol,"a replacement for the found pattern" doc_out:_0_symbol,"the transformed string" } FObject.subclass("memstat",1,1) { def _0_bang f = File.open("/proc/#{$$}/stat") send_out 0, Float(f.gets.split(" ")[22]) / 1024.0 f.close end doc:_0_bang,"lookup process stats for the currently running pd+ruby "+ "and figure out how much RAM it uses." doc_out:_0_float,"virtual size of RAM in kilobytes (includes swapped out and shared memory)" } FObject.subclass("sendgui",1,0) { def _0_list(*x) GridFlow.gui x.join(" ").gsub(/`/,";")+"\n" end install "sys_vgui", 1, 0 doc:_0_list,"a Tcl/Tk command to send to the pd client." } end # module GridFlow begin require "gridflow/rblti" GridFlow.post "Ruby-LTI support loaded." rescue Exception => e #GridFlow.post "%s", e.inspect #GridFlow.post "(rblti not found)" end