audite 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.2.0"
5
+ s.version = "0.3.0"
6
6
  s.author = "Matthias Georgi"
7
7
  s.email = "matti.georgi@gmail.com"
8
8
  s.homepage = "http://georgi.github.com/audite"
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 = ((pos / player.length_in_seconds) * Curses.cols).ceil
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
@@ -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
- for (i = 0; i < framesPerBuffer * 2; i++) {
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
  }
@@ -20,37 +20,31 @@ class Audite
20
20
 
21
21
  attr_reader :events, :active
22
22
 
23
- def initialize(buffer_size = 2**14)
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.write(process(@buffer_size * 2))
32
+ process @stream.write_from_mpg(@mp3)
33
+ @stream.wait
34
34
  end
35
35
  end
36
36
  end
37
37
 
38
- def silence(samples)
39
- Array.new(samples, 0)
38
+ def level
39
+ @stream.rms
40
40
  end
41
41
 
42
- def process(samples)
43
- if tell >= length
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
- silence(samples)
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.2.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-28 00:00:00.000000000 Z
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: