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