source: roaraudio/plugins/alsa/pcm.c @ 5439:7950543cabbc

Last change on this file since 5439:7950543cabbc was 5381:430b1d26e12d, checked in by phi, 12 years ago

updated copyright years

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