diff options
author | Eric Wong <normalperson@yhbt.net> | 2013-09-30 03:26:00 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2013-09-30 03:38:39 +0000 |
commit | 571d07e99bc0051ad51e1f5947a0ad28eefb557f (patch) | |
tree | 60fd5e17a10c884ded34c41e6748e493b5ed0311 /lib/dtas/player/client_handler.rb | |
parent | 1a0a4247b8031ec85536f547d2a66fa031586723 (diff) | |
download | dtas-571d07e99bc0051ad51e1f5947a0ad28eefb557f.tar.gz |
This adds the ability to seek internally within FLAC file based on the internal CUE sheet. Other formats may be supported in the future, but FLAC is the only one I know of which supports embedded cue sheets. Note: flac 1.3.0 is recommended for users of non-CDDA-compatible formats. See updates to dtas-player_protocol(7) for details.
Diffstat (limited to 'lib/dtas/player/client_handler.rb')
-rw-r--r-- | lib/dtas/player/client_handler.rb | 79 |
1 files changed, 72 insertions, 7 deletions
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: |