Newer
Older
intro2018 / mpg123.rb
@kanan kanan on 7 Aug 2018 7 KB add intro
#!/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