2 This file is part of PulseAudio.
4 Copyright 2013 Collabora Ltd.
5 Author: Arun Raghavan <arun.raghavan@collabora.co.uk>
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <pulsecore/log.h>
30 #include <pulsecore/macro.h>
31 #include <pulsecore/core-util.h>
33 #include "lo-test-util.h"
35 /* Keep the frequency high so RMS over ranges of a few ms remains relatively
39 static void nop_free_cb(void *p
) {
42 static void underflow_cb(struct pa_stream
*s
, void *userdata
) {
43 pa_log_warn("Underflow\n");
46 static void overflow_cb(struct pa_stream
*s
, void *userdata
) {
47 pa_log_warn("Overlow\n");
51 * We run a simple volume calibration so that we know we can detect the signal
52 * being played back. We start with the playback stream at 100% volume, and
55 * First, we then play a sine wave and increase the capture volume till the
56 * signal is clearly received.
58 * Next, we play back silence and make sure that the level is low enough to
59 * distinguish from when playback is happening.
61 * Finally, we hand off to the real read/write callbacks to run the actual
71 static int cal_state
= CALIBRATION_ONE
;
73 static void calibrate_write_cb(pa_stream
*s
, size_t nbytes
, void *userdata
) {
74 pa_lo_test_context
*ctx
= (pa_lo_test_context
*) userdata
;
75 int i
, r
, nsamp
= nbytes
/ ctx
->fs
;
79 /* Write out a sine tone */
80 for (i
= 0; i
< nsamp
; i
++)
81 tmp
[i
][0] = tmp
[i
][1] = cal_state
== CALIBRATION_ONE
? sinf(count
++ * TONE_HZ
* 2 * M_PI
/ ctx
->sample_spec
.rate
) : 0.0f
;
83 r
= pa_stream_write(s
, &tmp
, nbytes
, nop_free_cb
, 0, PA_SEEK_RELATIVE
);
86 if (cal_state
== CALIBRATION_DONE
)
87 pa_stream_set_write_callback(s
, ctx
->write_cb
, ctx
);
90 static void calibrate_read_cb(pa_stream
*s
, size_t nbytes
, void *userdata
) {
91 pa_lo_test_context
*ctx
= (pa_lo_test_context
*) userdata
;
93 static int skip
= 0, confirm
;
101 r
= pa_stream_peek(s
, (const void **)&in
, &l
);
106 /* For each state or volume step change, throw out a few samples so we know
107 * we're seeing the changed samples. */
114 case CALIBRATION_ONE
:
115 /* Try to detect the sine wave. RMS is 0.5, */
116 if (pa_rms(in
, nsamp
) < 0.40f
) {
121 pa_log_error("Capture signal too weak at 100%% volume (%g). Giving up.\n", pa_rms(in
, nsamp
));
122 pa_assert_not_reached();
125 pa_cvolume_set(&vol
, ctx
->sample_spec
.channels
, v
* PA_VOLUME_NORM
);
126 o
= pa_context_set_source_output_volume(ctx
->context
, pa_stream_get_index(s
), &vol
, NULL
, NULL
);
127 pa_assert(o
!= NULL
);
128 pa_operation_unref(o
);
130 /* Make sure the signal strength is steadily above our threshold */
133 pa_log_debug(stderr
, "Capture volume = %g (%g)\n", v
, pa_rms(in
, nsamp
));
135 cal_state
= CALIBRATION_ZERO
;
141 case CALIBRATION_ZERO
:
142 /* Now make sure silence doesn't trigger a false positive because
144 if (pa_rms(in
, nsamp
) > 0.1f
) {
145 fprintf(stderr
, "Too much noise on capture (%g). Giving up.\n", pa_rms(in
, nsamp
));
146 pa_assert_not_reached();
149 cal_state
= CALIBRATION_DONE
;
150 pa_stream_set_read_callback(s
, ctx
->read_cb
, ctx
);
162 /* This routine is called whenever the stream state changes */
163 static void stream_state_callback(pa_stream
*s
, void *userdata
) {
164 pa_lo_test_context
*ctx
= (pa_lo_test_context
*) userdata
;
166 switch (pa_stream_get_state(s
)) {
167 case PA_STREAM_UNCONNECTED
:
168 case PA_STREAM_CREATING
:
169 case PA_STREAM_TERMINATED
:
172 case PA_STREAM_READY
: {
176 /* Set volumes for calibration */
177 if (s
== ctx
->play_stream
) {
178 pa_cvolume_set(&vol
, ctx
->sample_spec
.channels
, PA_VOLUME_NORM
);
179 o
= pa_context_set_sink_input_volume(ctx
->context
, pa_stream_get_index(s
), &vol
, NULL
, NULL
);
181 pa_cvolume_set(&vol
, ctx
->sample_spec
.channels
, pa_sw_volume_from_linear(0.0));
182 o
= pa_context_set_source_output_volume(ctx
->context
, pa_stream_get_index(s
), &vol
, NULL
, NULL
);
186 pa_log_error("Could not set stream volume: %s\n", pa_strerror(pa_context_errno(ctx
->context
)));
187 pa_assert_not_reached();
189 pa_operation_unref(o
);
194 case PA_STREAM_FAILED
:
196 pa_log_error("Stream error: %s\n", pa_strerror(pa_context_errno(ctx
->context
)));
197 pa_assert_not_reached();
201 /* This is called whenever the context status changes */
202 static void context_state_callback(pa_context
*c
, void *userdata
) {
203 pa_lo_test_context
*ctx
= (pa_lo_test_context
*) userdata
;
204 pa_mainloop_api
*api
;
206 switch (pa_context_get_state(c
)) {
207 case PA_CONTEXT_CONNECTING
:
208 case PA_CONTEXT_AUTHORIZING
:
209 case PA_CONTEXT_SETTING_NAME
:
212 case PA_CONTEXT_READY
: {
213 pa_buffer_attr buffer_attr
;
217 /* Create playback stream */
218 buffer_attr
.maxlength
= -1;
219 buffer_attr
.tlength
= ctx
->sample_spec
.rate
* ctx
->fs
* ctx
->play_latency
/ 1000;
220 buffer_attr
.prebuf
= 0; /* Setting prebuf to 0 guarantees us the stream will run synchronously, no matter what */
221 buffer_attr
.minreq
= -1;
222 buffer_attr
.fragsize
= -1;
224 ctx
->play_stream
= pa_stream_new(c
, "loopback: play", &ctx
->sample_spec
, NULL
);
225 pa_assert(ctx
->play_stream
!= NULL
);
226 pa_stream_set_state_callback(ctx
->play_stream
, stream_state_callback
, ctx
);
227 pa_stream_set_write_callback(ctx
->play_stream
, calibrate_write_cb
, ctx
);
228 pa_stream_set_underflow_callback(ctx
->play_stream
, underflow_cb
, userdata
);
230 pa_stream_connect_playback(ctx
->play_stream
, getenv("TEST_SINK"), &buffer_attr
,
231 PA_STREAM_ADJUST_LATENCY
| PA_STREAM_AUTO_TIMING_UPDATE
, NULL
, NULL
);
233 /* Create capture stream */
234 buffer_attr
.maxlength
= -1;
235 buffer_attr
.tlength
= (uint32_t) -1;
236 buffer_attr
.prebuf
= 0;
237 buffer_attr
.minreq
= (uint32_t) -1;
238 buffer_attr
.fragsize
= ctx
->sample_spec
.rate
* ctx
->fs
* ctx
->rec_latency
/ 1000;
240 ctx
->rec_stream
= pa_stream_new(c
, "loopback: rec", &ctx
->sample_spec
, NULL
);
241 pa_assert(ctx
->rec_stream
!= NULL
);
242 pa_stream_set_state_callback(ctx
->rec_stream
, stream_state_callback
, ctx
);
243 pa_stream_set_read_callback(ctx
->rec_stream
, calibrate_read_cb
, ctx
);
244 pa_stream_set_overflow_callback(ctx
->rec_stream
, overflow_cb
, userdata
);
246 pa_stream_connect_record(ctx
->rec_stream
, getenv("TEST_SOURCE"), &buffer_attr
,
247 PA_STREAM_ADJUST_LATENCY
| PA_STREAM_AUTO_TIMING_UPDATE
);
252 case PA_CONTEXT_TERMINATED
:
253 api
= pa_mainloop_get_api(ctx
->mainloop
);
257 case PA_CONTEXT_FAILED
:
259 pa_log_error("Context error: %s\n", pa_strerror(pa_context_errno(c
)));
260 pa_assert_not_reached();
264 int pa_lo_test_init(pa_lo_test_context
*ctx
) {
265 /* FIXME: need to deal with non-float samples at some point */
266 pa_assert(ctx
->sample_spec
.format
== PA_SAMPLE_FLOAT32
);
268 ctx
->ss
= pa_sample_size(&ctx
->sample_spec
);
269 ctx
->fs
= pa_frame_size(&ctx
->sample_spec
);
271 ctx
->mainloop
= pa_mainloop_new();
272 ctx
->context
= pa_context_new(pa_mainloop_get_api(ctx
->mainloop
), ctx
->context_name
);
274 pa_context_set_state_callback(ctx
->context
, context_state_callback
, ctx
);
276 /* Connect the context */
277 if (pa_context_connect(ctx
->context
, NULL
, PA_CONTEXT_NOFLAGS
, NULL
) < 0) {
278 pa_log_error("pa_context_connect() failed.\n");
285 pa_context_unref(ctx
->context
);
286 pa_mainloop_free(ctx
->mainloop
);
291 int pa_lo_test_run(pa_lo_test_context
*ctx
) {
294 if (pa_mainloop_run(ctx
->mainloop
, &ret
) < 0) {
295 pa_log_error("pa_mainloop_run() failed.\n");
302 void pa_lo_test_deinit(pa_lo_test_context
*ctx
) {
303 if (ctx
->play_stream
) {
304 pa_stream_disconnect(ctx
->play_stream
);
305 pa_stream_unref(ctx
->play_stream
);
308 if (ctx
->rec_stream
) {
309 pa_stream_disconnect(ctx
->rec_stream
);
310 pa_stream_unref(ctx
->rec_stream
);
314 pa_context_unref(ctx
->context
);
317 pa_mainloop_free(ctx
->mainloop
);
320 float pa_rms(const float *s
, int n
) {
324 for (i
= 0; i
< n
; i
++)
327 return sqrtf(sq
/ n
);