audite 0.3.0 → 0.4.0

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.
@@ -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: []