ruby-alsa 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *~
3
+ coverage/*
4
+ spec.html
5
+ log/*
data/Gemfile CHANGED
@@ -1,9 +1,2 @@
1
- source :gemcutter
2
-
3
- gem "hoe"
4
- gem "newgem"
5
- gem "ffi"
6
-
7
- group :test do
8
- gem 'rspec'
9
- end
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby-alsa (0.8)
5
+ ffi (>= 0.6.3)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ZenTest (4.5.0)
11
+ autotest (4.4.6)
12
+ ZenTest (>= 4.4.1)
13
+ diff-lcs (1.1.2)
14
+ ffi (1.0.6)
15
+ rake (>= 0.8.7)
16
+ rake (0.8.7)
17
+ rake-debian-build (1.0.16)
18
+ rcov (0.9.9)
19
+ rspec (2.5.0)
20
+ rspec-core (~> 2.5.0)
21
+ rspec-expectations (~> 2.5.0)
22
+ rspec-mocks (~> 2.5.0)
23
+ rspec-core (2.5.1)
24
+ rspec-expectations (2.5.0)
25
+ diff-lcs (~> 1.1.2)
26
+ rspec-mocks (2.5.0)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ autotest
33
+ ffi (>= 0.6.3)
34
+ rake
35
+ rake-debian-build
36
+ rcov
37
+ rspec
38
+ ruby-alsa!
data/Rakefile CHANGED
@@ -1,31 +1,4 @@
1
- require 'rubygems'
2
- gem 'hoe', '>= 2.1.0'
3
- require 'hoe'
4
- require 'fileutils'
5
- require 'lib/alsa.rb'
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
6
3
 
7
- rubyforge_user_config = File.expand_path("~/.rubyforge/user-config.yml")
8
- unless File.exists?(rubyforge_user_config)
9
- mkdir_p File.dirname(rubyforge_user_config)
10
- touch rubyforge_user_config
11
- end
12
-
13
- Hoe.plugin :newgem
14
- # Hoe.plugin :website
15
- # Hoe.plugin :cucumberfeatures
16
-
17
- # Generate all the Rake tasks
18
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
19
- $hoe = Hoe.spec 'ruby-alsa' do
20
- self.developer 'Alban Peignier', 'alban@tryphon.eu'
21
- self.rubyforge_name = self.name # TODO this is default value
22
- self.extra_deps = [['ffi','>= 0.6.3']]
23
- self.url = 'http://projects.tryphon.eu/ruby-alsa'
24
- end
25
-
26
- require 'newgem/tasks'
27
4
  Dir['tasks/**/*.rake'].each { |t| load t }
28
-
29
- # TODO - want other tests/tasks run by default? Add them to the list
30
- # remove_task :default
31
- # task :default => [:spec, :features]
@@ -75,6 +75,25 @@ module ALSA::PCM
75
75
  end
76
76
  end
77
77
 
78
+ def buffer_time_max
79
+ value = nil
80
+ ALSA::try_to "get buffer time max" do
81
+ value_pointer = FFI::MemoryPointer.new(:int)
82
+ dir_pointer = FFI::MemoryPointer.new(:int)
83
+ dir_pointer.write_int(0)
84
+
85
+ error_code = ALSA::PCM::Native::hw_params_get_buffer_time_max self.handle, value_pointer, dir_pointer
86
+
87
+ value = value_pointer.read_int
88
+
89
+ value_pointer.free
90
+ dir_pointer.free
91
+
92
+ error_code
93
+ end
94
+ value
95
+ end
96
+
78
97
  def period_time
79
98
  value = nil
80
99
  ALSA::try_to "get period time" do
@@ -53,6 +53,7 @@ module ALSA::PCM
53
53
  attach_function :hw_params_set_period_time_near, :snd_pcm_hw_params_set_period_time_near, [ :pointer, :pointer, :pointer, :pointer ], :int
54
54
  attach_function :hw_params_get_period_time, :snd_pcm_hw_params_get_period_time, [ :pointer, :pointer, :pointer ], :int
55
55
  attach_function :hw_params_get_period_size, :snd_pcm_hw_params_get_period_size, [ :pointer, :pointer, :pointer ], :int
56
+ attach_function :hw_params_get_buffer_time_max, :snd_pcm_hw_params_get_buffer_time_max, [ :pointer, :pointer, :pointer ], :int
56
57
  attach_function :hw_params_get_buffer_size, :snd_pcm_hw_params_get_buffer_size, [ :pointer, :pointer ], :int
57
58
  attach_function :hw_params_set_buffer_time_near, :snd_pcm_hw_params_set_buffer_time_near, [ :pointer, :pointer, :pointer, :pointer ], :int
58
59
 
