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