source: roaraudio/plugins/alsa/pcm.c @ 3739:23dd0e5c4a68

Last change on this file since 3739:23dd0e5c4a68 was 3739:23dd0e5c4a68, checked in by phi, 14 years ago

added additional frame formats we support

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