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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -4
- data/README.md +4 -6
- data/Rakefile +1 -0
- data/audite.gemspec +5 -1
- data/bin/audite +11 -9
- data/ext/mpg123/mpg123.c +18 -2
- data/ext/portaudio/portaudio.c +34 -2
- data/lib/audite.rb +58 -10
- metadata +52 -11
checksums.yaml
ADDED
@@ -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
data/Gemfile
CHANGED
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
|
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/audite.gemspec
CHANGED
@@ -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.
|
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 #{
|
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
|
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
|
data/ext/mpg123/mpg123.c
CHANGED
@@ -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
|
-
|
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);
|
data/ext/portaudio/portaudio.c
CHANGED
@@ -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
|
-
|
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);
|
data/lib/audite.rb
CHANGED
@@ -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
|
44
|
-
|
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
|
-
@
|
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(
|
78
|
-
|
79
|
-
|
80
|
-
|
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.
|
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:
|
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:
|
98
|
+
rubygems_version: 2.4.3
|
58
99
|
signing_key:
|
59
|
-
specification_version:
|
100
|
+
specification_version: 4
|
60
101
|
summary: Portable mp3 player
|
61
102
|
test_files: []
|