00001 /* $Id: paplay.c 1179 2006-08-01 21:04:43Z lennart $ */ 00002 00003 /*** 00004 This file is part of PulseAudio. 00005 00006 PulseAudio is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU Lesser General Public License as published 00008 by the Free Software Foundation; either version 2 of the License, 00009 or (at your option) any later version. 00010 00011 PulseAudio is distributed in the hope that it will be useful, but 00012 WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 General Public License for more details. 00015 00016 You should have received a copy of the GNU Lesser General Public License 00017 along with PulseAudio; if not, write to the Free Software 00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00019 USA. 00020 ***/ 00021 00022 #ifdef HAVE_CONFIG_H 00023 #include <config.h> 00024 #endif 00025 00026 #include <signal.h> 00027 #include <string.h> 00028 #include <errno.h> 00029 #include <unistd.h> 00030 #include <assert.h> 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 #include <getopt.h> 00034 #include <locale.h> 00035 00036 #include <sndfile.h> 00037 00038 #include <pulse/pulseaudio.h> 00039 00040 #if PA_API_VERSION < 9 00041 #error Invalid PulseAudio API version 00042 #endif 00043 00044 static pa_context *context = NULL; 00045 static pa_stream *stream = NULL; 00046 static pa_mainloop_api *mainloop_api = NULL; 00047 00048 static char *stream_name = NULL, *client_name = NULL, *device = NULL; 00049 00050 static int verbose = 0; 00051 static pa_volume_t volume = PA_VOLUME_NORM; 00052 00053 static SNDFILE* sndfile = NULL; 00054 00055 static pa_sample_spec sample_spec = { 0, 0, 0 }; 00056 static pa_channel_map channel_map; 00057 static int channel_map_set = 0; 00058 00059 static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL; 00060 00061 /* A shortcut for terminating the application */ 00062 static void quit(int ret) { 00063 assert(mainloop_api); 00064 mainloop_api->quit(mainloop_api, ret); 00065 } 00066 00067 /* Connection draining complete */ 00068 static void context_drain_complete(pa_context*c, void *userdata) { 00069 pa_context_disconnect(c); 00070 } 00071 00072 /* Stream draining complete */ 00073 static void stream_drain_complete(pa_stream*s, int success, void *userdata) { 00074 pa_operation *o; 00075 00076 if (!success) { 00077 fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context))); 00078 quit(1); 00079 } 00080 00081 if (verbose) 00082 fprintf(stderr, "Playback stream drained.\n"); 00083 00084 pa_stream_disconnect(stream); 00085 pa_stream_unref(stream); 00086 stream = NULL; 00087 00088 if (!(o = pa_context_drain(context, context_drain_complete, NULL))) 00089 pa_context_disconnect(context); 00090 else { 00091 pa_operation_unref(o); 00092 00093 if (verbose) 00094 fprintf(stderr, "Draining connection to server.\n"); 00095 } 00096 } 00097 00098 /* This is called whenever new data may be written to the stream */ 00099 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { 00100 sf_count_t bytes; 00101 void *data; 00102 assert(s && length); 00103 00104 if (!sndfile) 00105 return; 00106 00107 data = pa_xmalloc(length); 00108 00109 if (readf_function) { 00110 size_t k = pa_frame_size(&sample_spec); 00111 00112 if ((bytes = readf_function(sndfile, data, length/k)) > 0) 00113 bytes *= k; 00114 00115 } else 00116 bytes = sf_read_raw(sndfile, data, length); 00117 00118 if (bytes > 0) 00119 pa_stream_write(s, data, bytes, pa_xfree, 0, PA_SEEK_RELATIVE); 00120 else 00121 pa_xfree(data); 00122 00123 if (bytes < length) { 00124 sf_close(sndfile); 00125 sndfile = NULL; 00126 pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL)); 00127 } 00128 } 00129 00130 /* This routine is called whenever the stream state changes */ 00131 static void stream_state_callback(pa_stream *s, void *userdata) { 00132 assert(s); 00133 00134 switch (pa_stream_get_state(s)) { 00135 case PA_STREAM_CREATING: 00136 case PA_STREAM_TERMINATED: 00137 break; 00138 00139 case PA_STREAM_READY: 00140 if (verbose) 00141 fprintf(stderr, "Stream successfully created\n"); 00142 break; 00143 00144 case PA_STREAM_FAILED: 00145 default: 00146 fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); 00147 quit(1); 00148 } 00149 } 00150 00151 /* This is called whenever the context status changes */ 00152 static void context_state_callback(pa_context *c, void *userdata) { 00153 assert(c); 00154 00155 switch (pa_context_get_state(c)) { 00156 case PA_CONTEXT_CONNECTING: 00157 case PA_CONTEXT_AUTHORIZING: 00158 case PA_CONTEXT_SETTING_NAME: 00159 break; 00160 00161 case PA_CONTEXT_READY: { 00162 pa_cvolume cv; 00163 00164 assert(c && !stream); 00165 00166 if (verbose) 00167 fprintf(stderr, "Connection established.\n"); 00168 00169 stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL); 00170 assert(stream); 00171 00172 pa_stream_set_state_callback(stream, stream_state_callback, NULL); 00173 pa_stream_set_write_callback(stream, stream_write_callback, NULL); 00174 pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL); 00175 00176 break; 00177 } 00178 00179 case PA_CONTEXT_TERMINATED: 00180 quit(0); 00181 break; 00182 00183 case PA_CONTEXT_FAILED: 00184 default: 00185 fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); 00186 quit(1); 00187 } 00188 } 00189 00190 /* UNIX signal to quit recieved */ 00191 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { 00192 if (verbose) 00193 fprintf(stderr, "Got SIGINT, exiting.\n"); 00194 quit(0); 00195 00196 } 00197 00198 static void help(const char *argv0) { 00199 00200 printf("%s [options] [FILE]\n\n" 00201 " -h, --help Show this help\n" 00202 " --version Show version\n\n" 00203 " -v, --verbose Enable verbose operations\n\n" 00204 " -s, --server=SERVER The name of the server to connect to\n" 00205 " -d, --device=DEVICE The name of the sink/source to connect to\n" 00206 " -n, --client-name=NAME How to call this client on the server\n" 00207 " --stream-name=NAME How to call this stream on the server\n" 00208 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n" 00209 " --channel-map=CHANNELMAP Set the channel map to the use\n", 00210 argv0); 00211 } 00212 00213 enum { 00214 ARG_VERSION = 256, 00215 ARG_STREAM_NAME, 00216 ARG_VOLUME, 00217 ARG_CHANNELMAP 00218 }; 00219 00220 int main(int argc, char *argv[]) { 00221 pa_mainloop* m = NULL; 00222 int ret = 1, r, c; 00223 char *bn, *server = NULL; 00224 const char *filename; 00225 SF_INFO sfinfo; 00226 00227 static const struct option long_options[] = { 00228 {"device", 1, NULL, 'd'}, 00229 {"server", 1, NULL, 's'}, 00230 {"client-name", 1, NULL, 'n'}, 00231 {"stream-name", 1, NULL, ARG_STREAM_NAME}, 00232 {"version", 0, NULL, ARG_VERSION}, 00233 {"help", 0, NULL, 'h'}, 00234 {"verbose", 0, NULL, 'v'}, 00235 {"volume", 1, NULL, ARG_VOLUME}, 00236 {"channel-map", 1, NULL, ARG_CHANNELMAP}, 00237 {NULL, 0, NULL, 0} 00238 }; 00239 00240 setlocale(LC_ALL, ""); 00241 00242 if (!(bn = strrchr(argv[0], '/'))) 00243 bn = argv[0]; 00244 else 00245 bn++; 00246 00247 while ((c = getopt_long(argc, argv, "d:s:n:hv", long_options, NULL)) != -1) { 00248 00249 switch (c) { 00250 case 'h' : 00251 help(bn); 00252 ret = 0; 00253 goto quit; 00254 00255 case ARG_VERSION: 00256 printf("paplay "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version()); 00257 ret = 0; 00258 goto quit; 00259 00260 case 'd': 00261 pa_xfree(device); 00262 device = pa_xstrdup(optarg); 00263 break; 00264 00265 case 's': 00266 pa_xfree(server); 00267 server = pa_xstrdup(optarg); 00268 break; 00269 00270 case 'n': 00271 pa_xfree(client_name); 00272 client_name = pa_xstrdup(optarg); 00273 break; 00274 00275 case ARG_STREAM_NAME: 00276 pa_xfree(stream_name); 00277 stream_name = pa_xstrdup(optarg); 00278 break; 00279 00280 case 'v': 00281 verbose = 1; 00282 break; 00283 00284 case ARG_VOLUME: { 00285 int v = atoi(optarg); 00286 volume = v < 0 ? 0 : v; 00287 break; 00288 } 00289 00290 case ARG_CHANNELMAP: 00291 if (!pa_channel_map_parse(&channel_map, optarg)) { 00292 fprintf(stderr, "Invalid channel map\n"); 00293 goto quit; 00294 } 00295 00296 channel_map_set = 1; 00297 break; 00298 00299 default: 00300 goto quit; 00301 } 00302 } 00303 00304 filename = optind < argc ? argv[optind] : "STDIN"; 00305 00306 memset(&sfinfo, 0, sizeof(sfinfo)); 00307 00308 if (optind < argc) 00309 sndfile = sf_open(filename, SFM_READ, &sfinfo); 00310 else 00311 sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0); 00312 00313 if (!sndfile) { 00314 fprintf(stderr, "Failed to open file '%s'\n", filename); 00315 goto quit; 00316 } 00317 00318 sample_spec.rate = sfinfo.samplerate; 00319 sample_spec.channels = sfinfo.channels; 00320 00321 readf_function = NULL; 00322 00323 switch (sfinfo.format & 0xFF) { 00324 case SF_FORMAT_PCM_16: 00325 case SF_FORMAT_PCM_U8: 00326 case SF_FORMAT_PCM_S8: 00327 sample_spec.format = PA_SAMPLE_S16NE; 00328 readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short; 00329 break; 00330 00331 case SF_FORMAT_ULAW: 00332 sample_spec.format = PA_SAMPLE_ULAW; 00333 break; 00334 00335 case SF_FORMAT_ALAW: 00336 sample_spec.format = PA_SAMPLE_ALAW; 00337 break; 00338 00339 case SF_FORMAT_FLOAT: 00340 case SF_FORMAT_DOUBLE: 00341 default: 00342 sample_spec.format = PA_SAMPLE_FLOAT32NE; 00343 readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float; 00344 break; 00345 } 00346 00347 assert(pa_sample_spec_valid(&sample_spec)); 00348 00349 if (channel_map_set && channel_map.channels != sample_spec.channels) { 00350 fprintf(stderr, "Channel map doesn't match file.\n"); 00351 goto quit; 00352 } 00353 00354 if (!client_name) { 00355 client_name = pa_locale_to_utf8(bn); 00356 if (!client_name) 00357 client_name = pa_utf8_filter(bn); 00358 } 00359 00360 if (!stream_name) { 00361 const char *n; 00362 00363 n = sf_get_string(sndfile, SF_STR_TITLE); 00364 00365 if (!n) 00366 n = filename; 00367 00368 stream_name = pa_locale_to_utf8(n); 00369 if (!stream_name) 00370 stream_name = pa_utf8_filter(n); 00371 } 00372 00373 if (verbose) { 00374 char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; 00375 pa_sample_spec_snprint(t, sizeof(t), &sample_spec); 00376 fprintf(stderr, "Using sample spec '%s'\n", t); 00377 } 00378 00379 /* Set up a new main loop */ 00380 if (!(m = pa_mainloop_new())) { 00381 fprintf(stderr, "pa_mainloop_new() failed.\n"); 00382 goto quit; 00383 } 00384 00385 mainloop_api = pa_mainloop_get_api(m); 00386 00387 r = pa_signal_init(mainloop_api); 00388 assert(r == 0); 00389 pa_signal_new(SIGINT, exit_signal_callback, NULL); 00390 #ifdef SIGPIPE 00391 signal(SIGPIPE, SIG_IGN); 00392 #endif 00393 00394 /* Create a new connection context */ 00395 if (!(context = pa_context_new(mainloop_api, client_name))) { 00396 fprintf(stderr, "pa_context_new() failed.\n"); 00397 goto quit; 00398 } 00399 00400 pa_context_set_state_callback(context, context_state_callback, NULL); 00401 00402 /* Connect the context */ 00403 pa_context_connect(context, server, 0, NULL); 00404 00405 /* Run the main loop */ 00406 if (pa_mainloop_run(m, &ret) < 0) { 00407 fprintf(stderr, "pa_mainloop_run() failed.\n"); 00408 goto quit; 00409 } 00410 00411 quit: 00412 if (stream) 00413 pa_stream_unref(stream); 00414 00415 if (context) 00416 pa_context_unref(context); 00417 00418 if (m) { 00419 pa_signal_done(); 00420 pa_mainloop_free(m); 00421 } 00422 00423 pa_xfree(server); 00424 pa_xfree(device); 00425 pa_xfree(client_name); 00426 pa_xfree(stream_name); 00427 00428 if (sndfile) 00429 sf_close(sndfile); 00430 00431 return ret; 00432 }