1 /* Pulse Audio sound handler
2  *
3  * Copyright 2008 Chris Bagwell And Sundry Contributors
4  */
5 
6 #include "sox_i.h"
7 
8 #include <pulse/simple.h>
9 #include <pulse/error.h>
10 
11 typedef struct {
12   pa_simple *pasp;
13 } priv_t;
14 
setup(sox_format_t * ft,int is_input)15 static int setup(sox_format_t *ft, int is_input)
16 {
17   priv_t *pa = (priv_t *)ft->priv;
18   char *server;
19   pa_stream_direction_t dir;
20   char *app_str;
21   char *dev;
22   pa_sample_spec spec;
23   int error;
24 
25   /* TODO: If user specified device of type "server:dev" then
26    * break up and override server.
27    */
28   server = NULL;
29 
30   if (is_input)
31   {
32     dir = PA_STREAM_RECORD;
33     app_str = "record";
34   }
35   else
36   {
37     dir = PA_STREAM_PLAYBACK;
38     app_str = "playback";
39   }
40 
41   if (strncmp(ft->filename, "default", (size_t)7) == 0)
42     dev = NULL;
43   else
44     dev = ft->filename;
45 
46   /* If user doesn't specify, default to some reasonable values.
47    * Since this is mainly for recording case, default to typical
48    * 16-bit values to prevent saving larger files then average user
49    * wants.  Power users can override to 32-bit if they wish.
50    */
51   if (ft->signal.channels == 0)
52     ft->signal.channels = 2;
53   if (ft->signal.rate == 0)
54     ft->signal.rate = 44100;
55   if (ft->encoding.bits_per_sample == 0)
56   {
57     ft->encoding.bits_per_sample = 16;
58     ft->encoding.encoding = SOX_ENCODING_SIGN2;
59   }
60 
61   spec.format = PA_SAMPLE_S32NE;
62   spec.rate = ft->signal.rate;
63   spec.channels = ft->signal.channels;
64 
65   pa->pasp = pa_simple_new(server, "SoX", dir, dev, app_str, &spec,
66                           NULL, NULL, &error);
67 
68   if (pa->pasp == NULL)
69   {
70     lsx_fail_errno(ft, SOX_EPERM, "can not open audio device: %s", pa_strerror(error));
71     return SOX_EOF;
72   }
73 
74   /* TODO: Is it better to convert format/rates in SoX or in
75    * always let Pulse Audio do it?  Since we don't know what
76    * hardware prefers, assume it knows best and give it
77    * what user specifies.
78    */
79 
80   return SOX_SUCCESS;
81 }
82 
startread(sox_format_t * ft)83 static int startread(sox_format_t *ft)
84 {
85     return setup(ft, 1);
86 }
87 
stopread(sox_format_t * ft)88 static int stopread(sox_format_t * ft)
89 {
90   priv_t *pa = (priv_t *)ft->priv;
91 
92   pa_simple_free(pa->pasp);
93 
94   return SOX_SUCCESS;
95 }
96 
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t nsamp)97 static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
98 {
99   priv_t *pa = (priv_t *)ft->priv;
100   size_t len;
101   int rc, error;
102 
103   /* Pulse Audio buffer lengths are true buffer lengths and not
104    * count of samples. */
105   len = nsamp * sizeof(sox_sample_t);
106 
107   rc = pa_simple_read(pa->pasp, buf, len, &error);
108 
109   if (rc < 0)
110   {
111     lsx_fail_errno(ft, SOX_EPERM, "error reading from pulse audio device: %s", pa_strerror(error));
112     return SOX_EOF;
113   }
114   else
115     return nsamp;
116 }
117 
startwrite(sox_format_t * ft)118 static int startwrite(sox_format_t * ft)
119 {
120     return setup(ft, 0);
121 }
122 
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t nsamp)123 static size_t write_samples(sox_format_t *ft, const sox_sample_t *buf, size_t nsamp)
124 {
125   priv_t *pa = (priv_t *)ft->priv;
126   size_t len;
127   int rc, error;
128 
129   if (!nsamp)
130     return 0;
131 
132   /* Pulse Audio buffer lengths are true buffer lengths and not
133    * count of samples. */
134   len = nsamp * sizeof(sox_sample_t);
135 
136   rc = pa_simple_write(pa->pasp, buf, len, &error);
137 
138   if (rc < 0)
139   {
140     lsx_fail_errno(ft, SOX_EPERM, "error writing to pulse audio device: %s", pa_strerror(error));
141     return SOX_EOF;
142   }
143 
144   return nsamp;
145 }
146 
stopwrite(sox_format_t * ft)147 static int stopwrite(sox_format_t * ft)
148 {
149   priv_t *pa = (priv_t *)ft->priv;
150   int error;
151 
152   pa_simple_drain(pa->pasp, &error);
153   pa_simple_free(pa->pasp);
154 
155   return SOX_SUCCESS;
156 }
157 
LSX_FORMAT_HANDLER(pulseaudio)158 LSX_FORMAT_HANDLER(pulseaudio)
159 {
160   static char const *const names[] = { "pulseaudio", NULL };
161   static unsigned const write_encodings[] = {
162     SOX_ENCODING_SIGN2, 32, 0,
163     0};
164   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
165     "Pulse Audio client",
166     names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
167     startread, read_samples, stopread,
168     startwrite, write_samples, stopwrite,
169     NULL, write_encodings, NULL, sizeof(priv_t)
170   };
171   return &handler;
172 }
173