source: roaraudio/plugins/alsa/pcm.c @ 3736:970298ece9e4

Last change on this file since 3736:970298ece9e4 was 3736:970298ece9e4, checked in by phi, 14 years ago

use most of maister's ALSA plugin patch

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