audite 0.2.0 → 0.3.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.
- data/audite.gemspec +1 -1
- data/bin/audite +9 -11
- data/ext/portaudio/portaudio.c +62 -9
- data/lib/audite.rb +9 -22
- metadata +2 -3
data/audite.gemspec
CHANGED
data/bin/audite
CHANGED
@@ -1,30 +1,25 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require 'audite'
|
5
4
|
require 'curses'
|
6
5
|
|
6
|
+
require_relative '../lib/audite'
|
7
|
+
|
7
8
|
player = Audite.new
|
8
|
-
player.load(ARGV[0])
|
9
|
-
player.start_stream
|
10
9
|
|
11
10
|
player.events.on(:complete) do
|
12
11
|
exit
|
13
12
|
end
|
14
13
|
|
15
|
-
player.events.on(:level) do |level|
|
16
|
-
p = (level * Curses.cols).ceil
|
17
|
-
Curses.setpos(2, 0)
|
18
|
-
Curses.addstr(">" * p + " " * (Curses.cols - p))
|
19
|
-
Curses.refresh
|
20
|
-
end
|
21
|
-
|
22
14
|
player.events.on(:position_change) do |pos|
|
23
|
-
p = (
|
15
|
+
p = (player.tell / player.length * Curses.cols).ceil
|
16
|
+
l = (player.level * Curses.cols).ceil
|
24
17
|
Curses.setpos(0, 0)
|
25
18
|
Curses.addstr("playing #{ARGV[0]} #{player.position.ceil} seconds of #{player.length_in_seconds.ceil} total")
|
26
19
|
Curses.setpos(1, 0)
|
27
20
|
Curses.addstr("#" * p + " " * (Curses.cols - p))
|
21
|
+
Curses.setpos(2, 0)
|
22
|
+
Curses.addstr(">" * l + " " * (Curses.cols - l))
|
28
23
|
Curses.refresh
|
29
24
|
end
|
30
25
|
|
@@ -33,6 +28,9 @@ Curses.noecho
|
|
33
28
|
Curses.stdscr.keypad(true)
|
34
29
|
Curses.clear
|
35
30
|
|
31
|
+
player.load(ARGV[0])
|
32
|
+
player.start_stream
|
33
|
+
|
36
34
|
while c = Curses.getch
|
37
35
|
case c
|
38
36
|
when Curses::KEY_LEFT
|
data/ext/portaudio/portaudio.c
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
#include <ruby.h>
|
2
|
+
#include <math.h>
|
2
3
|
#include <pthread.h>
|
3
4
|
#include <portaudio.h>
|
5
|
+
#include <mpg123.h>
|
4
6
|
|
5
7
|
typedef struct {
|
6
8
|
PaStream *stream;
|
7
9
|
int size;
|
8
10
|
float *buffer;
|
11
|
+
float rms;
|
9
12
|
pthread_mutex_t mutex;
|
10
13
|
pthread_cond_t cond;
|
11
14
|
} Portaudio;
|
@@ -41,9 +44,7 @@ static int paCallback(const void *inputBuffer,
|
|
41
44
|
|
42
45
|
pthread_mutex_lock(&portaudio->mutex);
|
43
46
|
|
44
|
-
|
45
|
-
*out++ = portaudio->buffer[i];
|
46
|
-
}
|
47
|
+
memcpy(out, portaudio->buffer, sizeof(float) * portaudio->size);
|
47
48
|
|
48
49
|
pthread_cond_broadcast(&portaudio->cond);
|
49
50
|
pthread_mutex_unlock(&portaudio->mutex);
|
@@ -94,6 +95,54 @@ VALUE portaudio_wait(void *ptr)
|
|
94
95
|
return Qnil;
|
95
96
|
}
|
96
97
|
|
98
|
+
float rms(float *v, int n)
|
99
|
+
{
|
100
|
+
int i;
|
101
|
+
float sum = 0.0;
|
102
|
+
|
103
|
+
for (i = 0; i < n; i++) {
|
104
|
+
sum += v[i] * v[i];
|
105
|
+
}
|
106
|
+
|
107
|
+
return sqrt(sum / n);
|
108
|
+
}
|
109
|
+
|
110
|
+
VALUE rb_portaudio_write_from_mpg(VALUE self, VALUE mpg)
|
111
|
+
{
|
112
|
+
int err;
|
113
|
+
size_t done = 0;
|
114
|
+
Portaudio *portaudio;
|
115
|
+
mpg123_handle *mh = NULL;
|
116
|
+
|
117
|
+
Data_Get_Struct(self, Portaudio, portaudio);
|
118
|
+
Data_Get_Struct(mpg, mpg123_handle, mh);
|
119
|
+
|
120
|
+
err = mpg123_read(mh, (unsigned char *) portaudio->buffer, portaudio->size * sizeof(float), &done);
|
121
|
+
|
122
|
+
portaudio->rms = rms(portaudio->buffer, portaudio->size);
|
123
|
+
|
124
|
+
switch (err) {
|
125
|
+
case MPG123_OK: return ID2SYM(rb_intern("ok"));
|
126
|
+
case MPG123_DONE: return ID2SYM(rb_intern("done"));
|
127
|
+
}
|
128
|
+
|
129
|
+
rb_raise(rb_eStandardError, "%s", mpg123_plain_strerror(err));
|
130
|
+
}
|
131
|
+
|
132
|
+
VALUE rb_portaudio_wait(VALUE self)
|
133
|
+
{
|
134
|
+
Portaudio *portaudio;
|
135
|
+
Data_Get_Struct(self, Portaudio, portaudio);
|
136
|
+
|
137
|
+
Data_Get_Struct(self, Portaudio, portaudio);
|
138
|
+
#ifdef RUBY_UBF_IO
|
139
|
+
rb_thread_blocking_region(portaudio_wait, portaudio, RUBY_UBF_IO, NULL);
|
140
|
+
#else
|
141
|
+
portaudio_wait(portaudio);
|
142
|
+
#endif
|
143
|
+
return self;
|
144
|
+
}
|
145
|
+
|
97
146
|
VALUE rb_portaudio_write(VALUE self, VALUE buffer)
|
98
147
|
{
|
99
148
|
int i, len;
|
@@ -112,15 +161,16 @@ VALUE rb_portaudio_write(VALUE self, VALUE buffer)
|
|
112
161
|
portaudio->buffer[i] = NUM2DBL(rb_ary_entry(buffer, i));
|
113
162
|
}
|
114
163
|
|
115
|
-
#ifdef RUBY_UBF_IO
|
116
|
-
rb_thread_blocking_region(portaudio_wait, portaudio, RUBY_UBF_IO, NULL);
|
117
|
-
#else
|
118
|
-
portaudio_wait(portaudio);
|
119
|
-
#endif
|
120
|
-
|
121
164
|
return self;
|
122
165
|
}
|
123
166
|
|
167
|
+
VALUE rb_portaudio_rms(VALUE self)
|
168
|
+
{
|
169
|
+
Portaudio *portaudio;
|
170
|
+
Data_Get_Struct(self, Portaudio, portaudio);
|
171
|
+
return rb_float_new(portaudio->rms);
|
172
|
+
}
|
173
|
+
|
124
174
|
VALUE rb_portaudio_start(VALUE self)
|
125
175
|
{
|
126
176
|
Portaudio *portaudio;
|
@@ -161,7 +211,10 @@ void Init_portaudio(void) {
|
|
161
211
|
rb_cPortaudio = rb_define_class("Portaudio", rb_cObject);
|
162
212
|
|
163
213
|
rb_define_singleton_method(rb_cPortaudio, "new", rb_portaudio_new, 1);
|
214
|
+
rb_define_method(rb_cPortaudio, "wait", rb_portaudio_wait, 0);
|
215
|
+
rb_define_method(rb_cPortaudio, "rms", rb_portaudio_rms, 0);
|
164
216
|
rb_define_method(rb_cPortaudio, "write", rb_portaudio_write, 1);
|
217
|
+
rb_define_method(rb_cPortaudio, "write_from_mpg", rb_portaudio_write_from_mpg, 1);
|
165
218
|
rb_define_method(rb_cPortaudio, "start", rb_portaudio_start, 0);
|
166
219
|
rb_define_method(rb_cPortaudio, "stop", rb_portaudio_stop, 0);
|
167
220
|
}
|
data/lib/audite.rb
CHANGED
@@ -20,37 +20,31 @@ class Audite
|
|
20
20
|
|
21
21
|
attr_reader :events, :active
|
22
22
|
|
23
|
-
def initialize(buffer_size = 2**
|
23
|
+
def initialize(buffer_size = 2**12)
|
24
24
|
@buffer_size = buffer_size
|
25
25
|
@events = Events.new
|
26
26
|
@stream = Portaudio.new(@buffer_size)
|
27
|
-
@thread = start_thread
|
28
27
|
end
|
29
28
|
|
30
29
|
def start_thread
|
31
30
|
Thread.start do
|
32
31
|
loop do
|
33
|
-
@stream.
|
32
|
+
process @stream.write_from_mpg(@mp3)
|
33
|
+
@stream.wait
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
38
|
+
def level
|
39
|
+
@stream.rms
|
40
40
|
end
|
41
41
|
|
42
|
-
def process(
|
43
|
-
if
|
42
|
+
def process(status)
|
43
|
+
if status == :done
|
44
44
|
stop_stream
|
45
45
|
events.trigger(:complete)
|
46
|
-
silence(samples)
|
47
|
-
|
48
|
-
elsif slice = read(samples)
|
49
|
-
events.trigger(:level, level(slice))
|
50
|
-
events.trigger(:position_change, position)
|
51
|
-
slice
|
52
46
|
else
|
53
|
-
|
47
|
+
events.trigger(:position_change, position)
|
54
48
|
end
|
55
49
|
|
56
50
|
rescue => e
|
@@ -83,6 +77,7 @@ class Audite
|
|
83
77
|
def load(file)
|
84
78
|
@file = file
|
85
79
|
@mp3 = Mpg123.new(file)
|
80
|
+
@thread ||= start_thread
|
86
81
|
end
|
87
82
|
|
88
83
|
def time_per_frame
|
@@ -101,10 +96,6 @@ class Audite
|
|
101
96
|
@mp3 ? @mp3.length : 0
|
102
97
|
end
|
103
98
|
|
104
|
-
def read(samples)
|
105
|
-
@mp3.read(samples)
|
106
|
-
end
|
107
|
-
|
108
99
|
def seconds_to_frames(seconds)
|
109
100
|
seconds / time_per_frame
|
110
101
|
end
|
@@ -140,10 +131,6 @@ class Audite
|
|
140
131
|
samples_to_seconds(length)
|
141
132
|
end
|
142
133
|
|
143
|
-
def level(slice)
|
144
|
-
slice.map {|i| i.abs }.max
|
145
|
-
end
|
146
|
-
|
147
134
|
def rewind(seconds = 2)
|
148
135
|
seek(position - seconds)
|
149
136
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: audite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Portable mp3 player built on mpg123 and portaudio
|
15
15
|
email: matti.georgi@gmail.com
|
@@ -59,4 +59,3 @@ signing_key:
|
|
59
59
|
specification_version: 3
|
60
60
|
summary: Portable mp3 player
|
61
61
|
test_files: []
|
62
|
-
has_rdoc:
|