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