OSDN Git Service

66d3a4719856d8d687ca77f5f3a3a9c1cf08bc02
[android-x86/external-alsa-lib.git] / src / pcm / pcm_copy.c
1 /**
2  * \file pcm/pcm_copy.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Copy Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Copy conversion
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_copy = "";
36 #endif
37
38 #ifndef DOC_HIDDEN
39 typedef struct {
40         /* This field need to be the first */
41         snd_pcm_plugin_t plug;
42 } snd_pcm_copy_t;
43 #endif
44
45 static int snd_pcm_copy_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
46 {
47         int err;
48         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
49         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
50                                          &access_mask);
51         if (err < 0)
52                 return err;
53         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
54         return 0;
55 }
56
57 static int snd_pcm_copy_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
58 {
59         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
60         _snd_pcm_hw_params_any(sparams);
61         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
62                                    &saccess_mask);
63         return 0;
64 }
65
66 static int snd_pcm_copy_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
67                                           snd_pcm_hw_params_t *sparams)
68 {
69         int err;
70         unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
71         err = _snd_pcm_hw_params_refine(sparams, links, params);
72         if (err < 0)
73                 return err;
74         return 0;
75 }
76         
77 static int snd_pcm_copy_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
78                                           snd_pcm_hw_params_t *sparams)
79 {
80         int err;
81         unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
82         err = _snd_pcm_hw_params_refine(params, links, sparams);
83         if (err < 0)
84                 return err;
85         return 0;
86 }
87
88 static int snd_pcm_copy_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
89 {
90         return snd_pcm_hw_refine_slave(pcm, params,
91                                        snd_pcm_copy_hw_refine_cprepare,
92                                        snd_pcm_copy_hw_refine_cchange,
93                                        snd_pcm_copy_hw_refine_sprepare,
94                                        snd_pcm_copy_hw_refine_schange,
95                                        snd_pcm_generic_hw_refine);
96 }
97
98 static int snd_pcm_copy_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
99 {
100         return snd_pcm_hw_params_slave(pcm, params,
101                                        snd_pcm_copy_hw_refine_cchange,
102                                        snd_pcm_copy_hw_refine_sprepare,
103                                        snd_pcm_copy_hw_refine_schange,
104                                        snd_pcm_generic_hw_params);
105 }
106
107 static snd_pcm_uframes_t
108 snd_pcm_copy_write_areas(snd_pcm_t *pcm,
109                          const snd_pcm_channel_area_t *areas,
110                          snd_pcm_uframes_t offset,
111                          snd_pcm_uframes_t size,
112                          const snd_pcm_channel_area_t *slave_areas,
113                          snd_pcm_uframes_t slave_offset,
114                          snd_pcm_uframes_t *slave_sizep)
115 {
116         if (size > *slave_sizep)
117                 size = *slave_sizep;
118         snd_pcm_areas_copy(slave_areas, slave_offset,
119                            areas, offset,
120                            pcm->channels, size, pcm->format);
121         *slave_sizep = size;
122         return size;
123 }
124
125 static snd_pcm_uframes_t
126 snd_pcm_copy_read_areas(snd_pcm_t *pcm,
127                         const snd_pcm_channel_area_t *areas,
128                         snd_pcm_uframes_t offset,
129                         snd_pcm_uframes_t size,
130                         const snd_pcm_channel_area_t *slave_areas,
131                         snd_pcm_uframes_t slave_offset,
132                         snd_pcm_uframes_t *slave_sizep)
133 {
134         if (size > *slave_sizep)
135                 size = *slave_sizep;
136         snd_pcm_areas_copy(areas, offset, 
137                            slave_areas, slave_offset,
138                            pcm->channels, size, pcm->format);
139         *slave_sizep = size;
140         return size;
141 }
142
143 static void snd_pcm_copy_dump(snd_pcm_t *pcm, snd_output_t *out)
144 {
145         snd_pcm_copy_t *copy = pcm->private_data;
146         snd_output_printf(out, "Copy conversion PCM\n");
147         if (pcm->setup) {
148                 snd_output_printf(out, "Its setup is:\n");
149                 snd_pcm_dump_setup(pcm, out);
150         }
151         snd_output_printf(out, "Slave: ");
152         snd_pcm_dump(copy->plug.gen.slave, out);
153 }
154
155 static const snd_pcm_ops_t snd_pcm_copy_ops = {
156         .close = snd_pcm_generic_close,
157         .info = snd_pcm_generic_info,
158         .hw_refine = snd_pcm_copy_hw_refine,
159         .hw_params = snd_pcm_copy_hw_params,
160         .hw_free = snd_pcm_generic_hw_free,
161         .sw_params = snd_pcm_generic_sw_params,
162         .channel_info = snd_pcm_generic_channel_info,
163         .dump = snd_pcm_copy_dump,
164         .nonblock = snd_pcm_generic_nonblock,
165         .async = snd_pcm_generic_async,
166         .mmap = snd_pcm_generic_mmap,
167         .munmap = snd_pcm_generic_munmap,
168         .query_chmaps = snd_pcm_generic_query_chmaps,
169         .get_chmap = snd_pcm_generic_get_chmap,
170         .set_chmap = snd_pcm_generic_set_chmap,
171 };
172
173 /**
174  * \brief Creates a new copy PCM
175  * \param pcmp Returns created PCM handle
176  * \param name Name of PCM
177  * \param slave Slave PCM handle
178  * \param close_slave When set, the slave PCM handle is closed with copy PCM
179  * \retval zero on success otherwise a negative error code
180  * \warning Using of this function might be dangerous in the sense
181  *          of compatibility reasons. The prototype might be freely
182  *          changed in future.
183  */
184 int snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
185 {
186         snd_pcm_t *pcm;
187         snd_pcm_copy_t *copy;
188         int err;
189         assert(pcmp && slave);
190         copy = calloc(1, sizeof(snd_pcm_copy_t));
191         if (!copy) {
192                 return -ENOMEM;
193         }
194         snd_pcm_plugin_init(&copy->plug);
195         copy->plug.read = snd_pcm_copy_read_areas;
196         copy->plug.write = snd_pcm_copy_write_areas;
197         copy->plug.undo_read = snd_pcm_plugin_undo_read_generic;
198         copy->plug.undo_write = snd_pcm_plugin_undo_write_generic;
199         copy->plug.gen.slave = slave;
200         copy->plug.gen.close_slave = close_slave;
201
202         err = snd_pcm_new(&pcm, SND_PCM_TYPE_COPY, name, slave->stream, slave->mode);
203         if (err < 0) {
204                 free(copy);
205                 return err;
206         }
207         pcm->ops = &snd_pcm_copy_ops;
208         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
209         pcm->private_data = copy;
210         pcm->poll_fd = slave->poll_fd;
211         pcm->poll_events = slave->poll_events;
212         pcm->tstamp_type = slave->tstamp_type;
213         snd_pcm_set_hw_ptr(pcm, &copy->plug.hw_ptr, -1, 0);
214         snd_pcm_set_appl_ptr(pcm, &copy->plug.appl_ptr, -1, 0);
215         *pcmp = pcm;
216
217         return 0;
218 }
219
220 /*! \page pcm_plugins
221
222 \section pcm_plugins_copy Plugin: copy
223
224 This plugin copies samples from master copy PCM to given slave PCM.
225 The channel count, format and rate must match for both of them. 
226
227 \code
228 pcm.name {
229         type copy               # Copy PCM
230         slave STR               # Slave name
231         # or
232         slave {                 # Slave definition
233                 pcm STR         # Slave PCM name
234                 # or
235                 pcm { }         # Slave PCM definition
236         }
237 }
238 \endcode
239
240 \subsection pcm_plugins_copy_funcref Function reference
241
242 <UL>
243   <LI>snd_pcm_copy_open()
244   <LI>_snd_pcm_copy_open()
245 </UL>
246
247 */
248
249 /**
250  * \brief Creates a new copy PCM
251  * \param pcmp Returns created PCM handle
252  * \param name Name of PCM
253  * \param root Root configuration node
254  * \param conf Configuration node with copy PCM description
255  * \param stream Stream type
256  * \param mode Stream mode
257  * \retval zero on success otherwise a negative error code
258  * \warning Using of this function might be dangerous in the sense
259  *          of compatibility reasons. The prototype might be freely
260  *          changed in future.
261  */
262 int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
263                        snd_config_t *root, snd_config_t *conf, 
264                        snd_pcm_stream_t stream, int mode)
265 {
266         snd_config_iterator_t i, next;
267         int err;
268         snd_pcm_t *spcm;
269         snd_config_t *slave = NULL, *sconf;
270         snd_config_for_each(i, next, conf) {
271                 snd_config_t *n = snd_config_iterator_entry(i);
272                 const char *id;
273                 if (snd_config_get_id(n, &id) < 0)
274                         continue;
275                 if (snd_pcm_conf_generic_id(id))
276                         continue;
277                 if (strcmp(id, "slave") == 0) {
278                         slave = n;
279                         continue;
280                 }
281                 SNDERR("Unknown field %s", id);
282                 return -EINVAL;
283         }
284         if (!slave) {
285                 SNDERR("slave is not defined");
286                 return -EINVAL;
287         }
288         err = snd_pcm_slave_conf(root, slave, &sconf, 0);
289         if (err < 0)
290                 return err;
291         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
292         snd_config_delete(sconf);
293         if (err < 0)
294                 return err;
295         err = snd_pcm_copy_open(pcmp, name, spcm, 1);
296         if (err < 0)
297                 snd_pcm_close(spcm);
298         return err;
299 }
300 #ifndef DOC_HIDDEN
301 SND_DLSYM_BUILD_VERSION(_snd_pcm_copy_open, SND_PCM_DLSYM_VERSION);
302 #endif