# clm.rb -- Ruby extension # Author: Michael Scholz # Created: 09/10/14 23:02:57 # Changed: 16/01/05 22:49:21 # Ruby extensions: # # array?(obj) alias list?(obj) # hash?(obj) # string?(obj) # regexp?(obj) # symbol?(obj) # number?(obj) # integer?(obj) # float?(obj) # rational?(obj) # complex?(obj) # boolean?(obj) # proc?(obj) # thunk?(obj) # method?(obj) # func?(obj) # mus?(obj) # get_func_name(n) # assert_type(condition, obj, pos, msg) # identity(arg) # ignore(*rest) # with_silence(exception) do |old_verbose, old_debug| ... end # # provided?(feature) # provide(feature) # features(all) # # Backward compatibility aliases and constants (from sndXX.scm) # # enum(*names) # # class Object # null? # function?(obj) # snd_func(name, *rest, &body) # set_snd_func(name, val, *rest, &body) # snd_apropos(str_or_sym) # # NilClass(arg) # Fixnum(arg) # # class NilClass # each # apply(func, *rest, &body) # empty? # zero? # nonzero? # to_vct # to_vector # to_poly # +(other) # -(other) # *(other) # # backward compatibility methods: # String#to_sym, Symbol#to_sym # make_array(len, init, &body) # Array#insert # Float#step # Numeric#to_c # Range#step # Enumerable#each_index # Enumerable#zip # # class Array # to_pairs # each_pair do |x, y| ... end # to_string(len) # first=(val) # last=(val) # pick # rand # rand! # add(other) # add!(other) # subtract(other) # subtract!(other) # multiply(other) # multiply!(other) # offset(scl) # offset!(scl) # scale(scl) # scale!(scl) # to_vector # car # car= # cadr # cadr= # caddr # caddr= # cadddr # cadddr= # caddddr # caddddr= # cdr # step(n) # apply(func, *rest, &body) # # class Vec < Array # Vec[] # initialize(len, init, &body) # inspect # to_s # to_vector # +(other) # -(other) # *(other) # # Vec(obj) # make_vector(len, init, &body) # vector?(obj) # vector(*args) # # class String # to_vector # to_vct # # Vct(obj) # make_vct!(len, init) do |i| ... end # # class Vct # Vct[] # name # to_vct # to_vector # apply(func, *rest, &body) # +(other) handles self.offset (Numeric) and self.add (Array, Vec, Vct) # -(other) handles self.offset (Numeric) and self.subtract (Array, Vec, Vct) # *(other) handles self.scale (Numeric) and self.multiply (Array, Vec, Vct) # step(n) # [](idx, size) # # class Fixnum # +(other) handles other.offset on Vct, Array, and Vec # *(other) handles other.scale on Vct, Array, and Vec # # class Float # +(other) handles other.offset on Vct, Array, and Vec # *(other) handles other.scale on Vct, Array, and Vec # # mus_a0(gen) # set_mus_a0(gen, val) # mus_a1(gen) # set_mus_a1(gen, val) # mus_a2(gen) # set_mus_a2(gen, val) # mus_b1(gen) # set_mus_b1(gen, val) # mus_b2(gen) # set_mus_b2(gen, val) # # class Mus # run(arg1, arg2) # apply(*rest) # inspect # close # xcoeff=(index, val) # ycoeff=(index, val) # a0 a0=(val) # a1 a1=(val) # a2 a2=(val) # b1 b1=(val) # b2 b2=(val) # # class Musgen base class for generators written in Ruby # initialize # inspect # to_s # run(val1, val2) # apply(*rest) # eql?(other) # reset # # class Numeric # positive? # negative? # # class Integer # even? # odd? # prime? # # module Enumerable # map_with_index do |x, i| ... end # map_with_index! do |x, i| ... end # clm_cycle # clm_cycle=(val) # # as_one_edit_rb(*origin, &body) # map_channel_rb(beg, dur, snd, chn, edpos, edname, &body) # map_chan_rb(beg, dur, edpos, snd, chn, &body) # # module Info # description=(text) # description # # class Proc # to_method(name, klass) # to_str # to_body # source # source= # # make_proc2method(name, prc) # make_proc_with_setter(name, getter, setter) # make_proc_with_source(string, bind) # proc_source(prc) set_proc_source(prc, val) # # Multi-line input to the Snd listener and Emacs/inf-snd.el # # $emacs_eval_hook.call(line) # run_emacs_eval_hook(line) # # class Snd_eval # Snd_eval.count_level(line) # # class Snd_prompt # initialize(level) # inspect # update(level) # reset # # start_emacs_eval(file) # start_listener_eval(file) # stop_emacs_eval # stop_listener_eval # # Debugging resp. inspecting local variables # # debug_properties(name) set_debug_properties(name, val) # debug_property(key, name) set_debug_property(key, val, name) # debug_binding(name) set_debug_binding(bind, name) # display_all_variables(name) # each_variables(bind, &body) # # let(*rest) do |*rest| ... end # # Utilities: # # close_sound_extend(snd) # times2samples(start, dur) # random(n) # logn(r, b) # car(v), cadr(v), caddr(v), cdr(v) # warning(*args), die(*args), error(*args) # clm_message(*args), message(*args), debug(*args), debug_trace(*args) # # class Snd # Snd.add_sound_path(path) # Snd.open_from_path(fname) # Snd.find_from_path(fname) # Snd.fullname(fname) # Snd.load_path # Snd.message(*args) # Snd.display(*args) # Snd.warning(*args) # Snd.die(*args) # Snd.error(*args) # Snd.debug(*args) # Snd.debug_trace(*args) # Snd.sounds # Snd.regions # Snd.marks(snd, chn) # Snd.snd(snd) # Snd.chn(chn) # Snd.catch(tag, retval) # Snd.throw(tag, *rest) # Snd.raise(tag, *rest) # # snd_catch(tag, retval) # snd_throw(tag, *rest) # snd_raise(tag, *rest) # # gloop(*args) do |args| ... end # get_args(args, key, default) # get_shift_args(args, key, default) # get_class_or_key(args, klass, key, default) # optkey(args, *rest) # load_init_file(file) # # edit_list_proc_counter # set_edit_list_proc_counter def make_polar(r, theta) Complex(cos(theta) * r, sin(theta) * r) end def make_rectangular(re, im = 1.0) Complex(re, im) end def array?(obj) obj.kind_of?(Array) end alias list? array? def hash?(obj) obj.kind_of?(Hash) end def string?(obj) obj.kind_of?(String) end def regexp?(obj) obj.kind_of?(Regexp) end def symbol?(obj) obj.kind_of?(Symbol) end def number?(obj) obj.kind_of?(Numeric) end def integer?(obj) obj.kind_of?(Fixnum) end def float?(obj) obj.kind_of?(Float) end def rational?(obj) obj.kind_of?(Rational) end def complex?(obj) obj.kind_of?(Complex) end def boolean?(obj) obj.kind_of?(TrueClass) or obj.kind_of?(FalseClass) end def proc?(obj) obj.kind_of?(Proc) end def thunk?(obj) obj.kind_of?(Proc) and obj.arity.zero? end def method?(obj) obj.kind_of?(Method) end def func?(obj) obj.kind_of?(String) or obj.kind_of?(Symbol) end def mus?(obj) obj.kind_of?(Mus) end def binding?(obj) obj.kind_of?(Binding) end def get_func_name(n = 1) if ca = caller(n)[0].scan(/^.*:in `(.*)'/).first ca.first else "top_level" end end def assert_type(condition, obj, pos, msg) condition or Kernel.raise(TypeError, format("%s: wrong type arg %d, %p, wanted %s", get_func_name(2), pos, obj, msg)) end def identity(arg) arg end def ignore(*rest) nil end unless defined? $LOADED_FEATURES alias $LOADED_FEATURES $" end def provided?(feature) $LOADED_FEATURES.map do |f| File.basename(f) end.member?(feature.to_s.tr("_", "-")) end def provide(feature) $LOADED_FEATURES.push(feature.to_s) end def features(all = nil) if all $LOADED_FEATURES.map do |f| File.basename(f) end else $LOADED_FEATURES.map do |f| next if f.include?("/") or f.include?(".") f end.compact end end # with_silence(exception) do |old_verbose, old_debug| ... end # # subpress debug messages (mostly on older Ruby versions) # # with_silence do $global_var ||= value end # with_silence(LoadError) do require("nonexistent.file") end def with_silence(exception = StandardError) old_verbose = $VERBOSE old_debug = $DEBUG $VERBOSE = false $DEBUG = false ret = if block_given? begin yield(old_verbose, old_debug) rescue exception false end else false end $VERBOSE = old_verbose $DEBUG = old_debug ret end # ruby19 moved complex.rb and rational.rb to C # (See ruby/Changelog Sun Mar 16 08:51:41 2008.) if defined? Complex require "cmath" include CMath else with_silence do # lib/complex.rb is deprecated require "complex" end include Math end unless defined? Rational with_silence do # lib/rational.rb is deprecated require "rational" end end unless provided?(:sndlib) with_silence do # warning: method redefined; discarding old rand require "sndlib" end end TWO_PI = PI * 2.0 unless defined? TWO_PI HALF_PI = PI * 0.5 unless defined? HALF_PI # # Backward compatibility aliases and constants (from sndXX.scm) # # alias new old if provided? :snd alias save_options save_state alias delete_samples_with_origin delete_samples alias default_output_type default_output_header_type alias default_output_format default_output_sample_type alias mus_audio_set_oss_buffers mus_oss_set_buffers unless defined? mus_file_data_clipped alias mus_file_data_clipped mus_clipping alias set_mus_file_data_clipped set_mus_clipping end alias mus_data_clipped mus_clipping alias set_mus_data_clipped set_mus_clipping alias dac_is_running playing # backwards compatibility for snd 8 alias make_ppolar make_two_pole alias make_zpolar make_two_zero alias make_average make_moving_average alias average moving_average alias average? moving_average? # snd10.scm def make_sum_of_sines(*args) sines, frequency, initial_phase = nil optkey(args, binding, [:sines, 1], [:frequency, 0.0], [:initial_phase, 0.0]) gen = make_nsin(:frequency, frequency, :n, sines) gen.phase = initial_phase gen end alias sum_of_sines nsin alias sum_of_sines? nsin? def make_sum_of_cosines(*args) cosines, frequency, initial_phase = nil optkey(args, binding, [:cosines, 1], [:frequency, 0.0], [:initial_phase, 0.0]) gen = make_ncos(:frequency, frequency, :n, cosines) gen.phase = initial_phase gen end alias sum_of_cosines ncos alias sum_of_cosines? ncos? def make_sine_summation(*args) frequency, initial_phase, n, a, ratio = nil optkey(args, binding, [:frequency, 0.0], [:initial_phase, 0.0], [:n, 1], [:a, 0.5], [:ratio, 1.0]) gen = make_nrxysin(:frequency, frequency, :ratio, ratio, :n, n, :r, a) gen.phase = initial_phase gen end alias sine_summation nrxysin alias sine_summation? nrxysin? # snd13.scm def clm_print(fmt, *args) snd_print(format(fmt, *args)) end unless defined? clm_print end # enum("foo", :bar, "FOO_BAR") # produces three constants # Foo == 0 # Bar == 1 # FOO_BAR == 2 if ?a.kind_of?(String) def enum(*names) names.map_with_index do |name, i| const_name = name.to_s const_name[0] = const_name[0].capitalize Object.const_set(const_name, i) const_name end end else def enum(*names) cap_alpha = ?A.kind_of?(String) ? ?A.sum : ?A lit_alpha = ?a.kind_of?(String) ? ?a.sum : ?a letter_diff = cap_alpha - lit_alpha names.map_with_index do |name, i| const_name = name.to_s if const_name[0].between?(?a, ?z) const_name[0] += letter_diff end Object.const_set(const_name, i) const_name end end end class Object def null? self.nil? or (self.respond_to?(:zero?) and self.zero?) or (self.respond_to?(:empty?) and self.empty?) or (self.respond_to?(:length) and self.length.zero?) end def function?(obj) func?(obj) and Snd.catch(:all, false) do self.method(obj) end.first rescue false end # Float(nil) ==> 0.0 like Integer(nil) ==> 0 alias old_Float Float def new_Float(numb) if numb.kind_of?(NilClass) 0.0 else old_Float(numb) end end alias Float new_Float def snd_func(name, *rest, &body) send(name.to_s, *rest, &body) end def set_snd_func(name, val, *rest, &body) send(format("set_%s", name.to_s), val, *rest, &body) end # snd_apropos(str_or_sym) # if `str_or_sym' is a symbol, returns snd_help result, # if `str_or_sym' is a string or regexp it looks in # self.public_methods, # self.protected_methods, # self.private_methods, # Object.constants, and # Kernel.global_variables and returns an array of strings or nil. # # [].snd_apropos(/^apply/) ==> ["apply", "apply_controls"] # vct(0).snd_apropos("subseq") ==> ["subseq", "vct_subseq"] # snd_apropos(/^mus_sound/) ==> ["mus_sound_...", ...] def snd_apropos(str_or_sym) case str_or_sym when Symbol snd_help(str_or_sym) when String, Regexp res = [] [self.public_methods, self.protected_methods, self.private_methods, Object.constants, Kernel.global_variables].each do |m| res += m.grep(/#{str_or_sym}/) end res else nil end end end def NilClass(arg) nil end alias Fixnum Integer class NilClass def each nil end def apply(func, *rest, &body) nil end def empty? true end # Integer(nil) ==> 0 def zero? true end def nonzero? false end def to_vct vector2vct([]) end def to_vector vector() end def to_poly poly() end def +(other) other end def -(other) other end def *(other) snd_func(other.class.name, nil) end end # If $DEBUG = true, on older Ruby versions warnings occur about # missing NilClass#to_str and Symbol#to_str if $DEBUG and RUBY_VERSION < "1.8.0" class Object def method_missing(id, *args) if id == :to_str self.class.class_eval do define_method(id, lambda do | | self.to_s end) end id.id2name else Kernel.raise(NameError, format("[version %s] undefined method `%s'", RUBY_VERSION, id.id2name)) end end end end class String def to_sym self.intern end unless defined? "a".to_sym end class Symbol def to_sym self end unless defined? :a.to_sym end alias object_id __id__ unless defined? object_id # Provides descriptions of instances of classes, see nb.rb, # xm-enved.rb, etc. # # m = lambda do |*args| puts args end # m.info = "my description" # puts m.info module Info def description=(val) @description = val.to_s end alias info= description= def description if defined?(@description) and string?(@description) and (not @description.empty?) @description else "no description available" end end alias info description end unless defined? snd_help alias snd_help get_help end $array_print_length = 10 def print_length $array_print_length end unless defined? print_length def set_print_length(val) $array_print_length = val end unless defined? set_print_length module Enumerable def map_with_index i = -1 self.map do |x| yield(x, i += 1) end end def map_with_index! i = -1 self.map! do |x| yield(x, i += 1) end end def clm_cycle unless defined? @clm_cycle_index then @clm_cycle_index = 0 end val = self[@clm_cycle_index % self.length] @clm_cycle_index += 1 if @clm_cycle_index == self.length then @clm_cycle_index = 0 end val end def clm_cycle=(val) unless defined? @clm_cycle_index then @clm_cycle_index = 0 end self[@clm_cycle_index % self.length] = val @clm_cycle_index += 1 if @clm_cycle_index == self.length then @clm_cycle_index = 0 end val end attr_accessor :clm_cycle_index # backward compatibility methods def each_index self.each_with_index do |val, i| yield(i) end end unless vct(0).respond_to?(:each_index) # Enumerable#zip, new in ruby core since 19-Nov-2002. # a = [4, 5, 6] # b = [7, 8, 9] # [1, 2, 3].zip(a, b) ==> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] # [1, 2].zip(a, b) ==> [[1, 4, 7], [2, 5, 8]] # a.zip([1, 2],[8]) ==> [[4, 1, 8], [5, 2, nil], [6, nil, nil]] def clm_zip(*objs) args = objs.map do |obj| obj.to_a end res = self.to_a res.each_with_index do |val, i| ary = [val] args.each do |obj| ary.push(obj[i]) end if block_given? yield(*ary) else res[i] = ary end end res end alias zip clm_zip unless [].respond_to?(:zip) end # Older Ruby versions lack Array.new(10) do |i| ... end # make_array # make_array(10) # make_array(10, 1.0) # make_array(10) do |i| ... end def make_array(len = 0, init = nil) if len >= 0 if block_given? Array.new(len, init).map_with_index do |x, i| yield(i) end else Array.new(len, init) end else Kernel.raise(TypeError, format("array length < 0 (%p)?", len)) end end class Array def insert(pos, *args) unless args.empty? if pos < 0 pos = self.length - (pos.abs - 1) end tmp = self.dup self[pos, args.length] = args self[pos + args.length..-1] = tmp[pos..-1] end self end unless defined? [].insert # [0.0, 0.0, 0.5, 0.2, 1.0, 1.0].to_pairs # ==> [[0.0, 0.0], [0.5, 0.2], [1.0, 1.0]] def to_pairs ary = [] self.step(2) do |a, b| ary.push([a, b]) end ary end # [0.0, 0.0, 0.5, 0.2, 1.0, 1.0].each_pair do |x, y| # print x, " ", y, "\n" # end ==> 0.0 0.0 # 0.5 0.2 # 1.0 1.0 def each_pair ary = [] self.step(2) do |a, b| ary.push(yield(a, b)) end ary end # prints flat float array more prettily def to_string(len = print_length) ary = self.flatten str = "[" ary.each_with_index do |val, i| if i < len str = str + "%1.3f, " % val.to_f else break end end if ary.length > len str += "..." else str.chop!.chop! end str += "]" end alias old_to_s to_s alias to_s inspect def first=(val) self[0] = val end def last=(val) self[-1] = val end # ary.pick ==> random value # ary.pick(3) ==> [x, y, z] # ary.pick(true) ==> whole ary randomized def array_pick(n = 1) n = self.length if n == true if n == 1 self[kernel_rand(self.length)] else (0...n).map do |i| self[kernel_rand(self.length)] end end end alias pick array_pick def array_rand tmp = self.dup tmp.each_index do |i| r = kernel_rand(tmp.length) tmp[r], tmp[i] = tmp[i], tmp[r] end tmp end alias rand array_rand def array_rand! self.each_index do |i| r = kernel_rand(self.length) self[r], self[i] = self[i], self[r] end self end alias rand! array_rand! def add(other) new_ary = self.dup [self.length, other.length].min.times do |i| new_ary[i] += other[i] end new_ary end def add!(other) [self.length, other.length].min.times do |i| self[i] += other[i] end self end def subtract(other) new_ary = self.dup [self.length, other.length].min.times do |i| new_ary[i] -= other[i] end new_ary end def subtract!(other) [self.length, other.length].min.times do |i| self[i] -= other[i] end self end def multiply(other) new_ary = self.dup [self.length, other.length].min.times do |i| new_ary[i] *= other[i] end new_ary end def multiply!(other) [self.length, other.length].min.times do |i| self[i] *= other[i] end self end def offset(scl) scl = Float(scl) self.class.new(self.length) do |i| self[i] + scl end end def offset!(scl) scl = Float(scl) self.map! do |val| val += scl end end def scale(scl) scl = Float(scl) self.class.new(self.length) do |i| self[i] * scl end end def scale!(scl) scl = Float(scl) self.map! do |val| val *= scl end end def to_vector Vec.new(self.length) do |i| Float(self[i]) end end def car self[0] end def car=(val) self[0] = val end def cadr self[1] end def cadr=(val) self[1] = val end def caddr self[2] end def caddr=(val) self[2] = val end def cadddr self[3] end def cadddr=(val) self[3] = val end def caddddr self[4] end def caddddr=(val) self[4] = val end def cdr self[1..-1] end def step(n = 1) 0.step(self.length - n, n) do |i| yield(*self[i, n]) end self end add_help(:apply, "Array#apply([:func,] *rest, &body) \ Applies function or procedure with possible rest args \ to each element of Array or subclasses of Array. [0, 1, 2].apply(\"a: %d\\n\") do |fmt, a| printf(fmt, a) end [0, 1, 2].apply(:printf, \"a: %d\\n\") both produce a: 0 a: 1 a: 2 [1, 2, 3, 4].apply(:+) # ==> 10 %w(snd sndplay with_sound).apply(:length) # ==> [3, 7, 10] [[1, 2, 3, 4], [1, 2, 3], [1, 2]].apply(:max) # ==> [4, 3, 2] [vct(0.1, 0.2, 0.3), vct(-0.1, -0.2, -0.3)].apply(:peak) # ==> [0.3, 0.3] sounds.apply(:map) do |s| puts s end sounds.apply(:close_sound)") def apply(func, *rest, &body) if block_given? and (not symbol?(func)) rest.unshift(func) self.map do |item| body.call(*rest + [item]) end else case func when Proc, Method self.map do |item| func.call(*rest + [item]) end when Symbol, String meths = self.methods if body and (meths.member?(func.to_s) or meths.member?(func.to_sym)) # map, each, ... self.send(func, *rest, &body) else receiver = self.compact.first meths = receiver.methods if receiver and (meths.member?(func.to_s) or meths.member?(func.to_sym)) # methods case func.to_sym when :+, :-, :* res = receiver self[1..-1].compact.map do |item| res = res.send(func, *rest + [item]) end res else len = rest.length + ((array?(receiver) and receiver.length) or 1) if receiver.method(func).arity.abs == len # remove_file (String(WS) in ws.rb) self.map do |item| send(func, *rest + [item]) end else # length, max, min, ... self.map do |item| item.send(func, *rest) end end end else # functions self.map do |item| send(func, *rest + [item]) end end end end end end # original operands +, -, and * can now handle nil and numberic # (offset, multiply) # # [].+(ary) concatenate arrays # [].+(number) [].add(number) unless defined? [].ary_plus alias old_ary_plus + def ary_plus(other) case other when Numeric self.offset(other) when NilClass self else self.old_ary_plus(other) end end alias + ary_plus end # [].-(ary) intersection # [1, 2, 3, 4] - [2, 3] ==> [1, 4] # [] - number [].offset unless defined? [].ary_minus alias old_ary_minus - def ary_minus(other) case other when Numeric self.offset(-other) when NilClass self else self.old_ary_minus(other) end end alias - ary_minus end # [].*(n) repetition or [].join(n) # [5] * 3 ==> [5, 5, 5] # ["foo", "bar"] * "-" ==> "foo-bar" unless defined? [].ary_times alias old_ary_times * def ary_times(other) case other when NilClass nil.to_a else self.old_ary_times(other) end end alias * ary_times end end # name Vector is in use (lib/ruby/1.9/matrix.rb) class Vec < Array def self.[](*ary) self.new(ary.length) do |i| ary[i] end end def initialize(len, init = 0.0, &body) @name = "vector" if len >= 0 if block_given? super(len, &body) else super(len, init) end else Kernel.raise(TypeError, format("array length < 0 (%p)?", len)) end if test = self.detect do |x| (not number?(x)) end Kernel.raise(TypeError, format("only numeric elements (%p)?", test)) end end def inspect str = "%s(" % @name self.each do |val| str = str + "%s, " % val end if self.length > 0 then str.chop!.chop! end str += ")" str end def to_s if self.length > 0 vals = ":" self.map do |val| vals = vals + " %s" % val end else vals = "" end format("#<%s[%d]%s>", self.class, self.length, vals) end def +(other) case other when Numeric self.offset(other).to_vector when Array, Vec, Vct self.add(other.to_vector) when NilClass self end end def -(other) case other when Numeric self.offset(-other).to_vector when Array, Vec, Vct self.subtract(other.to_vector) when NilClass self end end def *(other) case other when Numeric self.scale(other).to_vector when Array, Vec, Vct self.multiply(other.to_vector) when NilClass nil.to_vector end end end def Vec(obj) if obj.nil? then obj = [] end assert_type(obj.respond_to?(:to_vector), obj, 0, "an object containing method \ 'to_vector' (Vct, String, Array and subclasses)") obj.to_vector end def make_vector(len, init = 0.0, &body) Vec.new(len, init, &body) end def vector?(obj) obj.kind_of?(Vec) end def vector(*args) args.to_vector end class String def to_vector if self.scan(/^vector\([-+,.)\d\s]+/).null? nil else eval(self) end end def to_vct if self.scan(/^vct\([-+,.)\d\s]+/).null? nil else eval(self) end end end def Vct(obj) if obj.nil? then obj = [] end assert_type(obj.respond_to?(:to_vct), obj, 0, "an object containing method \ 'to_vct' (Vct, String, Array and subclasses)") obj.to_vct end def make_vct!(len, init = 0.0, &body) if block_given? Vct.new(len, &body) else Vct.new(len, init) end end class Vct def self.[](*ary) self.new(ary.length) do |i| ary[i] end end def name self.class.to_s.downcase end def to_vct self end def to_vector Vec.new(self.length) do |i| self[i] end end def apply(*rest, &body) self.to_a.apply(*rest, &body) end def +(other) case other when Numeric self.offset(other) when Array, Vec, Vct self.add(other.to_vct) when NilClass self end end def -(other) case other when Numeric self.offset(-other) when Array, Vec, Vct self.subtract(other.to_vct) when NilClass self end end def *(other) case other when Numeric self.scale(other) when Array, Vec, Vct self.multiply(other.to_vct) when NilClass nil.to_vct end end def step(n = 1, &body) self.to_a.step(n, &body) end # v = vct(0, 1, 2, 3, 4) # v[2..4] ==> vct(2.000, 3.000, 4.000) # v[2...4] ==> vct(2.000, 3.000) # v[3, 4] ==> vct(3.000, 4.000) # v[-1] ==> 4.0 def vct_ref_extend(idx, size = nil) case idx when Fixnum if idx < 0 then idx += self.length end if idx < 0 then Snd.raise(:out_of_range, "index < 0", idx) end if integer?(size) size += idx - 1 if size >= self.length then size = self.length - 1 end if size.between?(0, self.length - 1) and size >= idx self.subseq(idx, size) else nil.to_vct # i.e. false end else vct_ref(self, idx) end when Range beg = idx.first len = idx.last if beg < 0 then beg += self.length end if len < 0 then len += self.length end if len >= self.length then len = self.length - 1 end # exclude_end?: (1..2) ==> false # (1...2) ==> true if idx.exclude_end? then len -= 1 end if beg.between?(0, self.length - 1) and len >= beg self.subseq(beg, len) else nil.to_vct # i.e. false end end end # alias [] vct_ref_extend # This is required since Ruby 1.9. alias zip clm_zip if [].respond_to?(:zip) end class Fixnum # no reloading (load "clm.rb") unless defined? 0.new_int_plus alias int_plus + def new_int_plus(other) case other when Vct, Array, Vec other.offset(Float(self)) when NilClass self else self.int_plus(other) end end alias + new_int_plus end unless defined? 0.new_int_times alias int_times * def new_int_times(other) case other when Vct, Array, Vec other.scale(self) when NilClass 0 else self.int_times(other) end end alias * new_int_times end end class Float # no reloading (load "clm.rb") unless defined? 0.0.new_float_plus alias float_plus + def new_float_plus(other) case other when Vct, Array, Vec other.offset(self) when NilClass self else self.float_plus(other) end end alias + new_float_plus end unless defined? 0.0.new_float_times alias float_times * def new_float_times(other) case other when Vct, Array, Vec other.scale(self) when NilClass 0.0 else self.float_times(other) end end alias * new_float_times end unless defined? 0.0.imag def imag 0.0 end alias image imag end end def mus_a0(gen) mus_xcoeff(gen, 0) end def set_mus_a0(gen, val) set_mus_xcoeff(gen, 0, val) end def mus_a1(gen) mus_xcoeff(gen, 1) end def set_mus_a1(gen, val) set_mus_xcoeff(gen, 1, val) end def mus_a2(gen) mus_xcoeff(gen, 2) end def set_mus_a2(gen, val) set_mus_xcoeff(gen, 2, val) end def mus_b1(gen) mus_ycoeff(gen, 1) end def set_mus_b1(gen, val) set_mus_ycoeff(gen, 1, val) end def mus_b2(gen) mus_ycoeff(gen, 2) end def set_mus_b2(gen, val) set_mus_ycoeff(gen, 2, val) end class Mus # clm_gen.call(a1, a2) requires 2 arguments but clm_gen.run([a1, [a2]]) # 0, 1 or 2. # # clm_gen.run([arg1, [arg2]]) def run(arg1 = 0.0, arg2 = 0.0) mus_run(self, arg1, arg2) end def apply(*rest) mus_apply(self, *rest) end alias mus_inspect inspect def inspect "#<" + mus_describe(self) + ">" end def close mus_close(self) end # gen.xcoeff = 0, 0.4 # set_mus_xcoeff(gen, index, val) def xcoeff=(args) set_mus_xcoeff(self, *args.flatten[0, 2]) end # gen.ycoeff = 0, 0.4 # set_mus_ycoeff(gen, index, val) def ycoeff=(args) set_mus_ycoeff(self, *args.flatten[0, 2]) end def a0 mus_xcoeff(self, 0) end def a0=(val) set_mus_xcoeff(self, 0, val) end def a1 mus_xcoeff(self, 1) end def a1=(val) set_mus_xcoeff(self, 1, val) end def a2 mus_xcoeff(self, 2) end def a2=(val) set_mus_xcoeff(self, 2, val) end def b1 mus_ycoeff(self, 1) end def b1=(val) set_mus_ycoeff(self, 1, val) end def b2 mus_ycoeff(self, 2) end def b2=(val) set_mus_ycoeff(self, 2, val) end end # base class for generators written in Ruby class Musgen def initialize @frequency = $clm_default_frequency @phase = 0.0 @scaler = 1.0 @length = 0 @data = nil @increment = 0 @interp_type = -1 @file_name = "" end attr_accessor :frequency attr_accessor :phase attr_accessor :scaler attr_accessor :increment attr_reader :length attr_reader :data attr_reader :interp_type attr_reader :file_name def inspect format("%s.new()", self.class) end def to_s format("#<%s>", self.class) end def run(val1 = 0.0, val2 = 0.0) self.run_func(val1, val2) end alias call run def apply(*rest) self.run_func(*rest) end def eql?(other) self == other end def reset @frequency = $clm_default_frequency @phase = 0.0 @scaler = 1.0 @increment = 0 self end end class Numeric def positive? self > 0 end unless defined? 1.positive? def negative? self < 0 end unless defined? 1.negative? # According to Ruby's ChangeLog-2.0.0: # Wed Nov 21 21:53:29 2012 Tadayoshi Funaba # * complex.c (nucomp_to_c): added. def to_c Complex(self) end unless defined? 1.to_c end class Integer def even? self.modulo(2) == 0 end unless defined? 1.even? def odd? self.modulo(2) != 0 end unless defined? 1.odd? def prime? (self == 2) or (self.odd? and 3.step(sqrt(self), 2) do |i| return false if self.modulo(i) == 0 end) end end class Float # step accepts floats as arguments (implemented in newer versions) def step(upto, step) counter = self while counter < upto yield(counter) counter += step end counter end unless 1.1.respond_to?(:step) end class Range def step(n = 1, &body) self.to_a.step(n, &body) end unless defined? Range.new(0, 1).step end def as_one_edit_rb(*origin, &body) # ruby compatibility: # ruby pre 1.9: lambda do end.arity != lambda do | | end.arity # ruby 1.9: they are even (0) as_one_edit(lambda do | | body.call end, origin.empty? ? "" : format(*origin)) end def map_channel_rb(beg = 0, dur = false, snd = false, chn = false, edpos = false, edname = false, &func) map_channel(func, beg, dur, snd, chn, edpos, edname) end add_help(:map_chan_rb, "map_chan(func, start=0, end=false, edname=false, \ snd=false, chn=false, edpos=false) \ Applies FUNC to samples in the specified channel. \ It is the old (\"irregular\") version of map_channel.") def map_chan_rb(beg = 0, dur = false, ednam = false, snd = false, chn = false, edpos = false, &func) map_chan(func, beg, dur, ednam, snd, chn, edpos) end class Proc include Info alias run call add_help(:to_method, "Proc#to_method(name, klass=Object) \ Converts a Proc to a Method 'name' in the given class, default Object. \ NAME can be a string or a symbol. m = lambda do |*args| p args end m.to_method(:func) func(1, 2, 3) ==> [1, 2, 3] lambda do |x| p x end.to_method(:foo); foo(\"text1\") ==> \"text1\" lambda do |x| p x end.to_method(\"bar\"); bar(\"text2\") ==> \"text2\"") def to_method(name, klass = Object) name = case name when String name.intern when Symbol name end body = self klass.class_eval do define_method(name, body) end end # Important: # The following works only with newer ruby versions (I assume >= # 1.8.x). Proc#inspect must return # to # locate the source file of the procedure, not only #! # Functions to_str and to_body try to search the procedure source # code in a file determined by to_s. It is only a simple scanner # which doesn't look for the whole Ruby syntax. ;-) # # It doesn't work if no source file exists, i.e, if the code is # eval'ed by the Snd listener (or in Emacs). You must load the file # instead. # # with_sound(:notehook, # lambda do |name| snd_print(name) if name =~ /viol/ end) do # fm_violin(0, 1, 440, 0.3) # end # # $clm_notehook = lambda do |name| clm_print(name) if name =~ /viol/ end # # with_sound do # fm_violin(0, 1, 440, 0.3) # end # # with_sound(:save_body, true) do # ... # end # returns something like 'lambda do ... end' def to_str if body = self.source return body end file, line = self.to_s.sub(/>/, "").split(/@/).last.split(/:/) if file[0] == ?( and file[-1] == ?) if $VERBOSE warning("%s#%s: no file found for procedure %p", self.class, get_func_name, self) end body = "" elsif (not File.exist?(file)) if $VERBOSE warning("%s#%s: \ Sorry, you need a higher ruby version to use Proc#to_str. It works only with newer ruby versions (I assume >= 1.8.x). Proc#inspect must return # not only %p!", self.class, get_func_name, self) end body = "" else lineno = line.to_i body = "" blck = i = 0 first_line = true File.foreach(file) do |ln| i += 1 next if i < lineno body << ln if first_line if (ln.scan(/\s*do\b|\{/).length - ln.scan(/\s*end\b|\}/).length).zero? and (ln.scan(/\(/).length - ln.scan(/\)/).length).zero? break else first_line = false blck = 1 next end end next if /\s*\S+\s*(if|unless|while|until)+/ =~ ln break if (blck += Snd_eval.count_level(ln)).zero? break if blck.negative? end end unless self.source then self.source = body end body end # returns the inner body without 'lambda do end' def to_body if (body = self.to_str).null? "" elsif body.split(/\n/).length == 1 body.chomp.sub(/^(?:\s*\w+(?:\(.*\))??\s*(?:do\s+|\{\s*))(.*)\s*(?:end|\})$/, '\1').strip else body.split(/\n/)[1..-2].join("\n") end end # property set in g_edit_list_to_function (snd-edits.c) def source property(self.object_id, :proc_source) end def source=(val) set_property(self.object_id, :proc_source, val) end end def make_proc2method(name, prc) prc.to_method(name) end # produces two new functions: NAME and SET_NAME # val = 10 # make_proc_with_setter(:foo, lambda { puts val }, lambda { |a| val = a }) # foo ==> 10 # set_foo(12) # foo ==> 12 def make_proc_with_setter(name, getter, setter) make_proc2method(name, getter) make_proc2method(format("set_%s", name).intern, setter) end # prc = make_proc_with_source(%(lambda do |a, b, c| puts a, b, c end)) # prc.call(1, 2, 3) # prc.source ==> "lambda do |a, b, c| puts a, b, c end" # # With the second argument BIND one can use local variables known in # the current (or other) environment in the proc body: # # os = make_oscil(:frequency, 330) # prc = make_proc_with_source(%(lambda do | | # 10.times do |i| p os.run end # end), binding) # puts prc.source ==> lambda do | | 10.times do |i| p os.run end end # prc.call ==> ..., 0.748837699712728 # puts # prc.call ==> ..., 0.97679449812022 def make_proc_with_source(string, bind = binding) if proc?(prc = (res = Snd.catch(:all) do eval(string, bind) end).first) prc.source = string prc else Snd.raise(:runtime_error, res, prc, string) end end make_proc_with_setter(:proc_source, lambda do |prc| prc.source end, lambda do |prc, val| prc.source = val end) # Multi-line input to the Snd listener and Emacs/inf-snd.el. # A simple parser collects multi-line input, e.g. # # with_sound do # fm_violin(0.0, 0.1, 330, 0.1) # fm_violin(0.1, 0.1, 660, 0.1) # end # # and evals it. # # ~/.snd # set_listener_prompt("snd> ") # optional # start_listener_eval # installs read-hook for snd-listener input # start_emacs_eval # installs emacs-eval-hook make_hook("$emacs_eval_hook", 1, "\ emacs_eval_hook(line): \ called each time inf-snd.el sends a line to the Snd process. \ The hook functions may do their best to deal with multi-line input; \ they can collect multi-line input and eval it by itself. \ One example is install_eval_hooks(file, retval, input, hook, &reset_cursor) \ in clm.rb.") # inf-snd.el calls this function each time a line was sent to the # emacs buffer. def run_emacs_eval_hook(line) if $emacs_eval_hook.empty? # without emacs-eval-hook only single line eval file = "(emacs-eval-hook)" set_snd_input(:emacs) begin Snd.display(eval(line, TOPLEVEL_BINDING, file, 1).inspect) rescue Interrupt, ScriptError, NameError, StandardError Snd.display(verbose_message_string(true, "# ", file)) end set_snd_input(:snd) nil else $emacs_eval_hook.call(line) end end class Snd_eval class << Snd_eval Open_token = %w(class module def do { while until if unless case begin for) Close_token = %w(end }) def count_level(line) eval_level = 0 # skip strings and symbols which may contain reserved words line.gsub(/(:\w+|".+")/, "").split(/\b/).each do |s| case s when *Open_token eval_level += 1 when *Close_token eval_level -= 1 end end eval_level end end end class Snd_prompt # level number inserted into original prompt # ">" ==> "(0)>" # "snd> " ==> "snd(0)> " def initialize(level) @listener_prompt = listener_prompt @base_prompt = listener_prompt.split(/(\(\d+\))?(>)?\s*$/).car.to_s @rest_prompt = listener_prompt.scan(/>\s*$/).car.to_s update(level) end def inspect format("#<%s %s(0)%s>", self.class, @base_prompt, @rest_prompt) end def update(level) set_listener_prompt(format("%s(%d)%s", @base_prompt, level, @rest_prompt)) end def reset set_listener_prompt(@listener_prompt) end end def install_eval_hooks(file, retval, input, hook, &reset_cursor) eval_level = 0 eval_line = "" prompt = Snd_prompt.new(eval_level) reset_cursor.nil? or reset_cursor.call $exit_hook.add_hook!(file) do | | prompt.reset end hook.add_hook!(file) do |line| eval_line = eval_line + line + "\n" eval_level += Snd_eval.count_level(line) if eval_level.negative? eval_level = 0 eval_line = "" end if eval_level.zero? set_snd_input(input) begin Snd.display(eval(eval_line, TOPLEVEL_BINDING, file, 1).inspect) rescue Interrupt, ScriptError, NameError, StandardError Snd.display(verbose_message_string(true, "# ", file)) ensure eval_line = "" end end prompt.update(eval_level) reset_cursor.nil? or reset_cursor.call retval end end # installs the emacs-eval-hook def start_emacs_eval(name = "(emacs)") install_eval_hooks(name, nil, :emacs, $emacs_eval_hook) do $stdout.print(listener_prompt) $stdout.flush end end # installs the read-hook def start_listener_eval(name = "(snd)") set_show_listener(true) install_eval_hooks(name, true, :snd, $read_hook) end def stop_emacs_eval(name = "(emacs)") $emacs_eval_hook.remove_hook!(name) $exit_hook.run_hook_by_name(name) $exit_hook.remove_hook!(name) end def stop_listener_eval(name = "(snd)") $read_hook.remove_hook!(name) $exit_hook.run_hook_by_name(name) $exit_hook.remove_hook!(name) reset_listener_cursor clm_print("\n%s", listener_prompt) end # Debugging resp. inspecting local variables make_proc_with_setter(:debug_properties, lambda do |name| property(name, :debug_property) end, lambda do |name, val| set_property(name, :debug_property, val) end) make_proc_with_setter(:debug_property, lambda do |key, name| hash?(h = debug_properties(name)) and h[key] end, lambda do |key, val, name| h = debug_properties(name) unless hash?(h) and h.store(key, [val] + h[key]) a = property(:debug, :names) unless array?(a) and a.push(name) set_property(:debug, :names, [name]) end set_debug_properties(name, {key => [val]}) end end) make_proc_with_setter(:debug_binding, lambda do |name| debug_property(:binding, name) end, lambda do |bind, *name| set_debug_property(:binding, bind, (name[0] or get_func_name(3))) end) # Shows all local variables of last call of functions prepared with # set_debug_binding(binding) # # def function1 # [...] # set_debug_binding(binding) # end # def function2 # [...] # set_debug_binding(binding) # end # [...] # function1 # function2 # [...] # # display_all_variables def display_all_variables(name = nil) if name [name] else (property(:debug, :names) or []) end.each do |nm| debug_binding(nm).each do |bind| Snd.message("=== %s ===", nm) Snd.message() each_variables(bind) do |var, val| Snd.message("%s = %s", var, val.inspect) end Snd.message() end end end # each_variables provides all local variable names and their values in # the given proc context # # def function # [...] # each_variables do |k, v| # Snd.display("%s = %s", k, v) # end # end def each_variables(bind = binding, &prc) eval("local_variables", bind).each do |name| name = name.to_s prc.call(name, eval(name, bind)) end end # let(8, :foo, "bar") do |a, b, c| # printf("a: %d, b: %s, c: %s\n", a, b, c) # end # # Simulates a save local variable environment and restores old # variables to their original values. def let(*args, &prc) locals = Hash.new bind = prc.binding each_variables(bind) do |var, val| locals[var] = val end prc.call(*args) rescue Interrupt, ScriptError, NameError, StandardError Kernel.raise ensure @locals = locals locals.each_key do |name| eval("#{name} = @locals[#{name.inspect}]", bind) end remove_instance_variable("@locals") end # for irb (rgb.rb) def make_color(r, g, b) [:Pixel, 0] end unless defined? make_color def doc(*rest) # dummy for old Kernel.doc end ## ## Utilities ## if provided? :snd_nogui alias close_sound_extend close_sound else def close_sound_extend(snd) # 5 == Notebook if main_widgets[5] idx = Snd.sounds.index(snd) if idx.nil? then idx = 0 end close_sound(snd) snds = sounds() and set_selected_sound(snds[idx < snds.length ? idx : -1]) else close_sound(snd) end end end add_help(:times2samples, "times2samples(start, dur) \ START and DUR are in seconds; returns array [beg, end] in samples.") def times2samples(start, dur) beg = seconds2samples(start) [beg, beg + seconds2samples(dur)] end def random(val) if val.zero? val else case val when Fixnum kernel_rand(val) when Float val.negative? ? -mus_random(val).abs : mus_random(val).abs end end end def logn(r, b = 10) if r <= 0 Snd.raise(:ruby_error, r, "r must be > 0") end if b <= 0 or b == 1 Snd.raise(:ruby_error, b, "b must be > 0 and != 1") end log(r) / log(b) end def car(v) v[0] end def cadr(v) v[1] end def caddr(v) v[2] end def cdr(v) v[1..-1] end def verbose_message_string(stack_p, remark, *args) fmt_remark = format("\n%s", remark) str = if args.null? "" elsif args.length == 1 String(args.car) else format(*args) end str = if str.split(/\n/).length > 1 str.split(/\n/).join(fmt_remark) else format("%s%s", remark, str) end if $! str += format("[%p] %s (%s)", rb_error_to_mus_tag, snd_error_to_message, $!.class) if stack_p str += format("\n%s%s", remark, $!.backtrace.join(fmt_remark)) end else if stack_p and caller(2) str += format("\n%s%s", remark, caller(2).join(fmt_remark)) end end str end def warning(*args) str = "Warning: " << verbose_message_string($VERBOSE, nil, *args) if provided? :snd snd_warning(str) else clm_message(str) end nil end def die(*args) message(verbose_message_string(true, nil, *args)) exit(1) unless provided? :snd end def error(*args) Snd.raise(:runtime_error, verbose_message_string(true, nil, *args)) end make_proc_with_setter(:snd_input, lambda do property(:snd_input, :snd_listener) end, lambda do |val| set_property(:snd_input, :snd_listener, val) end) # like clm_print(fmt, *args) def clm_message(*args) msg = if args.null? "" elsif args.length == 1 String(args.car) else format(*args) end if provided? :snd if provided? :snd_nogui clm_print("%s\n", msg) else clm_print("\n%s", msg) end nil else $stdout.print(msg, "\n") end nil end # like clm_print(*args), in emacs it prepends msg with a comment sign def message(*args) clm_message(verbose_message_string(false, "# ", *args)) end # debug(var1, var2) ==> # def debug(*args) fmt = "" args.each do |arg| fmt += format("%s: %p", arg.class, arg) fmt += ", " end message("#", fmt.chomp(", ")) end def debug_trace(*args) debug(*args) clm_message(verbose_message_string(true, "# ")) end if provided?(:snd) then set_snd_input(:snd) end class Snd class << Snd Snd_path = Array.new if provided? :snd_motif def add_sound_path(path) Snd_path.push(path) add_directory_to_view_files_list(path) end else def add_sound_path(path) Snd_path.push(path) end end def fullname(fname) if File.exist?(fname) fname else f = File.basename(fname) Snd_path.each do |path| if File.exist?(path + "/" + f) return path + "/" + f end end Snd.raise(:no_such_file, fname) end end def open_from_path(fname) snd_file = Snd.fullname(fname) find_sound(snd_file) or open_sound(snd_file) end def find_from_path(fname) find_sound(Snd.fullname(fname)) end def load_path Snd_path end def message(*args) clm_message(verbose_message_string(false, "# ", *args)) end def display(*args) msg = if args.null? "" elsif args.length == 1 String(args.car) else format(*args) end if snd_input == :snd if provided? :snd_nogui clm_print("%s\n", msg) else clm_print("\n%s", msg) end else $stdout.print(msg, "\n") end nil end def warning(*args) if provided? :snd snd_warning(verbose_message_string($VERBOSE, nil, *args)) else args[0] = "Warning: " + String(args[0]) Snd.display(verbose_message_string($VERBOSE, "# ", *args)) end nil end def die(*args) Snd.display(verbose_message_string(true, nil, *args)) exit(1) unless provided? :snd end def error(*args) Snd.raise(:runtime_error, verbose_message_string(true, nil, *args)) end def debug(*args) if args.null? Snd.message("#") elsif args.length == 1 Snd.message("#", String(args.car)) else Snd.message("#", format(*args)) end end def debug_trace(*args) Snd.debug(*args) Snd.display(verbose_message_string(true, "# ")) end def sounds (Kernel.sounds or []).reverse end def regions (Kernel.regions or []).reverse end def marks(snd = false, chn = false) (Kernel.marks(snd, chn) or []) end def snd(sn = false) sn or selected_sound or Snd.sounds.car end def chn(ch = false) ch or selected_channel or 0 end def catch(tag = :all, retval = :undefined) old_debug = $DEBUG $DEBUG = false val = Kernel.catch(tag) do yield end # catch/throw part # [:snd_throw, tag, get_func_name(2), *rest] if array?(val) and val.car == :snd_throw if retval != :undefined if proc?(retval) retval.call(val.cdr) else [retval] end else val.cdr end else [val] end # ruby1.9/ChangeLog # Thu Feb 2 16:01:24 2006 Yukihiro Matsumoto # * error.c (Init_Exception): change NameError to direct subclass of # Exception so that default rescue do not handle it silently. rescue Interrupt, ScriptError, NameError, StandardError mus_tag = rb_error_to_mus_tag # raise part if (tag == mus_tag) or (tag == :all) if retval != :undefined if proc?(retval) retval.call(mus_tag, snd_error_to_message) else [retval] end else [mus_tag, snd_error_to_message] end else Kernel.raise end ensure $DEBUG = old_debug end def throw(tag, *rest) Kernel.throw(tag, [:snd_throw, tag, get_func_name(2), *rest]) end def raise(tag, *rest) msg = format("%s in %s:", tag, get_func_name(2)) rest.each do |s| msg += format(" %s,", s) end msg.chomp!(",") exception = case tag when :out_of_range RangeError when :wrong_type_arg TypeError when *Snd_error_tags StandardError else Ruby_exceptions[tag] or RuntimeError end Kernel.raise(exception, msg, caller(1)) end end end # almost all StandardError Snd_error_tags = [# clm2xen.c :mus_error, :no_such_method, :wrong_type_arg, # TypeError # snd-0.h :no_such_envelope, :no_such_sample, :no_such_edit, :cannot_save, :cant_update_file, # snd-chn.c :cant_open_file, # snd-dac.c :bad_format, :no_such_player, :arg_error, # snd-draw.c :no_such_widget, :no_such_graphics_context, :no_such_axis, :bad_length, # snd-edits.c :no_such_direction, :no_such_region, :no_such_auto_delete_choice, # snd-env.c :env_error, # snd-error.c :snd_error, # snd-gxcolormaps.c :no_such_colormap, :colormap_error, # snd-key.c :no_such_key, # snd-ladspa.c :no_such_plugin, :plugin_error, # snd-marks.c :no_such_mark, # snd-menu.c :no_such_menu, # snd-mix.c :no_such_mix, # snd-print.c :cannot_print, # snd-region.c :io_error, # run.c :wrong_number_of_args, :cannot_parse, # snd-snd.c :no_such_sound, :not_a_sound_file, :cannot_apply_controls, :bad_size, :snd_internal_error, # snd-xen.c :no_active_selection, :bad_arity, # snd-xmain.c :xt_error, # snd-xchn.c :no_such_color, # snd.c :snd_top_level, :gsl_error, # sndlib2xen.h :out_of_range, :no_such_channel, :no_such_file, :bad_type, :no_data, :bad_header, # xm.c :no_such_resource] def rb_error_to_mus_tag # to_s and string error-names intentional here # otherwise e.g. NameError goes to case StandardError! case $!.class.to_s when "StandardError" $!.message.split(/[: ]/).first.downcase.intern when "RangeError" :out_of_range when "TypeError" :wrong_type_arg when "ArgumentError" :wrong_number_of_args else # converts ruby exceptions to symbols: NoMethodError ==> :no_method_error $!.class.to_s.gsub(/([A-Z])/) do |c| "_" + c.tr("A-Z", "a-z") end[1..-1].intern end end def snd_error_to_message $!.message.split(/\n/).first.sub(/^.*: /, "") end add_help(:snd_catch, "snd_catch(tag=:all, retval=:undefined]) \ Catchs snd_throw and exceptions and \ returns body's last value wrapped in an array if all goes well. \ If a snd_throw tag meets snd_catch's, returns an array with the tag name, \ the function name from where was thrown \ and optional arguments given to snd_throw. \ If an exception was risen and the exception name meets tag name, \ returns an array with tag name and the exception message, \ otherwise reraises exception. \ If retval is given and tag matches exception or snd_throw tag, \ returns retval. \ If retval is a procedure, calls retval with tag name and message. res = snd_catch do 10 + 2 end puts res ==> [12] res = Snd.catch(:no_such_file) do open_sound(\"unknown-file.snd\") end puts res ==> [:no_such_file, \ \"open_sound: no_such_file: Unknown_file.snd No such file or directory\"] res = Snd.catch(:finish) do 10.times do |i| if i == 8 then snd_throw(:finish, i) end end end puts res ==> [:finish, \"top_level\", 8] res = Snd.catch(:all, lambda do |tag, msg| Snd.display([tag, msg]) end) do set_listener_prompt(17) end ==> [:wrong_type_arg, \ \"set_listener-prompt: wrong type arg 0, 17, wanted a string\"] puts res ==> nil The lambda function handles the error in the last case.") def snd_catch(tag = :all, retval = :undefined, &body) Snd.catch(tag, retval, &body) end add_help(:snd_throw, "snd_throw(tag, *rest) \ Jumps to the corresponding snd_catch(TAG) and returns an array \ with TAG, function name and possible *REST strings or values.") def snd_throw(tag, *rest) Snd.throw(tag, *rest) end class Break < StandardError end Ruby_exceptions = { :script_error => ScriptError, :load_error => LoadError, :name_error => NameError, :not_implemented_error => NotImplementedError, :syntax_error => SyntaxError, :interrupt => Interrupt, :system_exit => SystemExit, :standard_error => StandardError, :arg_error => ArgumentError, :float_domain_error => FloatDomainError, :index_error => IndexError, :io_error => IOError, :eof_error => EOFError, :local_jump_error => LocalJumpError, :no_memory_error => NoMemoryError, :range_error => RangeError, :regexp_error => RegexpError, :runtime_error => RuntimeError, :security_error => SecurityError, :system_call_error => SystemCallError, :system_stack_error => SystemStackError, :thread_error => ThreadError, :type_error => TypeError, :zero_division_error => ZeroDivisionError, :break => Break} add_help(:snd_raise, "snd_raise(tag, *rest) \ Raises an exception TAG with an error message \ containing function name, TAG and possible *REST strings or values. \ TAG is a symbol, \ a Ruby exception looks like :local_jump_error instead of LocalJumpError, \ a Snd error tag looks like :no_such_sound.") def snd_raise(tag, *rest) Snd.raise(tag, *rest) end def srate mus_srate end unless defined? srate # general purpose loop add_help(:gloop, "gloop(*args) { |args| ... } :step = 1 :before = nil (thunk) :after = nil (thunk) args[0]: Range (each) Hash(s) (each) Array(s) (each_with_index) [args.last == Fixnum ==> step] Fixnum (times) Fixnum [args[1] == :step ==> step] A general purpose loop, handling Range, Hash, Array, Vec, Vct, Fixnum, with optional step. Returns the result of body as array like map. Examples: Range gloop(0..3) do |i| puts i end Hash (loops over all Hashs consecutively) gloop({1 => :a, 2 => :b}, {11 => :aa => 22 => :bb}) do |k, v| print('key: ', k, ' value: ', v) puts end Array, Vec, Vct gloop([0, 1]) do |x, i| print(i, ': ', x) puts end Arrays with step (mixes all Arrays) gloop([0, 1, 2, 3], [:a, :b, :c, :d], [55, 66, 77, 88, 99], 2) do |x, i| print(i, ': ', x.inspect) puts end Numeric (like Integer#times) gloop(3) do |i| puts i end Numeric with step (like Integer#step) gloop(6, 2) do |i| puts i end a simple body call gloop do puts 'empty' end") def gloop(*args, &body) step = get_shift_args(args, :step, 1) before = get_shift_args(args, :before) after = get_shift_args(args, :after) do_extra = lambda do |thunk| thunk?(thunk) ? thunk.call : snd_func(thunk) end result = [] case args[0] when Range args[0].step(step) do |i| do_extra.call(before) if before result << body.call(i) do_extra.call(after) if after end when Array, Vec, Vct lmax = args.map do |x| x.length end.max 0.step(lmax - 1, step.round) do |i| do_extra.call(before) if before result << body.call(*args.map do |x| x[i] end << i) do_extra.call(after) if after end when Hash args.each do |x| x.each do |k, v| do_extra.call(before) if before result << body.call(k, v) do_extra.call(after) if after end end when Numeric 0.step(args[0], number?(args[1]) ? args[1] : step) do |i| do_extra.call(before) if before result << body.call(i) do_extra.call(after) if after end else do_extra.call(before) if before result << body.call do_extra.call(after) if after end result end # get_args(args, key, default = nil) # # returns value, whether DEFAULT or value of KEY found in ARGS def get_args(args, key, default = nil) if args.member?(key) arg = args[args.index(key) + 1] default = arg.nil? ? default : arg end default end def get_shift_args(args, key, default = nil) default = get_args(args, key, default) if args.member?(key) i = args.index(key) 2.times do args.delete_at(i) end end default end # var = get_class_or_key(args, Klass, :key, default = nil) def get_class_or_key(args, klass, key, default = nil) if (not symbol?(args.first)) and args.first.kind_of?(klass) args.shift else get_shift_args(args, key, default) end end # var1, var2, var3, var4 = optkey(args, [:key, default], # [:number, 1], # [Array, :list, [0, 1, 2, 3]], # :var_w/o_default_value) # # Key-default pairs must be included in brackets while keys alone can # be included in brackets or not, see last key # ":var_w/o_default_value" above. If no default value is specified, # nil is used. def optkey(args, *rest) args_1 = args.dup bind = binding?(rest.car) ? rest.shift : nil @locals = nil vals = rest.map do |keys| val = if array?(keys) case keys.length when 1 name = keys.car.to_s get_class_or_key(args_1, Object, keys.car, nil) when 2 name = keys.car.to_s get_class_or_key(args_1, keys.cadr.class, *keys) when 3 name = keys.cadr.to_s get_class_or_key(args_1, *keys) else assert_type(keys.length.between?(1, 3), keys, 1, "an array of one to three \ elements [class, :key, default]") end else name = keys.to_s get_class_or_key(args_1, Object, keys, nil) end @locals = val eval("#{name} = @locals", bind) val end remove_instance_variable("@locals") if vals.length == 1 vals.first else vals end end add_help(:load_init_file, "load_init_file(file) \ Returns false if FILE doesn't exist, otherwise loads it. \ FILE may reside in current working dir or in $HOME dir.") def load_init_file(file) if File.exist?(file) Snd.catch do load(file) end elsif File.exist?(f = ENV["HOME"] + "/" + file) Snd.catch do load(f) end else false end end let(-1) do |count| # see rotate_phase(func, snd, chn) in dsp.rb # it's necessary to produce a uniq method name make_proc_with_setter(:edit_list_proc_counter, lambda do count end, lambda do count += 1 end) end # clm.rb ends here