• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   PulseAudio is free software; you can redistribute it and/or modify
5   it under the terms of the GNU Lesser General Public License as published
6   by the Free Software Foundation; either version 2.1 of the License,
7   or (at your option) any later version.
8 
9   PulseAudio is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public License
15   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
16 ***/
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #include <signal.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <math.h>
29 
30 #include <check.h>
31 
32 #include <pulse/pulseaudio.h>
33 #include <pulse/mainloop.h>
34 
35 #define NSTREAMS 4
36 #define SINE_HZ 440
37 #define SAMPLE_HZ 8000
38 
39 static pa_context *context = NULL;
40 static pa_stream *streams[NSTREAMS];
41 static pa_mainloop_api *mainloop_api = NULL;
42 static const char *bname;
43 
44 static float data[SAMPLE_HZ]; /* one second space */
45 
46 static int n_streams_ready = 0;
47 
48 static const pa_buffer_attr buffer_attr = {
49     .maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
50     .tlength = (uint32_t) -1,
51     .prebuf = 0, /* Setting prebuf to 0 guarantees us the streams will run synchronously, no matter what */
52     .minreq = (uint32_t) -1,
53     .fragsize = 0
54 };
55 
nop_free_cb(void * p)56 static void nop_free_cb(void *p) {}
57 
underflow_cb(struct pa_stream * s,void * userdata)58 static void underflow_cb(struct pa_stream *s, void *userdata) {
59     int i = (int) (long) userdata;
60 
61     fprintf(stderr, "Stream %i finished\n", i);
62 
63     if (++n_streams_ready >= 2*NSTREAMS) {
64         fprintf(stderr, "We're done\n");
65         mainloop_api->quit(mainloop_api, 0);
66     }
67 }
68 
69 /* This routine is called whenever the stream state changes */
stream_state_callback(pa_stream * s,void * userdata)70 static void stream_state_callback(pa_stream *s, void *userdata) {
71     fail_unless(s != NULL);
72 
73     switch (pa_stream_get_state(s)) {
74         case PA_STREAM_UNCONNECTED:
75         case PA_STREAM_CREATING:
76         case PA_STREAM_TERMINATED:
77             break;
78 
79         case PA_STREAM_READY: {
80 
81             int r, i = (int) (long) userdata;
82 
83             fprintf(stderr, "Writing data to stream %i.\n", i);
84 
85             r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);
86             fail_unless(r == 0);
87 
88             /* Be notified when this stream is drained */
89             pa_stream_set_underflow_callback(s, underflow_cb, userdata);
90 
91             /* All streams have been set up, let's go! */
92             if (++n_streams_ready >= NSTREAMS) {
93                 fprintf(stderr, "Uncorking\n");
94                 pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
95             }
96 
97             break;
98         }
99 
100         default:
101         case PA_STREAM_FAILED:
102             fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
103             ck_abort();
104     }
105 }
106 
107 /* This is called whenever the context status changes */
context_state_callback(pa_context * c,void * userdata)108 static void context_state_callback(pa_context *c, void *userdata) {
109     fail_unless(c != NULL);
110 
111     switch (pa_context_get_state(c)) {
112         case PA_CONTEXT_CONNECTING:
113         case PA_CONTEXT_AUTHORIZING:
114         case PA_CONTEXT_SETTING_NAME:
115             break;
116 
117         case PA_CONTEXT_READY: {
118 
119             int i;
120             fprintf(stderr, "Connection established.\n");
121 
122             for (i = 0; i < NSTREAMS; i++) {
123                 char name[64];
124                 pa_format_info *formats[1];
125 
126                 formats[0] = pa_format_info_new();
127                 formats[0]->encoding = PA_ENCODING_PCM;
128                 pa_format_info_set_sample_format(formats[0], PA_SAMPLE_FLOAT32);
129                 pa_format_info_set_rate(formats[0], SAMPLE_HZ);
130                 pa_format_info_set_channels(formats[0], 1);
131 
132                 fprintf(stderr, "Creating stream %i\n", i);
133 
134                 snprintf(name, sizeof(name), "stream #%i", i);
135 
136                 streams[i] = pa_stream_new_extended(c, name, formats, 1, NULL);
137                 fail_unless(streams[i] != NULL);
138                 pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
139                 pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
140 
141                 pa_format_info_free(formats[0]);
142             }
143 
144             break;
145         }
146 
147         case PA_CONTEXT_TERMINATED:
148             mainloop_api->quit(mainloop_api, 0);
149             break;
150 
151         case PA_CONTEXT_FAILED:
152         default:
153             fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
154             ck_abort();
155     }
156 }
157 
START_TEST(extended_test)158 START_TEST (extended_test) {
159     pa_mainloop* m = NULL;
160     int i, ret = 1;
161 
162     for (i = 0; i < SAMPLE_HZ; i++)
163         data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
164 
165     for (i = 0; i < NSTREAMS; i++)
166         streams[i] = NULL;
167 
168     /* Set up a new main loop */
169     m = pa_mainloop_new();
170     fail_unless(m != NULL);
171 
172     mainloop_api = pa_mainloop_get_api(m);
173 
174     context = pa_context_new(mainloop_api, bname);
175     fail_unless(context != NULL);
176 
177     pa_context_set_state_callback(context, context_state_callback, NULL);
178 
179     /* Connect the context */
180     if (pa_context_connect(context, NULL, 0, NULL) < 0) {
181         fprintf(stderr, "pa_context_connect() failed.\n");
182         goto quit;
183     }
184 
185     if (pa_mainloop_run(m, &ret) < 0)
186         fprintf(stderr, "pa_mainloop_run() failed.\n");
187 
188 quit:
189     pa_context_unref(context);
190 
191     for (i = 0; i < NSTREAMS; i++)
192         if (streams[i])
193             pa_stream_unref(streams[i]);
194 
195     pa_mainloop_free(m);
196 
197     fail_unless(ret == 0);
198 }
199 END_TEST
200 
main(int argc,char * argv[])201 int main(int argc, char *argv[]) {
202     int failed = 0;
203     Suite *s;
204     TCase *tc;
205     SRunner *sr;
206 
207     bname = argv[0];
208 
209     s = suite_create("Extended");
210     tc = tcase_create("extended");
211     tcase_add_test(tc, extended_test);
212     /* 4s of audio, 0.5s grace time */
213     tcase_set_timeout(tc, 4.5);
214     suite_add_tcase(s, tc);
215 
216     sr = srunner_create(s);
217     srunner_run_all(sr, CK_NORMAL);
218     failed = srunner_ntests_failed(sr);
219     srunner_free(sr);
220 
221     return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
222 }
223