1/** 2 * \file pcm/pcm_file.c 3 * \ingroup PCM_Plugins 4 * \brief PCM File Plugin Interface 5 * \author Abramo Bagnara <abramo@alsa-project.org> 6 * \date 2000-2001 7 */ 8/* 9 * PCM - File plugin 10 * Copyright (c) 2000 by Abramo Bagnara <abramo@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 <string.h> 33#include "pcm_local.h" 34#include "pcm_plugin.h" 35 36#ifndef PIC 37/* entry for static linking */ 38const char *_snd_module_pcm_file = ""; 39#endif 40 41#ifndef DOC_HIDDEN 42 43/* keys to be replaced by real values in the filename */ 44#define LEADING_KEY '%' /* i.e. %r, %c, %b ... */ 45#define RATE_KEY 'r' 46#define CHANNELS_KEY 'c' 47#define BWIDTH_KEY 'b' 48#define FORMAT_KEY 'f' 49 50/* maximum length of a value */ 51#define VALUE_MAXLEN 64 52 53typedef enum _snd_pcm_file_format { 54 SND_PCM_FILE_FORMAT_RAW, 55 SND_PCM_FILE_FORMAT_WAV 56} snd_pcm_file_format_t; 57 58/* WAV format chunk */ 59struct wav_fmt { 60 short fmt; 61 short chan; 62 int rate; 63 int bps; 64 short bwidth; 65 short bits; 66}; 67 68typedef struct { 69 snd_pcm_generic_t gen; 70 char *fname; 71 char *final_fname; 72 int trunc; 73 int perm; 74 int fd; 75 char *ifname; 76 int ifd; 77 int format; 78 snd_pcm_uframes_t appl_ptr; 79 snd_pcm_uframes_t file_ptr_bytes; 80 snd_pcm_uframes_t wbuf_size; 81 size_t wbuf_size_bytes; 82 size_t wbuf_used_bytes; 83 char *wbuf; 84 size_t rbuf_size_bytes; 85 size_t rbuf_used_bytes; 86 char *rbuf; 87 snd_pcm_channel_area_t *wbuf_areas; 88 size_t buffer_bytes; 89 struct wav_fmt wav_header; 90 size_t filelen; 91} snd_pcm_file_t; 92 93#if __BYTE_ORDER == __LITTLE_ENDIAN 94#define TO_LE32(x) (x) 95#define TO_LE16(x) (x) 96#else 97#define TO_LE32(x) bswap_32(x) 98#define TO_LE16(x) bswap_16(x) 99#endif 100 101static int snd_pcm_file_append_value(char **string_p, char **index_ch_p, 102 int *len_p, const char *value) 103{ 104 char *string, *index_ch; 105 int index, len, value_len; 106 /* input pointer values */ 107 len = *(len_p); 108 string = *(string_p); 109 index_ch = *(index_ch_p); 110 111 value_len = strlen(value); 112 /* reallocation to accommodate the value */ 113 index = index_ch - string; 114 len += value_len; 115 string = realloc(string, len + 1); 116 if (!string) 117 return -ENOMEM; 118 index_ch = string + index; 119 /* concatenating the new value */ 120 strcpy(index_ch, value); 121 index_ch += value_len; 122 /* return values */ 123 *(len_p) = len; 124 *(string_p) = string; 125 *(index_ch_p) = index_ch; 126 return 0; 127} 128 129static int snd_pcm_file_replace_fname(snd_pcm_file_t *file, char **new_fname_p) 130{ 131 char value[VALUE_MAXLEN]; 132 char *fname = file->fname; 133 char *new_fname = NULL; 134 char *old_last_ch, *old_index_ch, *new_index_ch; 135 int old_len, new_len, err; 136 137 snd_pcm_t *pcm = file->gen.slave; 138 139 /* we want to keep fname, const */ 140 old_len = new_len = strlen(fname); 141 old_last_ch = fname + old_len - 1; 142 new_fname = malloc(new_len + 1); 143 if (!new_fname) 144 return -ENOMEM; 145 146 old_index_ch = fname; /* first character of the old name */ 147 new_index_ch = new_fname; /* first char of the new name */ 148 149 while (old_index_ch <= old_last_ch) { 150 if (*(old_index_ch) == LEADING_KEY && 151 old_index_ch != old_last_ch) { 152 /* is %, not last char, skipping and checking 153 next char */ 154 switch (*(++old_index_ch)) { 155 case RATE_KEY: 156 snprintf(value, sizeof(value), "%d", 157 pcm->rate); 158 err = snd_pcm_file_append_value(&new_fname, 159 &new_index_ch, &new_len, value); 160 if (err < 0) 161 return err; 162 break; 163 164 case CHANNELS_KEY: 165 snprintf(value, sizeof(value), "%d", 166 pcm->channels); 167 err = snd_pcm_file_append_value(&new_fname, 168 &new_index_ch, &new_len, value); 169 if (err < 0) 170 return err; 171 break; 172 173 case BWIDTH_KEY: 174 snprintf(value, sizeof(value), "%d", 175 pcm->frame_bits/pcm->channels); 176 err = snd_pcm_file_append_value(&new_fname, 177 &new_index_ch, &new_len, value); 178 if (err < 0) 179 return err; 180 break; 181 182 case FORMAT_KEY: 183 err = snd_pcm_file_append_value(&new_fname, 184 &new_index_ch, &new_len, 185 snd_pcm_format_name(pcm->format)); 186 if (err < 0) 187 return err; 188 break; 189 190 default: 191 /* non-key char, just copying */ 192 *(new_index_ch++) = *(old_index_ch); 193 } 194 /* next old char */ 195 old_index_ch++; 196 } else { 197 /* plain copying, shifting both strings to next chars */ 198 *(new_index_ch++) = *(old_index_ch++); 199 } 200 } 201 /* closing the new string */ 202 *(new_index_ch) = '\0'; 203 *(new_fname_p) = new_fname; 204 return 0; 205 206} 207 208static int snd_pcm_file_open_output_file(snd_pcm_file_t *file) 209{ 210 int err, fd; 211 212 /* fname can contain keys, generating final_fname */ 213 err = snd_pcm_file_replace_fname(file, &(file->final_fname)); 214 if (err < 0) 215 return err; 216 /*printf("DEBUG - original fname: %s, final fname: %s\n", 217 file->fname, file->final_fname);*/ 218 219 if (file->final_fname[0] == '|') { 220 /* pipe mode */ 221 FILE *pipe; 222 /* clearing */ 223 pipe = popen(file->final_fname + 1, "w"); 224 if (!pipe) { 225 SYSERR("running %s for writing failed", 226 file->final_fname); 227 return -errno; 228 } 229 fd = fileno(pipe); 230 } else { 231 if (file->trunc) 232 fd = open(file->final_fname, O_WRONLY|O_CREAT|O_TRUNC, 233 file->perm); 234 else { 235 fd = open(file->final_fname, O_WRONLY|O_CREAT|O_EXCL, 236 file->perm); 237 if (fd < 0) { 238 char *tmpfname = NULL; 239 int idx, len; 240 len = strlen(file->final_fname) + 6; 241 tmpfname = malloc(len); 242 if (!tmpfname) 243 return -ENOMEM; 244 for (idx = 1; idx < 10000; idx++) { 245 snprintf(tmpfname, len, 246 "%s.%04d", file->final_fname, 247 idx); 248 fd = open(tmpfname, 249 O_WRONLY|O_CREAT|O_EXCL, 250 file->perm); 251 if (fd >= 0) { 252 free(file->final_fname); 253 file->final_fname = tmpfname; 254 break; 255 } 256 } 257 if (fd < 0) { 258 SYSERR("open %s for writing failed", 259 file->final_fname); 260 free(tmpfname); 261 return -errno; 262 } 263 } 264 } 265 } 266 file->fd = fd; 267 return 0; 268} 269 270static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt) 271{ 272 fmt->fmt = TO_LE16(0x01); 273 fmt->chan = TO_LE16(pcm->channels); 274 fmt->rate = TO_LE32(pcm->rate); 275 fmt->bwidth = pcm->frame_bits / 8; 276 fmt->bps = fmt->bwidth * pcm->rate; 277 fmt->bits = snd_pcm_format_width(pcm->format); 278 fmt->bps = TO_LE32(fmt->bps); 279 fmt->bwidth = TO_LE16(fmt->bwidth); 280 fmt->bits = TO_LE16(fmt->bits); 281} 282 283static int write_wav_header(snd_pcm_t *pcm) 284{ 285 snd_pcm_file_t *file = pcm->private_data; 286 static const char header[] = { 287 'R', 'I', 'F', 'F', 288 0x24, 0, 0, 0, 289 'W', 'A', 'V', 'E', 290 'f', 'm', 't', ' ', 291 0x10, 0, 0, 0, 292 }; 293 static const char header2[] = { 294 'd', 'a', 't', 'a', 295 0, 0, 0, 0 296 }; 297 298 setup_wav_header(pcm, &file->wav_header); 299 300 if (write(file->fd, header, sizeof(header)) != sizeof(header) || 301 write(file->fd, &file->wav_header, sizeof(file->wav_header)) != 302 sizeof(file->wav_header) || 303 write(file->fd, header2, sizeof(header2)) != sizeof(header2)) { 304 int err = errno; 305 SYSERR("Write error.\n"); 306 return -err; 307 } 308 return 0; 309} 310 311/* fix up the length fields in WAV header */ 312static void fixup_wav_header(snd_pcm_t *pcm) 313{ 314 snd_pcm_file_t *file = pcm->private_data; 315 int len, ret; 316 317 /* RIFF length */ 318 if (lseek(file->fd, 4, SEEK_SET) == 4) { 319 len = (file->filelen + 0x24) > 0x7fffffff ? 320 0x7fffffff : (int)(file->filelen + 0x24); 321 len = TO_LE32(len); 322 ret = write(file->fd, &len, 4); 323 if (ret < 0) 324 return; 325 } 326 /* data length */ 327 if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) { 328 len = file->filelen > 0x7fffffff ? 329 0x7fffffff : (int)file->filelen; 330 len = TO_LE32(len); 331 ret = write(file->fd, &len, 4); 332 if (ret < 0) 333 return; 334 } 335} 336#endif /* DOC_HIDDEN */ 337 338 339 340static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes) 341{ 342 snd_pcm_file_t *file = pcm->private_data; 343 assert(bytes <= file->wbuf_used_bytes); 344 345 if (file->format == SND_PCM_FILE_FORMAT_WAV && 346 !file->wav_header.fmt) { 347 if (write_wav_header(pcm) < 0) 348 return; 349 } 350 351 while (bytes > 0) { 352 snd_pcm_sframes_t err; 353 size_t n = bytes; 354 size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes; 355 if (n > cont) 356 n = cont; 357 err = write(file->fd, file->wbuf + file->file_ptr_bytes, n); 358 if (err < 0) { 359 SYSERR("write failed"); 360 break; 361 } 362 bytes -= err; 363 file->wbuf_used_bytes -= err; 364 file->file_ptr_bytes += err; 365 if (file->file_ptr_bytes == file->wbuf_size_bytes) 366 file->file_ptr_bytes = 0; 367 file->filelen += err; 368 if ((snd_pcm_uframes_t)err != n) 369 break; 370 } 371} 372 373static void snd_pcm_file_add_frames(snd_pcm_t *pcm, 374 const snd_pcm_channel_area_t *areas, 375 snd_pcm_uframes_t offset, 376 snd_pcm_uframes_t frames) 377{ 378 snd_pcm_file_t *file = pcm->private_data; 379 while (frames > 0) { 380 snd_pcm_uframes_t n = frames; 381 snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr; 382 snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 383 if (n > cont) 384 n = cont; 385 if (n > avail) 386 n = avail; 387 snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr, 388 areas, offset, 389 pcm->channels, n, pcm->format); 390 frames -= n; 391 offset += n; 392 file->appl_ptr += n; 393 if (file->appl_ptr == file->wbuf_size) 394 file->appl_ptr = 0; 395 file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n); 396 if (file->wbuf_used_bytes > file->buffer_bytes) 397 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes); 398 assert(file->wbuf_used_bytes < file->wbuf_size_bytes); 399 } 400} 401 402static int snd_pcm_file_close(snd_pcm_t *pcm) 403{ 404 snd_pcm_file_t *file = pcm->private_data; 405 if (file->fname) { 406 if (file->wav_header.fmt) 407 fixup_wav_header(pcm); 408 free((void *)file->fname); 409 close(file->fd); 410 } 411 if (file->ifname) { 412 free((void *)file->ifname); 413 close(file->ifd); 414 } 415 return snd_pcm_generic_close(pcm); 416} 417 418static int snd_pcm_file_reset(snd_pcm_t *pcm) 419{ 420 snd_pcm_file_t *file = pcm->private_data; 421 int err = snd_pcm_reset(file->gen.slave); 422 if (err >= 0) { 423 /* FIXME: Questionable here */ 424 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 425 assert(file->wbuf_used_bytes == 0); 426 } 427 return err; 428} 429 430static int snd_pcm_file_drop(snd_pcm_t *pcm) 431{ 432 snd_pcm_file_t *file = pcm->private_data; 433 int err = snd_pcm_drop(file->gen.slave); 434 if (err >= 0) { 435 /* FIXME: Questionable here */ 436 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 437 assert(file->wbuf_used_bytes == 0); 438 } 439 return err; 440} 441 442static int snd_pcm_file_drain(snd_pcm_t *pcm) 443{ 444 snd_pcm_file_t *file = pcm->private_data; 445 int err = snd_pcm_drain(file->gen.slave); 446 if (err >= 0) { 447 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes); 448 assert(file->wbuf_used_bytes == 0); 449 } 450 return err; 451} 452 453static snd_pcm_sframes_t snd_pcm_file_rewindable(snd_pcm_t *pcm) 454{ 455 snd_pcm_file_t *file = pcm->private_data; 456 snd_pcm_sframes_t res = snd_pcm_rewindable(pcm); 457 snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 458 if (res > n) 459 res = n; 460 return res; 461} 462 463static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 464{ 465 snd_pcm_file_t *file = pcm->private_data; 466 snd_pcm_sframes_t err; 467 snd_pcm_uframes_t n; 468 469 n = snd_pcm_frames_to_bytes(pcm, frames); 470 if (n > file->wbuf_used_bytes) 471 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes); 472 err = snd_pcm_rewind(file->gen.slave, frames); 473 if (err > 0) { 474 file->appl_ptr = (file->appl_ptr - err + file->wbuf_size) % file->wbuf_size; 475 n = snd_pcm_frames_to_bytes(pcm, err); 476 file->wbuf_used_bytes -= n; 477 } 478 return err; 479} 480 481static snd_pcm_sframes_t snd_pcm_file_forwardable(snd_pcm_t *pcm) 482{ 483 snd_pcm_file_t *file = pcm->private_data; 484 snd_pcm_sframes_t res = snd_pcm_forwardable(pcm); 485 snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes); 486 if (res > n) 487 res = n; 488 return res; 489} 490 491static snd_pcm_sframes_t snd_pcm_file_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 492{ 493 snd_pcm_file_t *file = pcm->private_data; 494 snd_pcm_sframes_t err; 495 snd_pcm_uframes_t n; 496 497 n = snd_pcm_frames_to_bytes(pcm, frames); 498 if (file->wbuf_used_bytes + n > file->wbuf_size_bytes) 499 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes); 500 err = INTERNAL(snd_pcm_forward)(file->gen.slave, frames); 501 if (err > 0) { 502 file->appl_ptr = (file->appl_ptr + err) % file->wbuf_size; 503 n = snd_pcm_frames_to_bytes(pcm, err); 504 file->wbuf_used_bytes += n; 505 } 506 return err; 507} 508 509static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) 510{ 511 snd_pcm_file_t *file = pcm->private_data; 512 snd_pcm_channel_area_t areas[pcm->channels]; 513 snd_pcm_sframes_t n = snd_pcm_writei(file->gen.slave, buffer, size); 514 if (n > 0) { 515 snd_pcm_areas_from_buf(pcm, areas, (void*) buffer); 516 snd_pcm_file_add_frames(pcm, areas, 0, n); 517 } 518 return n; 519} 520 521static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 522{ 523 snd_pcm_file_t *file = pcm->private_data; 524 snd_pcm_channel_area_t areas[pcm->channels]; 525 snd_pcm_sframes_t n = snd_pcm_writen(file->gen.slave, bufs, size); 526 if (n > 0) { 527 snd_pcm_areas_from_bufs(pcm, areas, bufs); 528 snd_pcm_file_add_frames(pcm, areas, 0, n); 529 } 530 return n; 531} 532 533static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) 534{ 535 snd_pcm_file_t *file = pcm->private_data; 536 snd_pcm_channel_area_t areas[pcm->channels]; 537 snd_pcm_sframes_t n; 538 539 n = snd_pcm_readi(file->gen.slave, buffer, size); 540 if (n <= 0) 541 return n; 542 if (file->ifd >= 0) { 543 n = read(file->ifd, buffer, n * pcm->frame_bits / 8); 544 if (n < 0) 545 return n; 546 return n * 8 / pcm->frame_bits; 547 } 548 snd_pcm_areas_from_buf(pcm, areas, buffer); 549 snd_pcm_file_add_frames(pcm, areas, 0, n); 550 return n; 551} 552 553static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 554{ 555 snd_pcm_file_t *file = pcm->private_data; 556 snd_pcm_channel_area_t areas[pcm->channels]; 557 snd_pcm_sframes_t n; 558 559 if (file->ifd >= 0) { 560 SNDERR("DEBUG: Noninterleaved read not yet implemented.\n"); 561 return 0; /* TODO: Noninterleaved read */ 562 } 563 564 n = snd_pcm_readn(file->gen.slave, bufs, size); 565 if (n > 0) { 566 snd_pcm_areas_from_bufs(pcm, areas, bufs); 567 snd_pcm_file_add_frames(pcm, areas, 0, n); 568 } 569 return n; 570} 571 572static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm, 573 snd_pcm_uframes_t offset, 574 snd_pcm_uframes_t size) 575{ 576 snd_pcm_file_t *file = pcm->private_data; 577 snd_pcm_uframes_t ofs; 578 snd_pcm_uframes_t siz = size; 579 const snd_pcm_channel_area_t *areas; 580 snd_pcm_sframes_t result; 581 582 snd_pcm_mmap_begin(file->gen.slave, &areas, &ofs, &siz); 583 assert(ofs == offset && siz == size); 584 result = snd_pcm_mmap_commit(file->gen.slave, ofs, siz); 585 if (result > 0) 586 snd_pcm_file_add_frames(pcm, areas, ofs, result); 587 return result; 588} 589 590static int snd_pcm_file_hw_free(snd_pcm_t *pcm) 591{ 592 snd_pcm_file_t *file = pcm->private_data; 593 free(file->wbuf); 594 free(file->wbuf_areas); 595 file->wbuf = NULL; 596 file->wbuf_areas = NULL; 597 return snd_pcm_hw_free(file->gen.slave); 598} 599 600static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 601{ 602 snd_pcm_file_t *file = pcm->private_data; 603 unsigned int channel; 604 snd_pcm_t *slave = file->gen.slave; 605 int err = _snd_pcm_hw_params(slave, params); 606 if (err < 0) 607 return err; 608 file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size); 609 file->wbuf_size = slave->buffer_size * 2; 610 file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size); 611 file->wbuf_used_bytes = 0; 612 assert(!file->wbuf); 613 file->wbuf = malloc(file->wbuf_size_bytes); 614 if (file->wbuf == NULL) { 615 snd_pcm_file_hw_free(pcm); 616 return -ENOMEM; 617 } 618 file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels); 619 if (file->wbuf_areas == NULL) { 620 snd_pcm_file_hw_free(pcm); 621 return -ENOMEM; 622 } 623 file->appl_ptr = file->file_ptr_bytes = 0; 624 for (channel = 0; channel < slave->channels; ++channel) { 625 snd_pcm_channel_area_t *a = &file->wbuf_areas[channel]; 626 a->addr = file->wbuf; 627 a->first = slave->sample_bits * channel; 628 a->step = slave->frame_bits; 629 } 630 if (file->fd < 0) { 631 err = snd_pcm_file_open_output_file(file); 632 if (err < 0) { 633 SYSERR("failed opening output file %s", file->fname); 634 return err; 635 } 636 } 637 return 0; 638} 639 640static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out) 641{ 642 snd_pcm_file_t *file = pcm->private_data; 643 if (file->fname) 644 snd_output_printf(out, "File PCM (file=%s)\n", file->fname); 645 else 646 snd_output_printf(out, "File PCM (fd=%d)\n", file->fd); 647 if (file->final_fname) 648 snd_output_printf(out, "Final file PCM (file=%s)\n", 649 file->final_fname); 650 651 if (pcm->setup) { 652 snd_output_printf(out, "Its setup is:\n"); 653 snd_pcm_dump_setup(pcm, out); 654 } 655 snd_output_printf(out, "Slave: "); 656 snd_pcm_dump(file->gen.slave, out); 657} 658 659static const snd_pcm_ops_t snd_pcm_file_ops = { 660 .close = snd_pcm_file_close, 661 .info = snd_pcm_generic_info, 662 .hw_refine = snd_pcm_generic_hw_refine, 663 .hw_params = snd_pcm_file_hw_params, 664 .hw_free = snd_pcm_file_hw_free, 665 .sw_params = snd_pcm_generic_sw_params, 666 .channel_info = snd_pcm_generic_channel_info, 667 .dump = snd_pcm_file_dump, 668 .nonblock = snd_pcm_generic_nonblock, 669 .async = snd_pcm_generic_async, 670 .mmap = snd_pcm_generic_mmap, 671 .munmap = snd_pcm_generic_munmap, 672}; 673 674static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = { 675 .status = snd_pcm_generic_status, 676 .state = snd_pcm_generic_state, 677 .hwsync = snd_pcm_generic_hwsync, 678 .delay = snd_pcm_generic_delay, 679 .prepare = snd_pcm_generic_prepare, 680 .reset = snd_pcm_file_reset, 681 .start = snd_pcm_generic_start, 682 .drop = snd_pcm_file_drop, 683 .drain = snd_pcm_file_drain, 684 .pause = snd_pcm_generic_pause, 685 .rewindable = snd_pcm_file_rewindable, 686 .rewind = snd_pcm_file_rewind, 687 .forwardable = snd_pcm_file_forwardable, 688 .forward = snd_pcm_file_forward, 689 .resume = snd_pcm_generic_resume, 690 .link = snd_pcm_generic_link, 691 .link_slaves = snd_pcm_generic_link_slaves, 692 .unlink = snd_pcm_generic_unlink, 693 .writei = snd_pcm_file_writei, 694 .writen = snd_pcm_file_writen, 695 .readi = snd_pcm_file_readi, 696 .readn = snd_pcm_file_readn, 697 .avail_update = snd_pcm_generic_avail_update, 698 .mmap_commit = snd_pcm_file_mmap_commit, 699 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 700 .poll_descriptors = snd_pcm_generic_poll_descriptors, 701 .poll_revents = snd_pcm_generic_poll_revents, 702}; 703 704/** 705 * \brief Creates a new File PCM 706 * \param pcmp Returns created PCM handle 707 * \param name Name of PCM 708 * \param fname Output filename (or NULL if file descriptor fd is available) 709 * \param fd Output file descriptor 710 * \param ifname Input filename (or NULL if file descriptor ifd is available) 711 * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input 712 * redirection will be performed) 713 * \param trunc Truncate the file if it already exists 714 * \param fmt File format ("raw" or "wav" are available) 715 * \param perm File permission 716 * \param slave Slave PCM handle 717 * \param close_slave When set, the slave PCM handle is closed with copy PCM 718 * \retval zero on success otherwise a negative error code 719 * \warning Using of this function might be dangerous in the sense 720 * of compatibility reasons. The prototype might be freely 721 * changed in future. 722 */ 723int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, 724 const char *fname, int fd, const char *ifname, int ifd, 725 int trunc, 726 const char *fmt, int perm, snd_pcm_t *slave, int close_slave) 727{ 728 snd_pcm_t *pcm; 729 snd_pcm_file_t *file; 730 snd_pcm_file_format_t format; 731 struct timespec timespec; 732 int err; 733 734 assert(pcmp); 735 if (fmt == NULL || 736 strcmp(fmt, "raw") == 0) 737 format = SND_PCM_FILE_FORMAT_RAW; 738 else if (!strcmp(fmt, "wav")) 739 format = SND_PCM_FILE_FORMAT_WAV; 740 else { 741 SNDERR("file format %s is unknown", fmt); 742 return -EINVAL; 743 } 744 file = calloc(1, sizeof(snd_pcm_file_t)); 745 if (!file) { 746 return -ENOMEM; 747 } 748 749 /* opening output fname is delayed until writing, 750 when PCM params are known */ 751 if (fname) 752 file->fname = strdup(fname); 753 file->trunc = trunc; 754 file->perm = perm; 755 756 if (ifname) { 757 ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */ 758 if (ifd < 0) { 759 SYSERR("open %s for reading failed", ifname); 760 free(file); 761 return -errno; 762 } 763 file->ifname = strdup(ifname); 764 } 765 file->fd = fd; 766 file->ifd = ifd; 767 file->format = format; 768 file->gen.slave = slave; 769 file->gen.close_slave = close_slave; 770 771 err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode); 772 if (err < 0) { 773 free(file->fname); 774 free(file); 775 return err; 776 } 777 pcm->ops = &snd_pcm_file_ops; 778 pcm->fast_ops = &snd_pcm_file_fast_ops; 779 pcm->private_data = file; 780 pcm->poll_fd = slave->poll_fd; 781 pcm->poll_events = slave->poll_events; 782 pcm->mmap_shadow = 1; 783#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 784 pcm->monotonic = clock_gettime(CLOCK_MONOTONIC, ×pec) == 0; 785#else 786 pcm->monotonic = 0; 787#endif 788 snd_pcm_link_hw_ptr(pcm, slave); 789 snd_pcm_link_appl_ptr(pcm, slave); 790 *pcmp = pcm; 791 return 0; 792} 793 794/*! \page pcm_plugins 795 796\section pcm_plugins_file Plugin: File 797 798This plugin stores contents of a PCM stream to file or pipes the stream 799to a command, and optionally uses an existing file as an input data source 800(i.e., "virtual mic") 801 802\code 803pcm.name { 804 type file # File PCM 805 slave STR # Slave name 806 # or 807 slave { # Slave definition 808 pcm STR # Slave PCM name 809 # or 810 pcm { } # Slave PCM definition 811 } 812 file STR # Output filename (or shell command the stream 813 # will be piped to if STR starts with the pipe 814 # char). 815 # STR can contain format keys, replaced by 816 # real values corresponding to the stream: 817 # %r rate (replaced with: 48000) 818 # %c channels (replaced with: 2) 819 # %b bits per sample (replaced with: 16) 820 # %f sample format string 821 # (replaced with: S16_LE) 822 # %% replaced with % 823 or 824 file INT # Output file descriptor number 825 infile STR # Input filename - only raw format 826 or 827 infile INT # Input file descriptor number 828 [format STR] # File format ("raw" or "wav") 829 [perm INT] # Output file permission (octal, def. 0600) 830} 831\endcode 832 833\subsection pcm_plugins_file_funcref Function reference 834 835<UL> 836 <LI>snd_pcm_file_open() 837 <LI>_snd_pcm_file_open() 838</UL> 839 840*/ 841 842/** 843 * \brief Creates a new File PCM 844 * \param pcmp Returns created PCM handle 845 * \param name Name of PCM 846 * \param root Root configuration node 847 * \param conf Configuration node with File PCM description 848 * \param stream Stream type 849 * \param mode Stream mode 850 * \retval zero on success otherwise a negative error code 851 * \warning Using of this function might be dangerous in the sense 852 * of compatibility reasons. The prototype might be freely 853 * changed in future. 854 */ 855int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, 856 snd_config_t *root, snd_config_t *conf, 857 snd_pcm_stream_t stream, int mode) 858{ 859 snd_config_iterator_t i, next; 860 int err; 861 snd_pcm_t *spcm; 862 snd_config_t *slave = NULL, *sconf; 863 const char *fname = NULL, *ifname = NULL; 864 const char *format = NULL; 865 long fd = -1, ifd = -1, trunc = 1; 866 long perm = 0600; 867 snd_config_for_each(i, next, conf) { 868 snd_config_t *n = snd_config_iterator_entry(i); 869 const char *id; 870 if (snd_config_get_id(n, &id) < 0) 871 continue; 872 if (snd_pcm_conf_generic_id(id)) 873 continue; 874 if (strcmp(id, "slave") == 0) { 875 slave = n; 876 continue; 877 } 878 if (strcmp(id, "format") == 0) { 879 err = snd_config_get_string(n, &format); 880 if (err < 0) { 881 SNDERR("Invalid type for %s", id); 882 return -EINVAL; 883 } 884 continue; 885 } 886 if (strcmp(id, "file") == 0) { 887 err = snd_config_get_string(n, &fname); 888 if (err < 0) { 889 err = snd_config_get_integer(n, &fd); 890 if (err < 0) { 891 SNDERR("Invalid type for %s", id); 892 return -EINVAL; 893 } 894 } 895 continue; 896 } 897 if (strcmp(id, "infile") == 0) { 898 err = snd_config_get_string(n, &ifname); 899 if (err < 0) { 900 err = snd_config_get_integer(n, &ifd); 901 if (err < 0) { 902 SNDERR("Invalid type for %s", id); 903 return -EINVAL; 904 } 905 } 906 continue; 907 } 908 if (strcmp(id, "perm") == 0) { 909 err = snd_config_get_integer(n, &perm); 910 if (err < 0) { 911 SNDERR("Invalid type for %s", id); 912 return err; 913 } 914 if ((perm & ~0777) != 0) { 915 SNDERR("The field perm must be a valid file permission"); 916 return -EINVAL; 917 } 918 continue; 919 } 920 if (strcmp(id, "truncate") == 0) { 921 err = snd_config_get_bool(n); 922 if (err < 0) 923 return -EINVAL; 924 trunc = err; 925 continue; 926 } 927 SNDERR("Unknown field %s", id); 928 return -EINVAL; 929 } 930 if (!format) { 931 snd_config_t *n; 932 /* read defaults */ 933 if (snd_config_search(root, "defaults.pcm.file_format", &n) >= 0) { 934 err = snd_config_get_string(n, &format); 935 if (err < 0) { 936 SNDERR("Invalid file format"); 937 return -EINVAL; 938 } 939 } 940 } 941 if (!slave) { 942 SNDERR("slave is not defined"); 943 return -EINVAL; 944 } 945 err = snd_pcm_slave_conf(root, slave, &sconf, 0); 946 if (err < 0) 947 return err; 948 if ((!fname || strlen(fname) == 0) && fd < 0 && !ifname) { 949 snd_config_delete(sconf); 950 SNDERR("file is not defined"); 951 return -EINVAL; 952 } 953 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 954 snd_config_delete(sconf); 955 if (err < 0) 956 return err; 957 err = snd_pcm_file_open(pcmp, name, fname, fd, ifname, ifd, 958 trunc, format, perm, spcm, 1); 959 if (err < 0) 960 snd_pcm_close(spcm); 961 return err; 962} 963#ifndef DOC_HIDDEN 964SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION); 965#endif 966