ruby-alsa 0.7 → 0.8
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/.gitignore +5 -0
- data/Gemfile +2 -9
- data/Gemfile.lock +38 -0
- data/Rakefile +2 -29
- data/lib/alsa/pcm/hw_parameters.rb +19 -0
- data/lib/alsa/pcm/native.rb +1 -0
- data/lib/alsa/pcm/stream.rb +2 -1
- data/lib/alsa/version.rb +3 -0
- data/refs/.gitignore +2 -0
- data/refs/alsa_fullduplex.c +354 -0
- data/ruby-alsa.gemspec +21 -31
- data/script/duplex +35 -0
- data/spec/spec_helper.rb +5 -5
- data/tasks/rspec.rake +14 -12
- metadata +88 -39
- data/lib/alsa/sine.rb +0 -21
- data/log/test.log +0 -3499
- data/refs/alsa_player +0 -0
- data/refs/alsa_player_async +0 -0
- data/spec.html +0 -244
data/Gemfile
CHANGED
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 '
|
|
2
|
-
|
|
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
|
data/lib/alsa/pcm/native.rb
CHANGED
|
@@ -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
|
|
data/lib/alsa/pcm/stream.rb
CHANGED
|
@@ -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 /
|
|
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
|
data/lib/alsa/version.rb
ADDED
data/refs/.gitignore
ADDED
|
@@ -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
|
+
}
|