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