1 /** 2 * \file pcm/pcm_file.c 3 * \ingroup PCM_Plugins 4 * \brief PCM File Plugin Interface 5 * \author Abramo Bagnara <abramo (at) alsa-project.org> 6 * \date 2000-2001 7 */ 8 /* 9 * PCM - File plugin 10 * Copyright (c) 2000 by Abramo Bagnara <abramo (at) alsa-project.org> 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU Lesser General Public License as 15 * published by the Free Software Foundation; either version 2.1 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 * 27 */ 28 29 #include <endian.h> 30 #include <byteswap.h> 31 #include <ctype.h> 32 #include "pcm_local.h" 33 #include "pcm_plugin.h" 34 35 #ifndef PIC 36 /* entry for static linking */ 37 const char *_snd_module_pcm_file = ""; 38 #endif 39 40 #ifndef DOC_HIDDEN 41 42 typedef enum _snd_pcm_file_format { 43 SND_PCM_FILE_FORMAT_RAW, 44 SND_PCM_FILE_FORMAT_WAV 45 } snd_pcm_file_format_t; 46 47 /* WAV format chunk */ 48 struct wav_fmt { 49 short fmt; 50 short chan; 51 int rate; 52 int bps; 53 short bwidth; 54 short bits; 55 }; 56 57 typedef struct { 58 snd_pcm_generic_t gen; 59 char *fname; 60 int fd; 61 char *ifname; 62 int ifd; 63 int format; 64 snd_pcm_uframes_t appl_ptr; 65 snd_pcm_uframes_t file_ptr_bytes; 66 snd_pcm_uframes_t wbuf_size; 67 size_t wbuf_size_bytes; 68 size_t wbuf_used_bytes; 69 char *wbuf; 70 size_t rbuf_size_bytes; 71 size_t rbuf_used_bytes; 72 char *rbuf; 73 snd_pcm_channel_area_t *wbuf_areas; 74 size_t buffer_bytes; 75 struct wav_fmt wav_header; 76 size_t filelen; 77 } snd_pcm_file_t; 78 79 #if __BYTE_ORDER == __LITTLE_ENDIAN 80 #define TO_LE32(x) (x) 81 #define TO_LE16(x) (x) 82 #else 83 #define TO_LE32(x) bswap_32(x) 84 #define TO_LE16(x) bswap_16(x) 85 #endif 86 87 static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt) 88 { 89 fmt->fmt = TO_LE16(0x01); 90 fmt->chan = TO_LE16(pcm->channels); 91 fmt->rate = TO_LE32(pcm->rate); 92 fmt->bwidth = pcm->frame_bits / 8; 93 fmt->bps = fmt->bwidth * pcm->rate; 94 fmt->bits = snd_pcm_format_width(pcm->format); 95 fmt->bps = TO_LE32(fmt->bps); 96 fmt->bwidth = TO_LE16(fmt->bwidth); 97 fmt->bits = TO_LE16(fmt->bits); 98 } 99 100 static int write_wav_header(snd_pcm_t *pcm) 101 { 102 snd_pcm_file_t *file = pcm->private_data; 103 static const char header[] = { 104 'R', 'I', 'F', 'F', 105 0x24, 0, 0, 0, 106 'W', 'A', 'V', 'E', 107 'f', 'm', 't', ' ', 108 0x10, 0, 0, 0, 109 }; 110 static const char header2[] = { 111 'd', 'a', 't', 'a', 112 0, 0, 0, 0 113 }; 114 115 setup_wav_header(pcm, &file->wav_header); 116 117 if (write(file->fd, header, sizeof(header)) != sizeof(header) || 118 write(file->fd, &file->wav_header, sizeof(file->wav_header)) != 119 sizeof(file->wav_header) || 120 write(file->fd, header2, sizeof(header2)) != sizeof(header2)) { 121 int err = errno; 122 SYSERR("Write error.\n"); 123 return -err; 124 } 125 return 0; 126 } 127 128 /* fix up the length fields in WAV header */ 129 static void fixup_wav_header(snd_pcm_t *pcm) 130 { 131 snd_pcm_file_t *file = pcm->private_data; 132 int len, ret; 133 134 /* RIFF length */ 135 if (lseek(file->fd, 4, SEEK_SET) == 4) { 136 len = (file->filelen + 0x24) > 0x7fffffff ? 137 0x7fffffff : (int)(file->filelen + 0x24); 138 len = TO_LE32(len); 139 ret = write(file->fd, &len, 4); 140 if (ret < 0) 141 return; 142 } 143 /* data length */ 144 if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) { 145 len = file->filelen > 0x7fffffff ? 146 0x7fffffff : (int)file->filelen; 147 len = TO_LE32(len); 148 ret = write(file->fd, &len, 4); 149 if (ret < 0) 150 return; 151 } 152 } 153 #endif /* DOC_HIDDEN */ 154 155 static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes) 156 { 157 snd_pcm_file_t *file = pcm->private_data; 158 assert(bytes <= file->wbuf_used_bytes); 159 160 if (file->format == SND_PCM_FILE_FORMAT_WAV && 161 !file->wav_header.fmt) { 162 if (write_wav_header(pcm) < 0) 163 return; 164 } 165 166 while (bytes > 0) { 167 snd_pcm_sframes_t err; 168 size_t n = bytes; 169 size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes; 170 if (n > cont) 171 n = cont; 172 err = write(file->fd, file->wbuf + file->file_ptr_bytes, n); 173 if (err < 0) { 174 SYSERR("write failed"); 175 break; 176 } 177 bytes -= err; 178 file->wbuf_used_bytes -= err; 179 file->file_ptr_bytes += err; 180 if (file->file_ptr_bytes == file->wbuf_size_bytes) 181 file->file_ptr_bytes = 0; 182 file->filelen += err; 183 if ((snd_pcm_uframes_t)err != n) 184 break; 185 } 186 } 187 188 static void snd_pcm_file_add_frames(snd_pcm_t *pcm, 189 const snd_pcm_channel_area_t *areas, 190 snd_pcm_uframes_t offset, 191 snd_pcm_uframes_t frames) 192 { 193 snd_pcm_file_t *file = pcm->private_data; 194 while (frames > 0) { 195 snd_pcm_uframes_t n = frames; 196 snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr; 197 snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 198 if (n > cont) 199 n = cont; 200 if (n > avail) 201 n = avail; 202 snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr, 203 areas, offset, 204 pcm->channels, n, pcm->format); 205 frames -= n; 206 offset += n; 207 file->appl_ptr += n; 208 if (file->appl_ptr == file->wbuf_size) 209 file->appl_ptr = 0; 210 file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n); 211 if (file->wbuf_used_bytes > file->buffer_bytes) 212 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes); 213 assert(file->wbuf_used_bytes < file->wbuf_size_bytes); 214 } 215 } 216 217 static int snd_pcm_file_close(snd_pcm_t *pcm) 218 { 219 snd_pcm_file_t *file = pcm->private_data; 220 if (file->fname) { 221 if (file->wav_header.fmt) 222 fixup_wav_header(pcm); 223 free((void *)file->fname); 224 close(file->fd); 225 } 226 if (file->ifname) { 227 free((void *)file->ifname); 228 close(file->ifd); 229 } 230 return snd_pcm_generic_close(pcm); 231 } 232 233 static int snd_pcm_file_reset(snd_pcm_t *pcm) 234 { 235 snd_pcm_file_t *file = pcm->private_data; 236 int err = snd_pcm_reset(file->gen.slave); 237 if (err >= 0) { 238 /* FIXME: Questionable here */ 239 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 240 assert(file->wbuf_used_bytes == 0); 241 } 242 return err; 243 } 244 245 static int snd_pcm_file_drop(snd_pcm_t *pcm) 246 { 247 snd_pcm_file_t *file = pcm->private_data; 248 int err = snd_pcm_drop(file->gen.slave); 249 if (err >= 0) { 250 /* FIXME: Questionable here */ 251 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 252 assert(file->wbuf_used_bytes == 0); 253 } 254 return err; 255 } 256 257 static int snd_pcm_file_drain(snd_pcm_t *pcm) 258 { 259 snd_pcm_file_t *file = pcm->private_data; 260 int err = snd_pcm_drain(file->gen.slave); 261 if (err >= 0) { 262 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 263 assert(file->wbuf_used_bytes == 0); 264 } 265 return err; 266 } 267 268 static snd_pcm_sframes_t snd_pcm_file_rewindable(snd_pcm_t *pcm) 269 { 270 snd_pcm_file_t *file = pcm->private_data; 271 snd_pcm_sframes_t res = snd_pcm_rewindable(pcm); 272 snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 273 if (res > n) 274 res = n; 275 return res; 276 } 277 278 static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 279 { 280 snd_pcm_file_t *file = pcm->private_data; 281 snd_pcm_sframes_t err; 282 snd_pcm_uframes_t n; 283 284 n = snd_pcm_frames_to_bytes(pcm, frames); 285 if (n > file->wbuf_used_bytes) 286 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 287 err = snd_pcm_rewind(file->gen.slave, frames); 288 if (err > 0) { 289 file->appl_ptr = (file->appl_ptr - err + file->wbuf_size) % file->wbuf_size; 290 n = snd_pcm_frames_to_bytes(pcm, err); 291 file->wbuf_used_bytes -= n; 292 } 293 return err; 294 } 295 296 static snd_pcm_sframes_t snd_pcm_file_forwardable(snd_pcm_t *pcm) 297 { 298 snd_pcm_file_t *file = pcm->private_data; 299 snd_pcm_sframes_t res = snd_pcm_forwardable(pcm); 300 snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes); 301 if (res > n) 302 res = n; 303 return res; 304 } 305 306 static snd_pcm_sframes_t snd_pcm_file_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 307 { 308 snd_pcm_file_t *file = pcm->private_data; 309 snd_pcm_sframes_t err; 310 snd_pcm_uframes_t n; 311 312 n = snd_pcm_frames_to_bytes(pcm, frames); 313 if (file->wbuf_used_bytes + n > file->wbuf_size_bytes) 314 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes); 315 err = INTERNAL(snd_pcm_forward)(file->gen.slave, frames); 316 if (err > 0) { 317 file->appl_ptr = (file->appl_ptr + err) % file->wbuf_size; 318 n = snd_pcm_frames_to_bytes(pcm, err); 319 file->wbuf_used_bytes += n; 320 } 321 return err; 322 } 323 324 static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) 325 { 326 snd_pcm_file_t *file = pcm->private_data; 327 snd_pcm_channel_area_t areas[pcm->channels]; 328 snd_pcm_sframes_t n = snd_pcm_writei(file->gen.slave, buffer, size); 329 if (n > 0) { 330 snd_pcm_areas_from_buf(pcm, areas, (void*) buffer); 331 snd_pcm_file_add_frames(pcm, areas, 0, n); 332 } 333 return n; 334 } 335 336 static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 337 { 338 snd_pcm_file_t *file = pcm->private_data; 339 snd_pcm_channel_area_t areas[pcm->channels]; 340 snd_pcm_sframes_t n = snd_pcm_writen(file->gen.slave, bufs, size); 341 if (n > 0) { 342 snd_pcm_areas_from_bufs(pcm, areas, bufs); 343 snd_pcm_file_add_frames(pcm, areas, 0, n); 344 } 345 return n; 346 } 347 348 static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) 349 { 350 snd_pcm_file_t *file = pcm->private_data; 351 snd_pcm_channel_area_t areas[pcm->channels]; 352 snd_pcm_sframes_t n; 353 354 n = snd_pcm_readi(file->gen.slave, buffer, size); 355 if (n <= 0) 356 return n; 357 if (file->ifd >= 0) { 358 n = read(file->ifd, buffer, n * pcm->frame_bits / 8); 359 if (n < 0) 360 return n; 361 return n * 8 / pcm->frame_bits; 362 } 363 snd_pcm_areas_from_buf(pcm, areas, buffer); 364 snd_pcm_file_add_frames(pcm, areas, 0, n); 365 return n; 366 } 367 368 static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 369 { 370 snd_pcm_file_t *file = pcm->private_data; 371 snd_pcm_channel_area_t areas[pcm->channels]; 372 snd_pcm_sframes_t n; 373 374 if (file->ifd >= 0) { 375 SNDERR("DEBUG: Noninterleaved read not yet implemented.\n"); 376 return 0; /* TODO: Noninterleaved read */ 377 } 378 379 n = snd_pcm_readn(file->gen.slave, bufs, size); 380 if (n > 0) { 381 snd_pcm_areas_from_bufs(pcm, areas, bufs); 382 snd_pcm_file_add_frames(pcm, areas, 0, n); 383 } 384 return n; 385 } 386 387 static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm, 388 snd_pcm_uframes_t offset, 389 snd_pcm_uframes_t size) 390 { 391 snd_pcm_file_t *file = pcm->private_data; 392 snd_pcm_uframes_t ofs; 393 snd_pcm_uframes_t siz = size; 394 const snd_pcm_channel_area_t *areas; 395 snd_pcm_sframes_t result; 396 397 snd_pcm_mmap_begin(file->gen.slave, &areas, &ofs, &siz); 398 assert(ofs == offset && siz == size); 399 result = snd_pcm_mmap_commit(file->gen.slave, ofs, siz); 400 if (result > 0) 401 snd_pcm_file_add_frames(pcm, areas, ofs, result); 402 return result; 403 } 404 405 static int snd_pcm_file_hw_free(snd_pcm_t *pcm) 406 { 407 snd_pcm_file_t *file = pcm->private_data; 408 free(file->wbuf); 409 free(file->wbuf_areas); 410 file->wbuf = NULL; 411 file->wbuf_areas = NULL; 412 return snd_pcm_hw_free(file->gen.slave); 413 } 414 415 static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 416 { 417 snd_pcm_file_t *file = pcm->private_data; 418 unsigned int channel; 419 snd_pcm_t *slave = file->gen.slave; 420 int err = _snd_pcm_hw_params(slave, params); 421 if (err < 0) 422 return err; 423 file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size); 424 file->wbuf_size = slave->buffer_size * 2; 425 file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size); 426 file->wbuf_used_bytes = 0; 427 assert(!file->wbuf); 428 file->wbuf = malloc(file->wbuf_size_bytes); 429 if (file->wbuf == NULL) { 430 snd_pcm_file_hw_free(pcm); 431 return -ENOMEM; 432 } 433 file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels); 434 if (file->wbuf_areas == NULL) { 435 snd_pcm_file_hw_free(pcm); 436 return -ENOMEM; 437 } 438 file->appl_ptr = file->file_ptr_bytes = 0; 439 for (channel = 0; channel < slave->channels; ++channel) { 440 snd_pcm_channel_area_t *a = &file->wbuf_areas[channel]; 441 a->addr = file->wbuf; 442 a->first = slave->sample_bits * channel; 443 a->step = slave->frame_bits; 444 } 445 return 0; 446 } 447 448 static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out) 449 { 450 snd_pcm_file_t *file = pcm->private_data; 451 if (file->fname) 452 snd_output_printf(out, "File PCM (file=%s)\n", file->fname); 453 else 454 snd_output_printf(out, "File PCM (fd=%d)\n", file->fd); 455 if (pcm->setup) { 456 snd_output_printf(out, "Its setup is:\n"); 457 snd_pcm_dump_setup(pcm, out); 458 } 459 snd_output_printf(out, "Slave: "); 460 snd_pcm_dump(file->gen.slave, out); 461 } 462 463 static const snd_pcm_ops_t snd_pcm_file_ops = { 464 .close = snd_pcm_file_close, 465 .info = snd_pcm_generic_info, 466 .hw_refine = snd_pcm_generic_hw_refine, 467 .hw_params = snd_pcm_file_hw_params, 468 .hw_free = snd_pcm_file_hw_free, 469 .sw_params = snd_pcm_generic_sw_params, 470 .channel_info = snd_pcm_generic_channel_info, 471 .dump = snd_pcm_file_dump, 472 .nonblock = snd_pcm_generic_nonblock, 473 .async = snd_pcm_generic_async, 474 .mmap = snd_pcm_generic_mmap, 475 .munmap = snd_pcm_generic_munmap, 476 }; 477 478 static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = { 479 .status = snd_pcm_generic_status, 480 .state = snd_pcm_generic_state, 481 .hwsync = snd_pcm_generic_hwsync, 482 .delay = snd_pcm_generic_delay, 483 .prepare = snd_pcm_generic_prepare, 484 .reset = snd_pcm_file_reset, 485 .start = snd_pcm_generic_start, 486 .drop = snd_pcm_file_drop, 487 .drain = snd_pcm_file_drain, 488 .pause = snd_pcm_generic_pause, 489 .rewindable = snd_pcm_file_rewindable, 490 .rewind = snd_pcm_file_rewind, 491 .forwardable = snd_pcm_file_forwardable, 492 .forward = snd_pcm_file_forward, 493 .resume = snd_pcm_generic_resume, 494 .link = snd_pcm_generic_link, 495 .link_slaves = snd_pcm_generic_link_slaves, 496 .unlink = snd_pcm_generic_unlink, 497 .writei = snd_pcm_file_writei, 498 .writen = snd_pcm_file_writen, 499 .readi = snd_pcm_file_readi, 500 .readn = snd_pcm_file_readn, 501 .avail_update = snd_pcm_generic_avail_update, 502 .mmap_commit = snd_pcm_file_mmap_commit, 503 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 504 .poll_descriptors = snd_pcm_generic_poll_descriptors, 505 .poll_revents = snd_pcm_generic_poll_revents, 506 }; 507 508 /** 509 * \brief Creates a new File PCM 510 * \param pcmp Returns created PCM handle 511 * \param name Name of PCM 512 * \param fname Output filename (or NULL if file descriptor fd is available) 513 * \param fd Output file descriptor 514 * \param ifname Input filename (or NULL if file descriptor ifd is available) 515 * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input 516 * redirection will be performed) 517 * \param trunc Truncate the file if it already exists 518 * \param fmt File format ("raw" or "wav" are available) 519 * \param perm File permission 520 * \param slave Slave PCM handle 521 * \param close_slave When set, the slave PCM handle is closed with copy PCM 522 * \retval zero on success otherwise a negative error code 523 * \warning Using of this function might be dangerous in the sense 524 * of compatibility reasons. The prototype might be freely 525 * changed in future. 526 */ 527 int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, 528 const char *fname, int fd, const char *ifname, int ifd, 529 int trunc, 530 const char *fmt, int perm, snd_pcm_t *slave, int close_slave) 531 { 532 snd_pcm_t *pcm; 533 snd_pcm_file_t *file; 534 snd_pcm_file_format_t format; 535 struct timespec timespec; 536 char *tmpname = NULL; 537 int err; 538 539 assert(pcmp); 540 if (fmt == NULL || 541 strcmp(fmt, "raw") == 0) 542 format = SND_PCM_FILE_FORMAT_RAW; 543 else if (!strcmp(fmt, "wav")) 544 format = SND_PCM_FILE_FORMAT_WAV; 545 else { 546 SNDERR("file format %s is unknown", fmt); 547 return -EINVAL; 548 } 549 if (fname) { 550 if (trunc) 551 fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, perm); 552 else { 553 fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, perm); 554 if (fd < 0) { 555 int idx, len; 556 len = strlen(fname) + 6; 557 tmpname = malloc(len); 558 if (!tmpname) 559 return -ENOMEM; 560 for (idx = 1; idx < 10000; idx++) { 561 snprintf(tmpname, len, 562 "%s.%04d", fname, idx); 563 fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, perm); 564 if (fd >= 0) { 565 fname = tmpname; 566 break; 567 } 568 } 569 } 570 } 571 if (fd < 0) { 572 SYSERR("open %s for writing failed", fname); 573 free(tmpname); 574 return -errno; 575 } 576 } 577 file = calloc(1, sizeof(snd_pcm_file_t)); 578 if (!file) { 579 if (fname) 580 close(fd); 581 free(tmpname); 582 return -ENOMEM; 583 } 584 585 if (ifname) { 586 ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */ 587 if (ifd < 0) { 588 SYSERR("open %s for reading failed", ifname); 589 if (fname) 590 close(fd); 591 free(file); 592 free(tmpname); 593 return -errno; 594 } 595 } 596 597 if (fname) 598 file->fname = strdup(fname); 599 if (ifname) 600 file->ifname = strdup(ifname); 601 file->fd = fd; 602 file->ifd = ifd; 603 file->format = format; 604 file->gen.slave = slave; 605 file->gen.close_slave = close_slave; 606 607 err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode); 608 if (err < 0) { 609 free(file->fname); 610 free(file); 611 free(tmpname); 612 return err; 613 } 614 pcm->ops = &snd_pcm_file_ops; 615 pcm->fast_ops = &snd_pcm_file_fast_ops; 616 pcm->private_data = file; 617 pcm->poll_fd = slave->poll_fd; 618 pcm->poll_events = slave->poll_events; 619 pcm->mmap_shadow = 1; 620 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 621 pcm->monotonic = clock_gettime(CLOCK_MONOTONIC, ×pec) == 0; 622 #else 623 pcm->monotonic = 0; 624 #endif 625 snd_pcm_link_hw_ptr(pcm, slave); 626 snd_pcm_link_appl_ptr(pcm, slave); 627 *pcmp = pcm; 628 629 free(tmpname); 630 return 0; 631 } 632 633 /*! \page pcm_plugins 634 635 \section pcm_plugins_file Plugin: File 636 637 This plugin stores contents of a PCM stream to file, and optionally 638 uses an existing file as an input data source (i.e., "virtual mic") 639 640 \code 641 pcm.name { 642 type file # File PCM 643 slave STR # Slave name 644 # or 645 slave { # Slave definition 646 pcm STR # Slave PCM name 647 # or 648 pcm { } # Slave PCM definition 649 } 650 file STR # Output filename 651 or 652 file INT # Output file descriptor number 653 infile STR # Input filename - only raw format 654 or 655 infile INT # Input file descriptor number 656 [format STR] # File format ("raw" or "wav") 657 [perm INT] # Output file permission (octal, def. 0600) 658 } 659 \endcode 660 661 \subsection pcm_plugins_file_funcref Function reference 662 663 <UL> 664 <LI>snd_pcm_file_open() 665 <LI>_snd_pcm_file_open() 666 </UL> 667 668 */ 669 670 /** 671 * \brief Creates a new File PCM 672 * \param pcmp Returns created PCM handle 673 * \param name Name of PCM 674 * \param root Root configuration node 675 * \param conf Configuration node with File PCM description 676 * \param stream Stream type 677 * \param mode Stream mode 678 * \retval zero on success otherwise a negative error code 679 * \warning Using of this function might be dangerous in the sense 680 * of compatibility reasons. The prototype might be freely 681 * changed in future. 682 */ 683 int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, 684 snd_config_t *root, snd_config_t *conf, 685 snd_pcm_stream_t stream, int mode) 686 { 687 snd_config_iterator_t i, next; 688 int err; 689 snd_pcm_t *spcm; 690 snd_config_t *slave = NULL, *sconf; 691 const char *fname = NULL, *ifname = NULL; 692 const char *format = NULL; 693 long fd = -1, ifd = -1, trunc = 1; 694 long perm = 0600; 695 snd_config_for_each(i, next, conf) { 696 snd_config_t *n = snd_config_iterator_entry(i); 697 const char *id; 698 if (snd_config_get_id(n, &id) < 0) 699 continue; 700 if (snd_pcm_conf_generic_id(id)) 701 continue; 702 if (strcmp(id, "slave") == 0) { 703 slave = n; 704 continue; 705 } 706 if (strcmp(id, "format") == 0) { 707 err = snd_config_get_string(n, &format); 708 if (err < 0) { 709 SNDERR("Invalid type for %s", id); 710 return -EINVAL; 711 } 712 continue; 713 } 714 if (strcmp(id, "file") == 0) { 715 err = snd_config_get_string(n, &fname); 716 if (err < 0) { 717 err = snd_config_get_integer(n, &fd); 718 if (err < 0) { 719 SNDERR("Invalid type for %s", id); 720 return -EINVAL; 721 } 722 } 723 continue; 724 } 725 if (strcmp(id, "infile") == 0) { 726 err = snd_config_get_string(n, &ifname); 727 if (err < 0) { 728 err = snd_config_get_integer(n, &ifd); 729 if (err < 0) { 730 SNDERR("Invalid type for %s", id); 731 return -EINVAL; 732 } 733 } 734 continue; 735 } 736 if (strcmp(id, "perm") == 0) { 737 err = snd_config_get_integer(n, &perm); 738 if (err < 0) { 739 SNDERR("Invalid type for %s", id); 740 return err; 741 } 742 if ((perm & ~0777) != 0) { 743 SNDERR("The field perm must be a valid file permission"); 744 return -EINVAL; 745 } 746 continue; 747 } 748 if (strcmp(id, "truncate") == 0) { 749 err = snd_config_get_bool(n); 750 if (err < 0) 751 return -EINVAL; 752 trunc = err; 753 continue; 754 } 755 SNDERR("Unknown field %s", id); 756 return -EINVAL; 757 } 758 if (!format) { 759 snd_config_t *n; 760 /* read defaults */ 761 if (snd_config_search(root, "defaults.pcm.file_format", &n) >= 0) { 762 err = snd_config_get_string(n, &format); 763 if (err < 0) { 764 SNDERR("Invalid file format"); 765 return -EINVAL; 766 } 767 } 768 } 769 if (!slave) { 770 SNDERR("slave is not defined"); 771 return -EINVAL; 772 } 773 err = snd_pcm_slave_conf(root, slave, &sconf, 0); 774 if (err < 0) 775 return err; 776 if (!fname && fd < 0 && !ifname) { 777 snd_config_delete(sconf); 778 SNDERR("file is not defined"); 779 return -EINVAL; 780 } 781 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 782 snd_config_delete(sconf); 783 if (err < 0) 784 return err; 785 err = snd_pcm_file_open(pcmp, name, fname, fd, ifname, ifd, 786 trunc, format, perm, spcm, 1); 787 if (err < 0) 788 snd_pcm_close(spcm); 789 return err; 790 } 791 #ifndef DOC_HIDDEN 792 SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION); 793 #endif 794