From 7f107e8ce965bd58c8da0631cb1e5cf594b24835 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 24 Apr 2016 02:37:15 +0000 Subject: player: extra "cue" seeking functionality 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). --- Documentation/dtas-player_protocol.pod | 19 ++++++++-- lib/dtas/player/client_handler.rb | 66 ++++++++++++++++++++++++++++++---- 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||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") -- cgit v1.2.3-24-ge0c7