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