audite 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: baea4463996cab05f7b2fc2b73c86bed0e7b1edd
4
+ data.tar.gz: 2a8c6dfa90bc9cfc1dd793facd86345ebd00ad2f
5
+ SHA512:
6
+ metadata.gz: bda5687a14156ab552215563734a27dee5f28f97cbeb5261c33abd25eda7d4ad32264c67aa9ce443ef70db5b98de8519308f4e49af513bccc115bdd1f0591f20
7
+ data.tar.gz: 24d8c3007ffc63487689f23b5430be017cd8ba0c88193e61800c58d57a87f21e1d25fdccd31a8ad593956e09a61aa95f963d1a04e3e8b4e54560d2482b381a24
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  ext/*/Makefile
4
4
  ext/*/*.o
5
5
  ext/*/*.bundle
6
+ Gemfile.lock
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
- source "http://rubygems.org"
2
- group :test do
3
- gem "rspec", :require => "spec"
4
- end
1
+ source 'https://rubygems.org'
5
2
 
3
+ # Specify your gem's dependencies in audite.gemspec
4
+ gemspec
data/README.md CHANGED
@@ -31,17 +31,14 @@ player.events.on(:complete) do
31
31
  puts "COMPLETE"
32
32
  end
33
33
 
34
- player.events.on(:level) do |level|
35
- puts "LEVEL: #{level}"
36
- end
37
-
38
34
  player.events.on(:position_change) do |pos|
39
- puts "POSITION: #{pos} seconds"
35
+ puts "POSITION: #{pos} seconds level #{player.level}"
40
36
  end
41
37
 
42
38
  player.load('test.mp3')
43
39
  player.start_stream
44
- player.forward(20)
40
+ player.forward(20)
41
+ player.thread.join
45
42
 
46
43
  ```
47
44
 
@@ -62,6 +59,7 @@ gem install audite
62
59
 
63
60
  ## Debian / Ubuntu Install
64
61
  ```
62
+ apt-get install libjack0 libjack-dev
65
63
  apt-get install libportaudiocpp0 portaudio19-dev libmpg123-dev
66
64
  gem install audite
67
65
  ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -2,7 +2,7 @@ $:.push File.expand_path("../lib", __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "audite"
5
- s.version = "0.3.0"
5
+ s.version = "0.4.0"
6
6
  s.author = "Matthias Georgi"
7
7
  s.email = "matti.georgi@gmail.com"
8
8
  s.homepage = "http://georgi.github.com/audite"
@@ -14,6 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
15
15
  s.require_paths = ["lib"]
16
16
 
17
+ s.add_development_dependency "bundler", "~> 1.3"
18
+ s.add_development_dependency "rake"
19
+ s.add_development_dependency "rspec"
20
+
17
21
  s.extra_rdoc_files = ["README.md"]
18
22
 
19
23
  s.extensions << 'ext/mpg123/extconf.rb'
data/bin/audite CHANGED
@@ -1,21 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'rubygems'
4
3
  require 'curses'
5
-
6
- require_relative '../lib/audite'
4
+ require 'audite'
7
5
 
8
6
  player = Audite.new
9
7
 
10
- player.events.on(:complete) do
11
- exit
12
- end
13
-
14
8
  player.events.on(:position_change) do |pos|
15
9
  p = (player.tell / player.length * Curses.cols).ceil
16
10
  l = (player.level * Curses.cols).ceil
17
11
  Curses.setpos(0, 0)
18
- Curses.addstr("playing #{ARGV[0]} #{player.position.ceil} seconds of #{player.length_in_seconds.ceil} total")
12
+ Curses.addstr("playing #{player.current_song_name} #{player.position.ceil} seconds of #{player.length_in_seconds.ceil} total")
19
13
  Curses.setpos(1, 0)
20
14
  Curses.addstr("#" * p + " " * (Curses.cols - p))
21
15
  Curses.setpos(2, 0)
@@ -23,12 +17,18 @@ player.events.on(:position_change) do |pos|
23
17
  Curses.refresh
24
18
  end
25
19
 
20
+ player.events.on(:complete) do
21
+ if !player.active
22
+ player.close
23
+ end
24
+ end
25
+
26
26
  Curses.init_screen
27
27
  Curses.noecho
28
28
  Curses.stdscr.keypad(true)
29
29
  Curses.clear
30
30
 
31
- player.load(ARGV[0])
31
+ player.load(ARGV)
32
32
  player.start_stream
33
33
 
34
34
  while c = Curses.getch
@@ -41,3 +41,5 @@ while c = Curses.getch
41
41
  player.toggle
42
42
  end
43
43
  end
44
+
45
+ player.thread.join
@@ -39,7 +39,14 @@ VALUE rb_mpg123_new(VALUE klass, VALUE filename) {
39
39
  mpg123_format_none(mh);
40
40
  mpg123_format(mh, rate, channels, encoding);
41
41
 
42
- return Data_Wrap_Struct(rb_cMpg123, 0, cleanup, mh);
42
+ VALUE new_mpg123 = Data_Wrap_Struct(rb_cMpg123, 0, cleanup, mh);
43
+ rb_iv_set(new_mpg123, "@file", filename);
44
+ return new_mpg123;
45
+ }
46
+
47
+ static VALUE rb_mpg123_file(VALUE self, VALUE obj)
48
+ {
49
+ return rb_iv_get(self, "@file");
43
50
  }
44
51
 
45
52
  VALUE rb_mpg123_close(VALUE self)
@@ -48,7 +55,6 @@ VALUE rb_mpg123_close(VALUE self)
48
55
  return self;
49
56
  }
50
57
 
51
-
52
58
  VALUE rb_mpg123_read(VALUE self, VALUE _size)
53
59
  {
54
60
  int size = FIX2INT(_size);
@@ -77,6 +83,14 @@ VALUE rb_mpg123_read(VALUE self, VALUE _size)
77
83
 
78
84
  VALUE rb_mpg123_length(VALUE self)
79
85
  {
86
+ /*
87
+ * mpg123_length() only returns an estimated duration
88
+ * if the song hasn't previously been scanned.
89
+ * This can be incorrect if, for example, the song is corrupted
90
+ * and cannot be played after a certain point.
91
+ * Run mpg123_scan() first to get an accurate length reading.
92
+ */
93
+ mpg123_scan(DATA_PTR(self));
80
94
  return INT2FIX(mpg123_length(DATA_PTR(self)));
