X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/1059e1cc0c2ecfa237406949aa26155b6a5b9154..66f23d4fabf89ad09adbd4dfc15ac6b5b2b7da83:/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2sc-1.0.0/faust2sc diff --git a/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2sc-1.0.0/faust2sc b/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2sc-1.0.0/faust2sc deleted file mode 100755 index 375f420..0000000 --- a/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2sc-1.0.0/faust2sc +++ /dev/null @@ -1,629 +0,0 @@ -#!/usr/bin/env ruby -# ====================================================================== -# faust2sc - Generate language modules from Faust XML. -# Copyright (C) 2005-2008 Stefan Kersten -# ====================================================================== -# 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. -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 -# USA -# ====================================================================== - -# TODO: -# rexml is dog slow, maybe use libxml? - -require 'getoptlong' -require 'rexml/document' - -PROGRAM = File.basename($0) -PROGRAM_VERSION = "1.1.0" - -class Array - def flatten1 - res = [] - self.each { |l| - res += l - } - res - end -end - -module REXML - class Element - def to_i - self.text.to_i - end - def to_f - self.text.to_f - end - end -end - -class String - def encapitalize - self[0..0].upcase + self[1..-1] - end - def decapitalize(greedy=false) - unless greedy - self[0..0].downcase + self[1..-1] - else - res = self.clone - (0..res.size-1).each { |i| - c = res[i] - if 65 <= c && c <= 90 - res[i] = c + 32 - else - break - end - } - res - end - end -end - -def print_error(str) - $stderr.print("#{PROGRAM}[ERROR] #{str}") -end - -def print_info(str) - $stderr.print("#{PROGRAM}[INFO] #{str}") -end - -module Faust - class Widget - attr_reader :type, :id, :label, :init, :min, :max, :step - def initialize(node) - @type = node.attributes["type"] - @id = node.attributes["id"] - if (node.elements["label"].text) - @label = node.elements["label"].text - else - print_info("No label for widget ID #{id} - calling it widget#{id}\n") - @label = "widget#{id}" - end - if (@label =~ /^[0-9]/) - plabel = @label - @label = @type + "_" + @label - print_info("Widget label `#{plabel}' prefixed to become `#{@label}'\n") - end - dict = node.elements - @init = dict["init"].to_f - @min = dict["min"].to_f - @max = dict["max"].to_f - @step = dict["step"].to_f - if (@type == "button") - @max = 1 # no metadata other than name for buttons - @step = 1 - end - end - end - - class UI - attr_reader :active_widgets, :passive_widgets - def initialize(node) - @active_widgets = node.get_elements("//activewidgets/widget").collect { |x| Widget.new(x) } - @passive_widgets = node.get_elements("//passivewidgets/widget").collect { |x| Widget.new(x) } - end - end - - class Plugin - attr_reader :path, :name, :author, :copyright, :license, :inputs, :outputs, :ui - def initialize(path, node) - @path = path - %w(name author copyright license).each { |name| - instance_variable_set("@#{name}", node.elements["/faust/#{name}"].text) - } - %w(inputs outputs).each { |name| - instance_variable_set("@#{name}", node.elements["/faust/#{name}"].text.to_i) - } - @ui = UI.new(node.elements["/faust/ui"]) - end - def total_inputs - inputs + ui.active_widgets.size - end - def Plugin::from_file(path) - self.new(path, REXML::Document.new(File.open(path) { |io| io.read })) - end - end - - class Generator - attr_reader :plugins, :options - def initialize(plugins, options) - @plugins = plugins - @options = options - end - def lang - "unknown" - end - def generate(io) - generate_header(io) - generate_body(io) - generate_footer(io) - end - def generate_header(io) - end - def generate_footer(io) - end - def generate_body(io) - plugins.each_with_index { |plugin,i| - if plugin - begin - print_info("Generating #{lang} code for #{plugin.name} ...\n") - generate_plugin(io, plugin) - if i < (plugins.size - 1) - io.print("\n") - end - rescue - print_error("#{$!}\n") - $!.backtrace.each { |l| print_error(l + "\n") } - print_error("Omitting #{plugin.path}\n") - end - end - } - io.print("\n") - end - def generate_plugin(io, plugin) - raise "#{self.class}::generate_plugin() missing!" - end - end - - module Language - IDENTIFIER_REGEXP = /^[a-z][a-zA-Z0-9_]*[a-zA-Z0-9]?$/ - def make_identifier(name) - # gentle identifier massage - # remove quotes - name = name.sub(/^"([^"]*)"/, "\\1") - # replace invalid chars with underscores - name = name.downcase.gsub(/[^a-zA-Z0-9_]/, "_") - # reduce multiple underscores to one - name = name.gsub(/__+/, "_") - # remove leading/terminating underscores - name = name.sub(/(^_|_$)/, "") - # move leading digits to the end - name = name.sub(/^([0-9]+)_/, "") + ("_#{$1}" if $1).to_s - # if digit(s) only, prepend alpha prefix - if (name[0..0] =~ /^[0-9]/) - pname = name - name = "w" + name - print_info("Widget label `#{pname}' prefixed to become `#{name}'\n") - end - unless name =~ IDENTIFIER_REGEXP - raise "invalid identifier: \"#{name}\"" - end - name - end - def make_unique(list) - # bad, bad, bad - list = list.clone - res = [] - ids = {} - while hd = list.shift - if ids.has_key?(hd) - ids[hd] = id = ids[hd] + 1 - else - if list.include?(hd) - ids[hd] = id = 0 - end - end - res << (id ? "#{hd}_#{id}" : hd) - end - res - end - module_function :make_identifier, :make_unique - end - - module Haskell - INDENT = " " * 4 - - def mk_function_names(plugin) - fname = plugin.name.decapitalize(true) - lname = fname + "'" - [lname, fname] - end - module_function :mk_function_names - - class PluginGenerator - attr_reader :plugin - def initialize(plugin) - @plugin = plugin - end - def generate(io) - lname, fname = Haskell.mk_function_names(plugin) - gen_curry_func(io, lname, fname) - io << "\n" - gen_list_func(io, lname) - end - def gen_ugen(io, name, rate, inputs, num_outputs) - io << "UGen.mkUGen #{rate} \"#{name}\" #{inputs} (replicate #{num_outputs} #{rate}) (UGen.Special 0) Nothing" - io << "\n" - end - def gen_curry_func(io, lname, fname) - args = mk_args(plugin.total_inputs) - decs = args.collect { "UGen" } + ["UGen"] # add result type - io << "#{fname} :: #{decs.join(" -> ")}\n" - io << "#{fname} #{args.join(" ")} = #{lname} [#{args.join(',')}]\n" - end - def gen_list_func(io, fname) - io << "#{fname} :: [UGen] -> UGen\n" - io << "#{fname} args = " - gen_ugen(io, plugin.name, "UGen.AR", "args", plugin.outputs) - end - - protected - def mk_args(n, x="x") - (1..n).collect { |i| "#{x}#{i}" } - end - end # PluginGenerator - - class Generator < Faust::Generator - def initialize(plugins, options) - super(plugins, options) - @module = options["prefix"] - end - def lang - "haskell" - end - def generate_header(io) - gen_module(io, plugins.collect { |p| Haskell.mk_function_names(p) }.flatten1) - io << "\n" - gen_imports(io) - io << "\n" - end - def generate_plugin(io, plugin) - PluginGenerator.new(plugin).generate(io) - end - def gen_module(io, exports) - #m = @module.empty? ? "" : @module + "." - #io << "module #{m}#{plugin.name.encapitalize} (\n" - io << "module #{@module.empty? ? "Main" : @module} (\n" - io << exports.collect { |x| (INDENT * 1) + x }.join(",\n") << "\n" - io << ") where\n" - end - def gen_imports(io) - io << "import Sound.SC3.UGen (UGen)\n" - io << "import qualified Sound.SC3.UGen as UGen\n" - end - end - end - - module SC3 - include Language - - CLASS_REGEXP = /^[A-Z][a-zA-Z0-9_]*[a-zA-Z0-9]?$/ - - def path_to_unitname(path) - name = File.basename(path) - if ext_index = name.index(".") - name = name[0..ext_index-1] - end - name - end - def make_class_name(unit_name, prefix) - # The SuperCollider UGen class name generated here must match - # that generated in the Faust architecture file supercollider.cpp - if prefix == "" - prefix = "Faust" - end - class_name = unit_name - class_name = class_name.sub(/^#{prefix}/i, '') - class_name = prefix + '_' + class_name - cna = class_name.split(/[^0-9a-z]/i) - cna.each_index { |i| cna[i] = cna[i].encapitalize } # SC name convention - class_name = cna.join - print_info(unit_name + " -> " + class_name + "\n") - if class_name.length > 31 # maximum SC Node ID length is 31 (hashed) - print_info("Class name `#{class_name}' truncated to 1st 31 chars\n") - class_name = class_name[0,30] - end - unless class_name =~ CLASS_REGEXP - raise "invalid class name: \"#{class_name}\"" - end - class_name - end - module_function :path_to_unitname, :make_class_name - - class Faust::Widget - def sc3_identifier - l = self.label - l = l.gsub(/\([^\)]*\)/, '') - l = Language.make_identifier(l) - if l.length > 30 # maximum SC ParamSpec length is 31 (hashed) - lt = l[0,29] # leave a final char for make_unique - print_info("Label `#{l}' for #{self.type} #{self.id} truncated to " + - "`#{lt}' (30 chars)\n") - l = lt - end - l - end - def sc3_arg_string - "#{self.sc3_identifier}(#{self.init})" - end - def sc3_default - "(#{self.init})" - end - def sc3_default2 - "=(#{self.init})" # Parens make unary minus work in SC - end - end - - class PluginGenerator - attr_reader :unit_name, :class_name - def initialize(plugin, options) - @plugin = plugin - @options = options - @unit_name = plugin.name || SC3::path_to_unitname(plugin.path) - @class_name = SC3::make_class_name(@unit_name, options["prefix"]) - end - def inputs - @plugin.inputs - end - def outputs - @plugin.outputs - end - def superclass_name - @plugin.outputs > 1 ? "MultiOutUGen" : "UGen" - end - def input_names - (1..self.inputs).collect { |i| "in#{i}" } - end - def control_names - Language::make_unique(@plugin.ui.active_widgets.collect { |x| x.sc3_identifier }) - end - def decl_args # "name(default)" - cnames = self.control_names - cdefaults = @plugin.ui.active_widgets.collect { |x| x.sc3_default } - args = self.input_names + cnames.zip(cdefaults).collect { |ary| ary[0] + ary[1] } - args.empty? ? "" : " | " + args.join(", ") + " |" - end - def decl_args2 # "name=default" - cnames = self.control_names - cdefaults = @plugin.ui.active_widgets.collect { |x| x.sc3_default2 } - args = self.input_names + cnames.zip(cdefaults).collect { |ary| ary[0] + ary[1] } - args.empty? ? "" : args.join(", ") + ";" - end - def decl_args3 # args for SynthDef - cnames = self.control_names - cdefaults = @plugin.ui.active_widgets.collect { |x| x.sc3_default } - args = self.input_names.collect { |x| "\\#{x}.ar(0)" } \ - + cnames.zip(cdefaults).collect { |x| "\\#{x[0]}.kr#{x[1]}" } - args.empty? ? "" : args.join(", ") - end - def decl_buses # declare input buses for Synth - args = self.input_names.collect { |x| \ - " #{x}Bus = Bus.audio(s,1);\n"}; - end - def decl_args4 # args for Synth - cnames = self.control_names - cdefaults = @plugin.ui.active_widgets.collect { |x| x.sc3_default } - args = self.input_names.collect { |x| "#{x}:#{x}Bus.asMap"} \ - + cnames.collect { |x| "#{x}:#{x}Var" } - args.empty? ? "" : "\n\t[\t" + args.join(",\n\t\t") + "\n\t]" - end - def decl_metadata - cnames = self.control_names - cnames.collect! {|x| " \\"+x+":"} - warp = 0 - cmeta = @plugin.ui.active_widgets.collect \ - { |x| "[#{x.min}, #{x.max}, #{warp}, #{x.step}, #{x.init}].asSpec" } - # \\name1: [minval, maxval, warp, step, default, units].asSpec, - # Note: could append x.units as well - args = cnames.zip(cmeta).collect { |ary| ary[0] + ary[1] } - args.empty? ? "" : args.join(",\n ") - end - def new_args(rate) - ["'%s'" % rate] + self.input_names + self.control_names - end - def validate - args = self.input_names + self.control_names - unless args.uniq == args - raise "argument list not unique" - end - self - end - def generate(io) - self.validate - generate_decl(io) - generate_body(io) - end - def generate_decl(io) - io.print("#{@class_name} : #{self.superclass_name}\n") - print_info("UGen: #{@class_name}\n") - end - def generate_body(io) - io.print("{\n") - body = < 0 - body = body + < 1 # add initOutputs to overridden init: - io.print <0 - io.print "#{decl_buses}\n"; - end - io.print < Faust::SC3::Generator, - "haskell" => Faust::Haskell::Generator -} - -lang = "sclang" -generator = nil -output_file = nil - -options = { - "prefix" => "", - "synthdef" => false -} - -opts.each { | opt, arg | - case opt - when "--help" - usage - exit(0) - when "--lang" - lang = arg - when "--output" - output_file = arg - when "--prefix" - options["prefix"] = arg - options["module"] = arg - when "--synthdef" - options["synthdef"] = true - when "--version" - puts "#{PROGRAM} #{PROGRAM_VERSION}" - exit(0) - end -} - -if LANG_MAP.key?(lang) - generator = LANG_MAP[lang] -else - print_error("unknown output language #{lang}\n") - exit(1) -end - -if output_file - output = File.open(output_file, "w") -else - output = $stdout -end - -plugins = ARGV.collect { |file| - begin - print_info("Parsing #{file} ...\n") - Faust::Plugin.from_file(file) - rescue - print_error("#{$!}\n") - print_error("Omitting #{file}\n") - end -} - -begin - generator.new(plugins, options).generate(output) -ensure - output.close unless output === $stdout -end - -# EOF