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