81
95
  }
82
96
 
@@ -128,6 +142,8 @@ void Init_mpg123(void) {
128
142
 
129
143
  rb_define_singleton_method(rb_cMpg123, "new", rb_mpg123_new, 1);
130
144
 
145
+ rb_define_method(rb_cMpg123, "file", rb_mpg123_file, 0);
146
+
131
147
  rb_define_method(rb_cMpg123, "close", rb_mpg123_close, 0);
132
148
  rb_define_method(rb_cMpg123, "read", rb_mpg123_read, 1);
133
149
  rb_define_method(rb_cMpg123, "length", rb_mpg123_length, 0);
@@ -124,6 +124,7 @@ VALUE rb_portaudio_write_from_mpg(VALUE self, VALUE mpg)
124
124
  switch (err) {
125
125
  case MPG123_OK: return ID2SYM(rb_intern("ok"));
126
126
  case MPG123_DONE: return ID2SYM(rb_intern("done"));
127
+ case MPG123_NEED_MORE: return ID2SYM(rb_intern("need_more"));
127
128
  }
128
129
 
129
130
  rb_raise(rb_eStandardError, "%s", mpg123_plain_strerror(err));
@@ -134,9 +135,8 @@ VALUE rb_portaudio_wait(VALUE self)
134
135
  Portaudio *portaudio;
135
136
  Data_Get_Struct(self, Portaudio, portaudio);
136
137
 
137
- Data_Get_Struct(self, Portaudio, portaudio);
138
138
  #ifdef RUBY_UBF_IO
139
- rb_thread_blocking_region(portaudio_wait, portaudio, RUBY_UBF_IO, NULL);
139
+ rb_thread_call_without_gvl(portaudio_wait, portaudio, RUBY_UBF_IO, NULL);
140
140
  #else
141
141
  portaudio_wait(portaudio);
142
142
  #endif
@@ -201,6 +201,36 @@ VALUE rb_portaudio_stop(VALUE self)
201
201
  return self;
202
202
  }
