2 This file is part of PulseAudio.
4 Copyright 2009 Lennart Poettering
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.
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.
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
26 /* Shared between pacat/parec/paplay and the server */
28 #include <pulse/xmalloc.h>
29 #include <pulse/utf8.h>
31 #include <pulsecore/macro.h>
32 #include <pulsecore/core-util.h>
34 #include "sndfile-util.h"
36 int pa_sndfile_read_sample_spec(SNDFILE
*sf
, pa_sample_spec
*ss
) {
43 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
45 switch (sfi
.format
& SF_FORMAT_SUBMASK
) {
47 case SF_FORMAT_PCM_16
:
48 case SF_FORMAT_PCM_U8
:
49 case SF_FORMAT_PCM_S8
:
50 ss
->format
= PA_SAMPLE_S16NE
;
53 case SF_FORMAT_PCM_24
:
54 ss
->format
= PA_SAMPLE_S24NE
;
57 case SF_FORMAT_PCM_32
:
58 ss
->format
= PA_SAMPLE_S32NE
;
62 ss
->format
= PA_SAMPLE_ULAW
;
66 ss
->format
= PA_SAMPLE_ALAW
;
70 case SF_FORMAT_DOUBLE
:
72 ss
->format
= PA_SAMPLE_FLOAT32NE
;
76 ss
->rate
= (uint32_t) sfi
.samplerate
;
77 ss
->channels
= (uint8_t) sfi
.channels
;
79 if (!pa_sample_spec_valid(ss
))
85 int pa_sndfile_write_sample_spec(SF_INFO
*sfi
, pa_sample_spec
*ss
) {
89 sfi
->samplerate
= (int) ss
->rate
;
90 sfi
->channels
= (int) ss
->channels
;
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
;
100 ss
->format
= PA_SAMPLE_S16NE
;
101 sfi
->format
= SF_FORMAT_PCM_U8
;
104 case PA_SAMPLE_S16LE
:
105 case PA_SAMPLE_S16BE
:
106 ss
->format
= PA_SAMPLE_S16NE
;
107 sfi
->format
|= SF_FORMAT_PCM_16
;
110 case PA_SAMPLE_S24LE
:
111 case PA_SAMPLE_S24BE
:
112 ss
->format
= PA_SAMPLE_S24NE
;
113 sfi
->format
|= SF_FORMAT_PCM_24
;
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
;
122 case PA_SAMPLE_S32LE
:
123 case PA_SAMPLE_S32BE
:
124 ss
->format
= PA_SAMPLE_S32NE
;
125 sfi
->format
|= SF_FORMAT_PCM_32
;
129 sfi
->format
= SF_FORMAT_ULAW
;
133 sfi
->format
= SF_FORMAT_ALAW
;
136 case PA_SAMPLE_FLOAT32LE
:
137 case PA_SAMPLE_FLOAT32BE
:
139 ss
->format
= PA_SAMPLE_FLOAT32NE
;
140 sfi
->format
|= SF_FORMAT_FLOAT
;
145 if (!pa_sample_spec_valid(ss
))
151 int pa_sndfile_read_channel_map(SNDFILE
*sf
, pa_channel_map
*cm
) {
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
186 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
188 channels
= pa_xnew(int, sfi
.channels
);
189 if (!sf_command(sf
, SFC_GET_CHANNEL_MAP_INFO
,
190 channels
, sizeof(channels
[0]) * sfi
.channels
)) {
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
)) {
204 cm
->map
[c
] = table
[channels
[c
]];
209 if (!pa_channel_map_valid(cm
))
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
,
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
,
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
,
227 [PA_CHANNEL_POSITION_LFE
] = SF_CHANNEL_MAP_LFE
,
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
,
232 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SF_CHANNEL_MAP_SIDE_LEFT
,
233 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SF_CHANNEL_MAP_SIDE_RIGHT
,
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,
268 [PA_CHANNEL_POSITION_TOP_CENTER
] = SF_CHANNEL_MAP_TOP_CENTER
,
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
,
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
,
285 /* Suppress channel mapping for the obvious cases */
286 if (cm
->channels
== 1 && cm
->map
[0] == PA_CHANNEL_POSITION_MONO
)
289 if (cm
->channels
== 2 &&
290 cm
->map
[0] == PA_CHANNEL_POSITION_FRONT_LEFT
&&
291 cm
->map
[1] == PA_CHANNEL_POSITION_FRONT_RIGHT
)
294 channels
= pa_xnew(int, cm
->channels
);
295 for (c
= 0; c
< cm
->channels
; c
++) {
297 if (cm
->map
[c
] < 0 ||
298 cm
->map
[c
] >= PA_CHANNEL_POSITION_MAX
||
299 table
[cm
->map
[c
]] < 0) {
304 channels
[c
] = table
[cm
->map
[c
]];
307 if (!sf_command(sf
, SFC_SET_CHANNEL_MAP_INFO
, channels
, sizeof(channels
[0]) * cm
->channels
)) {
316 void pa_sndfile_init_proplist(SNDFILE
*sf
, pa_proplist
*p
) {
318 static const char* table
[] = {
319 [SF_STR_TITLE
] = PA_PROP_MEDIA_TITLE
,
320 [SF_STR_COPYRIGHT
] = PA_PROP_MEDIA_COPYRIGHT
,
321 [SF_STR_SOFTWARE
] = PA_PROP_MEDIA_SOFTWARE
,
322 [SF_STR_ARTIST
] = PA_PROP_MEDIA_ARTIST
,
323 [SF_STR_COMMENT
] = "media.comment",
324 [SF_STR_DATE
] = "media.date"
334 for (c
= 0; c
< PA_ELEMENTSOF(table
); c
++) {
341 if (!(s
= sf_get_string(sf
, c
)))
344 t
= pa_utf8_filter(s
);
345 pa_proplist_sets(p
, table
[c
], t
);
350 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
353 fi
.format
= sfi
.format
;
354 if (sf_command(sf
, SFC_GET_FORMAT_INFO
, &fi
, sizeof(fi
)) == 0 && fi
.name
) {
357 t
= pa_utf8_filter(fi
.name
);
358 pa_proplist_sets(p
, "media.format", t
);
363 pa_sndfile_readf_t
pa_sndfile_readf_function(const pa_sample_spec
*ss
) {
366 switch (ss
->format
) {
367 case PA_SAMPLE_S16NE
:
368 return (pa_sndfile_readf_t
) sf_readf_short
;
370 case PA_SAMPLE_S32NE
:
371 case PA_SAMPLE_S24_32NE
:
372 return (pa_sndfile_readf_t
) sf_readf_int
;
374 case PA_SAMPLE_FLOAT32NE
:
375 return (pa_sndfile_readf_t
) sf_readf_float
;
382 pa_assert_not_reached();
386 pa_sndfile_writef_t
pa_sndfile_writef_function(const pa_sample_spec
*ss
) {
389 switch (ss
->format
) {
390 case PA_SAMPLE_S16NE
:
391 return (pa_sndfile_writef_t
) sf_writef_short
;
393 case PA_SAMPLE_S32NE
:
394 case PA_SAMPLE_S24_32NE
:
395 return (pa_sndfile_writef_t
) sf_writef_int
;
397 case PA_SAMPLE_FLOAT32NE
:
398 return (pa_sndfile_writef_t
) sf_writef_float
;
405 pa_assert_not_reached();
409 int pa_sndfile_format_from_string(const char *name
) {
416 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR_COUNT
, &count
, sizeof(int)) == 0);
418 /* First try to match via full type string */
419 for (i
= 0; i
< count
; i
++) {
424 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
426 if (strcasecmp(name
, fi
.name
) == 0)
430 /* Then, try to match via the full extension */
431 for (i
= 0; i
< count
; i
++) {
436 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
438 if (strcasecmp(name
, fi
.extension
) == 0)
442 /* Then, try to match via the start of the type string */
443 for (i
= 0; i
< count
; i
++) {
448 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
450 if (strncasecmp(name
, fi
.extension
, strlen(name
)) == 0)
457 void pa_sndfile_dump_formats(void) {
460 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR_COUNT
, &count
, sizeof(int)) == 0);
462 for (i
= 0; i
< count
; i
++) {
467 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
468 printf("%s\t%s\n", fi
.extension
, fi
.name
);