• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 
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.
11 
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.
16 
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <signal.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <getopt.h>
33 #include <fcntl.h>
34 #include <locale.h>
35 
36 #include <sndfile.h>
37 
38 #include <pulse/pulseaudio.h>
39 #include <pulse/rtclock.h>
40 
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/i18n.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/sndfile-util.h>
46 #include <pulsecore/sample-util.h>
47 
48 #define TIME_EVENT_USEC 50000
49 
50 #define CLEAR_LINE "\x1B[K"
51 
52 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
53 static const char *purpose = NULL;
54 
55 static pa_context *context = NULL;
56 static pa_stream *stream = NULL;
57 static pa_mainloop_api *mainloop_api = NULL;
58 
59 /* Playback Mode (raw):
60  *
61  * We can only write audio to the PA stream in multiples of the stream's
62  * sample-spec frame size. Meanwhile, the STDIN read(2) system call can return
63  * a length much smaller than the frame-aligned size requested - leading to
64  * invalid writes. This can be reproduced by choosing a starved STDIN backend
65  * (e.g. "pacat /dev/random", "echo 1234 | pacat"), or an incomplete WAV file
66  * in raw non-paplay mode.
67  *
68  * Solve this by writing only frame-aligned sizes, while caching the resulting
69  * trailing partial frames here. This partial frame is then directly written
70  * in the next stream write iteration. Rinse and repeat.
71  */
72 static void *partialframe_buf = NULL;
73 static size_t partialframe_len = 0;
74 
75 /* Recording Mode buffers */
76 static void *buffer = NULL;
77 static size_t buffer_length = 0, buffer_index = 0;
78 
79 static void *silence_buffer = NULL;
80 static size_t silence_buffer_length = 0;
81 
82 static pa_io_event* stdio_event = NULL;
83 
84 static pa_proplist *proplist = NULL;
85 static char *device = NULL;
86 
87 static SNDFILE* sndfile = NULL;
88 
89 static bool verbose = false;
90 static pa_volume_t volume = PA_VOLUME_NORM;
91 static bool volume_is_set = false;
92 
93 static pa_sample_spec sample_spec = {
94     .format = PA_SAMPLE_S16LE,
95     .rate = 44100,
96     .channels = 2
97 };
98 static bool sample_spec_set = false;
99 
100 static pa_channel_map channel_map;
101 static bool channel_map_set = false;
102 
103 static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
104 static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL;
105 
106 static pa_stream_flags_t flags = 0;
107 
108 static size_t latency = 0, process_time = 0;
109 static int32_t latency_msec = 0, process_time_msec = 0;
110 
111 static bool raw = true;
112 static int file_format = -1;
113 
114 static uint32_t monitor_stream = PA_INVALID_INDEX;
115 
116 static uint32_t cork_requests = 0;
117 
118 /* A shortcut for terminating the application */
quit(int ret)119 static void quit(int ret) {
120     pa_assert(mainloop_api);
121     mainloop_api->quit(mainloop_api, ret);
122 }
123 
124 /* Connection draining complete */
context_drain_complete(pa_context * c,void * userdata)125 static void context_drain_complete(pa_context*c, void *userdata) {
126     pa_context_disconnect(c);
127 }
128 
129 /* Stream draining complete */
stream_drain_complete(pa_stream * s,int success,void * userdata)130 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
131     pa_operation *o = NULL;
132 
133     if (!success) {
134         pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context)));
135         quit(1);
136     }
137 
138     if (verbose)
139         pa_log(_("Playback stream drained."));
140 
141     pa_stream_disconnect(stream);
142     pa_stream_unref(stream);
143     stream = NULL;
144 
145     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
146         pa_context_disconnect(context);
147     else {
148         pa_operation_unref(o);
149         if (verbose)
150             pa_log(_("Draining connection to server."));
151     }
152 }
153 
154 /* Start draining */
start_drain(void)155 static void start_drain(void) {
156 
157     if (stream) {
158         pa_operation *o;
159 
160         pa_stream_set_write_callback(stream, NULL, NULL);
161 
162         if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
163             pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context)));
164             quit(1);
165             return;
166         }
167 
168         pa_operation_unref(o);
169     } else
170         quit(0);
171 }
172 
173 /* This is called whenever new data may be written to the stream */
stream_write_callback(pa_stream * s,size_t length,void * userdata)174 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
175     pa_assert(s);
176     pa_assert(length > 0);
177 
178     if (raw) {
179         pa_assert(!sndfile);
180 
181         if (stdio_event)
182             mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
183 
184     } else {
185         sf_count_t bytes;
186         void *data;
187 
188         pa_assert(sndfile);
189 
190         for (;;) {
191             size_t data_length = length;
192 
193             if (pa_stream_begin_write(s, &data, &data_length) < 0) {
194                 pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
195                 quit(1);
196                 return;
197             }
198 
199             if (readf_function) {
200                 size_t k = pa_frame_size(&sample_spec);
201 
202                 if ((bytes = readf_function(sndfile, data, (sf_count_t) (data_length/k))) > 0)
203                     bytes *= (sf_count_t) k;
204 
205             } else
206                 bytes = sf_read_raw(sndfile, data, (sf_count_t) data_length);
207 
208             if (bytes > 0)
209                 pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
210             else
211                 pa_stream_cancel_write(s);
212 
213             /* EOF? */
214             if (bytes < (sf_count_t) data_length) {
215                 start_drain();
216                 break;
217             }
218 
219             /* Request fulfilled */
220             if ((size_t) bytes >= length)
221                 break;
222 
223             length -= bytes;
224         }
225     }
226 }
227 
228 /* This is called whenever new data is available */
stream_read_callback(pa_stream * s,size_t length,void * userdata)229 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
230 
231     pa_assert(s);
232     pa_assert(length > 0);
233 
234     if (raw) {
235         pa_assert(!sndfile);
236 
237         if (stdio_event)
238             mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
239 
240         while (pa_stream_readable_size(s) > 0) {
241             const void *data;
242 
243             if (pa_stream_peek(s, &data, &length) < 0) {
244                 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
245                 quit(1);
246                 return;
247             }
248 
249             pa_assert(length > 0);
250 
251             /* If there is a hole in the stream, we generate silence, except
252              * if it's a passthrough stream in which case we skip the hole. */
253             if (data || !(flags & PA_STREAM_PASSTHROUGH)) {
254                 buffer = pa_xrealloc(buffer, buffer_index + buffer_length + length);
255                 if (data)
256                     memcpy((uint8_t *) buffer + buffer_index + buffer_length, data, length);
257                 else
258                     pa_silence_memory((uint8_t *) buffer + buffer_index + buffer_length, length, &sample_spec);
259 
260                 buffer_length += length;
261             }
262 
263             pa_stream_drop(s);
264         }
265 
266     } else {
267         pa_assert(sndfile);
268 
269         while (pa_stream_readable_size(s) > 0) {
270             sf_count_t bytes;
271             const void *data;
272 
273             if (pa_stream_peek(s, &data, &length) < 0) {
274                 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
275                 quit(1);
276                 return;
277             }
278 
279             pa_assert(length > 0);
280 
281             if (!data && (flags & PA_STREAM_PASSTHROUGH)) {
282                 pa_stream_drop(s);
283                 continue;
284             }
285 
286             if (!data && length > silence_buffer_length) {
287                 silence_buffer = pa_xrealloc(silence_buffer, length);
288                 pa_silence_memory((uint8_t *) silence_buffer + silence_buffer_length, length - silence_buffer_length, &sample_spec);
289                 silence_buffer_length = length;
290             }
291 
292             if (writef_function) {
293                 size_t k = pa_frame_size(&sample_spec);
294 
295                 if ((bytes = writef_function(sndfile, data ? data : silence_buffer, (sf_count_t) (length/k))) > 0)
296                     bytes *= (sf_count_t) k;
297 
298             } else
299                 bytes = sf_write_raw(sndfile, data ? data : silence_buffer, (sf_count_t) length);
300 
301             if (bytes < (sf_count_t) length)
302                 quit(1);
303 
304             pa_stream_drop(s);
305         }
306     }
307 }
308 
309 /* This routine is called whenever the stream state changes */
stream_state_callback(pa_stream * s,void * userdata)310 static void stream_state_callback(pa_stream *s, void *userdata) {
311     pa_assert(s);
312 
313     switch (pa_stream_get_state(s)) {
314         case PA_STREAM_CREATING:
315         case PA_STREAM_TERMINATED:
316             break;
317 
318         case PA_STREAM_READY:
319 
320             if (verbose) {
321                 const pa_buffer_attr *a;
322                 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
323 
324                 pa_log(_("Stream successfully created."));
325 
326                 if (!(a = pa_stream_get_buffer_attr(s)))
327                     pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
328                 else {
329 
330                     if (mode == PLAYBACK)
331                         pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a->maxlength, a->tlength, a->prebuf, a->minreq);
332                     else {
333                         pa_assert(mode == RECORD);
334                         pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a->maxlength, a->fragsize);
335                     }
336                 }
337 
338                 pa_log(_("Using sample spec '%s', channel map '%s'."),
339                         pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
340                         pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
341 
342                 pa_log(_("Connected to device %s (index: %u, suspended: %s)."),
343                         pa_stream_get_device_name(s),
344                         pa_stream_get_device_index(s),
345                         pa_yes_no(pa_stream_is_suspended(s)));
346             }
347 
348             break;
349 
350         case PA_STREAM_FAILED:
351         default:
352             pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
353             quit(1);
354     }
355 }
356 
stream_suspended_callback(pa_stream * s,void * userdata)357 static void stream_suspended_callback(pa_stream *s, void *userdata) {
358     pa_assert(s);
359 
360     if (verbose) {
361         if (pa_stream_is_suspended(s))
362             pa_log(_("Stream device suspended.%s"), CLEAR_LINE);
363         else
364             pa_log(_("Stream device resumed.%s"), CLEAR_LINE);
365     }
366 }
367 
stream_underflow_callback(pa_stream * s,void * userdata)368 static void stream_underflow_callback(pa_stream *s, void *userdata) {
369     pa_assert(s);
370 
371     if (verbose)
372         pa_log(_("Stream underrun.%s"),  CLEAR_LINE);
373 }
374 
stream_overflow_callback(pa_stream * s,void * userdata)375 static void stream_overflow_callback(pa_stream *s, void *userdata) {
376     pa_assert(s);
377 
378     if (verbose)
379         pa_log(_("Stream overrun.%s"), CLEAR_LINE);
380 }
381 
stream_started_callback(pa_stream * s,void * userdata)382 static void stream_started_callback(pa_stream *s, void *userdata) {
383     pa_assert(s);
384 
385     if (verbose)
386         pa_log(_("Stream started.%s"), CLEAR_LINE);
387 }
388 
stream_moved_callback(pa_stream * s,void * userdata)389 static void stream_moved_callback(pa_stream *s, void *userdata) {
390     pa_assert(s);
391 
392     if (verbose)
393         pa_log(_("Stream moved to device %s (%u, %ssuspended).%s"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "),  CLEAR_LINE);
394 }
395 
stream_buffer_attr_callback(pa_stream * s,void * userdata)396 static void stream_buffer_attr_callback(pa_stream *s, void *userdata) {
397     pa_assert(s);
398 
399     if (verbose)
400         pa_log(_("Stream buffer attributes changed.%s"),  CLEAR_LINE);
401 }
402 
stream_event_callback(pa_stream * s,const char * name,pa_proplist * pl,void * userdata)403 static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
404     char *t;
405 
406     pa_assert(s);
407     pa_assert(name);
408     pa_assert(pl);
409 
410     t = pa_proplist_to_string_sep(pl, ", ");
411     pa_log("Got event '%s', properties '%s'", name, t);
412 
413     if (pa_streq(name, PA_STREAM_EVENT_REQUEST_CORK)) {
414         if (cork_requests == 0) {
415             pa_log(_("Cork request stack is empty: corking stream"));
416             pa_operation_unref(pa_stream_cork(s, 1, NULL, NULL));
417         }
418         cork_requests++;
419     } else if (pa_streq(name, PA_STREAM_EVENT_REQUEST_UNCORK)) {
420         if (cork_requests == 1) {
421             pa_log(_("Cork request stack is empty: uncorking stream"));
422             pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
423         }
424         if (cork_requests == 0)
425             pa_log(_("Warning: Received more uncork requests than cork requests."));
426         else
427             cork_requests--;
428     }
429 
430     pa_xfree(t);
431 }
432 
433 /* This is called whenever the context status changes */
context_state_callback(pa_context * c,void * userdata)434 static void context_state_callback(pa_context *c, void *userdata) {
435     pa_assert(c);
436 
437     switch (pa_context_get_state(c)) {
438         case PA_CONTEXT_CONNECTING:
439         case PA_CONTEXT_AUTHORIZING:
440         case PA_CONTEXT_SETTING_NAME:
441             break;
442 
443         case PA_CONTEXT_READY: {
444             pa_buffer_attr buffer_attr;
445 
446             pa_assert(c);
447             pa_assert(!stream);
448 
449             if (verbose)
450                 pa_log(_("Connection established.%s"), CLEAR_LINE);
451 
452             if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
453                 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
454                 goto fail;
455             }
456 
457             pa_stream_set_state_callback(stream, stream_state_callback, NULL);
458             pa_stream_set_write_callback(stream, stream_write_callback, NULL);
459             pa_stream_set_read_callback(stream, stream_read_callback, NULL);
460             pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
461             pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
462             pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
463             pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
464             pa_stream_set_started_callback(stream, stream_started_callback, NULL);
465             pa_stream_set_event_callback(stream, stream_event_callback, NULL);
466             pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
467 
468             pa_zero(buffer_attr);
469             buffer_attr.maxlength = (uint32_t) -1;
470             buffer_attr.prebuf = (uint32_t) -1;
471 
472             if (latency_msec > 0) {
473                 buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
474                 flags |= PA_STREAM_ADJUST_LATENCY;
475             } else if (latency > 0) {
476                 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
477                 flags |= PA_STREAM_ADJUST_LATENCY;
478             } else
479                 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;
480 
481             if (process_time_msec > 0) {
482                 buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
483             } else if (process_time > 0)
484                 buffer_attr.minreq = (uint32_t) process_time;
485             else
486                 buffer_attr.minreq = (uint32_t) -1;
487 
488             if (mode == PLAYBACK) {
489                 pa_cvolume cv;
490                 if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
491                     pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
492                     goto fail;
493                 }
494 
495             } else {
496                 if (monitor_stream != PA_INVALID_INDEX && (pa_stream_set_monitor_stream(stream, monitor_stream) < 0)) {
497                     pa_log(_("Failed to set monitor stream: %s"), pa_strerror(pa_context_errno(c)));
498                     goto fail;
499                 }
500                 if (pa_stream_connect_record(stream, device, &buffer_attr, flags) < 0) {
501                     pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
502                     goto fail;
503                 }
504             }
505             break;
506         }
507 
508         case PA_CONTEXT_TERMINATED:
509             quit(0);
510             break;
511 
512         case PA_CONTEXT_FAILED:
513         default:
514             pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
515             goto fail;
516     }
517 
518     return;
519 
520 fail:
521     quit(1);
522 
523 }
524 
525 /* New data on STDIN **/
stdin_callback(pa_mainloop_api * a,pa_io_event * e,int fd,pa_io_event_flags_t f,void * userdata)526 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
527     uint8_t *buf = NULL;
528     size_t writable, towrite, r;
529 
530     pa_assert(a == mainloop_api);
531     pa_assert(e);
532     pa_assert(stdio_event == e);
533 
534     /* Stream not ready? */
535     if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY ||
536         !(writable = pa_stream_writable_size(stream))) {
537 
538         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
539         return;
540     }
541 
542     if (pa_stream_begin_write(stream, (void **)&buf, &writable) < 0) {
543         pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
544         quit(1);
545         return;
546     }
547 
548     /* Partial frame cached from a previous write iteration? */
549     if (partialframe_len) {
550         pa_assert(partialframe_len < pa_frame_size(&sample_spec));
551         memcpy(buf, partialframe_buf, partialframe_len);
552     }
553 
554     if ((r = pa_read(fd, buf + partialframe_len, writable - partialframe_len, userdata)) <= 0) {
555         if (r == 0) {
556             if (verbose)
557                 pa_log(_("Got EOF."));
558 
559             start_drain();
560 
561         } else {
562             pa_log(_("read() failed: %s"), strerror(errno));
563             quit(1);
564         }
565 
566         mainloop_api->io_free(stdio_event);
567         stdio_event = NULL;
568         return;
569     }
570     r += partialframe_len;
571 
572     /* Cache any trailing partial frames for the next write */
573     towrite = pa_frame_align(r, &sample_spec);
574     partialframe_len = r - towrite;
575 
576     if (partialframe_len)
577         memcpy(partialframe_buf, buf + towrite, partialframe_len);
578 
579     if (towrite) {
580         if (pa_stream_write(stream, buf, towrite, NULL, 0, PA_SEEK_RELATIVE) < 0) {
581             pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context)));
582             quit(1);
583             return;
584         }
585     } else
586         pa_stream_cancel_write(stream);
587 }
588 
589 /* Some data may be written to STDOUT */
stdout_callback(pa_mainloop_api * a,pa_io_event * e,int fd,pa_io_event_flags_t f,void * userdata)590 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
591     ssize_t r;
592 
593     pa_assert(a == mainloop_api);
594     pa_assert(e);
595     pa_assert(stdio_event == e);
596 
597     if (!buffer) {
598         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
599         return;
600     }
601 
602     pa_assert(buffer_length);
603 
604     if ((r = pa_write(fd, (uint8_t*) buffer+buffer_index, buffer_length, userdata)) <= 0) {
605         pa_log(_("write() failed: %s"), strerror(errno));
606         quit(1);
607 
608         mainloop_api->io_free(stdio_event);
609         stdio_event = NULL;
610         return;
611     }
612 
613     buffer_length -= (uint32_t) r;
614     buffer_index += (uint32_t) r;
615 
616     if (!buffer_length) {
617         pa_xfree(buffer);
618         buffer = NULL;
619         buffer_length = buffer_index = 0;
620     }
621 }
622 
623 /* UNIX signal to quit received */
exit_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)624 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
625     if (verbose)
626         pa_log(_("Got signal, exiting."));
627     quit(0);
628 }
629 
630 /* Show the current latency */
stream_update_timing_callback(pa_stream * s,int success,void * userdata)631 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
632     pa_usec_t l, usec;
633     int negative = 0;
634 
635     pa_assert(s);
636 
637     if (!success ||
638         pa_stream_get_time(s, &usec) < 0 ||
639         pa_stream_get_latency(s, &l, &negative) < 0) {
640         pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
641         quit(1);
642         return;
643     }
644 
645     fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
646             (float) usec / 1000000,
647             (float) l * (negative?-1.0f:1.0f));
648     fprintf(stderr, "        \r");
649 }
650 
651 #ifdef SIGUSR1
652 /* Someone requested that the latency is shown */
sigusr1_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)653 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
654 
655     if (!stream)
656         return;
657 
658     pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
659 }
660 #endif
661 
time_event_callback(pa_mainloop_api * m,pa_time_event * e,const struct timeval * t,void * userdata)662 static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
663     if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
664         pa_operation *o;
665         if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
666             pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
667         else
668             pa_operation_unref(o);
669     }
670 
671     pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC);
672 }
673 
help(const char * argv0)674 static void help(const char *argv0) {
675 
676     printf(_("%s [options]\n"
677              "%s\n\n"
678              "  -h, --help                            Show this help\n"
679              "      --version                         Show version\n\n"
680              "  -r, --record                          Create a connection for recording\n"
681              "  -p, --playback                        Create a connection for playback\n\n"
682              "  -v, --verbose                         Enable verbose operations\n\n"
683              "  -s, --server=SERVER                   The name of the server to connect to\n"
684              "  -d, --device=DEVICE                   The name of the sink/source to connect to. The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@ can be used to specify the default sink, source and monitor respectively.\n"
685              "  -n, --client-name=NAME                How to call this client on the server\n"
686              "      --stream-name=NAME                How to call this stream on the server\n"
687              "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
688              "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
689              "      --format=SAMPLEFORMAT             The sample format, see\n"
690              "                                        https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/SupportedAudioFormats/\n"
691              "                                        for possible values (defaults to s16ne)\n"
692              "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
693              "                                        (defaults to 2)\n"
694              "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
695              "      --fix-format                      Take the sample format from the sink/source the stream is\n"
696              "                                        being connected to.\n"
697              "      --fix-rate                        Take the sampling rate from the sink/source the stream is\n"
698              "                                        being connected to.\n"
699              "      --fix-channels                    Take the number of channels and the channel map\n"
700              "                                        from the sink/source the stream is being connected to.\n"
701              "      --no-remix                        Don't upmix or downmix channels.\n"
702              "      --no-remap                        Map channels by index instead of name.\n"
703              "      --latency=BYTES                   Request the specified latency in bytes.\n"
704              "      --process-time=BYTES              Request the specified process time per request in bytes.\n"
705              "      --latency-msec=MSEC               Request the specified latency in msec.\n"
706              "      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
707              "      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
708              "      --raw                             Record/play raw PCM data.\n"
709              "      --passthrough                     Passthrough data.\n"
710              "      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
711              "      --list-file-formats               List available file formats.\n"
712              "      --monitor-stream=INDEX            Record from the sink input with index INDEX.\n")
713            , argv0, purpose);
714 }
715 
716 enum {
717     ARG_VERSION = 256,
718     ARG_STREAM_NAME,
719     ARG_VOLUME,
720     ARG_SAMPLERATE,
721     ARG_SAMPLEFORMAT,
722     ARG_CHANNELS,
723     ARG_CHANNELMAP,
724     ARG_FIX_FORMAT,
725     ARG_FIX_RATE,
726     ARG_FIX_CHANNELS,
727     ARG_NO_REMAP,
728     ARG_NO_REMIX,
729     ARG_LATENCY,
730     ARG_PROCESS_TIME,
731     ARG_RAW,
732     ARG_PASSTHROUGH,
733     ARG_PROPERTY,
734     ARG_FILE_FORMAT,
735     ARG_LIST_FILE_FORMATS,
736     ARG_LATENCY_MSEC,
737     ARG_PROCESS_TIME_MSEC,
738     ARG_MONITOR_STREAM,
739 };
740 
main(int argc,char * argv[])741 int main(int argc, char *argv[]) {
742     pa_mainloop* m = NULL;
743     int ret = 1, c;
744     char *bn, *server = NULL;
745     pa_time_event *time_event = NULL;
746     const char *filename = NULL;
747     /* type for pa_read/_write. passed as userdata to the callbacks */
748     unsigned long type = 0;
749 
750     static const struct option long_options[] = {
751         {"record",       0, NULL, 'r'},
752         {"playback",     0, NULL, 'p'},
753         {"device",       1, NULL, 'd'},
754         {"server",       1, NULL, 's'},
755         {"client-name",  1, NULL, 'n'},
756         {"stream-name",  1, NULL, ARG_STREAM_NAME},
757         {"version",      0, NULL, ARG_VERSION},
758         {"help",         0, NULL, 'h'},
759         {"verbose",      0, NULL, 'v'},
760         {"volume",       1, NULL, ARG_VOLUME},
761         {"rate",         1, NULL, ARG_SAMPLERATE},
762         {"format",       1, NULL, ARG_SAMPLEFORMAT},
763         {"channels",     1, NULL, ARG_CHANNELS},
764         {"channel-map",  1, NULL, ARG_CHANNELMAP},
765         {"fix-format",   0, NULL, ARG_FIX_FORMAT},
766         {"fix-rate",     0, NULL, ARG_FIX_RATE},
767         {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
768         {"no-remap",     0, NULL, ARG_NO_REMAP},
769         {"no-remix",     0, NULL, ARG_NO_REMIX},
770         {"latency",      1, NULL, ARG_LATENCY},
771         {"process-time", 1, NULL, ARG_PROCESS_TIME},
772         {"property",     1, NULL, ARG_PROPERTY},
773         {"raw",          0, NULL, ARG_RAW},
774         {"passthrough",  0, NULL, ARG_PASSTHROUGH},
775         {"file-format",  2, NULL, ARG_FILE_FORMAT},
776         {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
777         {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
778         {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
779         {"monitor-stream", 1, NULL, ARG_MONITOR_STREAM},
780         {NULL,           0, NULL, 0}
781     };
782 
783     setlocale(LC_ALL, "");
784 #ifdef ENABLE_NLS
785     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
786 #endif
787 
788     bn = pa_path_get_filename(argv[0]);
789 
790     if (strstr(bn, "play")) {
791         mode = PLAYBACK;
792         raw = false;
793         purpose = _("Play back encoded audio files on a PulseAudio sound server.");
794     } else if (strstr(bn, "record")) {
795         mode = RECORD;
796         raw = false;
797         purpose = _("Capture audio data from a PulseAudio sound server and write it to a file.");
798     } else if (strstr(bn, "rec") || strstr(bn, "mon")) {
799         mode = RECORD;
800         raw = true;
801         purpose = _("Capture audio data from a PulseAudio sound server and write it to STDOUT or the specified file.");
802     } else { /* pacat */
803         mode = PLAYBACK;
804         raw = true;
805         purpose = _("Play back audio data from STDIN or the specified file on a PulseAudio sound server.");
806     }
807 
808     proplist = pa_proplist_new();
809 
810     while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
811 
812         switch (c) {
813             case 'h':
814                 help(bn);
815                 ret = 0;
816                 goto quit;
817 
818             case ARG_VERSION:
819                 printf(_("pacat %s\n"
820                          "Compiled with libpulse %s\n"
821                          "Linked with libpulse %s\n"),
822                        PACKAGE_VERSION,
823                        pa_get_headers_version(),
824                        pa_get_library_version());
825                 ret = 0;
826                 goto quit;
827 
828             case 'r':
829                 mode = RECORD;
830                 break;
831 
832             case 'p':
833                 mode = PLAYBACK;
834                 break;
835 
836             case 'd':
837                 pa_xfree(device);
838                 device = pa_xstrdup(optarg);
839                 break;
840 
841             case 's':
842                 pa_xfree(server);
843                 server = pa_xstrdup(optarg);
844                 break;
845 
846             case 'n': {
847                 char *t;
848 
849                 if (!(t = pa_locale_to_utf8(optarg)) ||
850                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
851 
852                     pa_log(_("Invalid client name '%s'"), t ? t : optarg);
853                     pa_xfree(t);
854                     goto quit;
855                 }
856 
857                 pa_xfree(t);
858                 break;
859             }
860 
861             case ARG_STREAM_NAME: {
862                 char *t;
863 
864                 if (!(t = pa_locale_to_utf8(optarg)) ||
865                     pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
866 
867                     pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
868                     pa_xfree(t);
869                     goto quit;
870                 }
871 
872                 pa_xfree(t);
873                 break;
874             }
875 
876             case 'v':
877                 verbose = 1;
878                 break;
879 
880             case ARG_VOLUME: {
881                 int v = atoi(optarg);
882                 volume = v < 0 ? 0U : (pa_volume_t) v;
883                 volume_is_set = true;
884                 break;
885             }
886 
887             case ARG_CHANNELS:
888                 sample_spec.channels = (uint8_t) atoi(optarg);
889                 sample_spec_set = true;
890                 break;
891 
892             case ARG_SAMPLEFORMAT:
893                 sample_spec.format = pa_parse_sample_format(optarg);
894                 sample_spec_set = true;
895                 break;
896 
897             case ARG_SAMPLERATE:
898                 sample_spec.rate = (uint32_t) atoi(optarg);
899                 sample_spec_set = true;
900                 break;
901 
902             case ARG_CHANNELMAP:
903                 if (!pa_channel_map_parse(&channel_map, optarg)) {
904                     pa_log(_("Invalid channel map '%s'"), optarg);
905                     goto quit;
906                 }
907 
908                 channel_map_set = true;
909                 break;
910 
911             case ARG_FIX_CHANNELS:
912                 flags |= PA_STREAM_FIX_CHANNELS;
913                 break;
914 
915             case ARG_FIX_RATE:
916                 flags |= PA_STREAM_FIX_RATE;
917                 break;
918 
919             case ARG_FIX_FORMAT:
920                 flags |= PA_STREAM_FIX_FORMAT;
921                 break;
922 
923             case ARG_NO_REMIX:
924                 flags |= PA_STREAM_NO_REMIX_CHANNELS;
925                 break;
926 
927             case ARG_NO_REMAP:
928                 flags |= PA_STREAM_NO_REMAP_CHANNELS;
929                 break;
930 
931             case ARG_LATENCY:
932                 if (((latency = (size_t) atoi(optarg))) <= 0) {
933                     pa_log(_("Invalid latency specification '%s'"), optarg);
934                     goto quit;
935                 }
936                 break;
937 
938             case ARG_PROCESS_TIME:
939                 if (((process_time = (size_t) atoi(optarg))) <= 0) {
940                     pa_log(_("Invalid process time specification '%s'"), optarg);
941                     goto quit;
942                 }
943                 break;
944 
945             case ARG_LATENCY_MSEC:
946                 if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
947                     pa_log(_("Invalid latency specification '%s'"), optarg);
948                     goto quit;
949                 }
950                 break;
951 
952             case ARG_PROCESS_TIME_MSEC:
953                 if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
954                     pa_log(_("Invalid process time specification '%s'"), optarg);
955                     goto quit;
956                 }
957                 break;
958 
959             case ARG_PROPERTY: {
960                 char *t;
961 
962                 if (!(t = pa_locale_to_utf8(optarg)) ||
963                     pa_proplist_setp(proplist, t) < 0) {
964 
965                     pa_xfree(t);
966                     pa_log(_("Invalid property '%s'"), optarg);
967                     goto quit;
968                 }
969 
970                 pa_xfree(t);
971                 break;
972             }
973 
974             case ARG_RAW:
975                 raw = true;
976                 break;
977 
978             case ARG_PASSTHROUGH:
979                 flags |= PA_STREAM_PASSTHROUGH;
980                 break;
981 
982             case ARG_FILE_FORMAT:
983                 if (optarg) {
984                     if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
985                         pa_log(_("Unknown file format %s."), optarg);
986                         goto quit;
987                     }
988                 }
989 
990                 raw = false;
991                 break;
992 
993             case ARG_LIST_FILE_FORMATS:
994                 pa_sndfile_dump_formats();
995                 ret = 0;
996                 goto quit;
997 
998             case ARG_MONITOR_STREAM:
999                 if (pa_atou(optarg, &monitor_stream) < 0) {
1000                     pa_log(_("Failed to parse the argument for --monitor-stream"));
1001                     goto quit;
1002                 }
1003                 break;
1004 
1005             default:
1006                 goto quit;
1007         }
1008     }
1009 
1010     if (!pa_sample_spec_valid(&sample_spec)) {
1011         pa_log(_("Invalid sample specification"));
1012         goto quit;
1013     }
1014 
1015     if (optind+1 == argc) {
1016         int fd;
1017 
1018         filename = argv[optind];
1019 
1020         if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
1021             pa_log(_("open(): %s"), strerror(errno));
1022             goto quit;
1023         }
1024 
1025         if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
1026             pa_log(_("dup2(): %s"), strerror(errno));
1027             goto quit;
1028         }
1029 
1030         pa_close(fd);
1031 
1032     } else if (optind+1 <= argc) {
1033         pa_log(_("Too many arguments."));
1034         goto quit;
1035     }
1036 
1037     if (!raw) {
1038         SF_INFO sfi;
1039         pa_zero(sfi);
1040 
1041         if (mode == RECORD) {
1042             /* This might patch up the sample spec */
1043             if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
1044                 pa_log(_("Failed to generate sample specification for file."));
1045                 goto quit;
1046             }
1047 
1048             if (file_format <= 0) {
1049                 char *extension;
1050                 if (filename && (extension = strrchr(filename, '.')))
1051                     file_format = pa_sndfile_format_from_string(extension+1);
1052                 if (file_format <= 0)
1053                     file_format = SF_FORMAT_WAV;
1054                 /* Transparently upgrade classic .wav to wavex for multichannel audio */
1055                 if (file_format == SF_FORMAT_WAV &&
1056                     (sample_spec.channels > 2 ||
1057                     (channel_map_set &&
1058                     !(sample_spec.channels == 1 && channel_map.map[0] == PA_CHANNEL_POSITION_MONO) &&
1059                     !(sample_spec.channels == 2 && channel_map.map[0] == PA_CHANNEL_POSITION_LEFT
1060                                                 && channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))))
1061                     file_format = SF_FORMAT_WAVEX;
1062             }
1063 
1064             sfi.format |= file_format;
1065 
1066 	    /*
1067 	     * Endianness has been set in pa_sndfile_write_sample_spec(), but
1068 	     * libsndfile errors out if endianness is set to anything other than
1069 	     * SF_ENDIAN_FILE for OGG or FLAC. Clear it.
1070 	     * For OGG, libsndfile accepts only subformat SF_FORMAT_VORBIS.
1071 	     */
1072 	    if (file_format == SF_FORMAT_OGG || file_format == SF_FORMAT_FLAC)
1073 		    sfi.format = (sfi.format & ~SF_FORMAT_ENDMASK) | SF_ENDIAN_FILE;
1074 	    if (file_format == SF_FORMAT_OGG)
1075 		    sfi.format = (sfi.format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS;
1076 
1077         }
1078 
1079         if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
1080                                    mode == RECORD ? SFM_WRITE : SFM_READ,
1081                                    &sfi, 0))) {
1082             pa_log(_("Failed to open audio file."));
1083             goto quit;
1084         }
1085 
1086         if (mode == PLAYBACK) {
1087             if (sample_spec_set)
1088                 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
1089 
1090             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1091                 pa_log(_("Failed to determine sample specification from file."));
1092                 goto quit;
1093             }
1094             sample_spec_set = true;
1095 
1096             if (!channel_map_set) {
1097                 /* Allow the user to overwrite the channel map on the command line */
1098                 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1099                     if (sample_spec.channels > 2)
1100                         pa_log(_("Warning: Failed to determine channel map from file."));
1101                 } else
1102                     channel_map_set = true;
1103             }
1104         }
1105     }
1106 
1107     if (!channel_map_set)
1108         pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1109 
1110     if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
1111         pa_log(_("Channel map doesn't match sample specification"));
1112         goto quit;
1113     }
1114 
1115     if (!raw) {
1116         pa_proplist *sfp;
1117 
1118         if (mode == PLAYBACK)
1119             readf_function = pa_sndfile_readf_function(&sample_spec);
1120         else {
1121             if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
1122                 pa_log(_("Warning: failed to write channel map to file."));
1123 
1124             writef_function = pa_sndfile_writef_function(&sample_spec);
1125         }
1126 
1127         /* Fill in libsndfile prop list data */
1128         sfp = pa_proplist_new();
1129         pa_sndfile_init_proplist(sndfile, sfp);
1130         pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
1131         pa_proplist_free(sfp);
1132     }
1133 
1134     if (verbose) {
1135         char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
1136 
1137         pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1138                 mode == RECORD ? _("recording") : _("playback"),
1139                 pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
1140                 pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
1141     }
1142 
1143     /* Fill in client name if none was set */
1144     if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
1145         char *t;
1146 
1147         if ((t = pa_locale_to_utf8(bn))) {
1148             pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
1149             pa_xfree(t);
1150         }
1151     }
1152 
1153     /* Fill in media name if none was set */
1154     if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
1155         const char *t;
1156 
1157         if ((t = filename) ||
1158             (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
1159             pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
1160 
1161         if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
1162             pa_log(_("Failed to set media name."));
1163             goto quit;
1164         }
1165     }
1166 
1167     if (raw && mode == PLAYBACK)
1168         partialframe_buf = pa_xmalloc(pa_frame_size(&sample_spec));
1169 
1170     /* Set up a new main loop */
1171     if (!(m = pa_mainloop_new())) {
1172         pa_log(_("pa_mainloop_new() failed."));
1173         goto quit;
1174     }
1175 
1176     mainloop_api = pa_mainloop_get_api(m);
1177 
1178     pa_assert_se(pa_signal_init(mainloop_api) == 0);
1179     pa_signal_new(SIGINT, exit_signal_callback, NULL);
1180     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1181 #ifdef SIGUSR1
1182     pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
1183 #endif
1184     pa_disable_sigpipe();
1185 
1186     if (raw) {
1187 #ifdef OS_IS_WIN32
1188         /* need to turn on binary mode for stdio io. Windows, meh */
1189         setmode(mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, O_BINARY);
1190 #endif
1191         if (!(stdio_event = mainloop_api->io_new(mainloop_api,
1192                                                  mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
1193                                                  mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
1194                                                  mode == PLAYBACK ? stdin_callback : stdout_callback, &type))) {
1195             pa_log(_("io_new() failed."));
1196             goto quit;
1197         }
1198     }
1199 
1200     /* Create a new connection context */
1201     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1202         pa_log(_("pa_context_new() failed."));
1203         goto quit;
1204     }
1205 
1206     pa_context_set_state_callback(context, context_state_callback, NULL);
1207 
1208     /* Connect the context */
1209     if (pa_context_connect(context, server, 0, NULL) < 0) {
1210         pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1211         goto quit;
1212     }
1213 
1214     if (verbose) {
1215         if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
1216             pa_log(_("pa_context_rttime_new() failed."));
1217             goto quit;
1218         }
1219     }
1220 
1221     /* Run the main loop */
1222     if (pa_mainloop_run(m, &ret) < 0) {
1223         pa_log(_("pa_mainloop_run() failed."));
1224         goto quit;
1225     }
1226 
1227 quit:
1228     if (stream)
1229         pa_stream_unref(stream);
1230 
1231     if (context)
1232         pa_context_unref(context);
1233 
1234     if (stdio_event) {
1235         pa_assert(mainloop_api);
1236         mainloop_api->io_free(stdio_event);
1237     }
1238 
1239     if (time_event) {
1240         pa_assert(mainloop_api);
1241         mainloop_api->time_free(time_event);
1242     }
1243 
1244     if (m) {
1245         pa_signal_done();
1246         pa_mainloop_free(m);
1247     }
1248 
1249     pa_xfree(silence_buffer);
1250     pa_xfree(buffer);
1251     pa_xfree(partialframe_buf);
1252 
1253     pa_xfree(server);
1254     pa_xfree(device);
1255 
1256     if (sndfile)
1257         sf_close(sndfile);
1258 
1259     if (proplist)
1260         pa_proplist_free(proplist);
1261 
1262     return ret;
1263 }
1264