source: roaraudio/plugins/alsa/pcm.c @ 3748:255ac9d65f87

Last change on this file since 3748:255ac9d65f87 was 3748:255ac9d65f87, checked in by phi, 14 years ago

added pollfh support

File size: 12.2 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 int                    fh;
44
45 ROAR_DBG("roar_pcm_start(*) = ?");
46
47 // If start is called several times in a row, just ignore it.
48 if (self->stream_opened)
49  return 0;
50
51 if ( roar_vio_simple_new_stream_obj(&(self->stream_vio), &(self->roar.con), &(self->stream),
52                                     self->info.rate, self->info.channels, self->info.bits, self->info.codec,
53                                     io->stream == SND_PCM_STREAM_PLAYBACK ? ROAR_DIR_PLAY : ROAR_DIR_MONITOR
54                                    ) == -1 ) {
55  return -EINVAL;
56 }
57
58 if ( roar_vio_ctl(&(self->stream_vio), io->stream == SND_PCM_STREAM_PLAYBACK ?
59                                        ROAR_VIO_CTL_GET_SELECT_WRITE_FH : ROAR_VIO_CTL_GET_SELECT_READ_FH,
60                   &fh) != -1 ) {
61
62  io->poll_fd     = fh;
63  io->poll_events = io->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
64
65  snd_pcm_ioplug_reinit_status(io);
66 }
67
68 // Stream is now active, yay.
69 self->stream_opened = 1;
70 self->writec = 0;
71
72 return 0;
73}
74
75// Simply stopping the stream. Will need to be restarted to play more.
76// Will be called several times together with roar_pcm_start()
77static int roar_pcm_stop (snd_pcm_ioplug_t *io) {
78 struct roar_alsa_pcm * self = io->private_data;
79
80 ROAR_DBG("roar_pcm_stop(*) = ?");
81
82 // If this is called several times in a row, just ignore.
83 if ( !self->stream_opened )
84  return 0;
85
86 roar_vio_close(&(self->stream_vio));
87 self->stream_opened = 0;
88
89 ROAR_DBG("roar_pcm_stop(*) = 0");
90
91 return 0;
92}
93
94static int roar_hw_constraint(struct roar_alsa_pcm * self) {
95 snd_pcm_ioplug_t *io = &(self->io);
96 static const snd_pcm_access_t access_list[] = {
97  SND_PCM_ACCESS_RW_INTERLEAVED
98 };
99 static const unsigned int formats[] = {
100  SND_PCM_FORMAT_S8,
101  SND_PCM_FORMAT_U8,
102  SND_PCM_FORMAT_A_LAW,
103  SND_PCM_FORMAT_MU_LAW,
104  SND_PCM_FORMAT_S16_LE,
105  SND_PCM_FORMAT_S16_BE,
106  SND_PCM_FORMAT_U16_LE,
107  SND_PCM_FORMAT_U16_BE,
108  SND_PCM_FORMAT_S32_LE,
109  SND_PCM_FORMAT_S32_BE,
110  SND_PCM_FORMAT_U32_LE,
111  SND_PCM_FORMAT_U32_BE,
112  SND_PCM_FORMAT_S24_3LE,
113  SND_PCM_FORMAT_S24_3BE,
114  SND_PCM_FORMAT_U24_3LE,
115  SND_PCM_FORMAT_U24_3BE,
116 };
117 int ret;
118
119 ROAR_DBG("roar_hw_constraint(*) = ?");
120
121 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
122        _as(access_list), access_list)) < 0 )
123  return ret;
124
125 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
126        _as(formats), formats)) < 0 )
127  return ret;
128
129 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
130        1, ROAR_MAX_CHANNELS)) < 0 )
131  return ret;
132
133 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 192000)) < 0 )
134  return ret;
135
136#if 0
137 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 1, 4294967295U)) < 0 )
138  return ret;
139
140 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 1, 4294967295U)) < 0 )
141  return ret;
142
143 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, 1, 4294967295U)) < 0 )
144  return ret;
145#else
146 // We shouldn't let ALSA use extremely low or high values, it will kill a kitty most likely. :v
147 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 1 << 6, 1 << 18)) < 0 )
148  return ret;
149
150 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 1, 1024)) < 0 )
151  return ret;
152
153 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, 1 << 13, 1 << 24)) < 0 )
154  return ret;
155#endif
156
157 ROAR_DBG("roar_hw_constraint(*) = 0");
158
159 return 0;
160}
161
162
163///////////////////////////////
164/// TODO: Needs to be implemented
165///////////////////////////////
166// This hacky hack should not be used, since apparently, using this, ALSA will think the audio buffer is always empty (?),
167// leading to completely broken blocking audio which will only work with audio players.
168// Will need a method to determine how much buffer data is available to write to the roar buffer without blocking.
169// We therefore also need to know the buffersize that ALSA uses.
170
171// Referring to alsa-lib/src/pcm/pcm_ioplug.c : snd_pcm_ioplug_hw_ptr_update
172static snd_pcm_sframes_t roar_pcm_pointer(snd_pcm_ioplug_t *io) {
173 struct roar_alsa_pcm * self = io->private_data;
174
175 ROAR_DBG("roar_pcm_pointer(*) = ?");
176
177 return snd_pcm_bytes_to_frames(io->pcm, self->writec);
178}
179
180// TODO: FIXME: add support for reading data!
181static snd_pcm_sframes_t roar_pcm_transfer(snd_pcm_ioplug_t *io,
182                                        const snd_pcm_channel_area_t *areas,
183                                        snd_pcm_uframes_t offset,
184                                        snd_pcm_uframes_t size) {
185 struct roar_alsa_pcm * self = io->private_data;
186 char * buf;
187 size_t len = size * self->info.channels * self->info.bits / 8;
188 ssize_t ret;
189
190 ROAR_DBG("roar_pcm_transfer(*) = ?");
191 ROAR_DBG("roar_pcm_transfer(*): len=%lu", (long unsigned int) len);
192
193 // Weird ALSA stuff.
194 buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8;
195
196 ret = roar_vio_write(&(self->stream_vio), buf, len);
197
198 if ( ret != -1 ) {
199  // We increment the written counter so that subsequent calls to pointer() will not cause
200  // the library to hang due to several quirks that ALSA uses to determine available size.
201  // This approach is bad, but is needed until pointer() is implemented correctly.
202  self->writec += ret;
203 } else {
204  return -EIO;
205 }
206
207 ROAR_DBG("roar_pcm_transfer(*) = %lli", (long long int)size);
208 return size;
209}
210
211
212///////////////////////////////
213/// TODO: Needs to be implemented
214///////////////////////////////
215static int roar_pcm_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp) {
216 (void)io;
217
218 ROAR_DBG("roar_pcm_delay(*) = ?");
219
220 // TODO: We need to set *delayp the latency in frames.
221 *delayp = 0;
222
223 return 0;
224}
225
226static int roar_pcm_prepare(snd_pcm_ioplug_t *io) {
227 ROAR_DBG("roar_pcm_prepare(*) = ?");
228
229 return roar_pcm_start(io);
230}
231
232static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
233 struct roar_alsa_pcm * self = io->private_data;
234
235 (void) params;
236
237 ROAR_DBG("roar_pcm_hw_params(*) = ?");
238
239 self->info.channels = io->channels;
240 self->info.rate     = io->rate;
241
242 switch (io->format) {
243  case SND_PCM_FORMAT_S8:
244    self->info.codec = ROAR_CODEC_PCM_U_LE;
245    self->info.bits  = 8;
246   break;
247  case SND_PCM_FORMAT_U8:
248    self->info.codec = ROAR_CODEC_PCM_U_LE;
249    self->info.bits  = 8;
250   break;
251  case SND_PCM_FORMAT_A_LAW:
252    self->info.codec = ROAR_CODEC_ALAW;
253    self->info.bits  = 8;
254   break;
255  case SND_PCM_FORMAT_MU_LAW:
256    self->info.codec = ROAR_CODEC_MULAW;
257    self->info.bits  = 8;
258   break;
259  case SND_PCM_FORMAT_S16_LE:
260    self->info.codec = ROAR_CODEC_PCM_S_LE;
261    self->info.bits  = 16;
262   break;
263  case SND_PCM_FORMAT_S16_BE:
264    self->info.codec = ROAR_CODEC_PCM_S_BE;
265    self->info.bits  = 16;
266   break;
267  case SND_PCM_FORMAT_U16_LE:
268    self->info.codec = ROAR_CODEC_PCM_U_LE;
269    self->info.bits  = 16;
270   break;
271  case SND_PCM_FORMAT_U16_BE:
272    self->info.codec = ROAR_CODEC_PCM_U_BE;
273    self->info.bits  = 16;
274   break;
275  case SND_PCM_FORMAT_S32_LE:
276    self->info.codec = ROAR_CODEC_PCM_S_LE;
277    self->info.bits  = 32;
278   break;
279  case SND_PCM_FORMAT_S32_BE:
280    self->info.codec = ROAR_CODEC_PCM_S_BE;
281    self->info.bits  = 32;
282   break;
283  case SND_PCM_FORMAT_U32_LE:
284    self->info.codec = ROAR_CODEC_PCM_U_LE;
285    self->info.bits  = 32;
286   break;
287  case SND_PCM_FORMAT_U32_BE:
288    self->info.codec = ROAR_CODEC_PCM_U_BE;
289    self->info.bits  = 32;
290   break;
291  case SND_PCM_FORMAT_S24_3LE:
292    self->info.codec = ROAR_CODEC_PCM_S_LE;
293    self->info.bits  = 24;
294   break;
295  case SND_PCM_FORMAT_S24_3BE:
296    self->info.codec = ROAR_CODEC_PCM_S_BE;
297    self->info.bits  = 24;
298   break;
299  case SND_PCM_FORMAT_U24_3LE:
300    self->info.codec = ROAR_CODEC_PCM_U_LE;
301    self->info.bits  = 24;
302   break;
303  case SND_PCM_FORMAT_U24_3BE:
304    self->info.codec = ROAR_CODEC_PCM_U_BE;
305    self->info.bits  = 24;
306   break;
307  default:
308    return-EINVAL;
309 }
310
311 ROAR_DBG("roar_pcm_hw_params(*) = 0");
312 return 0;
313}
314
315static int roar_pcm_close (snd_pcm_ioplug_t * io) {
316 struct roar_alsa_pcm * self = io->private_data;
317
318 ROAR_DBG("roar_pcm_close(*) = ?");
319
320 roar_disconnect(&(self->roar.con));
321
322 free(self);
323
324 return 0;
325}
326
327static snd_pcm_ioplug_callback_t roar_pcm_callback = {
328    .start                  = roar_pcm_start,
329    .stop                   = roar_pcm_stop,
330    .drain                  = NULL,
331    .pointer                = roar_pcm_pointer,
332    .transfer               = roar_pcm_transfer,
333    .delay                  = roar_pcm_delay,
334    .poll_descriptors_count = NULL,
335    .poll_descriptors       = NULL,
336    .poll_revents           = NULL,
337    .prepare                = roar_pcm_prepare,
338    .hw_params              = roar_pcm_hw_params,
339    .hw_free                = NULL,
340    .sw_params              = NULL,
341    .pause                  = NULL,
342    .resume                 = NULL,
343    .dump                   = NULL,
344    .close                  = roar_pcm_close,
345};
346
347SND_PCM_PLUGIN_DEFINE_FUNC(roar) {
348 struct roar_alsa_pcm * self;
349 snd_config_iterator_t i, next;
350 snd_config_t * n;
351 const char   * para;
352 const char   * server = NULL;
353 int            ret;
354
355 (void)root;
356
357 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = ?");
358
359 snd_config_for_each(i, next, conf) {
360  n = snd_config_iterator_entry(i);
361  if ( snd_config_get_id(n, &para) < 0 )
362   continue;
363
364  if ( !strcmp(para, "type") || !strcmp(para, "comment") )
365   continue;
366
367  if ( !strcmp(para, "server") ) {
368   if (snd_config_get_string(n, &server) < 0) {
369    return -EINVAL;
370   }
371  } else {
372   return -EINVAL;
373  }
374 }
375
376 errno = ENOSYS;
377
378 if ( (self = malloc(sizeof(struct roar_alsa_pcm))) == NULL )
379  return -errno;
380
381 memset(self, 0, sizeof(struct roar_alsa_pcm));
382
383 errno = ENOSYS;
384 if ( roar_simple_connect(&(self->roar.con), (char*)server, "ALSA Plugin") == -1 ) {
385  free(self);
386  return -errno;
387 }
388
389 self->io.version      = SND_PCM_IOPLUG_VERSION;
390 self->io.name         = "RoarAudio Plugin";
391 self->io.poll_fd      = -1;
392 self->io.poll_events  =  0;
393 self->io.mmap_rw      =  0;
394 self->io.callback     = &roar_pcm_callback;
395 self->io.private_data =  self;
396
397 if ( (ret = snd_pcm_ioplug_create(&(self->io), name, stream, mode)) < 0 ) {
398  roar_disconnect(&(self->roar.con));
399  free(self);
400  return ret;
401 }
402
403 if ( (ret = roar_hw_constraint(self)) < 0 ) {
404  snd_pcm_ioplug_delete(&(self->io));
405  roar_disconnect(&(self->roar.con));
406  free(self);
407  return ret;
408 }
409
410 *pcmp = self->io.pcm;
411
412 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = 0");
413
414 return 0;
415}
416
417SND_PCM_PLUGIN_SYMBOL(roar);
418
419int __snd_pcm_roar_open_dlsym_pcm_001 (void) {
420 ROAR_DBG("__snd_pcm_roar_open_dlsym_pcm_001(void) = 0");
421 return 0;
422}
423
424//ll
Note: See TracBrowser for help on using the repository browser.