172 lines
3.7 KiB
Ruby
Executable File
172 lines
3.7 KiB
Ruby
Executable File
require 'open3'
|
|
|
|
class MPG123Player
|
|
include Observable
|
|
attr_reader :error, :paused
|
|
attr_accessor :logger
|
|
|
|
def initialize()
|
|
@volume = 100
|
|
@muted = false
|
|
@inqueue = []
|
|
begin
|
|
@pin, @pout, @perr = Open3.popen3 "mpg123 --keep-open --remote"
|
|
Thread.new do mpg123read end
|
|
Thread.new do mpg123send end
|
|
changed
|
|
notify_observers :state => :inited
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
end
|
|
|
|
def play(track)
|
|
@last_track = track
|
|
unless track.nil? || track.is_a?(Hash) && track[:error]
|
|
mpg123puts "load #{track["mpg123url"]}"
|
|
@pstate = 2
|
|
end
|
|
changed
|
|
notify_observers :state => :load, :track => track
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def playing?
|
|
@pstate == 2 && !@paused
|
|
end
|
|
|
|
def pause()
|
|
@paused = !@paused
|
|
mpg123puts "pause"
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def stop()
|
|
mpg123puts "stop"
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def volume= (val)
|
|
@volume += val
|
|
@volume = [@volume, 100].min
|
|
@volume = [@volume, 0].max
|
|
mpg123puts "V #{@volume}"
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def volume()
|
|
@volume
|
|
end
|
|
|
|
def mute
|
|
@muted = !@muted
|
|
mpg123puts "V 0" if @muted
|
|
mpg123puts "V #{@volume}" unless @muted
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def muted?
|
|
@muted
|
|
end
|
|
|
|
def close()
|
|
@perr.close
|
|
@pout.close
|
|
@pin.close
|
|
changed
|
|
notify_observers :state => :closed
|
|
rescue => err
|
|
@error = err
|
|
changed
|
|
notify_observers :state => :error, :error => err
|
|
end
|
|
|
|
def mpg123puts(out)
|
|
@inqueue << out
|
|
@logger.debug {">> #{out}"} #
|
|
end
|
|
|
|
private
|
|
|
|
def mpg123send
|
|
while not @pin.closed?
|
|
begin
|
|
last_cmd = nil
|
|
@logger.debug "queue size #{@inqueue.size}"
|
|
@inqueue.select! do |cmd|
|
|
if cmd[0,4] == "load"
|
|
last_cmd = cmd
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
@inqueue << last_cmd unless last_cmd.nil?
|
|
while out = @inqueue.shift
|
|
@pin.puts out
|
|
end
|
|
select(nil, nil, nil, 0.4)
|
|
rescue
|
|
end
|
|
end
|
|
end
|
|
|
|
def mpg123read
|
|
while not @pout.closed?
|
|
begin
|
|
io = IO.select([@pout, @perr])
|
|
io = io.first.first
|
|
response = io.read_nonblock 1024
|
|
lines = response.split "\n"
|
|
lines.each do |line|
|
|
@logger.debug {"<< #{line}"} #
|
|
if line =~ /@F\s(\S*)\s(\S*)\s(\S*)\s(\S*)\s*/
|
|
changed
|
|
notify_observers :state => :info,
|
|
:frame => $1.to_i,
|
|
:frameleft => $2.to_i,
|
|
:time => $3,
|
|
:timeleft => $4
|
|
elsif line =~ /@P\s(\S*)\s*/
|
|
@pstate = $1.to_i
|
|
changed
|
|
if @paused
|
|
if @pstate == 2
|
|
notify_observers :state => :resume
|
|
@paused = false
|
|
elsif @pstate == 1
|
|
notify_observers :state => :pause
|
|
end
|
|
else
|
|
notify_observers :state => :play if @pstate == 2
|
|
notify_observers :state => :stop if @pstate == 1
|
|
end
|
|
elsif line =~ /@E.*Unfinished command:.*/
|
|
play @last_track
|
|
else
|
|
#puts "don't know #{line}"
|
|
end
|
|
end
|
|
rescue
|
|
end
|
|
end
|
|
end
|
|
end
|