source: roaraudio/plugins/alsa/pcm.c @ 3738:791afc7d0aa4

Last change on this file since 3738:791afc7d0aa4 was 3738:791afc7d0aa4, checked in by phi, 14 years ago

cleanup, use control connection

File size: 10.4 KB
Line 
1//pcm.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010
5 *      Copyright (C) Hans-Kristian 'maister' Arntzen - 2010
6 *
7 *  This file is part of libroar a part of RoarAudio,
8 *  a cross-platform sound system for both, home and professional use.
9 *  See README for details.
10 *
11 *  This file is free software; you can redistribute it and/or modify
12 *  it under the terms of the GNU General Public License version 3
13 *  as published by the Free Software Foundation.
14 *
15 *  libroar is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with this software; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 *  Boston, MA 02110-1301, USA.
24 *
25 *  NOTE for everyone want's to change something and send patches:
26 *  read README and HACKING! There a addition information on
27 *  the license of this document you need to read before you send
28 *  any patches.
29 *
30 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
31 *  or libpulse*:
32 *  The libs libroaresd, libroararts and libroarpulse link this lib
33 *  and are therefore GPL. Because of this it may be illigal to use
34 *  them with any software that uses libesd, libartsc or libpulse*.
35 */
36
37#include "roar.h"
38
39// Equvivalent to prepare(). Starts a stream. Also needs to reset the writec since pointer() will do funny
40// things without it. Will be called several times during a program!
41static int roar_pcm_start (snd_pcm_ioplug_t * io) {
42 struct roar_alsa_pcm * self = io->private_data;
43
44 ROAR_DBG("roar_pcm_start(*) = ?");
45
46 // If start is called several times in a row, just ignore it.
47 if (self->stream_opened)
48  return 0;
49
50 if ( roar_vio_simple_new_stream_obj(&(self->stream_vio), &(self->roar.con), &(self->stream),
51                                     self->info.rate, self->info.channels, self->info.bits, self->info.codec,
52                                     io->stream == SND_PCM_STREAM_PLAYBACK ? ROAR_DIR_PLAY : ROAR_DIR_MONITOR
53                                    ) == -1 ) {
54  return -EINVAL;
55 }
56
57 // Stream is now active, yay.
58 self->stream_opened = 1;
59 self->writec = 0;
60
61 return 0;
62}
63
64// Simply stopping the stream. Will need to be restarted to play more.
65// Will be called several times together with roar_pcm_start()
66static int roar_pcm_stop (snd_pcm_ioplug_t *io) {
67 struct roar_alsa_pcm * self = io->private_data;
68
69 ROAR_DBG("roar_pcm_stop(*) = ?");
70
71 // If this is called several times in a row, just ignore.
72 if ( !self->stream_opened )
73  return 0;
74
75 roar_vio_close(&(self->stream_vio));
76 self->stream_opened = 0;
77
78 ROAR_DBG("roar_pcm_stop(*) = 0");
79
80 return 0;
81}
82
83static int roar_hw_constraint(struct roar_alsa_pcm * self) {
84 snd_pcm_ioplug_t *io = &(self->io);
85 static const snd_pcm_access_t access_list[] = {
86  SND_PCM_ACCESS_RW_INTERLEAVED
87 };
88 static const unsigned int formats[] = {
89  // TODO: add list of additioal formats we support
90  SND_PCM_FORMAT_U8,
91  SND_PCM_FORMAT_A_LAW,
92  SND_PCM_FORMAT_MU_LAW,
93  SND_PCM_FORMAT_S16_LE,
94  SND_PCM_FORMAT_S16_BE,
95 };
96 int ret;
97
98 ROAR_DBG("roar_hw_constraint(*) = ?");
99
100 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
101        _as(access_list), access_list)) < 0 )
102  return ret;
103
104 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
105        _as(formats), formats)) < 0 )
106  return ret;
107
108 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
109        1, ROAR_MAX_CHANNELS)) < 0 )
110  return ret;
111
112 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 192000)) < 0 )
113  return ret;
114
115#if 0
116 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 1, 4294967295U)) < 0 )
117  return ret;
118
119 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 1, 4294967295U)) < 0 )
120  return ret;
121
122 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, 1, 4294967295U)) < 0 )
123  return ret;
124#else
125 // We shouldn't let ALSA use extremely low or high values, it will kill a kitty most likely. :v
126 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 1 << 6, 1 << 18)) < 0 )
127  return ret;
128
129 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 1, 1024)) < 0 )
130  return ret;
131
132 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, 1 << 13, 1 << 24)) < 0 )
133  return ret;
134#endif
135
136 ROAR_DBG("roar_hw_constraint(*) = 0");
137
138 return 0;
139}
140
141
142///////////////////////////////
143/// TODO: Needs to be implemented
144///////////////////////////////
145// This hacky hack should not be used, since apparently, using this, ALSA will think the audio buffer is always empty (?),
146// leading to completely broken blocking audio which will only work with audio players.
147// Will need a method to determine how much buffer data is available to write to the roar buffer without blocking.
148// We therefore also need to know the buffersize that ALSA uses.
149
150// Referring to alsa-lib/src/pcm/pcm_ioplug.c : snd_pcm_ioplug_hw_ptr_update
151static snd_pcm_sframes_t roar_pcm_pointer(snd_pcm_ioplug_t *io) {
152 struct roar_alsa_pcm * self = io->private_data;
153
154 ROAR_DBG("roar_pcm_pointer(*) = ?");
155
156 return snd_pcm_bytes_to_frames(io->pcm, self->writec);
157}
158
159// TODO: FIXME: add support for reading data!
160static snd_pcm_sframes_t roar_pcm_transfer(snd_pcm_ioplug_t *io,
161                                        const snd_pcm_channel_area_t *areas,
162                                        snd_pcm_uframes_t offset,
163                                        snd_pcm_uframes_t size) {
164 struct roar_alsa_pcm * self = io->private_data;
165 char * buf;
166 size_t len = size * self->info.channels * self->info.bits / 8;
167 ssize_t ret;
168
169 ROAR_DBG("roar_pcm_transfer(*) = ?");
170 ROAR_DBG("roar_pcm_transfer(*): len=%lu", (long unsigned int) len);
171
172 // Weird ALSA stuff.
173 buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8;
174
175 ret = roar_vio_write(&(self->stream_vio), buf, len);
176
177 if ( ret != -1 ) {
178  // We increment the written counter so that subsequent calls to pointer() will not cause
179  // the library to hang due to several quirks that ALSA uses to determine available size.
180  // This approach is bad, but is needed until pointer() is implemented correctly.
181  self->writec += ret;
182 } else {
183  return -EIO;
184 }
185
186 ROAR_DBG("roar_pcm_transfer(*) = %lli", (long long int)size);
187 return size;
188}
189
190
191///////////////////////////////
192/// TODO: Needs to be implemented
193///////////////////////////////
194static int roar_pcm_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp) {
195 (void)io;
196
197 ROAR_DBG("roar_pcm_delay(*) = ?");
198
199 // TODO: We need to set *delayp the latency in frames.
200 *delayp = 0;
201
202 return 0;
203}
204
205static int roar_pcm_prepare(snd_pcm_ioplug_t *io) {
206 ROAR_DBG("roar_pcm_prepare(*) = ?");
207
208 return roar_pcm_start(io);
209}
210
211static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
212 struct roar_alsa_pcm * self = io->private_data;
213
214 (void) params;
215
216 ROAR_DBG("roar_pcm_hw_params(*) = ?");
217
218 self->info.channels = io->channels;
219 self->info.rate     = io->rate;
220
221 switch (io->format) {
222  case SND_PCM_FORMAT_U8:
223    self->info.codec = ROAR_CODEC_PCM_U_LE;
224    self->info.bits  = 8;
225   break;
226  case SND_PCM_FORMAT_A_LAW:
227    self->info.codec = ROAR_CODEC_ALAW;
228    self->info.bits  = 8;
229   break;
230  case SND_PCM_FORMAT_MU_LAW:
231    self->info.codec = ROAR_CODEC_MULAW;
232    self->info.bits  = 8;
233   break;
234  case SND_PCM_FORMAT_S16_LE:
235    self->info.codec = ROAR_CODEC_PCM_S_LE;
236    self->info.bits  = 16;
237   break;
238  case SND_PCM_FORMAT_S16_BE:
239    self->info.codec = ROAR_CODEC_PCM_S_BE;
240    self->info.bits  = 16;
241   break;
242  default:
243    return-EINVAL;
244 }
245
246 ROAR_DBG("roar_pcm_hw_params(*) = 0");
247 return 0;
248}
249
250static int roar_pcm_close (snd_pcm_ioplug_t * io) {
251 struct roar_alsa_pcm * self = io->private_data;
252
253 ROAR_DBG("roar_pcm_close(*) = ?");
254
255 roar_disconnect(&(self->roar.con));
256
257 free(self);
258
259 return 0;
260}
261
262static snd_pcm_ioplug_callback_t roar_pcm_callback = {
263    .start                  = roar_pcm_start,
264    .stop                   = roar_pcm_stop,
265    .drain                  = NULL,
266    .pointer                = roar_pcm_pointer,
267    .transfer               = roar_pcm_transfer,
268    .delay                  = roar_pcm_delay,
269    .poll_descriptors_count = NULL,
270    .poll_descriptors       = NULL,
271    .poll_revents           = NULL,
272    .prepare                = roar_pcm_prepare,
273    .hw_params              = roar_pcm_hw_params,
274    .hw_free                = NULL,
275    .sw_params              = NULL,
276    .pause                  = NULL,
277    .resume                 = NULL,
278    .dump                   = NULL,
279    .close                  = roar_pcm_close,
280};
281
282SND_PCM_PLUGIN_DEFINE_FUNC(roar) {
283 struct roar_alsa_pcm * self;
284 snd_config_iterator_t i, next;
285 snd_config_t * n;
286 const char   * para;
287 const char   * server = NULL;
288 int            ret;
289
290 (void)root;
291
292 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = ?");
293
294 snd_config_for_each(i, next, conf) {
295  n = snd_config_iterator_entry(i);
296  if ( snd_config_get_id(n, &para) < 0 )
297   continue;
298
299  if ( !strcmp(para, "type") || !strcmp(para, "comment") )
300   continue;
301
302  if ( !strcmp(para, "server") ) {
303   if (snd_config_get_string(n, &server) < 0) {
304    return -EINVAL;
305   }
306  } else {
307   return -EINVAL;
308  }
309 }
310
311 errno = ENOSYS;
312
313 if ( (self = malloc(sizeof(struct roar_alsa_pcm))) == NULL )
314  return -errno;
315
316 memset(self, 0, sizeof(struct roar_alsa_pcm));
317
318 errno = ENOSYS;
319 if ( roar_simple_connect(&(self->roar.con), (char*)server, "ALSA Plugin") == -1 ) {
320  free(self);
321  return -errno;
322 }
323
324 self->io.version      = SND_PCM_IOPLUG_VERSION;
325 self->io.name         = "RoarAudio Plugin";
326 self->io.poll_fd      = -1;
327 self->io.poll_events  =  0;
328 self->io.mmap_rw      =  0;
329 self->io.callback     = &roar_pcm_callback;
330 self->io.private_data =  self;
331
332 if ( (ret = snd_pcm_ioplug_create(&(self->io), name, stream, mode)) < 0 ) {
333  roar_disconnect(&(self->roar.con));
334  free(self);
335  return ret;
336 }
337
338 if ( (ret = roar_hw_constraint(self)) < 0 ) {
339  snd_pcm_ioplug_delete(&(self->io));
340  roar_disconnect(&(self->roar.con));
341  free(self);
342  return ret;
343 }
344
345 *pcmp = self->io.pcm;
346
347 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = 0");
348
349 return 0;
350}
351
352SND_PCM_PLUGIN_SYMBOL(roar);
353
354int __snd_pcm_roar_open_dlsym_pcm_001 (void) {
355 ROAR_DBG("__snd_pcm_roar_open_dlsym_pcm_001(void) = 0");
356 return 0;
357}
358
359//ll
Note: See TracBrowser for help on using the repository browser.