@@ -27,7 +27,7 @@ module ALSA::PCM
27
27
  self.hardware_parameters = hardware_attributes
28
28
 
29
29
  change_software_parameters do |sw_params|
30
- sw_params.available_minimum = buffer_frame_count / 2
30
+ sw_params.available_minimum = buffer_frame_count / 4
31
31
  end
32
32
 
33
33
  ALSA::PCM::Native.prepare(handle)
@@ -56,6 +56,7 @@ module ALSA::PCM
56
56
 
57
57
  begin
58
58
  yield hw_params
59
+ hw_params.update_attributes :buffer_time => hw_params.buffer_time_max
59
60
 
60
61
  ALSA::try_to "set hw parameters" do
61
62
  ALSA::PCM::Native::hw_params self.handle, hw_params.handle
@@ -0,0 +1,3 @@
1
+ module ALSA
2
+ VERSION = "0.8"
3
+ end
data/refs/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ alsa_player
2
+ alsa_player_async
@@ -0,0 +1,354 @@
1
+ /*
2
+ ciacciax@yahoo.com
3
+ http://mailman.alsa-project.org/pipermail/alsa-devel/2007-May/001100.html
4
+
5
+ gcc -o alsa_fullduplex -lasound alsa_fullduplex.c
6
+ */
7
+
8
+ #include <sys/time.h>
9
+ #include <sys/resource.h>
10
+ #include <alsa/asoundlib.h>
11
+ #include <stdio.h>
12
+ #include <math.h>
13
+
14
+ #define PLAYBACK 0
15
+ #define CAPTURE 1
16
+ #define CARD "hw:0,0"
17
+
18
+ static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
19
+ static unsigned int rate = 44100; /* stream rate */
20
+ static snd_pcm_uframes_t period_size = 512; /* period size */
21
+ static short buffer[512];
22
+
23
+ typedef void (*area_callback)(const snd_pcm_channel_area_t*, snd_pcm_uframes_t, int);
24
+
25
+ void capture_area(const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, int count);
26
+ void playback_area(const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, int count);
27
+
28
+ struct STREAM_DESC {
29
+ char *name;
30
+ snd_pcm_stream_t stream;
31
+ area_callback func;
32
+ unsigned short poll_flag;
33
+ };
34
+
35
+ struct STREAM_DESC descriptors[2] = {
36
+ { .name = "PLAYBACK",
37
+ .stream = SND_PCM_STREAM_PLAYBACK,
38
+ .func = playback_area,
39
+ .poll_flag = POLLOUT,
40
+ },
41
+ { .name = "CAPTURE",
42
+ .stream = SND_PCM_STREAM_CAPTURE,
43
+ .func = capture_area,
44
+ .poll_flag = POLLIN,
45
+ }
46
+ };
47
+
48
+ void capture_area(const snd_pcm_channel_area_t *areas,
49
+ snd_pcm_uframes_t offset, int count)
50
+ {
51
+ short *isamples; // interleaved samples
52
+ short *dst;
53
+
54
+ assert(areas[0].first == 0);
55
+ assert(areas[0].step == 2 * 8 * sizeof(short));
56
+ assert(areas[1].first == 8 * sizeof(short));
57
+ assert(areas[1].step == 2 * 8 * sizeof(short));
58
+ assert(areas[0].addr == areas[1].addr);
59
+ assert(count == 512);
60
+
61
+ isamples = (short *)areas[0].addr + offset * 2; // offset for two interleaved channels
62
+ dst = buffer;
63
+
64
+ while (count-- > 0)
65
+ {
66
+ *dst++ = (short)*isamples;
67
+ isamples++;
68
+ isamples++;
69
+ }
70
+ }
71
+
72
+ void playback_area(const snd_pcm_channel_area_t *areas,
73
+ snd_pcm_uframes_t offset, int count)
74
+ {
75
+ short *isamples; // interleaved samples
76
+ short *src;
77
+
78
+ assert(areas[0].first == 0);
79
+ assert(areas[0].step == 2 * 8 * sizeof(short));
80
+ assert(areas[1].first == 8 * sizeof(short));
81
+ assert(areas[1].step == 2 * 8 * sizeof(short));
82
+ assert(areas[0].addr == areas[1].addr);
83
+ assert(count == 512);
84
+
85
+ isamples = (short *)areas[0].addr + offset * 2; // offset for two interleaved channels
86
+ src = buffer;
87
+
88
+ while (count-- > 0)
89
+ {
90
+ *isamples++ = *src;
91
+ *isamples++ = *src;
92
+ src++;
93
+ }
94
+ }
95
+
96
+ /*
97
+ * Underrun and suspend recovery
98
+ */
99
+ static int xrun_recovery(snd_pcm_t *handle, int err)
100
+ {
101
+ printf("xrun_recovery...\n");
102
+ if (err == -EPIPE) { /* under-run */
103
+ err = snd_pcm_prepare(handle);
104
+ if (err < 0)
105
+ printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
106
+ return 0;
107
+ } else if (err == -ESTRPIPE) {
108
+ while ((err = snd_pcm_resume(handle)) == -EAGAIN)
109
+ sleep(1); /* wait until the suspend flag is released */
110
+ if (err < 0) {
111
+ err = snd_pcm_prepare(handle);
112
+ if (err < 0)
113
+ printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
114
+ }
115
+ return 0;
116
+ }
117
+ return err;
118
+ }
119
+
120
+ int setparams(const char *name, snd_pcm_t **handle, snd_pcm_stream_t stream)
121
+ {
122
+ int err, dir;
123
+ unsigned int rrate;
124
+ snd_pcm_hw_params_t *hw_params;
125
+
126
+ snd_pcm_uframes_t buffer_size;
127
+
128
+ if ((err = snd_pcm_open (handle, name, stream, SND_PCM_NONBLOCK)) < 0) {
129
+ fprintf (stderr, "cannot open audio device %s (%s)\n",
130
+ name,
131
+ snd_strerror (err));
132
+ return err;
133
+ }
134
+
135
+ if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
136
+ fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
137
+ snd_strerror (err));
138
+ return err;
139
+ }
140
+
141
+ if ((err = snd_pcm_hw_params_any (*handle, hw_params)) < 0) {
142
+ fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
143
+ snd_strerror (err));
144
+ return err;
145
+ }
146
+
147
+ if ((err = snd_pcm_hw_params_set_access (*handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
148
+ fprintf (stderr, "cannot set access type (%s)\n",
149
+ snd_strerror (err));
150
+ return err;
151
+ }
152
+
153
+ if ((err = snd_pcm_hw_params_set_format (*handle, hw_params, format)) < 0) {
154
+ fprintf (stderr, "cannot set sample format (%s)\n",
155
+ snd_strerror (err));
156
+ return err;
157
+ }
158
+
159
+ // Rate
160
+ rrate = rate;
161
+ printf("Trying to set rate near %d\n", rrate);
162
+ if ((err = snd_pcm_hw_params_set_rate_near (*handle, hw_params, &rrate, 0)) < 0) {
163
+ fprintf (stderr, "cannot set sample rate (%s)\n",
164
+ snd_strerror (err));
165
+ return err;
166
+ }
167
+ printf("Rate set to %d\n", rrate);
168
+
169
+ // Buffer (in frames size)
170
+ printf("Trying to set buffer size %d\n", 1024);
171
+ if ((err = snd_pcm_hw_params_set_buffer_size (*handle, hw_params, 1024)) < 0) {
172
+ fprintf (stderr, "cannot set buffer size (%s)\n",
173
+ snd_strerror (err));
174
+ return err;
175
+ }
176
+
177
+ if ((err = snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size)) < 0) {
178
+ fprintf (stderr, "cannot get buffer size (%s)\n",
179
+ snd_strerror (err));
180
+ return err;
181
+ }
182
+ printf("Buffer size set to %ld\n", buffer_size);
183
+
184
+ printf("Trying to set period to %ld\n", period_size);
185
+ dir = 0;
186
+ if ((err = snd_pcm_hw_params_set_period_size (*handle, hw_params, period_size, dir)) < 0) {
187
+ fprintf (stderr, "cannot set period_size (%s)\n",
188
+ snd_strerror (err));
189
+ return err;
190
+ }
191
+ printf("Period set to %ld\n", period_size);
192
+
193
+ if ((err = snd_pcm_hw_params_set_channels (*handle, hw_params, 2)) < 0) {
194
+ fprintf (stderr, "cannot set channel count (%s)\n",
195
+ snd_strerror (err));
196
+ return err;
197
+ }
198
+
199
+ if ((err = snd_pcm_hw_params (*handle, hw_params)) < 0) {
200
+ fprintf (stderr, "cannot set parameters (%s)\n",
201
+ snd_strerror (err));
202
+ return err;
203
+ }
204
+
205
+ snd_pcm_hw_params_free (hw_params);
206
+
207
+ if ((err = snd_pcm_prepare (*handle)) < 0) {
208
+ fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
209
+ snd_strerror (err));
210
+ return err;
211
+ }
212
+ return 0;
213
+ }
214
+
215
+ int transfer_loop(snd_pcm_t *handle, int *first, area_callback func)
216
+ {
217
+ const snd_pcm_channel_area_t *my_areas;
218
+ snd_pcm_uframes_t offset, frames, size;
219
+ snd_pcm_sframes_t avail, commitres;
220
+ snd_pcm_state_t state;
221
+ int err;
222
+
223
+ while (1) {
224
+ state = snd_pcm_state(handle);
225
+ if (state == SND_PCM_STATE_XRUN) {
226
+ err = xrun_recovery(handle, -EPIPE);
227
+ if (err < 0) {
228
+ printf("XRUN recovery failed: %s\n", snd_strerror(err));
229
+ return err;
230
+ }
231
+ *first = 1;
232
+ } else if (state == SND_PCM_STATE_SUSPENDED) {
233
+ err = xrun_recovery(handle, -ESTRPIPE);
234
+ if (err < 0) {
235
+ printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
236
+ return err;
237
+ }
238
+ }
239
+ avail = snd_pcm_avail_update(handle);
240
+ if (avail < 0) {
241
+ err = xrun_recovery(handle, avail);
242
+ if (err < 0) {
243
+ printf("avail update failed: %s\n", snd_strerror(err));
244
+ return err;
245
+ }
246
+ *first = 1;
247
+ continue;
248
+ }
249
+ if (avail < period_size) {
250
+ if (*first) {
251
+ *first = 0;
252
+ printf("snd_pcm_start\n");
253
+ err = snd_pcm_start(handle);
254
+ if (err < 0) {
255
+ printf("Start error: %s\n", snd_strerror(err));
256
+ exit(EXIT_FAILURE);
257
+ }
258
+ } else {
259
+ // err = snd_pcm_wait(playback_handle, -1);
260
+ // return to the main loop (poll)
261
+ return 0;
262
+ }
263
+ continue;
264
+ }
265
+ size = period_size;
266
+ while (size > 0) {
267
+ frames = size;
268
+ err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
269
+ if (err < 0) {
270
+ if ((err = xrun_recovery(handle, err)) < 0) {
271
+ printf("MMAP begin avail error: %s\n", snd_strerror(err));
272
+ exit(EXIT_FAILURE);
273
+ }
274
+ *first = 1;
275
+ }
276
+
277
+ func(my_areas, offset, frames);
278
+
279
+ commitres = snd_pcm_mmap_commit(handle, offset, frames);
280
+ if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
281
+ if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
282
+ printf("MMAP commit error: %s\n", snd_strerror(err));
283
+ exit(EXIT_FAILURE);
284
+ }
285
+ *first = 1;
286
+ }
287
+ size -= frames;
288
+ }
289
+ }
290
+ }
291
+
292
+ int main(int argc, char *argv[])
293
+ {
294
+ int i, err;
295
+ snd_pcm_t *handles[2];
296
+ struct pollfd ufds[2];
297
+ unsigned short revents;
298
+ int first[2] = {1, 0};
299
+
300
+ for (i = 0; i < 2; i++)
301
+ {
302
+ if (setparams(CARD, &handles[i], descriptors[i].stream) < 0)
303
+ {
304
+ printf("Could not initialize stream %s\n", descriptors[i].name);
305
+ exit(1);
306
+ }
307
+
308
+ assert(snd_pcm_poll_descriptors_count(handles[i]) == 1);
309
+ if ((err = snd_pcm_poll_descriptors(handles[i], &ufds[i], 1)) < 0)
310
+ {
311
+ printf("Unable to obtain poll descriptors for stream %s\n", descriptors[i].name);
312
+ exit(1);
313
+ }
314
+ }
315
+
316
+ // The capture stream has to be started manually...
317
+ err = snd_pcm_start(handles[CAPTURE]);
318
+ if (err < 0)
319
+ {
320
+ printf("Start error: %s\n", snd_strerror(err));
321
+ exit(EXIT_FAILURE);
322
+ }
323
+
324
+ while (1) {
325
+ err = poll(ufds, 2, -1);
326
+
327
+ for (i = 0; i < 2; i++)
328
+ {
329
+ if (snd_pcm_poll_descriptors_revents(handles[i], &ufds[i], 1, &revents) < 0)
330
+ {
331
+ printf("Error getting revents for %s\n", descriptors[i].name);
332
+ exit(1);
333
+ }
334
+
335
+ if (revents & descriptors[i].poll_flag)
336
+ {
337
+ if (transfer_loop(handles[i], &first[i], descriptors[i].func) < 0)
338
+ {
339
+ printf("transfer_loop error for %s\n", descriptors[i].name);
340
+ // TODO exit?!?
341
+ }
342
+ else {
343
+ printf(".");
344
+ }
345
+ }
346
+ }
347
+ }
348
+
349
+ for (i = 0; i < 2; i++)
350
+ {
351
+ snd_pcm_close (handles[i]);
352
+ }
353
+ exit (0);
354
+ }