OSDN Git Service

d8ed59d8e99974cfef25d0b6ab6f73bd1d961693
[android-x86/external-alsa-lib.git] / src / pcm / pcm_file.c
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 <byteswap.h>
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
32
33 #ifndef PIC
34 /* entry for static linking */
35 const char *_snd_module_pcm_file = "";
36 #endif
37
38 #ifndef DOC_HIDDEN
39
40 typedef enum _snd_pcm_file_format {
41         SND_PCM_FILE_FORMAT_RAW
42 } snd_pcm_file_format_t;
43
44 typedef struct {
45         snd_pcm_t *slave;
46         int close_slave;
47         char *fname;
48         int fd;
49         int format;
50         snd_pcm_uframes_t appl_ptr;
51         snd_pcm_uframes_t file_ptr_bytes;
52         snd_pcm_uframes_t wbuf_size;
53         size_t wbuf_size_bytes;
54         size_t wbuf_used_bytes;
55         char *wbuf;
56         snd_pcm_channel_area_t *wbuf_areas;
57         size_t buffer_bytes;
58 } snd_pcm_file_t;
59
60 #endif /* DOC_HIDDEN */
61
62 static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
63 {
64         snd_pcm_file_t *file = pcm->private_data;
65         assert(bytes <= file->wbuf_used_bytes);
66         while (bytes > 0) {
67                 snd_pcm_sframes_t err;
68                 size_t n = bytes;
69                 size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes;
70                 if (n > cont)
71                         n = cont;
72                 err = write(file->fd, file->wbuf + file->file_ptr_bytes, n);
73                 if (err < 0) {
74                         SYSERR("write failed");
75                         break;
76                 }
77                 bytes -= err;
78                 file->wbuf_used_bytes -= err;
79                 file->file_ptr_bytes += err;
80                 if (file->file_ptr_bytes == file->wbuf_size_bytes)
81                         file->file_ptr_bytes = 0;
82                 if ((snd_pcm_uframes_t)err != n)
83                         break;
84         }
85 }
86
87 static void snd_pcm_file_add_frames(snd_pcm_t *pcm, 
88                                     const snd_pcm_channel_area_t *areas,
89                                     snd_pcm_uframes_t offset,
90                                     snd_pcm_uframes_t frames)
91 {
92         snd_pcm_file_t *file = pcm->private_data;
93         while (frames > 0) {
94                 snd_pcm_uframes_t n = frames;
95                 snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr;
96                 snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
97                 if (n > cont)
98                         n = cont;
99                 if (n > avail)
100                         n = avail;
101                 snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr, 
102                                    areas, offset,
103                                    pcm->channels, n, pcm->format);
104                 frames -= n;
105                 file->appl_ptr += n;
106                 if (file->appl_ptr == file->wbuf_size)
107                         file->appl_ptr = 0;
108                 file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n);
109                 if (file->wbuf_used_bytes > file->buffer_bytes)
110                         snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes);
111                 assert(file->wbuf_used_bytes < file->wbuf_size_bytes);
112         }
113 }
114
115 static int snd_pcm_file_close(snd_pcm_t *pcm)
116 {
117         snd_pcm_file_t *file = pcm->private_data;
118         int err = 0;
119         if (file->close_slave)
120                 err = snd_pcm_close(file->slave);
121         if (file->fname) {
122                 free((void *)file->fname);
123                 close(file->fd);
124         }
125         free(file);
126         return 0;
127 }
128
129 static int snd_pcm_file_nonblock(snd_pcm_t *pcm, int nonblock)
130 {
131         snd_pcm_file_t *file = pcm->private_data;
132         return snd_pcm_nonblock(file->slave, nonblock);
133 }
134
135 static int snd_pcm_file_async(snd_pcm_t *pcm, int sig, pid_t pid)
136 {
137         snd_pcm_file_t *file = pcm->private_data;
138         return snd_pcm_async(file->slave, sig, pid);
139 }
140
141 static int snd_pcm_file_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
142 {
143         snd_pcm_file_t *file = pcm->private_data;
144         return snd_pcm_info(file->slave, info);
145 }
146
147 static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
148 {
149         snd_pcm_file_t *file = pcm->private_data;
150         return snd_pcm_channel_info(file->slave, info);
151 }
152
153 static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
154 {
155         snd_pcm_file_t *file = pcm->private_data;
156         return snd_pcm_status(file->slave, status);
157 }
158
159 static snd_pcm_state_t snd_pcm_file_state(snd_pcm_t *pcm)
160 {
161         snd_pcm_file_t *file = pcm->private_data;
162         return snd_pcm_state(file->slave);
163 }
164
165 static int snd_pcm_file_avail(snd_pcm_t *pcm, snd_pcm_uframes_t *availp)
166 {
167         snd_pcm_file_t *file = pcm->private_data;
168         return snd_pcm_avail(file->slave, availp);
169 }
170
171 static int snd_pcm_file_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
172 {
173         snd_pcm_file_t *file = pcm->private_data;
174         return snd_pcm_delay(file->slave, delayp);
175 }
176
177 static int snd_pcm_file_prepare(snd_pcm_t *pcm)
178 {
179         snd_pcm_file_t *file = pcm->private_data;
180         return snd_pcm_prepare(file->slave);
181 }
182
183 static int snd_pcm_file_reset(snd_pcm_t *pcm)
184 {
185         snd_pcm_file_t *file = pcm->private_data;
186         int err = snd_pcm_reset(file->slave);
187         if (err >= 0) {
188                 /* FIXME: Questionable here */
189                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
190                 assert(file->wbuf_used_bytes == 0);
191         }
192         return err;
193 }
194
195 static int snd_pcm_file_start(snd_pcm_t *pcm)
196 {
197         snd_pcm_file_t *file = pcm->private_data;
198         return snd_pcm_start(file->slave);
199 }
200
201 static int snd_pcm_file_drop(snd_pcm_t *pcm)
202 {
203         snd_pcm_file_t *file = pcm->private_data;
204         int err = snd_pcm_drop(file->slave);
205         if (err >= 0) {
206                 /* FIXME: Questionable here */
207                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
208                 assert(file->wbuf_used_bytes == 0);
209         }
210         return err;
211 }
212
213 static int snd_pcm_file_drain(snd_pcm_t *pcm)
214 {
215         snd_pcm_file_t *file = pcm->private_data;
216         int err = snd_pcm_drain(file->slave);
217         if (err >= 0) {
218                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
219                 assert(file->wbuf_used_bytes == 0);
220         }
221         return err;
222 }
223
224 static int snd_pcm_file_pause(snd_pcm_t *pcm, int enable)
225 {
226         snd_pcm_file_t *file = pcm->private_data;
227         return snd_pcm_pause(file->slave, enable);
228 }
229
230 static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
231 {
232         snd_pcm_file_t *file = pcm->private_data;
233         snd_pcm_sframes_t err = snd_pcm_rewind(file->slave, frames);
234         if (err > 0) {
235                 snd_pcm_uframes_t n = snd_pcm_frames_to_bytes(pcm, frames);
236                 snd_pcm_sframes_t ptr;
237                 assert(n >= file->wbuf_used_bytes);
238                 ptr = file->appl_ptr - err;
239                 if (ptr < 0)
240                         ptr += file->wbuf_size;
241                 file->wbuf_used_bytes -= n;
242         }
243         return err;
244 }
245
246 static int snd_pcm_file_resume(snd_pcm_t *pcm)
247 {
248         snd_pcm_file_t *file = pcm->private_data;
249         return snd_pcm_resume(file->slave);
250 }
251
252 static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
253 {
254         snd_pcm_file_t *file = pcm->private_data;
255         snd_pcm_channel_area_t areas[pcm->channels];
256         snd_pcm_sframes_t n = snd_pcm_writei(file->slave, buffer, size);
257         if (n > 0) {
258                 snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
259                 snd_pcm_file_add_frames(pcm, areas, 0, n);
260         }
261         return n;
262 }
263
264 static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
265 {
266         snd_pcm_file_t *file = pcm->private_data;
267         snd_pcm_channel_area_t areas[pcm->channels];
268         snd_pcm_sframes_t n = snd_pcm_writen(file->slave, bufs, size);
269         if (n > 0) {
270                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
271                 snd_pcm_file_add_frames(pcm, areas, 0, n);
272         }
273         return n;
274 }
275
276 static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
277 {
278         snd_pcm_file_t *file = pcm->private_data;
279         snd_pcm_channel_area_t areas[pcm->channels];
280         snd_pcm_sframes_t n = snd_pcm_readi(file->slave, buffer, size);
281         if (n > 0) {
282                 snd_pcm_areas_from_buf(pcm, areas, buffer);
283                 snd_pcm_file_add_frames(pcm, areas, 0, n);
284         }
285         return n;
286 }
287
288 static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
289 {
290         snd_pcm_file_t *file = pcm->private_data;
291         snd_pcm_channel_area_t areas[pcm->channels];
292         snd_pcm_sframes_t n = snd_pcm_writen(file->slave, bufs, size);
293         if (n > 0) {
294                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
295                 snd_pcm_file_add_frames(pcm, areas, 0, n);
296         }
297         return n;
298 }
299
300 static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
301                                                   snd_pcm_uframes_t offset,
302                                                   snd_pcm_uframes_t size)
303 {
304         snd_pcm_file_t *file = pcm->private_data;
305         snd_pcm_uframes_t ofs;
306         snd_pcm_uframes_t siz = size;
307         const snd_pcm_channel_area_t *areas;
308         snd_pcm_sframes_t result;
309
310         snd_pcm_mmap_begin(file->slave, &areas, &ofs, &siz);
311         assert(ofs == offset && siz == size);
312         result = snd_pcm_mmap_commit(file->slave, ofs, siz);
313         if (result > 0)
314                 snd_pcm_file_add_frames(pcm, areas, ofs, result);
315         return result;
316 }
317
318 static snd_pcm_sframes_t snd_pcm_file_avail_update(snd_pcm_t *pcm)
319 {
320         snd_pcm_file_t *file = pcm->private_data;
321         return snd_pcm_avail_update(file->slave);
322 }
323
324 static int snd_pcm_file_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
325 {
326         snd_pcm_file_t *file = pcm->private_data;
327         return snd_pcm_hw_refine(file->slave, params);
328 }
329
330 static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
331 {
332         snd_pcm_file_t *file = pcm->private_data;
333         unsigned int channel;
334         snd_pcm_t *slave = file->slave;
335         int err = _snd_pcm_hw_params(slave, params);
336         if (err < 0)
337                 return err;
338         file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size);
339         file->wbuf_size = slave->buffer_size * 2;
340         file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size);
341         assert(!file->wbuf);
342         file->wbuf = malloc(file->wbuf_size_bytes);
343         file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels);
344         file->appl_ptr = file->file_ptr_bytes = 0;
345         for (channel = 0; channel < slave->channels; ++channel) {
346                 snd_pcm_channel_area_t *a = &file->wbuf_areas[channel];
347                 a->addr = file->wbuf;
348                 a->first = slave->sample_bits * channel;
349                 a->step = slave->frame_bits;
350         }
351         return 0;
352 }
353
354 static int snd_pcm_file_hw_free(snd_pcm_t *pcm)
355 {
356         snd_pcm_file_t *file = pcm->private_data;
357         if (file->wbuf) {
358                 free(file->wbuf);
359                 free(file->wbuf_areas);
360                 file->wbuf = 0;
361                 file->wbuf_areas = 0;
362         }
363         return snd_pcm_hw_free(file->slave);
364 }
365
366 static int snd_pcm_file_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
367 {
368         snd_pcm_file_t *file = pcm->private_data;
369         return snd_pcm_sw_params(file->slave, params);
370 }
371
372 static int snd_pcm_file_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
373 {
374         return 0;
375 }
376
377 static int snd_pcm_file_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
378 {
379         return 0;
380 }
381
382 static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
383 {
384         snd_pcm_file_t *file = pcm->private_data;
385         if (file->fname)
386                 snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
387         else
388                 snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
389         if (pcm->setup) {
390                 snd_output_printf(out, "Its setup is:\n");
391                 snd_pcm_dump_setup(pcm, out);
392         }
393         snd_output_printf(out, "Slave: ");
394         snd_pcm_dump(file->slave, out);
395 }
396
397 static snd_pcm_ops_t snd_pcm_file_ops = {
398         close: snd_pcm_file_close,
399         info: snd_pcm_file_info,
400         hw_refine: snd_pcm_file_hw_refine,
401         hw_params: snd_pcm_file_hw_params,
402         hw_free: snd_pcm_file_hw_free,
403         sw_params: snd_pcm_file_sw_params,
404         channel_info: snd_pcm_file_channel_info,
405         dump: snd_pcm_file_dump,
406         nonblock: snd_pcm_file_nonblock,
407         async: snd_pcm_file_async,
408         mmap: snd_pcm_file_mmap,
409         munmap: snd_pcm_file_munmap,
410 };
411
412 static snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
413         status: snd_pcm_file_status,
414         state: snd_pcm_file_state,
415         avail: snd_pcm_file_avail,
416         delay: snd_pcm_file_delay,
417         prepare: snd_pcm_file_prepare,
418         reset: snd_pcm_file_reset,
419         start: snd_pcm_file_start,
420         drop: snd_pcm_file_drop,
421         drain: snd_pcm_file_drain,
422         pause: snd_pcm_file_pause,
423         rewind: snd_pcm_file_rewind,
424         resume: snd_pcm_file_resume,
425         writei: snd_pcm_file_writei,
426         writen: snd_pcm_file_writen,
427         readi: snd_pcm_file_readi,
428         readn: snd_pcm_file_readn,
429         avail_update: snd_pcm_file_avail_update,
430         mmap_commit: snd_pcm_file_mmap_commit,
431 };
432
433 /**
434  * \brief Creates a new File PCM
435  * \param pcmp Returns created PCM handle
436  * \param name Name of PCM
437  * \param fname Filename (or NULL if file descriptor is available)
438  * \param fd File descriptor
439  * \param fmt File format ("raw" is supported only)
440  * \param slave Slave PCM handle
441  * \param close_slave When set, the slave PCM handle is closed with copy PCM
442  * \retval zero on success otherwise a negative error code
443  * \warning Using of this function might be dangerous in the sense
444  *          of compatibility reasons. The prototype might be freely
445  *          changed in future.
446  */
447 int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
448                       const char *fname, int fd, const char *fmt,
449                       snd_pcm_t *slave, int close_slave)
450 {
451         snd_pcm_t *pcm;
452         snd_pcm_file_t *file;
453         snd_pcm_file_format_t format;
454         int err;
455         assert(pcmp);
456         if (fmt == NULL ||
457             strcmp(fmt, "raw") == 0)
458                 format = SND_PCM_FILE_FORMAT_RAW;
459         else {
460                 SNDERR("file format %s is unknown", fmt);
461                 return -EINVAL;
462         }
463         if (fname) {
464                 fd = open(fname, O_WRONLY|O_CREAT, 0666);
465                 if (fd < 0) {
466                         SYSERR("open %s failed", fname);
467                         return -errno;
468                 }
469         }
470         file = calloc(1, sizeof(snd_pcm_file_t));
471         if (!file) {
472                 if (fname)
473                         close(fd);
474                 return -ENOMEM;
475         }
476         
477         if (fname)
478                 file->fname = strdup(fname);
479         file->fd = fd;
480         file->format = format;
481         file->slave = slave;
482         file->close_slave = close_slave;
483
484         err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode);
485         if (err < 0) {
486                 if (fname)
487                         free(file->fname);
488                 free(file);
489                 return err;
490         }
491         pcm->ops = &snd_pcm_file_ops;
492         pcm->fast_ops = &snd_pcm_file_fast_ops;
493         pcm->private_data = file;
494         pcm->poll_fd = slave->poll_fd;
495         snd_pcm_link_hw_ptr(pcm, slave);
496         snd_pcm_link_appl_ptr(pcm, slave);
497         *pcmp = pcm;
498
499         return 0;
500 }
501
502 /*! \page pcm_plugins
503
504 \section pcm_plugins_file Plugin: File
505
506 This plugin stores contents of a PCM stream to file.
507
508 \code
509 pcm.name {
510         type file               # File PCM
511         slave STR               # Slave name
512         # or
513         slave {                 # Slave definition
514                 pcm STR         # Slave PCM name
515                 # or
516                 pcm { }         # Slave PCM definition
517         }
518         file STR                # Filename
519         or
520         file INT                # File descriptor number
521         [format STR]            # File format (only "raw" at the moment)
522 }
523 \endcode
524
525 \subsection pcm_plugins_file_funcref Function reference
526
527 <UL>
528   <LI>snd_pcm_file_open()
529   <LI>_snd_pcm_file_open()
530 </UL>
531
532 */
533
534 /**
535  * \brief Creates a new File PCM
536  * \param pcmp Returns created PCM handle
537  * \param name Name of PCM
538  * \param root Root configuration node
539  * \param conf Configuration node with File PCM description
540  * \param stream Stream type
541  * \param mode Stream mode
542  * \retval zero on success otherwise a negative error code
543  * \warning Using of this function might be dangerous in the sense
544  *          of compatibility reasons. The prototype might be freely
545  *          changed in future.
546  */
547 int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
548                        snd_config_t *root, snd_config_t *conf, 
549                        snd_pcm_stream_t stream, int mode)
550 {
551         snd_config_iterator_t i, next;
552         int err;
553         snd_pcm_t *spcm;
554         snd_config_t *slave = NULL, *sconf;
555         const char *fname = NULL;
556         const char *format = NULL;
557         long fd = -1;
558         snd_config_for_each(i, next, conf) {
559                 snd_config_t *n = snd_config_iterator_entry(i);
560                 const char *id;
561                 if (snd_config_get_id(n, &id) < 0)
562                         continue;
563                 if (snd_pcm_conf_generic_id(id))
564                         continue;
565                 if (strcmp(id, "slave") == 0) {
566                         slave = n;
567                         continue;
568                 }
569                 if (strcmp(id, "format") == 0) {
570                         err = snd_config_get_string(n, &format);
571                         if (err < 0) {
572                                 SNDERR("Invalid type for %s", id);
573                                 return -EINVAL;
574                         }
575                         continue;
576                 }
577                 if (strcmp(id, "file") == 0) {
578                         err = snd_config_get_string(n, &fname);
579                         if (err < 0) {
580                                 err = snd_config_get_integer(n, &fd);
581                                 if (err < 0) {
582                                         SNDERR("Invalid type for %s", id);
583                                         return -EINVAL;
584                                 }
585                         }
586                         continue;
587                 }
588                 SNDERR("Unknown field %s", id);
589                 return -EINVAL;
590         }
591         if (!slave) {
592                 SNDERR("slave is not defined");
593                 return -EINVAL;
594         }
595         err = snd_pcm_slave_conf(root, slave, &sconf, 0);
596         if (err < 0)
597                 return err;
598         if (!fname && fd < 0) {
599                 snd_config_delete(sconf);
600                 SNDERR("file is not defined");
601                 return -EINVAL;
602         }
603         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
604         snd_config_delete(sconf);
605         if (err < 0)
606                 return err;
607         err = snd_pcm_file_open(pcmp, name, fname, fd, format, spcm, 1);
608         if (err < 0)
609                 snd_pcm_close(spcm);
610         return err;
611 }
612 #ifndef DOC_HIDDEN
613 SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION);
614 #endif