From 6c025858196b348c83ddd50e9233eb85c86c71d6 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 27 Dec 2014 08:01:13 +0000 Subject: player: support playing splitfx YAML files This allows splitfx users to test CUE breakpoints and run file-specific effects without interrupting their normal flow. --- examples/splitfx.sample.yml | 8 ++++ lib/dtas/player.rb | 4 +- lib/dtas/source/splitfx.rb | 92 +++++++++++++++++++++++++++++++++++++++++ lib/dtas/splitfx.rb | 8 ++++ test/test_player_integration.rb | 6 +-- 5 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 lib/dtas/source/splitfx.rb diff --git a/examples/splitfx.sample.yml b/examples/splitfx.sample.yml index 297e50b..3826012 100644 --- a/examples/splitfx.sample.yml +++ b/examples/splitfx.sample.yml @@ -11,6 +11,14 @@ comments: ARTIST: John Smith ALBUM: Hello World YEAR: 2013 +# override the normal sox command for dtas-player playback: +command: exec sox "$INFILE" $SOXFMT - $TRIMFX $RGFX $FX +env: + # these effects may be used in any command in this file, including targets + FX: + highpass -1 120 highpass 40 highpass 40 + ladspa -lr tap_limiter -10 9.5 + stats track_start: 1 # 0 for pregap/intro tracks cdda_align: true tracks: diff --git a/lib/dtas/player.rb b/lib/dtas/player.rb index 39e5abf..0ae8cef 100644 --- a/lib/dtas/player.rb +++ b/lib/dtas/player.rb @@ -8,6 +8,7 @@ require_relative 'source' require_relative 'source/sox' require_relative 'source/av' require_relative 'source/ff' +require_relative 'source/splitfx' require_relative 'source/cmd' require_relative 'sink' require_relative 'unix_server' @@ -44,9 +45,10 @@ class DTAS::Player # :nodoc: @current = nil @watchers = {} @source_map = { - "sox" => DTAS::Source::Sox.new, + "sox" => (sox = DTAS::Source::Sox.new), "av" => DTAS::Source::Av.new, "ff" => DTAS::Source::Ff.new, + "splitfx" => DTAS::Source::SplitFX.new(sox), } source_map_reload end diff --git a/lib/dtas/source/splitfx.rb b/lib/dtas/source/splitfx.rb new file mode 100644 index 0000000..ad9e7c1 --- /dev/null +++ b/lib/dtas/source/splitfx.rb @@ -0,0 +1,92 @@ +# Copyright (C) 2014, all contributors +# License: GPLv3 or later +require 'yaml' +require_relative 'sox' +require_relative '../splitfx' + +class DTAS::Source::SplitFX < DTAS::Source::Sox # :nodoc: + MAX_YAML_SIZE = 512 * 1024 + attr_writer :sox + + SPLITFX_DEFAULTS = SOX_DEFAULTS.merge("tryorder" => 3) + + def initialize(sox = DTAS::Source::Sox.new) + command_init(SPLITFX_DEFAULTS) + @sox = sox + end + + def try(ymlfile, offset = nil) + @splitfx = @ymlhash = nil + st = File.stat(ymlfile) + return false if !st.file? || st.size > MAX_YAML_SIZE + + # read 4 bytes first to ensure we have a YAML file with a hash: + buf = "" + File.open(ymlfile, "rb") do |fp| + return false if fp.read(4, buf) != "---\n" + buf << fp.read + end + + sfx = DTAS::SplitFX.new + begin + Dir.chdir(File.dirname(ymlfile)) do # ugh + sfx.import(@ymlhash = YAML.load(buf)) + sfx.infile.replace(File.expand_path(sfx.infile)) + end + @splitfx = sfx + rescue + return false + end + @infile = ymlfile + sox = @sox.try(sfx.infile, offset) or return false + rv = source_file_dup(ymlfile, offset) + rv.sox = sox + rv.env = sfx.env + rv + end + + def __load_comments + @ymlhash["comments"] || @sox.__load_comments + end + + def command_string + @ymlhash["command"] || @sox.command_string + end + + def spawn(player_format, rg_state, opts) + raise "BUG: #{self.inspect}#spawn called twice" if @to_io + e = @env.merge!(player_format.to_env) + e["INFILE"] = @sox.infile + + # make sure these are visible to the "current" command... + e["TRIMFX"] = @offset ? "trim #@offset" : nil + e["RGFX"] = rg_state.effect(self) || nil + e.merge!(@rg.to_env) if @rg + + @pid = dtas_spawn(e, command_string, opts) + end + + def to_hsh + to_hash.delete_if { |k,v| v == SPLITFX_DEFAULTS[k] } + end + + def format + @sox.format + end + + def samples! + @sox.samples! + end + + def samples + @sox.samples + end + + def source_defaults + SPLITFX_DEFAULTS + end + + def cuebreakpoints + @splitfx.cuebreakpoints + end +end diff --git a/lib/dtas/splitfx.rb b/lib/dtas/splitfx.rb index ac39233..9af3faf 100644 --- a/lib/dtas/splitfx.rb +++ b/lib/dtas/splitfx.rb @@ -12,6 +12,7 @@ class DTAS::SplitFX # :nodoc: '$TRIMFX $RATEFX $DITHERFX' include DTAS::Process include DTAS::XS + attr_reader :infile, :env class Skip < Struct.new(:tstart) # :nodoc: def commit(_) @@ -76,6 +77,7 @@ class DTAS::SplitFX # :nodoc: } @tracks = [] @infmt = nil # wait until input is assigned + @cuebp = nil # for playback end def _bool(hash, key) @@ -334,4 +336,10 @@ class DTAS::SplitFX # :nodoc: end false end + + def cuebreakpoints + rv = @cuebp and return rv + require_relative 'cue_index' + @cuebp = @tracks.map { |t| DTAS::CueIndex.new(1, "#{t.tstart}s") } + end end diff --git a/test/test_player_integration.rb b/test/test_player_integration.rb index 66f599e..2525ac5 100644 --- a/test/test_player_integration.rb +++ b/test/test_player_integration.rb @@ -208,11 +208,11 @@ class TestPlayerIntegration < Testcase def test_source_ed s = client_socket - assert_equal "sox av ff", s.req("source ls") + assert_equal "sox av ff splitfx", s.req("source ls") s.req_ok("source ed av tryorder=-1") - assert_equal "av sox ff", s.req("source ls") + assert_equal "av sox ff splitfx", s.req("source ls") s.req_ok("source ed av tryorder=") - assert_equal "sox av ff", s.req("source ls") + assert_equal "sox av ff splitfx", s.req("source ls") s.req_ok("source ed sox command=true") sox = YAML.load(s.req("source cat sox")) -- cgit v1.2.3-24-ge0c7