]> code.delx.au - pulseaudio/blob - src/utils/paplay.c
replace memory allocation function calls with pa_xXXXX()
[pulseaudio] / src / utils / paplay.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <signal.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <getopt.h>
34
35 #include <sndfile.h>
36
37 #include <polyp/polypaudio.h>
38
39 #if PA_API_VERSION != 9
40 #error Invalid Polypaudio API version
41 #endif
42
43 static pa_context *context = NULL;
44 static pa_stream *stream = NULL;
45 static pa_mainloop_api *mainloop_api = NULL;
46
47 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
48
49 static int verbose = 0;
50 static pa_volume_t volume = PA_VOLUME_NORM;
51
52 static SNDFILE* sndfile = NULL;
53
54 static pa_sample_spec sample_spec = { 0, 0, 0 };
55 static pa_channel_map channel_map;
56 static int channel_map_set = 0;
57
58 static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
59
60 /* A shortcut for terminating the application */
61 static void quit(int ret) {
62 assert(mainloop_api);
63 mainloop_api->quit(mainloop_api, ret);
64 }
65
66 /* Connection draining complete */
67 static void context_drain_complete(pa_context*c, void *userdata) {
68 pa_context_disconnect(c);
69 }
70
71 /* Stream draining complete */
72 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
73 pa_operation *o;
74
75 if (!success) {
76 fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
77 quit(1);
78 }
79
80 if (verbose)
81 fprintf(stderr, "Playback stream drained.\n");
82
83 pa_stream_disconnect(stream);
84 pa_stream_unref(stream);
85 stream = NULL;
86
87 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
88 pa_context_disconnect(context);
89 else {
90 pa_operation_unref(o);
91
92 if (verbose)
93 fprintf(stderr, "Draining connection to server.\n");
94 }
95 }
96
97 /* This is called whenever new data may be written to the stream */
98 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
99 sf_count_t bytes;
100 void *data;
101 assert(s && length);
102
103 if (!sndfile)
104 return;
105
106 data = pa_xmalloc(length);
107
108 if (readf_function) {
109 size_t k = pa_frame_size(&sample_spec);
110
111 if ((bytes = readf_function(sndfile, data, length/k)) > 0)
112 bytes *= k;
113
114 } else
115 bytes = sf_read_raw(sndfile, data, length);
116
117 if (bytes > 0)
118 pa_stream_write(s, data, bytes, pa_xfree, 0, PA_SEEK_RELATIVE);
119 else
120 pa_xfree(data);
121
122 if (bytes < length) {
123 sf_close(sndfile);
124 sndfile = NULL;
125 pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL));
126 }
127 }
128
129 /* This routine is called whenever the stream state changes */
130 static void stream_state_callback(pa_stream *s, void *userdata) {
131 assert(s);
132
133 switch (pa_stream_get_state(s)) {
134 case PA_STREAM_CREATING:
135 case PA_STREAM_TERMINATED:
136 break;
137
138 case PA_STREAM_READY:
139 if (verbose)
140 fprintf(stderr, "Stream successfully created\n");
141 break;
142
143 case PA_STREAM_FAILED:
144 default:
145 fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
146 quit(1);
147 }
148 }
149
150 /* This is called whenever the context status changes */
151 static void context_state_callback(pa_context *c, void *userdata) {
152 assert(c);
153
154 switch (pa_context_get_state(c)) {
155 case PA_CONTEXT_CONNECTING:
156 case PA_CONTEXT_AUTHORIZING:
157 case PA_CONTEXT_SETTING_NAME:
158 break;
159
160 case PA_CONTEXT_READY: {
161 pa_cvolume cv;
162
163 assert(c && !stream);
164
165 if (verbose)
166 fprintf(stderr, "Connection established.\n");
167
168 stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL);
169 assert(stream);
170
171 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
172 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
173 pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL);
174
175 break;
176 }
177
178 case PA_CONTEXT_TERMINATED:
179 quit(0);
180 break;
181
182 case PA_CONTEXT_FAILED:
183 default:
184 fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
185 quit(1);
186 }
187 }
188
189 /* UNIX signal to quit recieved */
190 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
191 if (verbose)
192 fprintf(stderr, "Got SIGINT, exiting.\n");
193 quit(0);
194
195 }
196
197 static void help(const char *argv0) {
198
199 printf("%s [options] [FILE]\n\n"
200 " -h, --help Show this help\n"
201 " --version Show version\n\n"
202 " -v, --verbose Enable verbose operations\n\n"
203 " -s, --server=SERVER The name of the server to connect to\n"
204 " -d, --device=DEVICE The name of the sink/source to connect to\n"
205 " -n, --client-name=NAME How to call this client on the server\n"
206 " --stream-name=NAME How to call this stream on the server\n"
207 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
208 " --channel-map=CHANNELMAP Set the channel map to the use\n",
209 argv0);
210 }
211
212 enum {
213 ARG_VERSION = 256,
214 ARG_STREAM_NAME,
215 ARG_VOLUME,
216 ARG_CHANNELMAP
217 };
218
219 int main(int argc, char *argv[]) {
220 pa_mainloop* m = NULL;
221 int ret = 1, r, c;
222 char *bn, *server = NULL;
223 const char *filename;
224 SF_INFO sfinfo;
225
226 static const struct option long_options[] = {
227 {"device", 1, NULL, 'd'},
228 {"server", 1, NULL, 's'},
229 {"client-name", 1, NULL, 'n'},
230 {"stream-name", 1, NULL, ARG_STREAM_NAME},
231 {"version", 0, NULL, ARG_VERSION},
232 {"help", 0, NULL, 'h'},
233 {"verbose", 0, NULL, 'v'},
234 {"volume", 1, NULL, ARG_VOLUME},
235 {"channel-map", 1, NULL, ARG_CHANNELMAP},
236 {NULL, 0, NULL, 0}
237 };
238
239 if (!(bn = strrchr(argv[0], '/')))
240 bn = argv[0];
241 else
242 bn++;
243
244 while ((c = getopt_long(argc, argv, "d:s:n:hv", long_options, NULL)) != -1) {
245
246 switch (c) {
247 case 'h' :
248 help(bn);
249 ret = 0;
250 goto quit;
251
252 case ARG_VERSION:
253 printf("paplay "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
254 ret = 0;
255 goto quit;
256
257 case 'd':
258 pa_xfree(device);
259 device = pa_xstrdup(optarg);
260 break;
261
262 case 's':
263 pa_xfree(server);
264 server = pa_xstrdup(optarg);
265 break;
266
267 case 'n':
268 pa_xfree(client_name);
269 client_name = pa_xstrdup(optarg);
270 break;
271
272 case ARG_STREAM_NAME:
273 pa_xfree(stream_name);
274 stream_name = pa_xstrdup(optarg);
275 break;
276
277 case 'v':
278 verbose = 1;
279 break;
280
281 case ARG_VOLUME: {
282 int v = atoi(optarg);
283 volume = v < 0 ? 0 : v;
284 break;
285 }
286
287 case ARG_CHANNELMAP:
288 if (!pa_channel_map_parse(&channel_map, optarg)) {
289 fprintf(stderr, "Invalid channel map\n");
290 goto quit;
291 }
292
293 channel_map_set = 1;
294 break;
295
296 default:
297 goto quit;
298 }
299 }
300
301 filename = optind < argc ? argv[optind] : "STDIN";
302
303 memset(&sfinfo, 0, sizeof(sfinfo));
304
305 if (optind < argc)
306 sndfile = sf_open(filename, SFM_READ, &sfinfo);
307 else
308 sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0);
309
310 if (!sndfile) {
311 fprintf(stderr, "Failed to open file '%s'\n", filename);
312 goto quit;
313 }
314
315 sample_spec.rate = sfinfo.samplerate;
316 sample_spec.channels = sfinfo.channels;
317
318 readf_function = NULL;
319
320 switch (sfinfo.format & 0xFF) {
321 case SF_FORMAT_PCM_16:
322 case SF_FORMAT_PCM_U8:
323 case SF_FORMAT_PCM_S8:
324 sample_spec.format = PA_SAMPLE_S16NE;
325 readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
326 break;
327
328 case SF_FORMAT_ULAW:
329 sample_spec.format = PA_SAMPLE_ULAW;
330 break;
331
332 case SF_FORMAT_ALAW:
333 sample_spec.format = PA_SAMPLE_ALAW;
334 break;
335
336 case SF_FORMAT_FLOAT:
337 case SF_FORMAT_DOUBLE:
338 default:
339 sample_spec.format = PA_SAMPLE_FLOAT32NE;
340 readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
341 break;
342 }
343
344 assert(pa_sample_spec_valid(&sample_spec));
345
346 if (channel_map_set && channel_map.channels != sample_spec.channels) {
347 fprintf(stderr, "Channel map doesn't match file.\n");
348 goto quit;
349 }
350
351 if (!client_name)
352 client_name = pa_xstrdup(bn);
353
354 if (!stream_name) {
355 const char *n = sf_get_string(sndfile, SF_STR_TITLE);
356 stream_name = pa_xstrdup(n ? n : filename);
357 }
358
359 if (verbose) {
360 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
361 pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
362 fprintf(stderr, "Using sample spec '%s'\n", t);
363 }
364
365 /* Set up a new main loop */
366 if (!(m = pa_mainloop_new())) {
367 fprintf(stderr, "pa_mainloop_new() failed.\n");
368 goto quit;
369 }
370
371 mainloop_api = pa_mainloop_get_api(m);
372
373 r = pa_signal_init(mainloop_api);
374 assert(r == 0);
375 pa_signal_new(SIGINT, exit_signal_callback, NULL);
376 #ifdef SIGPIPE
377 signal(SIGPIPE, SIG_IGN);
378 #endif
379
380 /* Create a new connection context */
381 if (!(context = pa_context_new(mainloop_api, client_name))) {
382 fprintf(stderr, "pa_context_new() failed.\n");
383 goto quit;
384 }
385
386 pa_context_set_state_callback(context, context_state_callback, NULL);
387
388 /* Connect the context */
389 pa_context_connect(context, server, 0, NULL);
390
391 /* Run the main loop */
392 if (pa_mainloop_run(m, &ret) < 0) {
393 fprintf(stderr, "pa_mainloop_run() failed.\n");
394 goto quit;
395 }
396
397 quit:
398 if (stream)
399 pa_stream_unref(stream);
400
401 if (context)
402 pa_context_unref(context);
403
404 if (m) {
405 pa_signal_done();
406 pa_mainloop_free(m);
407 }
408
409 pa_xfree(server);
410 pa_xfree(device);
411 pa_xfree(client_name);
412 pa_xfree(stream_name);
413
414 if (sndfile)
415 sf_close(sndfile);
416
417 return ret;
418 }