#!/usr/bin/ruby # -*- coding: utf-8 -*- require 'em-websocket' require 'pp' require 'json' require 'set' require 'open3' mydir=File.dirname($0) require(File.expand_path(ENV['SUI'] || 'sui.rb', mydir)) PORT = 4946 # if !ARGV[0] # STDERR.puts "Choose playback music file\n:" # STDERR.puts "\t#{$0}" # exit 1 # end # frame = nil # e = IO.pipe class MP3Player def initialize(file) list(file) @index = 0 end #再生する音楽リストを取得 def list(file) @music_list = [] IO.foreach(file) do |line| # nanka.txtを読み込んでnanka.mp3と曲名を分ける if /(.*\.mp3)(\s+(.*))?/ =~ line.strip # あとでcursesで曲名をだす。 f = $1 test(?s, f) and @music_list << [f, $3 || File.basename(f)] #リストに入っているmpsファイルを@music_listへ入れる end end end #mpg123をバックグラウンド操作する attr_accessor :music_list def play(frame = @frame) #音楽の再生.止まるとフレーム数なるものを返すので変数に入れる music = @music_list[@index][0] #frameを0に設定 @frame = frame || "0" cmd = ["mpg123", "-v", "-k", @frame, music] # ここから親の処理 @mp3thread = Thread.new { Open3.popen3(*cmd) do |i, o, e, thr| # open3.popen3で実行中プロセスの標準出力,エラー出力を扱える。つまり、cmdの標準出力を扱える。 # e = IO.pipe # e[1].close # 親の出力端は使わないので @pid = thr.pid # 子プロセス(mpg123)と通信するメソッド eline = "" while chr = e.read(1) # 1字ずつ読み、eに足していく eline << chr # elineに追加 next unless /[\r\n]/ =~ chr # CRかLFが来たら次の行(line)に移る if /(Frame#|Time:)\s+([0-9:.]+)/ =~ eline @frame = $2 ## STDERR.printf("Frame = #%d\r", @frame) eline = "" end end end } return @music_list[@index][1] end #誰かが押したら曲を一時止めるよメソッド def stop(chime = nil) if @pid Process.kill(:INT, @pid) rescue nil Thread.new { @mp3thread.join #ボタンを押すとchimeがなるように(x) system("mpg123 -q botan.mp3") if chime } @pid = nil end end # while true # a = STDIN.gets # if a.nil? || /q/ =~ a # Process.kill(:INT, pid) # Process.kill(:INT, pid) # break # elsif /n/=~ a # Process.kill(:INT, pid) # end # end # 次の曲を再生する def nextMusic(arg = 1) stop @index+=arg if @index >= @music_list.length return nil end @frame = "0" return sprintf("%02d : %s", @index, @music_list[@index][1]) end # e[1].close # def prompt(file) # STDERR.printf("\n[%s] playback - command(next music:n、quit:q); ", file) # end # Thread.new do # while not e[0].closed? # line = "" # while l = e[0].getc # if l != "\r"[0] && l != "\n"[0] # line << l.chr # else # break # end # end # if /Frame\#\s*(\d+)/n =~ line # STDERR.printf "%s ", $1 if $DEBUG # frame =$1.to_i # elsif /stream .* (.+) \.\.\./n =~ line # prompt($1) # end # end # end def prevMusic(arg = -1) nextMusic(arg) end def setMusic(n) @index = [[0, n].max, @music_list.length-1].min return nextMusic(0) end end mp3play = MP3Player.new(ARGV[0]) #クライアントの操作 clients = Set.new ans = {} cli2tm = {} # print("No clients yet...") #サーバスタート EM.run do sui = SUI.new(mp3play.music_list) EM::WebSocket.start({:host => "0.0.0.0", :port => PORT}) do |ws_conn| ws_conn.onopen do clients << ws_conn # p "hello"+ws_conn sui.printf("Team %d\n", clients.length) end ws_conn.onmessage do |message| pp message warn = nil if message > "" if ans[message] ans[message] << ws_conn else # First time ans[message] = [] ans[message] << ws_conn cli2tm[ws_conn] = message # Remenber team name mp3play.stop(true) end announce = ans.keys.join(", ") clients.each do |conn| tm = cli2tm[conn] #一度押した後の重複を防ぐ warn = if tm && ans[tm] && ans[tm].length > 1 # printf("tm=%d\n", tm) warn ="You are pushed." end rank = (ans.keys.index(tm) || -1) + 1 answer = {"info" => announce, "warn" => warn, "rank" => rank} conn.send(JSON.pretty_generate(answer)) end else ws_conn.send(JSON.pretty_generate({"warn"=>"Plase enter your term name."})) end end ws_conn.onclose do clients.delete(ws_conn) #clientからpushが来たらclient.lengthに追加 sui.printf("%d guest\n", clients.length) end #clientにメッセージを送る def broadcast(clients, mesg) clients.each {|c| c.send(JSON.generate({"info" => mesg})) } end cast_nonpush_clients = lambda {|mesg| # Send to non-pushing clients s = JSON.generate({"info" => mesg}) clients.each {|c| team = cli2tm[c] c.send(s) if !ans.has_key?(team) } } EM::defer do #班がpushしたかどうかの判定、suiに表示させる while line = sui.action case line.chomp when /p/ cast_nonpush_clients.call("START shimasita.") # STDERR.printf("START: %s", mp3play.play) sui.printf("START: %s", mp3play.play) when /s/ mp3play.stop #mp3playを止める when /[nr0..9]/ sui.puts("Reset clients") s = JSON.pretty_generate({"info" => "Ready..."}) broadcast(clients, "Ready...") ans = {}; cli2tm = {}; if /n/ =~ line sui.printf("NEXT: %s\n", mp3play.nextMusic) elsif /^\d+/ =~ line sui.printf("%d: %s\n", line.to_i, mp3play.setMusic(line.to_i)) end end end exit end end end #mpg123をバックグラウンド操作 # pid = fork do # STDERR.printf("Hello %s playback now.", ARGV.join(" ")) # STDERR.reopen(e[1]) # e[0].close # exec "mpg123", "-v", *ARGV # end # #ここから親の処理 # e[1].close # def prompt(file) # STDERR.printf("\n[%s] playback - command(next music:n、quit:q); ", file) # end # Thread.new do # while not e[0].closed? # line = "" # while l = e[0].getc # if l != "\r"[0] && l != "\n"[0] # line << l.chr # else # break # end # end # if /Frame\#\s*(\d+)/n =~ line # STDERR.printf "%s ", $1 if $DEBUG # frame =$1.to_i # elsif /stream .* (.+) \.\.\./n =~ line # prompt($1) # end # end # end # Thread.new do # Process.wait # printf("Stopping at frame %s\n", frame) # exit 0 # end # while true # a = STDIN.gets # if a.nil? || /q/ =~ a # Process.kill(:INT, pid) # Process.kill(:INT, pid) # break # elsif /n/=~ a # Process.kill(:INT, pid) # end # end