203
203
 
204
+ VALUE rb_portaudio_stream_stopped(VALUE self)
205
+ {
206
+ Portaudio *portaudio;
207
+ Data_Get_Struct(self, Portaudio, portaudio);
208
+ int err = Pa_IsStreamStopped(portaudio->stream);
209
+
210
+ if (err == 1) {
211
+ return Qtrue;
212
+ } else if (err == 0) {
213
+ return Qfalse;
214
+ }
215
+
216
+ rb_raise(rb_eStandardError, "%s", Pa_GetErrorText(err));
217
+ }
218
+
219
+ VALUE rb_portaudio_close(VALUE self)
220
+ {
221
+ Portaudio *portaudio;
222
+ Data_Get_Struct(self, Portaudio, portaudio);
223
+ int err = Pa_CloseStream(portaudio->stream);
224
+
225
+ pthread_cond_broadcast(&portaudio->cond);
226
+
227
+ if (err != paNoError) {
228
+ rb_raise(rb_eStandardError, "%s", Pa_GetErrorText(err));
229
+ }
230
+
231
+ return self;
232
+ }
233
+
204
234
  void Init_portaudio(void) {
205
235
  int err = Pa_Initialize();
206
236
 
@@ -212,6 +242,8 @@ void Init_portaudio(void) {
212
242
 
213
243
  rb_define_singleton_method(rb_cPortaudio, "new", rb_portaudio_new, 1);
214
244
  rb_define_method(rb_cPortaudio, "wait", rb_portaudio_wait, 0);
245
+ rb_define_method(rb_cPortaudio, "stopped?", rb_portaudio_stream_stopped, 0);
246
+ rb_define_method(rb_cPortaudio, "close", rb_portaudio_close, 0);
215
247
  rb_define_method(rb_cPortaudio, "rms", rb_portaudio_rms, 0);
216
248
  rb_define_method(rb_cPortaudio, "write", rb_portaudio_write, 1);
217
249
  rb_define_method(rb_cPortaudio, "write_from_mpg", rb_portaudio_write_from_mpg, 1);
@@ -1,6 +1,8 @@
1
1
  require 'portaudio'
2
2
  require 'mpg123'
3
3
 
4
+ trap('INT') { puts "\nClosing" ; exit }
5
+
4
6
  class Audite
5
7
  class Events
6
8
  def initialize
@@ -18,16 +20,17 @@ class Audite
18
20
  end
19
21
  end
20
22
 
21
- attr_reader :events, :active
23
+ attr_reader :events, :active, :stream, :mp3, :thread, :file, :song_list
22
24
 
23
25
  def initialize(buffer_size = 2**12)
24
26
  @buffer_size = buffer_size
25
27
  @events = Events.new
26
28
  @stream = Portaudio.new(@buffer_size)
29
+ @song_list = []
27
30
  end
28
31
 
29
32
  def start_thread
30
- Thread.start do
33
+ @thread ||= Thread.start do
31
34
  loop do
32
35
  process @stream.write_from_mpg(@mp3)
33
36
  @stream.wait
@@ -40,8 +43,8 @@ class Audite
40
43
  end
41
44
 
42
45
  def process(status)
43
- if status == :done
44
- stop_stream
46
+ if [:done, :need_more].include? status
47
+ request_next_song
45
48
  events.trigger(:complete)
46
49
  else
47
50
  events.trigger(:position_change, position)
@@ -52,17 +55,41 @@ class Audite
52
55
  $stderr.puts e.backtrace
53
56
  end
54
57
 
58
+ def current_song_name
59
+ File.basename mp3.file
60
+ end
61
+
62
+ def request_next_song
63
+ if songs_in_queue?
64
+ set_current_song
65
+ start_stream
66
+ else
67
+ stop_stream
68
+ end
69
+ end
70
+
71
+ def close
72
+ stream.close
73
+ exit
74
+ end
75
+
55
76
  def start_stream
56
- unless @active
77
+ unless @active || !song_loaded?
57
78
  @active = true
58
79
  @stream.start
80
+ start_thread
81
+ events.trigger(:toggle, @active)
59
82
  end
60
83
  end
61
84
 
62
85
  def stop_stream
63
86
  if @active
64
87
  @active = false
65
- @stream.stop
88
+ @thread = nil unless @thread.alive?
89
+ unless @stream.stopped?
90
+ @stream.stop
91
+ events.trigger(:toggle, @active)
92
+ end
66
93
  end
67
94
  end
68
95
 
@@ -74,10 +101,31 @@ class Audite
74
101
  end
75
102
  end
76
103
 
77
- def load(file)
78
- @file = file
79
- @mp3 = Mpg123.new(file)
80
- @thread ||= start_thread
104
+ def load(files)
105
+ files = [] << files unless Array === files
106
+ files.each {|file| queue file }
107
+ set_current_song
108
+ end
109
+
110
+ def song_loaded?
111
+ !@mp3.nil?
112
+ end
113
+
114
+ def set_current_song
115
+ @mp3 = song_list.shift
116
+ start_thread
117
+ end
118
+
119
+ def queue song
120
+ @song_list << Mpg123.new(song)
121
+ end
122
+
123
+ def queued_songs
124
+ @song_list.map {|s| File.basename s.file }
125
+ end
126
+
127
+ def songs_in_queue?
128
+ !@song_list.empty?
81
129
  end
82
130
 
83
131
  def time_per_frame
metadata CHANGED
@@ -1,16 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
5
- prerelease:
4
+ version: 0.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Matthias Georgi
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-29 00:00:00.000000000 Z
13
- dependencies: []
11
+ date: 2015-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
14
55
  description: Portable mp3 player built on mpg123 and portaudio
15
56
  email: matti.georgi@gmail.com
16
57
  executables:
@@ -21,10 +62,11 @@ extensions:
21
62
  extra_rdoc_files:
22
63
  - README.md
23
64
  files:
24
- - .gitignore
65
+ - ".gitignore"
25
66
  - Gemfile
26
67
  - LICENSE
27
68
  - README.md
69
+ - Rakefile
28
70
  - audite.gemspec
29
71
  - bin/audite
30
72
  - ext/mpg123/extconf.rb
@@ -36,26 +78,25 @@ files:
36
78
  - spec/audite_spec.rb
37
79
  homepage: http://georgi.github.com/audite
38
80
  licenses: []
81
+ metadata: {}
39
82
  post_install_message:
40
83
  rdoc_options: []
41
84
  require_paths:
42
85
  - lib
43
86
  required_ruby_version: !ruby/object:Gem::Requirement
44
- none: false
45
87
  requirements:
46
- - - ! '>='
88
+ - - ">="
47
89
  - !ruby/object:Gem::Version
48
90
  version: '0'
49
91
  required_rubygems_version: !ruby/object:Gem::Requirement
50
- none: false
51
92
  requirements:
52
- - - ! '>='
93
+ - - ">="
53
94
  - !ruby/object:Gem::Version
54
95
  version: '0'
55
96
  requirements: []
56
97
  rubyforge_project:
57
- rubygems_version: 1.8.24
98
+ rubygems_version: 2.4.3
58
99
  signing_key:
59
- specification_version: 3
100
+ specification_version: 4
60
101
  summary: Portable mp3 player
61
102
  test_files: []