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>
33 #include "sndfile-util.h"
35 int pa_sndfile_read_sample_spec(SNDFILE
*sf
, pa_sample_spec
*ss
) {
42 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
44 switch (sfi
.format
& SF_FORMAT_SUBMASK
) {
46 case SF_FORMAT_PCM_16
:
47 case SF_FORMAT_PCM_U8
:
48 case SF_FORMAT_PCM_S8
:
49 ss
->format
= PA_SAMPLE_S16NE
;
52 case SF_FORMAT_PCM_24
:
53 ss
->format
= PA_SAMPLE_S24NE
;
56 case SF_FORMAT_PCM_32
:
57 ss
->format
= PA_SAMPLE_S32NE
;
61 ss
->format
= PA_SAMPLE_ULAW
;
65 ss
->format
= PA_SAMPLE_ALAW
;
69 case SF_FORMAT_DOUBLE
:
71 ss
->format
= PA_SAMPLE_FLOAT32NE
;
75 ss
->rate
= (uint32_t) sfi
.samplerate
;
76 ss
->channels
= (uint8_t) sfi
.channels
;
78 if (!pa_sample_spec_valid(ss
))
84 int pa_sndfile_write_sample_spec(SF_INFO
*sfi
, pa_sample_spec
*ss
) {
88 sfi
->samplerate
= (int) ss
->rate
;
89 sfi
->channels
= (int) ss
->channels
;
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
;
99 ss
->format
= PA_SAMPLE_S16NE
;
100 sfi
->format
= SF_FORMAT_PCM_U8
;
103 case PA_SAMPLE_S16LE
:
104 case PA_SAMPLE_S16BE
:
105 ss
->format
= PA_SAMPLE_S16NE
;
106 sfi
->format
|= SF_FORMAT_PCM_16
;
109 case PA_SAMPLE_S24LE
:
110 case PA_SAMPLE_S24BE
:
111 ss
->format
= PA_SAMPLE_S24NE
;
112 sfi
->format
|= SF_FORMAT_PCM_24
;
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
;
121 case PA_SAMPLE_S32LE
:
122 case PA_SAMPLE_S32BE
:
123 ss
->format
= PA_SAMPLE_S32NE
;
124 sfi
->format
|= SF_FORMAT_PCM_32
;
128 sfi
->format
= SF_FORMAT_ULAW
;
132 sfi
->format
= SF_FORMAT_ALAW
;
135 case PA_SAMPLE_FLOAT32LE
:
136 case PA_SAMPLE_FLOAT32BE
:
138 ss
->format
= PA_SAMPLE_FLOAT32NE
;
139 sfi
->format
|= SF_FORMAT_FLOAT
;
144 if (!pa_sample_spec_valid(ss
))
150 int pa_sndfile_read_channel_map(SNDFILE
*sf
, pa_channel_map
*cm
) {
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
185 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
187 channels
= pa_xnew(int, sfi
.channels
);
188 if (!sf_command(sf
, SFC_GET_CHANNEL_MAP_INFO
,
189 channels
, sizeof(channels
[0]) * sfi
.channels
)) {
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
)) {
203 cm
->map
[c
] = table
[channels
[c
]];
208 if (!pa_channel_map_valid(cm
))
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
,
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
,
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
,
226 [PA_CHANNEL_POSITION_LFE
] = SF_CHANNEL_MAP_LFE
,
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
,
231 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SF_CHANNEL_MAP_SIDE_LEFT
,
232 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SF_CHANNEL_MAP_SIDE_RIGHT
,
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,
267 [PA_CHANNEL_POSITION_TOP_CENTER
] = SF_CHANNEL_MAP_TOP_CENTER
,
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
,
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
,
284 /* Suppress channel mapping for the obvious cases */
285 if (cm
->channels
== 1 && cm
->map
[0] == PA_CHANNEL_POSITION_MONO
)
288 if (cm
->channels
== 2 &&
289 cm
->map
[0] == PA_CHANNEL_POSITION_FRONT_LEFT
&&
290 cm
->map
[1] == PA_CHANNEL_POSITION_FRONT_RIGHT
)
293 channels
= pa_xnew(int, cm
->channels
);
294 for (c
= 0; c
< cm
->channels
; c
++) {
296 if (cm
->map
[c
] < 0 ||
297 cm
->map
[c
] >= PA_CHANNEL_POSITION_MAX
||
298 table
[cm
->map
[c
]] < 0) {
303 channels
[c
] = table
[cm
->map
[c
]];
306 if (!sf_command(sf
, SFC_SET_CHANNEL_MAP_INFO
, channels
, sizeof(channels
[0]) * cm
->channels
)) {
315 void pa_sndfile_init_proplist(SNDFILE
*sf
, pa_proplist
*p
) {
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"
333 for (c
= 0; c
< PA_ELEMENTSOF(table
); c
++) {
340 if (!(s
= sf_get_string(sf
, c
)))
343 t
= pa_utf8_filter(s
);
344 pa_proplist_sets(p
, table
[c
], t
);
349 pa_assert_se(sf_command(sf
, SFC_GET_CURRENT_SF_INFO
, &sfi
, sizeof(sfi
)) == 0);
352 fi
.format
= sfi
.format
;
353 if (sf_command(sf
, SFC_GET_FORMAT_INFO
, &fi
, sizeof(fi
)) == 0 && fi
.name
) {
356 t
= pa_utf8_filter(fi
.name
);
357 pa_proplist_sets(p
, "media.format", t
);
362 pa_sndfile_readf_t
pa_sndfile_readf_function(const pa_sample_spec
*ss
) {
365 switch (ss
->format
) {
366 case PA_SAMPLE_S16NE
:
367 return (pa_sndfile_readf_t
) sf_readf_short
;
369 case PA_SAMPLE_S32NE
:
370 case PA_SAMPLE_S24_32NE
:
371 return (pa_sndfile_readf_t
) sf_readf_int
;
373 case PA_SAMPLE_FLOAT32NE
:
374 return (pa_sndfile_readf_t
) sf_readf_float
;
381 pa_assert_not_reached();
385 pa_sndfile_writef_t
pa_sndfile_writef_function(const pa_sample_spec
*ss
) {
388 switch (ss
->format
) {
389 case PA_SAMPLE_S16NE
:
390 return (pa_sndfile_writef_t
) sf_writef_short
;
392 case PA_SAMPLE_S32NE
:
393 case PA_SAMPLE_S24_32NE
:
394 return (pa_sndfile_writef_t
) sf_writef_int
;
396 case PA_SAMPLE_FLOAT32NE
:
397 return (pa_sndfile_writef_t
) sf_writef_float
;
404 pa_assert_not_reached();
408 int pa_sndfile_format_from_string(const char *name
) {
415 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR_COUNT
, &count
, sizeof(int)) == 0);
417 /* First try to match via full type string */
418 for (i
= 0; i
< count
; i
++) {
423 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
425 if (strcasecmp(name
, fi
.name
) == 0)
429 /* Then, try to match via the full extension */
430 for (i
= 0; i
< count
; i
++) {
435 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
437 if (strcasecmp(name
, fi
.extension
) == 0)
441 /* Then, try to match via the start of the type string */
442 for (i
= 0; i
< count
; i
++) {
447 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR
, &fi
, sizeof(fi
)) == 0);
449 if (strncasecmp(name
, fi
.name
, strlen(name
)) == 0)
456 void pa_sndfile_dump_formats(void) {
459 pa_assert_se(sf_command(NULL
, SFC_GET_FORMAT_MAJOR_COUNT
, &count
, sizeof(int)) == 0);
461 for (i
= 0; i
< count
; i
++) {
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
);