about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2016-04-24 02:37:15 +0000
committerEric Wong <e@80x24.org>2016-04-24 02:37:15 +0000
commit7f107e8ce965bd58c8da0631cb1e5cf594b24835 (patch)
treed23ef8750e5785df9c858362b7ef30dc4b304976
parent80e62f416c6b7c7651b4af664592efe69ade729e (diff)
downloaddtas-7f107e8ce965bd58c8da0631cb1e5cf594b24835.tar.gz
These commands allow easier jumping within a track marked by
embedded CUE sheets.  I've found them helpful for tracking out
large recordings of multiple tracks (e.g. vinyl transfers or
live concert recordings).
-rw-r--r--Documentation/dtas-player_protocol.pod19
-rw-r--r--lib/dtas/player/client_handler.rb66
2 files changed, 77 insertions, 8 deletions
diff --git a/Documentation/dtas-player_protocol.pod b/Documentation/dtas-player_protocol.pod
index d0dd25e..be9f4ef 100644
--- a/Documentation/dtas-player_protocol.pod
+++ b/Documentation/dtas-player_protocol.pod
@@ -96,7 +96,7 @@ Commands here should be alphabetized according to `LC_ALL=C sort'
 
 PENDING: this may be renamed to "queue clear" or "queue-clear"
 
-=item cue - display the index/offsets of the embedded cue sheet
+=item cue - display the index/offsets of the embedded CUE sheet
 
 =item cue next - skip to the next cue sheet offset
 
@@ -109,11 +109,26 @@ This may just seek to the beginning
 if there is no embedded cue sheet or if we are playing the first
 (embedded) track.
 
-=item cue goto INTEGER - go to the cue index denoted by INTEGER
+=item cue goto INTEGER [TIMESTAMP] - go to the cue index denoted by INTEGER
 
 0 is first track as returned by "cue".
 Negative values of INTEGER allows selecting track relative to the last
 track (-1 is the last track, -2 is the penultimate, and so on).
+The optional TIMESTAMP allows starting the index track at a given
+point, negative values allow going to a set point before the given
+index.   In other words: "cue goto 1 -5" seeks to the last five seconds
+of the first track while "cue goto 1 5" seeks to the fifth second of
+the second track
+
+=item cue seek TIMESTAMP - seek within the current cue index
+
+Like the normal "seek" command, but this confines the seeking
+within the currently playing index.  As with "seek",
+"+" and "-" prefixes allow seeking relative to the currently
+playing position
+
+Using a "=-" prefix allows seeking to the previous track within
+the CUE sheet, relative to the currently playing track.
 
 =item current - output information about the current track/command in YAML
 
diff --git a/lib/dtas/player/client_handler.rb b/lib/dtas/player/client_handler.rb
index 8a15974..838abf7 100644
--- a/lib/dtas/player/client_handler.rb
+++ b/lib/dtas/player/client_handler.rb
@@ -186,6 +186,21 @@ module DTAS::Player::ClientHandler # :nodoc:
     bytes = bytes < 0 ? 0 : bytes # maybe negative in case of sink errors
   end
 
+  def __goto_offset_samples(offset)
+    if offset.sub!(/\A\+/, '')
+      __offset_to_samples(offset)
+    elsif offset.sub!(/\A-/, '')
+      __offset_to_samples(offset) * -1
+    else # ignore leading '=' for sox compat with 2nd "trim" arg
+      __offset_to_samples(offset)
+    end
+  end
+
+  def __offset_to_samples(offset)
+    offset.sub!(/s\z/, '') and return offset.to_i
+    @current.format.hhmmss_to_samples(offset)
+  end
+
   # returns seek offset as an Integer in sample count
   def __seek_offset_adj(dir, offset)
     if offset.sub!(/s\z/, '')
@@ -377,7 +392,7 @@ module DTAS::Player::ClientHandler # :nodoc:
   end
 
   def dpc_seek(io, msg)
-    offset = msg[0]
+    offset = msg[0] or return io.emit('ERR usage: seek OFFSET')
     if @current
       if @current.respond_to?(:infile)
         begin
@@ -730,6 +745,7 @@ module DTAS::Player::ClientHandler # :nodoc:
   end
 
   def __bp_prev_next(io, msg, cur, bp)
+    # msg = [ (prev|next) [, (track|pregap|subindex|<digit>|any) ]
     case type = msg[1]
     when nil, "track"
       bp.keep_if(&:track?)
@@ -772,6 +788,47 @@ module DTAS::Player::ClientHandler # :nodoc:
     io.emit("OK")
   end
 
+  def __bp_seek(io, msg, cur, bp)
+    offset = msg[1] or return io.emit('ERR usage: cue seek OFFSET')
+
+    # relative + offset work just like normal, non-CUE "seek"
+    offset =~ /\A[\+-]/ and return dpc_seek(io, [ offset ])
+
+    # "=-" is special, it means go before the current index start:
+    dir = offset.sub!(/\A=-/, '') ? -1 : 1
+
+    begin
+      offset = __offset_to_samples(offset)
+    rescue ArgumentError
+      return io.emit('ERR bad time format')
+    end
+
+    ds = __current_decoded_samples
+    fmt = cur.format
+    prev = 0
+    bp.each do |ci|
+      ci_offset = ci.offset_samples(fmt)
+      break if ci_offset >= ds
+      prev = ci_offset
+    end
+    offset = offset * dir + prev
+    seek_internal(cur, "#{offset < 0 ? 0 : offset}s")
+    io.emit('OK')
+  end
+
+  def __bp_goto(io, msg, cur, bp)
+    index = msg[1] or return io.emit('NOINDEX')
+    ci = bp[index.to_i] or return io.emit('BADINDEX')
+    if offset = msg[2]
+      fmt = cur.format
+      offset = "#{ci.offset_samples(fmt) + __goto_offset_samples(offset)}s"
+    else
+      offset = ci.offset
+    end
+    seek_internal(cur, offset)
+    io.emit('OK')
+  end
+
   def dpc_cue(io, msg)
     cur = @current
     if cur.respond_to?(:cuebreakpoints)
@@ -782,11 +839,8 @@ module DTAS::Player::ClientHandler # :nodoc:
         io.emit(tmp.to_yaml)
       when "next", "prev"
         return __bp_prev_next(io, msg, cur, bp)
-      when "goto"
-        index = msg[1] or return io.emit("NOINDEX")
-        ci = bp[index.to_i] or return io.emit("BADINDEX")
-        seek_internal(cur, ci.offset)
-        return io.emit("OK")
+      when 'seek' then return __bp_seek(io, msg, cur, bp)
+      when 'goto' then return __bp_goto(io, msg, cur, bp)
       end
     else
       io.emit("NOCUE")