From 9bf21db1e0ad815187b8c93ef985ef591a606645 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 20 Jan 2015 02:19:08 +0000 Subject: rename the TrimFX class to TFX TrimFX was too ambiguous with the common environment variable we use throughout dtas. Since TFX is more limited in scope but should be more frequently-typed by users (of -splitfx) we'll use the shorter name here. --- lib/dtas/tfx.rb | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dtas/trimfx.rb | 165 ----------------------------------------------------- 2 files changed, 165 insertions(+), 165 deletions(-) create mode 100644 lib/dtas/tfx.rb delete mode 100644 lib/dtas/trimfx.rb (limited to 'lib/dtas') diff --git a/lib/dtas/tfx.rb b/lib/dtas/tfx.rb new file mode 100644 index 0000000..479ff5c --- /dev/null +++ b/lib/dtas/tfx.rb @@ -0,0 +1,165 @@ +# Copyright (C) 2013-2015 all contributors +# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) +require_relative '../dtas' +require_relative 'parse_time' +require_relative 'format' +require 'shellwords' + +# this will represent a trim section inside -splitfx for applying +# effects to only a part of the output +class DTAS::TFX + include DTAS::ParseTime + + attr_reader :tbeg + attr_reader :tlen + attr_reader :cmd + + def initialize(args, format = DTAS::Format.new) + @format = format + args = args.dup + case args.shift + when :pad # [ :pad, start_time, end_time ] + @tbeg = args.shift + @tlen = args.shift - @tbeg + when "trim" + parse_trim!(args) + when "all" + @tbeg = 0 + @tlen = nil + else + raise ArgumentError, "#{args.inspect} not understood" + end + case tmp = args.shift + when "sh" then @cmd = args + when "sox" then tfx_sox(args) + when "eca" then tfx_eca(args) + when nil + @cmd = [] + else + raise ArgumentError, "unknown effect type: #{tmp}" + end + end + + def tfx_sox(args) + @cmd = %w(sox $SOXIN $SOXOUT $TRIMFX) + @cmd.concat(args) + end + + def tfx_eca(args) + @cmd = %w(sox $SOXIN $SOX2ECA $TRIMFX) + @cmd.concat(%w(| ecasound $ECAFMT -i stdin -o stdout)) + @cmd.concat(args) + @cmd.concat(%w(| sox $ECA2SOX - $SOXOUT)) + end + + def to_sox_arg + if @tbeg && @tlen + %W(trim #{@tbeg}s #{@tlen}s) + elsif @tbeg + return [] if @tbeg == 0 + %W(trim #{@tbeg}s) + else + [] + end + end + + # tries to interpret "trim" time args the same way the sox trim effect does + # This takes _time_ arguments only, not sample counts; + # otherwise, deviations from sox are considered bugs in dtas + def parse_trim!(args) + tbeg = parse_time(args.shift) + if args[0] =~ /\A=?[\d\.]+\z/ + tlen = args.shift + is_stop_time = tlen.sub!(/\A=/, "") ? true : false + tlen = parse_time(tlen) + tlen = tlen - tbeg if is_stop_time + @tlen = (tlen * @format.rate).round + else + @tlen = nil + end + @tbeg = (tbeg * @format.rate).round + end + + def <=>(other) + tbeg <=> other.tbeg + end + + # for stable sorting + class TFXSort < Struct.new(:tfx, :idx) + def <=>(other) + cmp = tfx <=> other.tfx + 0 == cmp ? idx <=> other.idx : cmp + end + end + + # sorts and converts an array of TFX objects into non-overlapping arrays + # of epochs + # + # input: + # [ tfx1, tfx2, tfx3, ... ] + # + # output: + # [ + # [ tfx1 ], # first epoch + # [ tfx2, tfx3 ], # second epoch + # ... + # ] + # There are multiple epochs only if ranges overlap, + # There is only one epoch if there are no overlaps + def self.schedule(ary) + sorted = [] + ary.each_with_index { |tfx, i| sorted << TFXSort[tfx, i] } + sorted.sort! + rv = [] + epoch = 0 + prev_end = 0 + defer = [] + + begin + while tfxsort = sorted.shift + tfx = tfxsort.tfx + if tfx.tbeg >= prev_end + # great, no overlap, append to the current epoch + prev_end = tfx.tbeg + tfx.tlen + (rv[epoch] ||= []) << tfx + else + # overlapping region, we'll need a new epoch + defer << tfxsort + end + end + + if defer[0] # do we need another epoch? + epoch += 1 + sorted = defer + defer = [] + prev_end = 0 + end + end while sorted[0] + + rv + end + + # like schedule, but fills in the gaps with pass-through (no-op) TFX objs + # This does not change the number of epochs. + def self.expand(ary, total_samples) + rv = [] + schedule(ary).each_with_index do |sary, epoch| + tip = 0 + dst = rv[epoch] = [] + while tfx = sary.shift + if tfx.tbeg > tip + # fill in the previous gap + nfx = new([:pad, tip, tfx.tbeg]) + dst << nfx + dst << tfx + tip = tfx.tbeg + tfx.tlen + end + end + if tip < total_samples # fill until the last chunk + nfx = new([:pad, tip, total_samples]) + dst << nfx + end + end + rv + end +end diff --git a/lib/dtas/trimfx.rb b/lib/dtas/trimfx.rb deleted file mode 100644 index 33b21ca..0000000 --- a/lib/dtas/trimfx.rb +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (C) 2013-2015 all contributors -# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) -require_relative '../dtas' -require_relative 'parse_time' -require_relative 'format' -require 'shellwords' - -# this will represent a trim section inside -splitfx for applying -# effects to only a part of the output -class DTAS::TrimFX - include DTAS::ParseTime - - attr_reader :tbeg - attr_reader :tlen - attr_reader :cmd - - def initialize(args, format = DTAS::Format.new) - @format = format - args = args.dup - case args.shift - when :pad # [ :pad, start_time, end_time ] - @tbeg = args.shift - @tlen = args.shift - @tbeg - when "trim" - parse_trim!(args) - when "all" - @tbeg = 0 - @tlen = nil - else - raise ArgumentError, "#{args.inspect} not understood" - end - case tmp = args.shift - when "sh" then @cmd = args - when "sox" then tfx_sox(args) - when "eca" then tfx_eca(args) - when nil - @cmd = [] - else - raise ArgumentError, "unknown effect type: #{tmp}" - end - end - - def tfx_sox(args) - @cmd = %w(sox $SOXIN $SOXOUT $TRIMFX) - @cmd.concat(args) - end - - def tfx_eca(args) - @cmd = %w(sox $SOXIN $SOX2ECA $TRIMFX) - @cmd.concat(%w(| ecasound $ECAFMT -i stdin -o stdout)) - @cmd.concat(args) - @cmd.concat(%w(| sox $ECA2SOX - $SOXOUT)) - end - - def to_sox_arg - if @tbeg && @tlen - %W(trim #{@tbeg}s #{@tlen}s) - elsif @tbeg - return [] if @tbeg == 0 - %W(trim #{@tbeg}s) - else - [] - end - end - - # tries to interpret "trim" time args the same way the sox trim effect does - # This takes _time_ arguments only, not sample counts; - # otherwise, deviations from sox are considered bugs in dtas - def parse_trim!(args) - tbeg = parse_time(args.shift) - if args[0] =~ /\A=?[\d\.]+\z/ - tlen = args.shift - is_stop_time = tlen.sub!(/\A=/, "") ? true : false - tlen = parse_time(tlen) - tlen = tlen - tbeg if is_stop_time - @tlen = (tlen * @format.rate).round - else - @tlen = nil - end - @tbeg = (tbeg * @format.rate).round - end - - def <=>(other) - tbeg <=> other.tbeg - end - - # for stable sorting - class TFXSort < Struct.new(:tfx, :idx) - def <=>(other) - cmp = tfx <=> other.tfx - 0 == cmp ? idx <=> other.idx : cmp - end - end - - # sorts and converts an array of TrimFX objects into non-overlapping arrays - # of epochs - # - # input: - # [ tfx1, tfx2, tfx3, ... ] - # - # output: - # [ - # [ tfx1 ], # first epoch - # [ tfx2, tfx3 ], # second epoch - # ... - # ] - # There are multiple epochs only if ranges overlap, - # There is only one epoch if there are no overlaps - def self.schedule(ary) - sorted = [] - ary.each_with_index { |tfx, i| sorted << TFXSort[tfx, i] } - sorted.sort! - rv = [] - epoch = 0 - prev_end = 0 - defer = [] - - begin - while tfxsort = sorted.shift - tfx = tfxsort.tfx - if tfx.tbeg >= prev_end - # great, no overlap, append to the current epoch - prev_end = tfx.tbeg + tfx.tlen - (rv[epoch] ||= []) << tfx - else - # overlapping region, we'll need a new epoch - defer << tfxsort - end - end - - if defer[0] # do we need another epoch? - epoch += 1 - sorted = defer - defer = [] - prev_end = 0 - end - end while sorted[0] - - rv - end - - # like schedule, but fills in the gaps with pass-through (no-op) TrimFX objs - # This does not change the number of epochs. - def self.expand(ary, total_samples) - rv = [] - schedule(ary).each_with_index do |sary, epoch| - tip = 0 - dst = rv[epoch] = [] - while tfx = sary.shift - if tfx.tbeg > tip - # fill in the previous gap - nfx = new([:pad, tip, tfx.tbeg]) - dst << nfx - dst << tfx - tip = tfx.tbeg + tfx.tlen - end - end - if tip < total_samples # fill until the last chunk - nfx = new([:pad, tip, total_samples]) - dst << nfx - end - end - rv - end -end -- cgit v1.2.3-24-ge0c7