diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dtas/cue_index.rb | 39 | ||||
-rw-r--r-- | lib/dtas/player.rb | 2 | ||||
-rw-r--r-- | lib/dtas/player/client_handler.rb | 79 | ||||
-rw-r--r-- | lib/dtas/source/file.rb | 29 |
4 files changed, 142 insertions, 7 deletions
diff --git a/lib/dtas/cue_index.rb b/lib/dtas/cue_index.rb new file mode 100644 index 0000000..09c3709 --- /dev/null +++ b/lib/dtas/cue_index.rb @@ -0,0 +1,39 @@ +# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors +# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) +require_relative '../dtas' +class DTAS::CueIndex + attr_reader :offset + attr_reader :index + + def initialize(index, offset) + @index = index.to_i + + # must be compatible with the sox "trim" effect + @offset = offset # "#{INTEGER}s" (samples) or HH:MM:SS:FRAC + end + + def to_hash + { "index" => @index, "offset" => @offset } + end + + def offset_samples(format) + case @offset + when /\A(\d+)s\z/ + $1.to_i + else + format.hhmmss_to_samples(@offset) + end + end + + def pregap? + @index == 0 + end + + def track? + @index == 1 + end + + def subindex? + @index > 1 + end +end diff --git a/lib/dtas/player.rb b/lib/dtas/player.rb index 7567dfc..efefcbf 100644 --- a/lib/dtas/player.rb +++ b/lib/dtas/player.rb @@ -206,6 +206,8 @@ class DTAS::Player # :nodoc: io.emit("OK") when "rg" rg_handler(io, msg) + when "cue" + cue_handler(io, msg) when "skip" skip_handler(io, msg) when "sink" diff --git a/lib/dtas/player/client_handler.rb b/lib/dtas/player/client_handler.rb index 91dfada..2f356ab 100644 --- a/lib/dtas/player/client_handler.rb +++ b/lib/dtas/player/client_handler.rb @@ -351,6 +351,16 @@ module DTAS::Player::ClientHandler # :nodoc: @paused ? do_play : do_pause end + def seek_internal(cur, offset) + if cur.requeued + @queue[0][1] = offset + else + @queue.unshift([ cur.infile, offset ]) + cur.requeued = true + __buf_reset(cur.dst) # trigger EPIPE + end + end + def do_seek(io, offset) if @current if @current.respond_to?(:infile) @@ -364,13 +374,7 @@ module DTAS::Player::ClientHandler # :nodoc: rescue ArgumentError return io.emit("ERR bad time format") end - if @current.requeued - @queue[0][1] = offset - else - @queue.unshift([ @current.infile, offset ]) - @current.requeued = true - __buf_reset(@current.dst) # trigger EPIPE - end + seek_internal(@current, offset) else return io.emit("ERR unseekable") end @@ -607,5 +611,66 @@ module DTAS::Player::ClientHandler # :nodoc: io.emit("OK") end end + + def __bp_prev_next(io, msg, cur, bp) + case type = msg[1] + when nil, "track" + bp.keep_if { |ci| ci.track? } + when "pregap" + bp.keep_if { |ci| ci.pregap? } + when "subindex" # any subindex + bp.keep_if { |ci| ci.subindex? } + when /\A\d+\z/ # exact subindex match + si = type.to_i + bp.keep_if { |ci| ci.index == si } + when "any" # anything goes + else + return io.emit("INVALID TYPE") + end + fmt = cur.format + case msg[0] + when "next" + ds = __current_decoded_samples + bp.each do |ci| + next if ci.offset_samples(fmt) < ds + seek_internal(cur, ci.offset) + return io.emit("OK") + end + # go to the next (real) track if not found + __current_drop + when "prev" + os = cur.offset_samples # where we currently started + bp.reverse_each do |ci| + next if ci.offset_samples(fmt) >= os + seek_internal(cur, ci.offset) + return io.emit("OK") + end + # offset may be nil/zero if we couldn't find a previous breakpoint + seek_internal(cur, '0') + end + io.emit("OK") + end + + def cue_handler(io, msg) + cur = @current + if cur.respond_to?(:cuebreakpoints) + offset = nil + bp = cur.cuebreakpoints + case cmd = msg[0] + when nil + tmp = { "infile" => cur.infile, "cue" => bp.map { |ci| ci.to_hash } } + 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") + end + else + io.emit("NOCUE") + end + end end # :startdoc: diff --git a/lib/dtas/source/file.rb b/lib/dtas/source/file.rb index 3dadc67..8657b0c 100644 --- a/lib/dtas/source/file.rb +++ b/lib/dtas/source/file.rb @@ -5,6 +5,7 @@ require_relative '../source' require_relative '../command' require_relative '../format' require_relative '../process' +require_relative '../cue_index' module DTAS::Source::File # :nodoc: attr_reader :infile @@ -33,6 +34,7 @@ module DTAS::Source::File # :nodoc: @offset = offset @comments = nil @samples = nil + @cuebp = nil @rg = nil end @@ -90,4 +92,31 @@ module DTAS::Source::File # :nodoc: defaults = source_defaults # see dtas/source/{av,sox}.rb to_source_cat.delete_if { |k,v| v == defaults[k] } end + + def cuebreakpoints + rv = @cuebp and return rv + rv = [] + begin + str = qx(@env, %W(metaflac --export-cuesheet-to=- #@infile)) + rescue + return rv + end + str.scan(/^ INDEX (\d+) (\S+)/) do |m| + index = m[0] + time = m[1].dup + case time + when /\A\d+\z/ + time << "s" # sample count (flac 1.3.0) + else # HH:MM:SS:FF + # FF/75 CDDA frames per second, convert to fractional seconds + time.sub!(/:(\d+)\z/, "") + frames = $1.to_f + if frames > 0 + time = sprintf("#{time}.%0.6g", frames / 75.0) + end + end + rv << DTAS::CueIndex.new(index, time) + end + @cuebp = rv + end end |