mmplayer 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a60391f734494cf22b9e343379660079567721e8
4
- data.tar.gz: 4d75bfe1d744172b82fc9866ef9e4af6735d68e4
3
+ metadata.gz: a7696f764235df86a1aa44baa06e9aac9f3d2aa3
4
+ data.tar.gz: 79bb9a6de61e332396a1ca14fba2f7bcf5e7c153
5
5
  SHA512:
6
- metadata.gz: 64d867081ec9fb0b087076da7981aa978b7cc7ca704f01f7c6c8b4d0fa440c40383ecdc93171370e87ba91d0282d4bb6325aa7669c69118d60124c0bc1013054
7
- data.tar.gz: 18ca9e448f75dc6d2d97cb071060ea5e49af680a991bb184cb55b9e752454f2a1a45d0073986b97e60182f9afc1d2688b20e87648a8d447a6589e26cbb563943
6
+ metadata.gz: 83026c50689a282c33c8463ec018159cc4f984d6e6cc51308dbe7440210e7452f3096e76f5a361b601666458c22885740b290a35d729f84ca8478bfb445dc66d
7
+ data.tar.gz: 2b84b86a16ee4a02ac0290575697967c5f1cb7e387ef6998d4478cd9e708f80c25822dbcf9f62907a07c2b5f5cc69a72c67fc86938fe46cf4381a34f1bebeb81
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Control [MPlayer](http://en.wikipedia.org/wiki/MPlayer) with MIDI
6
6
 
7
- MPlayer is a free, cross-platform, command-line driven, highly configurable, (often) GUI-less open-source media player.
7
+ MPlayer is a free, cross-platform, command-line driven, highly configurable, (often) GUI-less open-source media player.
8
8
 
9
9
  Enabling MPlayer to be controlled by MIDI opens up a host of possibilities for live video performance, media automation and more
10
10
 
@@ -14,9 +14,9 @@ This project provides a Ruby DSL to define realtime interactions between MIDI in
14
14
 
15
15
  You'll need to install MPlayer before using this. That can usually be accomplished with a package manager eg `brew install mplayer` depending on what OS you're using.
16
16
 
17
- This project itself can be installed as a Ruby Gem using
17
+ This project itself can be installed as a Ruby Gem using
18
18
 
19
- `gem install mmplayer`
19
+ `gem install mmplayer`
20
20
 
21
21
  Or if you're using Bundler, add this to your Gemfile
22
22
 
@@ -34,13 +34,15 @@ require "mmplayer"
34
34
  rx_channel 0
35
35
 
36
36
  system(:start) { play("1.mov") }
37
-
37
+
38
38
  note(1) { play("2.mov") }
39
39
  note("C2") { play("3.mov") }
40
40
 
41
41
  cc(1) { |value| volume(:set, value) }
42
42
  cc(20) { |value| seek(to_percent(value), :percent) }
43
43
 
44
+ eof { puts "finished" }
45
+
44
46
  end
45
47
 
46
48
  @player.start
@@ -24,7 +24,7 @@ require "mmplayer/player"
24
24
 
25
25
  module MMPlayer
26
26
 
27
- VERSION = "0.0.5"
27
+ VERSION = "0.0.6"
28
28
 
29
29
  # Shortcut to Context constructor
30
30
  def self.new(*args, &block)
@@ -27,10 +27,8 @@ module MMPlayer
27
27
  # @return [Boolean]
28
28
  def start(options = {})
29
29
  @midi.start
30
- unless !!options[:background]
31
- loop { sleep(0.1) } until @player.active?
32
- loop { sleep(0.1) } while @player.active?
33
- end
30
+ @playback_thread = playback_loop
31
+ @playback_thread.join unless !!options[:background]
34
32
  true
35
33
  end
36
34
 
@@ -42,6 +40,24 @@ module MMPlayer
42
40
  true
43
41
  end
44
42
 
43
+ private
44
+
45
+ # Main playback loop
46
+ def playback_loop
47
+ thread = Thread.new do
48
+ begin
49
+ until @player.active?
50
+ sleep(0.1)
51
+ end
52
+ @player.playback_loop
53
+ rescue Exception => exception
54
+ Thread.main.raise(exception)
55
+ end
56
+ end
57
+ thread.abort_on_exception = true
58
+ thread
59
+ end
60
+
45
61
  end
46
62
 
47
63
  end
@@ -5,6 +5,17 @@ module MMPlayer
5
5
  # Instructions dealing with the MPlayer
6
6
  module Player
7
7
 
8
+ # Assign a callback for when a file finishes playback
9
+ # @param [Proc] callback The callback to execute when a file finishes playback
10
+ # @return [Hash]
11
+ def on_end_of_file(&callback)
12
+ @player.add_end_of_file_callback(&callback)
13
+ end
14
+ alias_method :end_of_file, :on_end_of_file
15
+ alias_method :eof, :on_end_of_file
16
+
17
+ private
18
+
8
19
  # Add delegators to local player methods
9
20
  def self.included(base)
10
21
  base.send(:extend, Forwardable)
@@ -9,6 +9,10 @@ module MMPlayer
9
9
  @flags = "-fixed-vo -idle"
10
10
  @flags += " #{options[:flags]}" unless options[:flags].nil?
11
11
  @messenger = Messenger.new
12
+ @callback = {}
13
+ @is_playing = false
14
+ @is_eof_handled = true
15
+ @player_threads = []
12
16
  end
13
17
 
14
18
  # Play a media file
@@ -19,7 +23,10 @@ module MMPlayer
19
23
  if @player.nil?
20
24
  false
21
25
  else
22
- @player.load_file(file)
26
+ with_player_thread do
27
+ @player.load_file(file)
28
+ handle_start
29
+ end
23
30
  true
24
31
  end
25
32
  end
@@ -27,7 +34,28 @@ module MMPlayer
27
34
  # Is MPlayer active?
28
35
  # @return [Boolean]
29
36
  def active?
30
- !@player.nil? && !@player.stdout.gets.nil?
37
+ !@player.nil?
38
+ end
39
+
40
+ # Handle events while the player is running
41
+ # @return [Boolean]
42
+ def playback_loop
43
+ loop do
44
+ if @is_playing && !@is_eof_handled && get_player_output.size < 1
45
+ handle_eof
46
+ else
47
+ sleep(0.05)
48
+ end
49
+ end
50
+ true
51
+ end
52
+
53
+ # Add a callback to be called at the end of playback of a media file
54
+ # @param [Proc] block
55
+ # @return [Boolean]
56
+ def add_end_of_file_callback(&block)
57
+ @callback[:end_of_file] = block
58
+ true
31
59
  end
32
60
 
33
61
  # Media progress information
@@ -66,12 +94,33 @@ module MMPlayer
66
94
  # @return [Boolean]
67
95
  def quit
68
96
  @player.quit
69
- @player_thread.kill
97
+ @player_threads.each(&:kill)
70
98
  true
71
99
  end
72
100
 
73
101
  private
74
102
 
103
+ # Get player output from stdout
104
+ def get_player_output
105
+ @player.stdout.gets.inspect.strip.gsub(/(\\n|[\\"])/, '').strip
106
+ end
107
+
108
+ # Handle the end of playback for a single media file
109
+ def handle_eof
110
+ @is_eof_handled = true
111
+ @is_playing = false
112
+ STDOUT.flush
113
+ @callback[:end_of_file].call unless @callback[:end_of_file].nil?
114
+ true
115
+ end
116
+
117
+ # Handle the beginning of playback for a single media file
118
+ def handle_start
119
+ loop until get_player_output.size > 1
120
+ @is_playing = true
121
+ @is_eof_handled = false
122
+ end
123
+
75
124
  # Get progress percentage from the MPlayer report
76
125
  def get_percentage(report)
77
126
  percent = (report[:position] / report[:length]) * 100
@@ -95,19 +144,29 @@ module MMPlayer
95
144
  @player.get(key).strip.to_f
96
145
  end
97
146
 
147
+ # Call the given block within a new thread
148
+ def with_player_thread(&block)
149
+ thread = Thread.new do
150
+ begin
151
+ yield
152
+ rescue Exception => exception
153
+ Thread.main.raise(exception)
154
+ end
155
+ end
156
+ thread.abort_on_exception = true
157
+ @player_threads << thread
158
+ thread
159
+ end
160
+
98
161
  # Ensure that the MPlayer process is invoked
99
162
  # @param [String] file The media file to invoke MPlayer with
100
163
  # @return [MPlayer::Slave]
101
164
  def ensure_player(file)
102
- if @player.nil? && @player_thread.nil?
103
- @player_thread = Thread.new do
104
- begin
105
- @player = MPlayer::Slave.new(file, :options => @flags)
106
- rescue Exception => exception
107
- Thread.main.raise(exception)
108
- end
165
+ if @player.nil? && @player_threads.empty?
166
+ with_player_thread do
167
+ @player = MPlayer::Slave.new(file, :options => @flags)
168
+ handle_start
109
169
  end
110
- @player_thread.abort_on_exception = true
111
170
  end
112
171
  end
113
172
 
@@ -10,12 +10,15 @@ class MMPlayer::PlayerTest < Minitest::Test
10
10
  def get(*args)
11
11
  "0.1\n"
12
12
  end
13
+
14
+ def load_file(something)
15
+ end
13
16
  end
14
17
  @player = MMPlayer::Player.new
15
18
  @mplayer = MPlayer.new
16
19
  @player.stubs(:ensure_player).returns(@mplayer)
17
20
  @player.instance_variable_set("@player", @mplayer)
18
- @player.instance_variable_set("@player_thread", Thread.new {})
21
+ @player.instance_variable_set("@player_threads", [Thread.new {}])
19
22
  @player.send(:ensure_player, "")
20
23
  end
21
24
 
@@ -68,12 +71,12 @@ class MMPlayer::PlayerTest < Minitest::Test
68
71
 
69
72
  setup do
70
73
  @mplayer.expects(:quit).once
71
- @player.instance_variable_get("@player_thread").expects(:kill).once
74
+ @player.instance_variable_get("@player_threads").first.expects(:kill).once
72
75
  end
73
76
 
74
77
  teardown do
75
78
  @mplayer.unstub(:quit)
76
- @player.instance_variable_get("@player_thread").unstub(:kill)
79
+ @player.instance_variable_get("@player_threads").first.unstub(:kill)
77
80
  end
78
81
 
79
82
  should "exit MPlayer and kill the player thread" do
@@ -105,12 +108,10 @@ class MMPlayer::PlayerTest < Minitest::Test
105
108
 
106
109
  setup do
107
110
  @player.expects(:ensure_player).once.returns(@mplayer)
108
- @mplayer.expects(:load_file).once
109
111
  end
110
112
 
111
113
  teardown do
112
114
  @player.unstub(:ensure_player)
113
- @mplayer.unstub(:load_file)
114
115
  end
115
116
 
116
117
  should "lazily invoke mplayer and play" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mmplayer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ari Russo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-02 00:00:00.000000000 Z
11
+ date: 2015-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest