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