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
33int 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
86int 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
151int 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
217int 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
318void 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
369pa_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
393pa_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
417int 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
448void 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