From 68584de37a58367cf5621b4af359f2a810a9e9d0 Mon Sep 17 00:00:00 2001 From: njh Date: Mon, 28 Apr 2008 22:51:02 +0000 Subject: [PATCH] Added ruby class for controlling mpg123 git-svn-id: svn://scm.orgis.org/mpg123/trunk@1475 35dc7657-300d-0410-a2e5-dc2837fedb53 --- misc/ruby-mpg123.rb | 179 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 misc/ruby-mpg123.rb diff --git a/misc/ruby-mpg123.rb b/misc/ruby-mpg123.rb new file mode 100644 index 00000000..737050c9 --- /dev/null +++ b/misc/ruby-mpg123.rb @@ -0,0 +1,179 @@ +#!/usr/bin/ruby +# +# Ruby control interface for mpg123 +# By Nicholas Humfrey +# +# With thanks to Marc Lehmann (author of perl module Audio::Play::MPG123) +# + +require "open3" + +class Mpg123 + attr_reader :version, :state, :info, :error + attr_reader :frame, :time + + STATE_STOPPED = 0 + STATE_PAUSED = 1 + STATE_PLAYING = 2 + + def initialize(args=Array.new) + + ## Set instance variable defaults + @version = nil + @state = STATE_STOPPED + @info = Hash.new + @error = nil + @frame = 0 + @time = 0.0 + + ## Open pipe to mpg123 proccess + cmd = ['mpg123','--remote', *args] + @pipe_in, @pipe_out, pipe_err = Open3.popen3(*cmd) + ## FIXME: redirect pipe_err to stderr + ## FIXME: check for errors + + # Read in the first line from mpg123 + parse_line( readline_nonblock(1.0) ) + ## FIXME: ensure that @version is non-nil + + # Start the reader thread + @thread = Thread.new { + loop do + line = @pipe_out.gets + if line.nil? + $stderr.puts "Reached EOF when reading from mpg123 pipe." + raise("Reached EOF when reading from mpg123 pipe.") + else + parse_line(line) + end + end + } + end + + def self.finalize(id) + ## FIXME: cleanup mpg123 process and thread + puts "Mpg123.finalize(#{id})" + end + + def poll(timeout=0.1) + line = readline( timeout ) + return if line == nil + + begin + parse_line( line ) + # Check for more lines (without a timeout) + line = readline(0) + end until line == nil + end + + def load(path) + @pipe_in.puts("LOAD #{path}") + end + + def loadpause(path) + @pipe_in.puts("LOADPAUSE #{path}") + end + + def pause + @pipe_in.puts("PAUSE") + end + + def paused? + return (@state==STATE_PAUSED) + end + + def playing? + return (@state==STATE_PLAYING) + end + + def stopped? + return (@state==STATE_STOPPED) + end + + def stop + @pipe_in.puts("STOP") + end + + def jump(frame) + @pipe_in.puts("JUMP #{frame}") + end + + def volume(percent) + @pipe_in.puts("VOLUME #{percent}") + end + + def rva(mode) + @pipe_in.puts("RVA #{mode}") + end + + def eq(channel, band, value) + @pipe_in.puts("EQ #{channel} #{band} #{value}") + end + + def seek(sample) + @pipe_in.puts("SEEK #{sample}") + end + + def simple_eq(bass,mid,treble) + @pipe_in.puts("SEQ #{bass} #{mid} #{treble}") + end + + + +private + + ## Parse a line sent to us from mpg123 + def parse_line(line) + + if (line =~ /^@F (\d+) (\d+) ([\d\.]+) ([\d\.]+)$/) + @frame = $1.to_i + #FIXME: @frames_remaining = $2.to_i + @time = $3.to_f + #FIXME: @time_remaining = $4.to_f + elsif (line =~ /^@S ([\d\.]+) (\d+) (\d+) (.+)/) + @info['mpeg_version'] = $1 + @info['layer'] = $2 + @info['sample_rate'] = $3 + @info['mode'] = $4 + ## FIXME: more available: + #@{$self}{qw(type layer samplerate mode mode_extension + # bpf channels copyrighted error_protected + # emphasis bitrate extension lsf)}=split /\s+/,$1; + @state = STATE_PLAYING + elsif (line =~ /^@I ID3:(.{30})(.{30})(.{30})(....)(.{30})(.*)$/) + @info['title']=$1; @info['artist']=$2; + @info['album']=$3; @info['year']=$4; + @info['comment']=$5; @info['genre']=$6; + elsif (line =~ /^@I ICY-META: StreamTitle='(.*)';$/) + @info['title']=$1; + #delete @{$self}{qw(artist album year comment genre)} + elsif (line =~ /^@I (.*)$/) + @info['title']=$1; + #delete @{$self}{qw(artist album year comment genre)} + elsif (line =~ /^@R (.+)/) + @version = $1 + elsif (line =~ /^@P (\d)/) + @state = $1.to_i + elsif (line =~ /^@E (.*)$/) + @error = $1 + $stderr.puts("Error: #{$1}") + raise("Error: #{$1}") + elsif (line =~ /^@/) + $stderr.puts "unrecognised line: #{line}" + else + $stderr.puts "unrecognised data: #{line}" + end + end + + def readline_nonblock(timeout) + # Is there any data available? + unless IO.select([@pipe_out], nil, nil, timeout).nil? + # Yes; read the line in and parse it + return @pipe_out.gets + else + # No; nothing available + return nil + end + end + +end