• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2009 Lennart Poettering
5 
6   PulseAudio 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.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 /* Shared between pacat/parec/paplay and the server */
25 
26 #include <pulse/xmalloc.h>
27 #include <pulse/utf8.h>
28 
29 #include <pulsecore/macro.h>
30 
31 #include "sndfile-util.h"
32 
pa_sndfile_read_sample_spec(SNDFILE * sf,pa_sample_spec * ss)33 int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
34     SF_INFO sfi;
35     int sf_errno;
36 
37     pa_assert(sf);
38     pa_assert(ss);
39 
40     pa_zero(sfi);
41     if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
42         pa_log_error("sndfile: %s", sf_error_number(sf_errno));
43         return -1;
44     }
45 
46     switch (sfi.format & SF_FORMAT_SUBMASK) {
47 
48         case SF_FORMAT_PCM_16:
49         case SF_FORMAT_PCM_U8:
50         case SF_FORMAT_PCM_S8:
51             ss->format = PA_SAMPLE_S16NE;
52             break;
53 
54         case SF_FORMAT_PCM_24:
55             ss->format = PA_SAMPLE_S24NE;
56             break;
57 
58         case SF_FORMAT_PCM_32:
59             ss->format = PA_SAMPLE_S32NE;
60             break;
61 
62         case SF_FORMAT_ULAW:
63             ss->format = PA_SAMPLE_ULAW;
64             break;
65 
66         case SF_FORMAT_ALAW:
67             ss->format = PA_SAMPLE_ALAW;
68             break;
69 
70         case SF_FORMAT_FLOAT:
71         case SF_FORMAT_DOUBLE:
72         default:
73             ss->format = PA_SAMPLE_FLOAT32NE;
74             break;
75     }
76 
77     ss->rate = (uint32_t) sfi.samplerate;
78     ss->channels = (uint8_t) sfi.channels;
79 
80     if (!pa_sample_spec_valid(ss))
81         return -1;
82 
83     return 0;
84 }
85 
pa_sndfile_write_sample_spec(SF_INFO * sfi,pa_sample_spec * ss)86 int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
87     pa_assert(sfi);
88     pa_assert(ss);
89 
90     sfi->samplerate = (int) ss->rate;
91     sfi->channels = (int) ss->channels;
92 
93     if (pa_sample_format_is_le(ss->format) > 0)
94         sfi->format = SF_ENDIAN_LITTLE;
95     else if (pa_sample_format_is_be(ss->format) > 0)
96         sfi->format = SF_ENDIAN_BIG;
97 
98     switch (ss->format) {
99 
100         case PA_SAMPLE_U8:
101             ss->format = PA_SAMPLE_S16NE;
102             sfi->format = SF_FORMAT_PCM_U8;
103             break;
104 
105         case PA_SAMPLE_S16LE:
106         case PA_SAMPLE_S16BE:
107             ss->format = PA_SAMPLE_S16NE;
108             sfi->format |= SF_FORMAT_PCM_16;
109             break;
110 
111         case PA_SAMPLE_S24LE:
112         case PA_SAMPLE_S24BE:
113             ss->format = PA_SAMPLE_S24NE;
114             sfi->format |= SF_FORMAT_PCM_24;
115             break;
116 
117         case PA_SAMPLE_S24_32LE:
118         case PA_SAMPLE_S24_32BE:
119             ss->format = PA_SAMPLE_S24_32NE;
120             sfi->format |= SF_FORMAT_PCM_32;
121             break;
122 
123         case PA_SAMPLE_S32LE:
124         case PA_SAMPLE_S32BE:
125             ss->format = PA_SAMPLE_S32NE;
126             sfi->format |= SF_FORMAT_PCM_32;
127             break;
128 
129         case PA_SAMPLE_ULAW:
130             sfi->format = SF_FORMAT_ULAW;
131             break;
132 
133         case PA_SAMPLE_ALAW:
134             sfi->format = SF_FORMAT_ALAW;
135             break;
136 
137         case PA_SAMPLE_FLOAT32LE:
138         case PA_SAMPLE_FLOAT32BE:
139         default:
140             ss->format = PA_SAMPLE_FLOAT32NE;
141             sfi->format |= SF_FORMAT_FLOAT;
142             break;
143     }
144 
145     if (!pa_sample_spec_valid(ss))
146         return -1;
147 
148     return 0;
149 }
150 
pa_sndfile_read_channel_map(SNDFILE * sf,pa_channel_map * cm)151 int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) {
152 
153     static const pa_channel_position_t table[] = {
154         [SF_CHANNEL_MAP_MONO] =                  PA_CHANNEL_POSITION_MONO,
155         [SF_CHANNEL_MAP_LEFT] =                  PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distinguishes left and front-left, which we don't */
156         [SF_CHANNEL_MAP_RIGHT] =                 PA_CHANNEL_POSITION_FRONT_RIGHT,
157         [SF_CHANNEL_MAP_CENTER] =                PA_CHANNEL_POSITION_FRONT_CENTER,
158         [SF_CHANNEL_MAP_FRONT_LEFT] =            PA_CHANNEL_POSITION_FRONT_LEFT,
159         [SF_CHANNEL_MAP_FRONT_RIGHT] =           PA_CHANNEL_POSITION_FRONT_RIGHT,
160         [SF_CHANNEL_MAP_FRONT_CENTER] =          PA_CHANNEL_POSITION_FRONT_CENTER,
161         [SF_CHANNEL_MAP_REAR_CENTER] =           PA_CHANNEL_POSITION_REAR_CENTER,
162         [SF_CHANNEL_MAP_REAR_LEFT] =             PA_CHANNEL_POSITION_REAR_LEFT,
163         [SF_CHANNEL_MAP_REAR_RIGHT] =            PA_CHANNEL_POSITION_REAR_RIGHT,
164         [SF_CHANNEL_MAP_LFE] =                   PA_CHANNEL_POSITION_LFE,
165         [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] =  PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
166         [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
167         [SF_CHANNEL_MAP_SIDE_LEFT] =             PA_CHANNEL_POSITION_SIDE_LEFT,
168         [SF_CHANNEL_MAP_SIDE_RIGHT] =            PA_CHANNEL_POSITION_SIDE_RIGHT,
169         [SF_CHANNEL_MAP_TOP_CENTER] =            PA_CHANNEL_POSITION_TOP_CENTER,
170         [SF_CHANNEL_MAP_TOP_FRONT_LEFT] =        PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
171         [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] =       PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
172         [SF_CHANNEL_MAP_TOP_FRONT_CENTER] =      PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
173         [SF_CHANNEL_MAP_TOP_REAR_LEFT] =         PA_CHANNEL_POSITION_TOP_REAR_LEFT,
174         [SF_CHANNEL_MAP_TOP_REAR_RIGHT] =        PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
175         [SF_CHANNEL_MAP_TOP_REAR_CENTER] =       PA_CHANNEL_POSITION_TOP_REAR_CENTER
176     };
177 
178     SF_INFO sfi;
179     int sf_errno;
180     int *channels;
181     unsigned c;
182 
183     pa_assert(sf);
184     pa_assert(cm);
185 
186     pa_zero(sfi);
187     if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
188         pa_log_error("sndfile: %s", sf_error_number(sf_errno));
189         return -1;
190     }
191 
192     channels = pa_xnew(int, sfi.channels);
193     if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * sfi.channels)) {
194         pa_xfree(channels);
195         return -1;
196     }
197 
198     cm->channels = (uint8_t) sfi.channels;
199     for (c = 0; c < cm->channels; c++) {
200         if (channels[c] <= SF_CHANNEL_MAP_INVALID ||
201             (unsigned) channels[c] >= PA_ELEMENTSOF(table)) {
202             pa_xfree(channels);
203             return -1;
204         }
205 
206         cm->map[c] = table[channels[c]];
207     }
208 
209     pa_xfree(channels);
210 
211     if (!pa_channel_map_valid(cm))
212         return -1;
213 
214     return 0;
215 }
216 
pa_sndfile_write_channel_map(SNDFILE * sf,pa_channel_map * cm)217 int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) {
218     static const int table[PA_CHANNEL_POSITION_MAX] = {
219         [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO,
220 
221         [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT,
222         [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT,
223         [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER,
224 
225         [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER,
226         [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT,
227         [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT,
228 
229         [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE,
230 
231         [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,
232         [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,
233 
234         [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT,
235         [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT,
236 
237         [PA_CHANNEL_POSITION_AUX0] = -1,
238         [PA_CHANNEL_POSITION_AUX1] = -1,
239         [PA_CHANNEL_POSITION_AUX2] = -1,
240         [PA_CHANNEL_POSITION_AUX3] = -1,
241         [PA_CHANNEL_POSITION_AUX4] = -1,
242         [PA_CHANNEL_POSITION_AUX5] = -1,
243         [PA_CHANNEL_POSITION_AUX6] = -1,
244         [PA_CHANNEL_POSITION_AUX7] = -1,
245         [PA_CHANNEL_POSITION_AUX8] = -1,
246         [PA_CHANNEL_POSITION_AUX9] = -1,
247         [PA_CHANNEL_POSITION_AUX10] = -1,
248         [PA_CHANNEL_POSITION_AUX11] = -1,
249         [PA_CHANNEL_POSITION_AUX12] = -1,
250         [PA_CHANNEL_POSITION_AUX13] = -1,
251         [PA_CHANNEL_POSITION_AUX14] = -1,
252         [PA_CHANNEL_POSITION_AUX15] = -1,
253         [PA_CHANNEL_POSITION_AUX16] = -1,
254         [PA_CHANNEL_POSITION_AUX17] = -1,
255         [PA_CHANNEL_POSITION_AUX18] = -1,
256         [PA_CHANNEL_POSITION_AUX19] = -1,
257         [PA_CHANNEL_POSITION_AUX20] = -1,
258         [PA_CHANNEL_POSITION_AUX21] = -1,
259         [PA_CHANNEL_POSITION_AUX22] = -1,
260         [PA_CHANNEL_POSITION_AUX23] = -1,
261         [PA_CHANNEL_POSITION_AUX24] = -1,
262         [PA_CHANNEL_POSITION_AUX25] = -1,
263         [PA_CHANNEL_POSITION_AUX26] = -1,
264         [PA_CHANNEL_POSITION_AUX27] = -1,
265         [PA_CHANNEL_POSITION_AUX28] = -1,
266         [PA_CHANNEL_POSITION_AUX29] = -1,
267         [PA_CHANNEL_POSITION_AUX30] = -1,
268         [PA_CHANNEL_POSITION_AUX31] = -1,
269 
270         [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER,
271 
272         [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT,
273         [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT,
274         [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER ,
275 
276         [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT,
277         [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT,
278         [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER,
279     };
280 
281     int *channels;
282     unsigned c;
283 
284     pa_assert(sf);
285     pa_assert(cm);
286 
287     /* Suppress channel mapping for the obvious cases */
288     if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO)
289         return 0;
290 
291     if (cm->channels == 2 &&
292         cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT &&
293         cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT)
294         return 0;
295 
296     channels = pa_xnew(int, cm->channels);
297     for (c = 0; c < cm->channels; c++) {
298 
299         if (cm->map[c] < 0 ||
300             cm->map[c] >= PA_CHANNEL_POSITION_MAX ||
301             table[cm->map[c]] < 0) {
302             pa_xfree(channels);
303             return -1;
304         }
305 
306         channels[c] = table[cm->map[c]];
307     }
308 
309     if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * cm->channels)) {
310         pa_xfree(channels);
311         return -1;
312     }
313 
314     pa_xfree(channels);
315     return 0;
316 }
317 
pa_sndfile_init_proplist(SNDFILE * sf,pa_proplist * p)318 void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) {
319 
320     static const char* table[] = {
321         [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE,
322         [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT,
323         [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE,
324         [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST,
325         [SF_STR_COMMENT] = "media.comment",
326         [SF_STR_DATE] = "media.date"
327     };
328 
329     SF_INFO sfi;
330     SF_FORMAT_INFO fi;
331     int sf_errno;
332     unsigned c;
333 
334     pa_assert(sf);
335     pa_assert(p);
336 
337     for (c = 0; c < PA_ELEMENTSOF(table); c++) {
338         const char *s;
339         char *t;
340 
341         if (!table[c])
342             continue;
343 
344         if (!(s = sf_get_string(sf, c)))
345             continue;
346 
347         t = pa_utf8_filter(s);
348         pa_proplist_sets(p, table[c], t);
349         pa_xfree(t);
350     }
351 
352     pa_zero(sfi);
353     if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
354         pa_log_error("sndfile: %s", sf_error_number(sf_errno));
355         return;
356     }
357 
358     pa_zero(fi);
359     fi.format = sfi.format;
360     if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) {
361         char *t;
362 
363         t = pa_utf8_filter(fi.name);
364         pa_proplist_sets(p, "media.format", t);
365         pa_xfree(t);
366     }
367 }
368 
pa_sndfile_readf_function(const pa_sample_spec * ss)369 pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) {
370     pa_assert(ss);
371 
372     switch (ss->format) {
373         case PA_SAMPLE_S16NE:
374             return (pa_sndfile_readf_t) sf_readf_short;
375 
376         case PA_SAMPLE_S32NE:
377         case PA_SAMPLE_S24_32NE:
378             return (pa_sndfile_readf_t) sf_readf_int;
379 
380         case PA_SAMPLE_FLOAT32NE:
381             return (pa_sndfile_readf_t) sf_readf_float;
382 
383         case PA_SAMPLE_ULAW:
384         case PA_SAMPLE_ALAW:
385         case PA_SAMPLE_S24NE:
386             return NULL;
387 
388         default:
389             pa_assert_not_reached();
390     }
391 }
392 
pa_sndfile_writef_function(const pa_sample_spec * ss)393 pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) {
394     pa_assert(ss);
395 
396     switch (ss->format) {
397         case PA_SAMPLE_S16NE:
398             return (pa_sndfile_writef_t) sf_writef_short;
399 
400         case PA_SAMPLE_S32NE:
401         case PA_SAMPLE_S24_32NE:
402             return (pa_sndfile_writef_t) sf_writef_int;
403 
404         case PA_SAMPLE_FLOAT32NE:
405             return (pa_sndfile_writef_t) sf_writef_float;
406 
407         case PA_SAMPLE_ULAW:
408         case PA_SAMPLE_ALAW:
409         case PA_SAMPLE_S24NE:
410             return NULL;
411 
412         default:
413             pa_assert_not_reached();
414     }
415 }
416 
pa_sndfile_format_from_string(const char * name)417 int pa_sndfile_format_from_string(const char *name) {
418     int i, count = 0;
419 
420     if (!name[0])
421         return -1;
422 
423     pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
424 
425     for (i = 0; i < count; i++) {
426         SF_FORMAT_INFO fi;
427         pa_zero(fi);
428         fi.format = i;
429 
430         pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
431 
432         /* First try to match via full type string */
433         if (strcasecmp(name, fi.name) == 0)
434             return fi.format;
435 
436         /* Then, try to match via the full extension */
437         if (strcasecmp(name, fi.extension) == 0)
438             return fi.format;
439 
440         /* Then, try to match via the start of the type string */
441         if (strncasecmp(name, fi.name, strlen(name)) == 0)
442             return fi.format;
443     }
444 
445     return -1;
446 }
447 
pa_sndfile_dump_formats(void)448 void pa_sndfile_dump_formats(void) {
449     int i, count = 0;
450 
451     pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
452 
453     for (i = 0; i < count; i++) {
454         SF_FORMAT_INFO fi;
455         pa_zero(fi);
456         fi.format = i;
457 
458         pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
459         printf("%s\t%s\n", fi.extension, fi.name);
460     }
461 }
462