* [PATCH 1/3] introduce epoch for scheduling tfx bits
@ 2015-07-05 2:21 Eric Wong
2015-07-05 2:21 ` [PATCH 2/3] parse_time: pass through floats and integers Eric Wong
2015-07-05 2:21 ` [PATCH 3/3] huge revamp Eric Wong
0 siblings, 2 replies; 3+ messages in thread
From: Eric Wong @ 2015-07-05 2:21 UTC (permalink / raw)
To: spew
From: Eric Wong <normalperson@yhbt.net>
This will end up being part of an editing pipeline between
multiple processes.
---
lib/dtas/epoch.rb | 29 +++++++++++++++++++++++++++++
lib/dtas/tfx.rb | 24 ++++++++++++++----------
2 files changed, 43 insertions(+), 10 deletions(-)
create mode 100644 lib/dtas/epoch.rb
diff --git a/lib/dtas/epoch.rb b/lib/dtas/epoch.rb
new file mode 100644
index 0000000..f6f385c
--- /dev/null
+++ b/lib/dtas/epoch.rb
@@ -0,0 +1,29 @@
+# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative 'buffer'
+=begin
+ tbeg, tlen = 5, 20
+ DTAS::TFX.expand(all_tfx, 100 * 48000).each do |ary|
+ DTAS::Epoch.new(ary, tbeg)
+ end
+=end
+class DTAS::Epoch
+ attr_reader :soxin, :soxout
+
+ def initialize(tfx_ary, tbeg = 0) # array from DTAS::TFX.expand
+ @tbeg = tbeg
+ @tfx_ary = tfx_ary
+ @soxin = DTAS::Buffer.new
+ @passed_bytes = 0
+ end
+
+ def epoch_run(soxout, format)
+ @tfx_ary.each do |tfx|
+ next if tfx.tend < @tbeg
+ @soxin.broadcast(tfx.inputs)
+ soxin
+ @soxin.broadcast(soxout)
+ tfx.tbeg
+ end
+ end
+end
diff --git a/lib/dtas/tfx.rb b/lib/dtas/tfx.rb
index 479ff5c..607d26b 100644
--- a/lib/dtas/tfx.rb
+++ b/lib/dtas/tfx.rb
@@ -10,8 +10,8 @@ require 'shellwords'
class DTAS::TFX
include DTAS::ParseTime
- attr_reader :tbeg
- attr_reader :tlen
+ attr_reader :tbeg # samples
+ attr_reader :tlen # samples
attr_reader :cmd
def initialize(args, format = DTAS::Format.new)
@@ -67,21 +67,25 @@ class DTAS::TFX
# 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)
+ beg = 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
+ len = args.shift
+ is_stop_time = len.sub!(/\A=/, "") ? true : false
+ len = parse_time(len)
+ len = len - beg if is_stop_time
+ @tlen = (len * @format.rate).round
else
@tlen = nil
end
- @tbeg = (tbeg * @format.rate).round
+ @tbeg = (beg * @format.rate).round
end
def <=>(other)
- tbeg <=> other.tbeg
+ @tbeg <=> other.tbeg
+ end
+
+ def tend
+ @tbeg + @tlen
end
# for stable sorting
--
EW
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/3] parse_time: pass through floats and integers
2015-07-05 2:21 [PATCH 1/3] introduce epoch for scheduling tfx bits Eric Wong
@ 2015-07-05 2:21 ` Eric Wong
2015-07-05 2:21 ` [PATCH 3/3] huge revamp Eric Wong
1 sibling, 0 replies; 3+ messages in thread
From: Eric Wong @ 2015-07-05 2:21 UTC (permalink / raw)
To: spew
This makes it easier to use in a user-friendly scripting interface
---
lib/dtas/parse_time.rb | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/dtas/parse_time.rb b/lib/dtas/parse_time.rb
index 7dd41c6..aa4ac0d 100644
--- a/lib/dtas/parse_time.rb
+++ b/lib/dtas/parse_time.rb
@@ -3,9 +3,11 @@
require_relative '../dtas'
module DTAS::ParseTime
- # convert a string time to seconds, returning a Flot or Integer
+ # convert a string time to seconds, returning a Floot or Integer
def parse_time(time)
case time
+ when Float, Integer
+ time
when /\A\d+\z/
time.to_i
when /\A[\d\.]+\z/
--
EW
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 3/3] huge revamp
2015-07-05 2:21 [PATCH 1/3] introduce epoch for scheduling tfx bits Eric Wong
2015-07-05 2:21 ` [PATCH 2/3] parse_time: pass through floats and integers Eric Wong
@ 2015-07-05 2:21 ` Eric Wong
1 sibling, 0 replies; 3+ messages in thread
From: Eric Wong @ 2015-07-05 2:21 UTC (permalink / raw)
To: spew
---
bin/dtas-script | 63 +++++++++++++++
examples/script.sample.rb | 65 +++++++++++++++
examples/tfx.sample.yml | 27 -------
lib/dtas/parse_freq.rb | 14 ++++
lib/dtas/script.rb | 201 ++++++++++++++++++++++++++++++++++++++++++++++
lib/dtas/tfx.rb | 72 ++++-------------
test/test_script.rb | 53 ++++++++++++
test/test_tfx.rb | 69 ++++------------
8 files changed, 424 insertions(+), 140 deletions(-)
create mode 100755 bin/dtas-script
create mode 100644 examples/script.sample.rb
delete mode 100644 examples/tfx.sample.yml
create mode 100644 lib/dtas/parse_freq.rb
create mode 100644 lib/dtas/script.rb
create mode 100644 test/test_script.rb
diff --git a/bin/dtas-script b/bin/dtas-script
new file mode 100755
index 0000000..05778cf
--- /dev/null
+++ b/bin/dtas-script
@@ -0,0 +1,63 @@
+#!/usr/bin/env ruby
+# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require 'dtas/script'
+require 'optionparser'
+$stdout.sync = $stderr.sync = false
+usage = "#$0 SCRIPT.rb [OPTIONS] [trim START [LENGTH]]"
+outfmt = {}
+out = $stdout
+dry_run = false
+OptionParser.new('', 24, ' ') do |op|
+ op.banner = usage
+ op.on('-p', '--sox-pipe', 'alias for `-t sox -') { outfmt[:type] = 'sox' }
+ op.on('-t', '--type TYPE') { |type| outfmt[:type] = type }
+ op.on('-r', '--rate RATE') { |rate|
+ include DTAS::ParseFreq
+ outfmt[:rate] = parse_freq(rate)
+ }
+ op.on('-c', '--channels CHANNELS', Integer) { |c| outfmt[:channels] = c }
+ op.on('-b', '--bits BITS', Integer) { |bits| outfmt[:bits] = bits }
+ op.on('-o', '--output FILE') do |file|
+ out = File.open(file, 'w')
+ out.sync = false
+ file =~ /\.(\w+)\z/ and outfmt[:type] ||= $1
+ end
+ op.on('-n', '--dry-run', 'only print commands, do not run them') do
+ dry_run = true
+ end
+ op.on('-h', '--help') do
+ puts(op.to_s)
+ exit
+ end
+ op.parse!(ARGV)
+end
+
+s = DTAS::Script.new
+script = ARGV.shift or abort usage
+
+# trim may be the first arg
+case arg = ARGV.shift
+when 'trim'
+ require 'dtas/parse_time'
+ include DTAS::ParseTime
+ tbeg = ARGV.shift or abort usage
+ tbeg = parse_time(tbeg)
+ case ARGV[0]
+ when nil # ok, play until the end
+ when /\A=?[\d\.:]+\z/
+ tlen = ARGV.shift
+ absolute = tlen.sub!(/\A=/, '') # 44:00 =44:55
+ tlen = parse_time(tlen)
+ tlen -= tbeg if absolute
+ else
+ abort usage
+ end
+end
+
+# ARGV now contains other effects we add at the end...
+
+s.instance_eval(File.read(script), script)
+format = s.format
+outfmt.each { |k, v| format.__send__("#{k}=", v) }
+s.emit(out, format, tbeg, tlen)
diff --git a/examples/script.sample.rb b/examples/script.sample.rb
new file mode 100644
index 0000000..2031938
--- /dev/null
+++ b/examples/script.sample.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/env dtas-script
+# To the extent possible under law, Eric Wong has waived all copyright and
+# related or neighboring rights to this example.
+# Note: be sure to update test/test_lang.rb if you change this,
+# test_lang.rb relies on this.
+
+# declare the primary backing file (which may be :null and a format for
+# a silent backing file)
+
+input 'foo.flac'
+
+# The above maps channel 1 and 2 of `omnis.flac' to channels 3 and 4 for
+# further processing. `:bypass' is a shortcut
+
+# instead of updating ENV directly, `env' is locally scoped
+env.update(SOX_OPTS: "#{ENV['SOX_OPTS']} -R")
+
+# rumble filter for the entire file
+at(0) { sox %w(highpass 20) }
+# at(0) { %w(sox highpass 20) } # just an array is fine, too
+
+# the following commands are equivalent, start the effect at
+# 52 seconds and have it last for 1 second:
+at(52..53) { sox %w(gain -6) }
+at(52, 1) { sh 'sox $SOXIN $SOXOUT $TRIMFX gain -6' }
+# TODO
+# at(52, 1) { sh 'sox', :SOXIN, :SOXOUT, :TRIMFX, 'gain', '-6' ) # safer
+
+# as are the following (for little endian machines)
+at(52, 1) { eca %w(-eadb:-6) }
+at(52, 1) {
+ sh 'sox $SOXIN $SOX2ECA $TRIMFX | ' \
+ 'ecasound $ECAFMT -i stdin -o stdout -eadb:-6 | ' \
+ 'sox $ECA2SOX - $SOXOUT'
+}
+
+# TODO
+at(55..60) do
+ # Linkwitz-Riley filters (highpass 400 highpass 400 lowpass 460 lowpass 460)
+ # to isolate a region and apply limiter to it.
+ freq(400..480) { sox %w(ladspa -lr tap_limiter -5 4) }
+
+ # use a LR lowpass at everything below 400 Hz
+ freq(-400) { sox %w(compand 6:0.01,0.05 6:-4,-6,0,-6) }
+
+ # ditto for anything above 4000Hz
+ freq(4000) { sox %w(compand 6:0.01,0.05 6:-4,-6,0,-6) }
+
+ # shortcuts also work
+ freq('4k') { sox %w(compand 6:0.01,0.05 6:-4,-6,0,-6) }
+end if false
+
+# Anything may be limited to a single or a group of channels
+ch(1) { sox %w(delay 0.001) }
+ch(1, 2) { sox %w(delay 0.001) }
+
+ch(1..3) { sox %w(delay 0.01) }
+
+# the final mix down into stereo
+at(0) do
+ sox 'remix 1v0.5,3v0.5 2v0.5,4v0.5'
+end
+
+# SOX2ECA='-tf32 -c$CHANNELS -r$RATE'
+# ECAFMT='-f32_le,$CHANNELS,$RATE
diff --git a/examples/tfx.sample.yml b/examples/tfx.sample.yml
deleted file mode 100644
index b8add0b..0000000
--- a/examples/tfx.sample.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-# To the extent possible under law, Eric Wong has waived all copyright and
-# related or neighboring rights to this example.
-# Note: be sure to update test/test_trimfx.rb if you change this,
-# test_trimfx.rb relies on this.
----
-infile: foo.flac
-env: !omap
- PATH: $PATH
- SOX_OPTS: $SOX_OPTS -R
- I2: second.flac
- I3: third.flac
-comments:
- ARTIST: John Smith
- ALBUM: Hello World
- YEAR: 2013
-track_start: 1
-effects:
-# the following commands are equivalent
-- trim 52 =53 sh sox $SOXIN $SOXOUT $TRIMFX gain -6
-- trim 52 1 sox gain -6 # shorthand
-
-# as are the following (for little endian machines)
-- trim 52 1 eca -eadb:-6
-- trim 52 1 sh sox $SOXIN $SOX2ECA $TRIMFX | ecasound $ECAFMT
- -i stdin -o stdout -eadb:-6 | sox $ECA2SOX - $SOXOUT
-# SOX2ECA='-tf32 -c$CHANNELS -r$RATE'
-# ECAFMT='-f32_le,$CHANNELS,$RATE
diff --git a/lib/dtas/parse_freq.rb b/lib/dtas/parse_freq.rb
new file mode 100644
index 0000000..b079eae
--- /dev/null
+++ b/lib/dtas/parse_freq.rb
@@ -0,0 +1,14 @@
+# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+module DTAS::ParseFreq
+ def parse_freq(val)
+ case val
+ when String
+ val = val.dup
+ mult = val.sub!(/k\z/, '') ? 1000 : 1
+ (val.to_f * mult).to_i
+ else
+ val.to_i
+ end
+ end
+end
diff --git a/lib/dtas/script.rb b/lib/dtas/script.rb
new file mode 100644
index 0000000..4446c66
--- /dev/null
+++ b/lib/dtas/script.rb
@@ -0,0 +1,201 @@
+# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+
+require_relative '../dtas'
+require_relative 'epoch'
+require_relative 'tfx'
+require_relative 'parse_time'
+require_relative 'parse_freq'
+
+class DTAS::Script
+ # env hash
+ attr_reader :env
+
+ # for dtas-script use only
+ def format
+ @format ||= make_format
+ end
+
+ # runs the effects described in the given block at the specified time
+ def at(tbeg, tlen = nil, fade: nil, &block)
+ case tbeg
+ when Range # seconds
+ tlen and err('at(start..end) takes only one Range')
+ r = tbeg
+ r.exclude_end? and err('at(start..end) cannot exclude end')
+
+ tbeg = parse_time(r.first)
+ tend = r.last
+ case tend
+ when String # account for '=POSITION'
+ tlen = parse_tlen(tend, tbeg, 'at(start..end)')
+ else
+ tlen = parse_time(tend) - tbeg
+ end
+ when Numeric # seconds
+ usage = 'at(START_TIME, [ END_POSITION])'
+ tlen = parse_tlen(tlen, tbeg, usage)
+ when String
+ if tbeg.include?(' ')
+ usage = 'at("START_POSITION END_POSITION")'
+ tlen and err(usage)
+ tmp = tbeg.split(/\s+/)
+ tmp.size == 2 or err(usage)
+
+ tbeg = parse_time(tmp[0])
+ tlen = parse_tlen(tmp[1], tbeg, usage)
+ else
+ usage = 'at("START_POSITION", "END_POSITION")'
+ tbeg = parse_time(tbeg)
+ tlen = parse_tlen(tlen, tbeg, usage)
+ end
+ else
+ err('at(start..end) OR at("START_POSITION END_POSITION")')
+ end
+
+ check_time_overlaps(tbeg, tlen)
+ continue(:at, tbeg, tlen, fade, block)
+ end # at
+
+ def sox(effects); cmd(:sox, effects); end
+ def eca(effects); cmd(:eca, effects); end
+ def sh(args); cmd(:sh, args); end
+
+ def ch(channel, *rest, &block)
+ channels = [ channel, *channels ].inject([]) do |m,c|
+ case c
+ when Integer
+ m << c
+ else # Range or Array
+ m.concat(Array(c))
+ end
+ end
+ err("ch(CHANNELS): no channels specified") unless channels
+ continue(:ch, channels, block)
+ end
+
+ # returns 'file'
+ def input(file, infmt = nil)
+ @inputs.empty? or err('only one input currently supported')
+ case file
+ when String
+ @inputs << file
+ infmt and err('format must not be specified with input file')
+ file
+ when Numeric
+ # input(length, rate: 48000, channels: 2)
+ infmt ||= {}
+ infmt[:rate] ||= 44100
+ infmt[:channels] ||= 2
+ unknown = infmt.keys - [ :rate, :channels ]
+ unknown.empty? or err("unknown keys in format: #{unknown.inspect}")
+
+ ret = [ length, infmt ]
+ @inputs << ret
+ ret
+ when Hash # TODO
+ # input 'cards.flac' => :bypass,
+ # 'omnis.flac' => { 1 => 3, 2 => 4 },
+ # The above maps channel 1 and 2 of `omnis.flac' to channels 3 and 4 for
+ # further processing. `:bypass' is a shortcut for {1 => 1, 2 => 2, ... }
+ else
+ err('input(file)')
+ end
+ end
+
+ def emit(io, outfmt, tbeg = 0, tlen = nil)
+ p [ io, ouitfmt ]
+ end
+
+private
+ include DTAS::ParseTime
+
+ def cmd(type, args)
+ tfx = nil
+ chan = nil # all channels
+ @scope.reverse_each do |s|
+ case s[0]
+ when :at
+ tbeg, tlen, fade = s[1], s[2], s[3]
+ tfx ||= [ type, tbeg, tlen, fade, args ]
+ break if chan
+ when :ch
+ chan ||= s[1]
+ break if tfx
+ end
+ end
+ tfx ||= [ type, 0, nil, nil, args ]
+ @tfx << [ chan, tfx ]
+ end
+
+ def continue(*args)
+ @scope << args
+ begin
+ return args.last.call
+ ensure
+ @scope.pop
+ end
+ end
+
+ def err(msg)
+ raise ArgumentError, "usage: #{msg}"
+ end
+
+ def parse_tlen(tlen, tbeg, usage)
+ case tlen
+ when String
+ absolute = tlen.sub!(/\A=/, '') # 44:00 =44:55
+ tlen = parse_time(tlen)
+ tlen -= tbeg if absolute
+ when nil, Numeric
+ # OK
+ else
+ err(usage)
+ end
+ tlen
+ end
+
+ def check_time_overlaps(tbeg, tlen)
+ @scope.each do |s|
+ next if s[0] != :at
+
+ tbeg < s[1] and
+ err("nested at scope starts before existing scope #{s[1]}")
+
+ if tlen && s[2]
+ tend = tbeg + tlen
+ send = s[1] + s[2]
+ tend > send and
+ err("nested at scope ends after existing scope #{s[2]}")
+ end
+ end
+ end
+
+ def make_format
+ input = @inputs[0]
+ case input
+ when String
+ DTAS::Format.from_file(env_s, input)
+ else # [ length, format_hash ]
+ # stringify keys
+ h = {}
+ input[1].each { |k,v| h[k.to_s.freeze] = v }
+ DTAS::Format.load(h)
+ end
+ end
+
+ def env_s
+ h = {}
+ @env.each { |k,v| h[k.to_s.freeze] = v }
+ h
+ end
+
+ def initialize
+ @scope = []
+ @at = []
+ @inputs = []
+ @tfx = []
+ @env = {}
+ @format = nil # lazy, wait for input
+ end
+end
diff --git a/lib/dtas/tfx.rb b/lib/dtas/tfx.rb
index 607d26b..f9870aa 100644
--- a/lib/dtas/tfx.rb
+++ b/lib/dtas/tfx.rb
@@ -1,42 +1,26 @@
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
# 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 # samples
attr_reader :tlen # samples
attr_reader :cmd
- def initialize(args, format = DTAS::Format.new)
- @format = format
- args = args.dup
- case args.shift
+ def initialize(type, tbeg, tlen, args = nil)
+ @type = type
+ @tbeg = tbeg
+ case type
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 = []
+ @tlen = tlen ? tlen - @tbeg : nil
+ raise ArgumentError, 'args must not be specified for pad' if args
+ when :sox, :eca, :sh
+ @tlen = tlen
+ @args = args
else
- raise ArgumentError, "unknown effect type: #{tmp}"
+ raise ArgumentError, "#{type.inspect} not understood"
end
end
@@ -52,34 +36,6 @@ class DTAS::TFX
@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)
- beg = parse_time(args.shift)
- if args[0] =~ /\A=?[\d\.]+\z/
- len = args.shift
- is_stop_time = len.sub!(/\A=/, "") ? true : false
- len = parse_time(len)
- len = len - beg if is_stop_time
- @tlen = (len * @format.rate).round
- else
- @tlen = nil
- end
- @tbeg = (beg * @format.rate).round
- end
-
def <=>(other)
@tbeg <=> other.tbeg
end
@@ -145,7 +101,7 @@ class DTAS::TFX
# 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)
+ def self.expand(ary, total_samples = nil)
rv = []
schedule(ary).each_with_index do |sary, epoch|
tip = 0
@@ -153,14 +109,14 @@ class DTAS::TFX
while tfx = sary.shift
if tfx.tbeg > tip
# fill in the previous gap
- nfx = new([:pad, tip, tfx.tbeg])
+ 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])
+ if total_samples && tip < total_samples # fill until the last chunk
+ nfx = new(:pad, tip, total_samples)
dst << nfx
end
end
diff --git a/test/test_script.rb b/test/test_script.rb
new file mode 100644
index 0000000..ab225e2
--- /dev/null
+++ b/test/test_script.rb
@@ -0,0 +1,53 @@
+# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative 'helper'
+require 'dtas/script'
+
+class TestScript < Testcase
+ def setup
+ @s = DTAS::Script.new
+ end
+
+ def test_ch
+ t = self
+ @s.instance_eval do
+ ch(1) { sox %w(gain -1) }
+ end
+ tfx = @s.instance_variable_get(:@tfx)
+ assert_equal [[[1], [:sox, 0, nil, nil, %w(gain -1)]]], tfx
+
+ @s.instance_eval do
+ at(6..66) do
+ ch(2) { sox %w(gain -9) }
+ end
+ end
+
+ assert_equal 2, tfx.size
+ assert_equal [[2], [:sox, 6, 60, nil, %w(gain -9)]], tfx[1]
+ end
+
+ def test_at
+ check = inner = nil
+ t = self
+ @s.instance_eval do
+ at(5..6) do
+ t.assert_equal 1, t.scope.size
+ t.assert_equal [:at, 5, 1], t.scope[0][0..2]
+ check = 'ok'
+
+ at(5.5..5.7) do
+ t.assert_equal 2, t.scope.size
+ inner = 'ok'
+ end
+ end
+ t.assert_equal 0, t.scope.size
+ t.assert_equal 'ok', check, 'block called'
+ t.assert_equal 'ok', inner, 'inner block called'
+ end
+ assert_equal 0, scope.size
+ end
+
+ def scope
+ @s.instance_variable_get(:@scope)
+ end
+end
diff --git a/test/test_tfx.rb b/test/test_tfx.rb
index 59aa697..c261c5e 100644
--- a/test/test_tfx.rb
+++ b/test/test_tfx.rb
@@ -5,77 +5,36 @@ require 'dtas/tfx'
require 'dtas/format'
require 'yaml'
+# internal class for dtas/script
class TestTFX < Testcase
- def rate
- 44100
- end
-
- def test_example
- ex = YAML.load(File.read("examples/tfx.sample.yml"))
- effects = []
- ex["effects"].each do |line|
- words = Shellwords.split(line)
- case words[0]
- when "trim"
- tfx = DTAS::TFX.new(words)
- assert_equal 52 * rate, tfx.tbeg
- assert_equal rate, tfx.tlen
- effects << tfx
- end
- end
- assert_equal 4, effects.size
- end
-
- def test_all
- tfx = DTAS::TFX.new(%w(all))
- assert_equal 0, tfx.tbeg
- assert_nil tfx.tlen
- assert_equal [], tfx.to_sox_arg
- end
-
- def test_time
- tfx = DTAS::TFX.new(%w(trim 2:30 3.1))
- assert_equal 150 * rate, tfx.tbeg
- assert_equal((3.1 * rate).round, tfx.tlen)
- end
-
- def test_to_sox_arg
- tfx = DTAS::TFX.new(%w(trim 1 0.5))
- assert_equal %w(trim 44100s 22050s), tfx.to_sox_arg
-
- tfx = DTAS::TFX.new(%w(trim 1 sox vol -1dB))
- assert_equal %w(trim 44100s), tfx.to_sox_arg
- end
-
- def test_tfx_effects
- tfx = DTAS::TFX.new(%w(trim 1 sox vol -1dB))
- assert_equal %w(sox $SOXIN $SOXOUT $TRIMFX vol -1dB), tfx.cmd
+ def rate(ary)
+ ary.map { |x| x * 44100 }
end
def test_schedule_simple
fx = [
- DTAS::TFX.new(%w(trim 1 0.3)),
- DTAS::TFX.new(%w(trim 2 0.2)),
- DTAS::TFX.new(%w(trim 0.5 0.5)),
+ DTAS::TFX.new(:sox, 1, 0.3),
+ DTAS::TFX.new(:sox, 2, 0.2),
+ DTAS::TFX.new(:sox, 0.5, 0.5),
].shuffle
ary = DTAS::TFX.schedule(fx)
assert_operator 1, :==, ary.size
- assert_equal [ 22050, 44100, 88200 ], ary[0].map(&:tbeg)
- assert_equal [ 22050, 13230, 8820 ], ary[0].map(&:tlen)
+ assert_equal [ 22050, 44100, 88200 ], rate(ary[0].map(&:tbeg))
+ assert_equal [ 22050, 13230, 8820 ], rate(ary[0].map(&:tlen))
end
def test_schedule_overlaps
fx = [
- DTAS::TFX.new(%w(trim 1 0.3 sox)),
- DTAS::TFX.new(%w(trim 1.1 0.2 sox)),
- DTAS::TFX.new(%w(trim 0.5 0.5 sox)),
+ DTAS::TFX.new(:sox, 1, 0.3),
+ DTAS::TFX.new(:sox, 1.1, 0.2),
+ DTAS::TFX.new(:sox, 0.5, 0.5),
]
ary = DTAS::TFX.schedule(fx)
assert_equal 2, ary.size
- assert_equal [ 22050, 44100 ], ary[0].map(&:tbeg)
- assert_equal [ 48510 ], ary[1].map(&:tbeg)
+ assert_equal [ 22050, 44100 ], rate(ary[0].map(&:tbeg))
+ assert_equal [ 48510 ], rate(ary[1].map(&:tbeg)).map(&:round) # :x
- ex = DTAS::TFX.expand(fx, 10 * rate)
+ ex = DTAS::TFX.expand(fx, 10)
assert_equal 2, ex.size
assert_equal 0, ex[0][0].tbeg
assert_equal 3, ex[0].size
--
EW
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2015-07-05 2:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-05 2:21 [PATCH 1/3] introduce epoch for scheduling tfx bits Eric Wong
2015-07-05 2:21 ` [PATCH 2/3] parse_time: pass through floats and integers Eric Wong
2015-07-05 2:21 ` [PATCH 3/3] huge revamp Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).