source: roaraudio/plugins/alsavs/pcm_roar.c @ 5174:c24a2089056a

Last change on this file since 5174:c24a2089056a was 5174:c24a2089056a, checked in by phi, 13 years ago

added some experimental fork of the old alsa plugin using VS API

File size: 16.3 KB
Line 
1//pcm_roar.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2011
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//#define DEBUG
38#include "roar.h"
39
40// Equvivalent to prepare(). Starts a stream. Might/will be called several times during a program!
41/////////////////////////////////////////////////
42// Status: Should be mostly complete.
43// Condition for invalid fh is not solved here.
44////////////////////////////////////////////////
45static int roar_pcm_start (snd_pcm_ioplug_t * io) {
46 struct roar_alsa_pcm * self = io->private_data;
47 int fh;
48 int val;
49 int err;
50
51 ROAR_DBG("roar_pcm_start(*) = ?");
52
53 // If start is called several times in a row, just ignore it.
54 if (self->stream_opened)
55  return 0;
56
57 if ( (self->vss = roar_vs_new_from_con(&(self->roar.con), &err)) == NULL ) {
58  roar_err_set(err);
59  roar_err_update();
60  return -errno;
61 }
62
63 if ( roar_vs_stream(self->vss, &(self->info),
64                     io->stream == SND_PCM_STREAM_PLAYBACK ? ROAR_DIR_PLAY : ROAR_DIR_MONITOR,
65                     &err) == -1 ) {
66  roar_vs_close(self->vss, ROAR_VS_TRUE, NULL);
67  self->vss = NULL;
68  roar_err_set(err);
69  roar_err_update();
70  return -errno;
71 }
72
73 if ( roar_vs_buffer(self->vss, self->bufsize, &err) == -1 ) {
74  roar_vs_close(self->vss, ROAR_VS_TRUE, NULL);
75  self->vss = NULL;
76  roar_err_set(err);
77  roar_err_update();
78  return -errno;
79 }
80
81 val = ROAR_VS_ASYNCLEVEL_AUTO;
82 if ( roar_vs_ctl(self->vss, ROAR_VS_CMD_SET_ASYNC, &val, &err) == -1 ) {
83  roar_vs_close(self->vss, ROAR_VS_TRUE, NULL);
84  self->vss = NULL;
85  roar_err_set(err);
86  roar_err_update();
87  return -errno;
88 }
89
90 if ( self->stream_role != ROAR_ROLE_UNKNOWN ) {
91  if ( roar_vs_role(self->vss, self->stream_role, &err) == -1 ) {
92   ROAR_WARN("roar_pcm_start(*): Can not set stream role: %s(%i): %s",
93             roar_role2str(self->stream_role), self->stream_role, roar_error2str(roar_error));
94  }
95 }
96
97 if ( roar_vio_ctl(roar_vs_vio_obj(self->vss, NULL),
98    io->stream == SND_PCM_STREAM_PLAYBACK ? ROAR_VIO_CTL_GET_SELECT_WRITE_FH :
99    ROAR_VIO_CTL_GET_SELECT_READ_FH, &fh) != 1 ) {
100  io->poll_fd = fh;
101  io->poll_events = io->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
102 }
103
104 // Oh, no, what should we do here if roar_vio_ctl() fails to grab a valid fh to poll?
105 // In any case, ALSA will error out the next time it tries to poll() if we don't give it a valid fh.
106
107 snd_pcm_ioplug_reinit_status(io);
108
109 // Stream is now active, yay.
110 self->stream_opened = 1;
111
112//RM: self->bufptr = 0;
113 ROAR_DBG("roar_pcm_start(*): Starting background thread");
114 self->thread_active = 1; // We have to activate the thread before starting it, because the thread lives on that thread_active is 1.
115 if ( pthread_create(&(self->thread), NULL, roar_plugin_thread, self) < 0 ) {
116  self->thread_active = 0;
117  ROAR_DBG("roar_pcm_start(*) = -1");
118  return -1;
119 }
120 pthread_mutex_lock(&(self->lock)); // wait for the background thread to wake up.
121 ROAR_DBG("roar_pcm_start(*): background thread was successfully started");
122
123 ROAR_DBG("roar_pcm_start(*) = 0");
124 return 0;
125}
126
127void roar_plugin_reset(struct roar_alsa_pcm *self) {
128 if ( !self->stream_opened )
129  return;
130
131 roar_vs_close(self->vss, ROAR_VS_TRUE, NULL);
132 self->vss = NULL;
133 self->stream_opened = 0;
134 self->thread_active = 0;
135//RM: self->bufptr = 0;
136//RM: self->last_ptr = 0;
137//RM: self->total_written = 0;
138//RM: self->has_written = 0;
139}
140
141
142
143// Simply stopping the stream. Will need to be restarted to play more.
144// Will be called several times together with roar_pcm_start()
145///////////////////////////////////////////////////
146// Status: Still needs some error checking for the pthread calls, but
147// should work.
148//////////////////////////////////////////////////
149static int roar_pcm_stop (snd_pcm_ioplug_t *io) {
150 struct roar_alsa_pcm * self = io->private_data;
151
152 // If this is called several times in a row, just ignore.
153
154 ROAR_DBG("roar_pcm_stop(*) = 0");
155
156
157 if ( self->thread_active ) {
158  self->thread_active = 0;
159  pthread_cond_signal(&(self->cond));
160  pthread_join(self->thread, NULL);
161 }
162
163 roar_plugin_reset(self);
164
165 return 0;
166}
167
168///////////////////////////////
169// Status: Should be complete.
170///////////////////////////////
171static int roar_hw_constraint(struct roar_alsa_pcm * self) {
172 snd_pcm_ioplug_t *io = &(self->io);
173 static const snd_pcm_access_t access_list[] = {
174  SND_PCM_ACCESS_RW_INTERLEAVED
175 };
176 static const unsigned int formats[] = {
177  SND_PCM_FORMAT_S8,
178  SND_PCM_FORMAT_U8,
179  SND_PCM_FORMAT_A_LAW,
180  SND_PCM_FORMAT_MU_LAW,
181  SND_PCM_FORMAT_S16_LE,
182  SND_PCM_FORMAT_S16_BE,
183  SND_PCM_FORMAT_U16_LE,
184  SND_PCM_FORMAT_U16_BE,
185  SND_PCM_FORMAT_S32_LE,
186  SND_PCM_FORMAT_S32_BE,
187  SND_PCM_FORMAT_U32_LE,
188  SND_PCM_FORMAT_U32_BE,
189  SND_PCM_FORMAT_S24_3LE,
190  SND_PCM_FORMAT_S24_3BE,
191  SND_PCM_FORMAT_U24_3LE,
192  SND_PCM_FORMAT_U24_3BE,
193 };
194 int ret;
195
196 ROAR_DBG("roar_hw_constraint(*) = ?");
197
198 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
199     _as(access_list), access_list)) < 0 )
200  return ret;
201
202 if ( (ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
203     _as(formats), formats)) < 0 )
204  return ret;
205
206 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
207     1, ROAR_MAX_CHANNELS)) < 0 )
208  return ret;
209
210 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 192000)) < 0 )
211  return ret;
212
213 // We shouldn't let ALSA use extremely low or high values, it will kill a kitty most likely. :v
214 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 1 << 6, 1 << 18)) < 0 )
215  return ret;
216
217 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 1, 1024)) < 0 )
218  return ret;
219
220 if ( (ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, 1 << 13, 1 << 24)) < 0 )
221  return ret;
222
223 ROAR_DBG("roar_hw_constraint(*) = 0");
224
225 return 0;
226}
227
228// Referring to alsa-lib/src/pcm/pcm_ioplug.c : snd_pcm_ioplug_hw_ptr_update
229///////////////////////////////////////////////////////////
230// Status: Mostly complete, but uses a really nasty hack!
231///////////////////////////////////////////////////////////
232static snd_pcm_sframes_t roar_pcm_pointer(snd_pcm_ioplug_t *io) {
233 struct roar_alsa_pcm * self = io->private_data;
234 int ptr;
235 ssize_t avail;
236
237 ROAR_DBG("roar_pcm_pointer(*) = ?");
238
239 // Did ALSA just call snd_pcm_reset() or something like that without calling the plugin?
240 // We should restart our stream as well.
241 if ( io->appl_ptr < self->last_ptr ) {
242  roar_pcm_stop(io);
243  roar_pcm_start(io);
244 }
245
246#if 0
247 // ALSA has a weird way of calculating how much data can be written to the audio buffer.
248 // It uses the formula:
249 // avail = bufsize + ptr - io->appl_ptr; (??!?)
250 // We really want this:
251 // avail = bufsize - ptr;
252 // This is the obvious way, so we have to manipulate ptr like this:
253 // ptr = io->appl_ptr - ptr;
254
255 pthread_mutex_lock(&(self->lock));
256 ptr = snd_pcm_bytes_to_frames(io->pcm, self->bufptr);
257 pthread_mutex_unlock(&(self->lock));
258#else
259 pthread_mutex_lock(&(self->lock));
260 avail = roar_vs_get_avail_write(self->vss, NULL);
261 pthread_mutex_unlock(&(self->lock));
262
263 ROAR_DBG("roar_pcm_pointer(*): avail=%lli", (long long int)avail);
264
265 ptr = snd_pcm_bytes_to_frames(io->pcm, self->bufsize - avail);
266#endif
267
268 ptr = io->appl_ptr - ptr;
269 self->last_ptr = io->appl_ptr;
270
271 ROAR_DBG("roar_pcm_pointer(*) appl_ptr: %i, ptr: %i, calculated avail frames: %i", (int)io->appl_ptr, (int)ptr, (int)(io->appl_ptr - ptr));
272 ROAR_DBG("roar_pcm_pointer(*) = %i", ptr);
273 return ptr;
274}
275
276// TODO: FIXME: add support for reading data!
277//////////////////////////////////////////////////
278// Status: For writing, this should be complete.
279//////////////////////////////////////////////////
280static snd_pcm_sframes_t roar_pcm_transfer(snd_pcm_ioplug_t *io,
281  const snd_pcm_channel_area_t *areas,
282  snd_pcm_uframes_t offset,
283  snd_pcm_uframes_t size) {
284 struct roar_alsa_pcm * self = io->private_data;
285 char * buf;
286 size_t len = size * self->info.channels * self->info.bits / 8;
287 ssize_t ret;
288
289 ROAR_DBG("roar_pcm_transfer(*) = ?");
290 ROAR_DBG("roar_pcm_transfer(*): len=%lu", (long unsigned int) len);
291
292 // Weird ALSA stuff.
293 buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8;
294
295 ret = roar_plugin_write(self, buf, len);
296
297 if ( ret == -1 )
298  return -EIO;
299
300 ROAR_DBG("roar_pcm_transfer(*) = %lli", (long long int)size);
301 return size;
302}
303
304///////////////////////////////////////////////////////////////////
305// Status: Still missing proper delay measurements from the roar server.
306// Only uses a blind timer now. In ideal conditions, this will work well.
307///////////////////////////////////////////////////////////////////
308static int roar_pcm_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp) {
309 struct roar_alsa_pcm * self = io->private_data;
310 roar_mus_t lat;
311
312 ROAR_DBG("roar_pcm_delay(*) = ?");
313
314 // TODO: We need to set *delayp the latency in frames.
315 pthread_mutex_lock(&(self->lock));
316 roar_plugin_drain(self); // why do we call this?
317
318 lat = roar_vs_latency2(self->vss, ROAR_VS_BACKEND_DEFAULT, ROAR_VS_NOWAIT, NULL);
319 *delayp = lat * self->info.rate;
320
321 pthread_mutex_unlock(&(self->lock));
322
323 return 0;
324}
325
326////////////////////
327// Status: Complete
328////////////////////
329static int roar_pcm_prepare(snd_pcm_ioplug_t *io) {
330 ROAR_DBG("roar_pcm_prepare(*) = ?");
331
332 return roar_pcm_start(io);
333}
334
335///////////////////////////////////////////////////////////////////////////////////////////////////////////////
336// Status: This should be mostly complete.
337// I'm not sure if hw_params can be called several times during one stream without stop() start() in between.
338// This will mean a memory leak, and possibly breakage should the buffer size change itself.
339///////////////////////////////////////////////////////////////////////////////////////////////////////////////
340static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
341 struct roar_alsa_pcm * self = io->private_data;
342 snd_pcm_uframes_t buffersize;
343 int err;
344
345 ROAR_DBG("roar_pcm_hw_params(*) = ?");
346
347 self->info.channels = io->channels;
348 self->info.rate     = io->rate;
349
350 switch (io->format) {
351  case SND_PCM_FORMAT_S8:
352   self->info.codec = ROAR_CODEC_PCM_U_LE;
353   self->info.bits  = 8;
354   break;
355  case SND_PCM_FORMAT_U8:
356   self->info.codec = ROAR_CODEC_PCM_U_LE;
357   self->info.bits  = 8;
358   break;
359  case SND_PCM_FORMAT_A_LAW:
360   self->info.codec = ROAR_CODEC_ALAW;
361   self->info.bits  = 8;
362   break;
363  case SND_PCM_FORMAT_MU_LAW:
364   self->info.codec = ROAR_CODEC_MULAW;
365   self->info.bits  = 8;
366   break;
367  case SND_PCM_FORMAT_S16_LE:
368   self->info.codec = ROAR_CODEC_PCM_S_LE;
369   self->info.bits  = 16;
370   break;
371  case SND_PCM_FORMAT_S16_BE:
372   self->info.codec = ROAR_CODEC_PCM_S_BE;
373   self->info.bits  = 16;
374   break;
375  case SND_PCM_FORMAT_U16_LE:
376   self->info.codec = ROAR_CODEC_PCM_U_LE;
377   self->info.bits  = 16;
378   break;
379  case SND_PCM_FORMAT_U16_BE:
380   self->info.codec = ROAR_CODEC_PCM_U_BE;
381   self->info.bits  = 16;
382   break;
383  case SND_PCM_FORMAT_S32_LE:
384   self->info.codec = ROAR_CODEC_PCM_S_LE;
385   self->info.bits  = 32;
386   break;
387  case SND_PCM_FORMAT_S32_BE:
388   self->info.codec = ROAR_CODEC_PCM_S_BE;
389   self->info.bits  = 32;
390   break;
391  case SND_PCM_FORMAT_U32_LE:
392   self->info.codec = ROAR_CODEC_PCM_U_LE;
393   self->info.bits  = 32;
394   break;
395  case SND_PCM_FORMAT_U32_BE:
396   self->info.codec = ROAR_CODEC_PCM_U_BE;
397   self->info.bits  = 32;
398   break;
399  case SND_PCM_FORMAT_S24_3LE:
400   self->info.codec = ROAR_CODEC_PCM_S_LE;
401   self->info.bits  = 24;
402   break;
403  case SND_PCM_FORMAT_S24_3BE:
404   self->info.codec = ROAR_CODEC_PCM_S_BE;
405   self->info.bits  = 24;
406   break;
407  case SND_PCM_FORMAT_U24_3LE:
408   self->info.codec = ROAR_CODEC_PCM_U_LE;
409   self->info.bits  = 24;
410   break;
411  case SND_PCM_FORMAT_U24_3BE:
412   self->info.codec = ROAR_CODEC_PCM_U_BE;
413   self->info.bits  = 24;
414   break;
415  default:
416   return -EINVAL;
417 }
418
419 if ((err = snd_pcm_hw_params_get_buffer_size(params, &buffersize) < 0))
420  return err;
421
422 ROAR_DBG("roar_pcm_hw_params(*) buffersize (bytes): %i", (int)buffersize);
423
424 //self->bufsize = snd_pcm_frames_to_bytes(io->pcm, buffersize);
425 self->bufsize = self->info.bits * self->info.channels * buffersize / 8;
426//RM: self->buffer = malloc(self->bufsize);
427//RM: if (self->buffer == NULL)
428//RM:  return -1;
429//RM: self->bufptr = 0;
430
431 ROAR_DBG("roar_pcm_hw_params(*) Setting buffersize (bytes): %i", (int)self->bufsize);
432 ROAR_DBG("roar_pcm_hw_params(*) = 0");
433 return 0;
434}
435
436///////////////////////////////////
437// Status: This should be complete.
438// This is the last cleanup function to be called by ALSA.
439///////////////////////////////////
440static int roar_pcm_close (snd_pcm_ioplug_t * io) {
441 struct roar_alsa_pcm * self = io->private_data;
442
443 ROAR_DBG("roar_pcm_close(*) = ?");
444
445 roar_disconnect(&(self->roar.con));
446
447 pthread_mutex_destroy(&(self->lock));
448 pthread_mutex_destroy(&(self->cond_lock));
449 pthread_cond_destroy(&(self->cond));
450
451//RM: free(self->buffer);
452 free(self);
453
454 return 0;
455}
456
457static snd_pcm_ioplug_callback_t roar_pcm_callback = {
458 .start                  = roar_pcm_start,
459 .stop                   = roar_pcm_stop,
460 .pointer                = roar_pcm_pointer,
461 .transfer               = roar_pcm_transfer,
462 .delay                  = roar_pcm_delay,
463 .prepare                = roar_pcm_prepare,
464 .hw_params              = roar_pcm_hw_params,
465 .close                  = roar_pcm_close,
466};
467
468SND_PCM_PLUGIN_DEFINE_FUNC(roar) {
469 struct roar_alsa_pcm * self;
470 snd_config_iterator_t i, next;
471 snd_config_t * n;
472 const char   * para;
473 const char   * server = NULL;
474 const char   * tmp;
475 int            role = ROAR_ROLE_UNKNOWN;
476 int            ret;
477
478 (void)root;
479
480 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = ?");
481
482 snd_config_for_each(i, next, conf) {
483  n = snd_config_iterator_entry(i);
484  if ( snd_config_get_id(n, &para) < 0 )
485   continue;
486
487  if ( !strcmp(para, "type") || !strcmp(para, "comment") || !strcmp(para, "hint") )
488   continue;
489
490  if ( !strcmp(para, "server") ) {
491   if (snd_config_get_string(n, &server) < 0) {
492    return -EINVAL;
493   }
494  } else if ( !strcmp(para, "role") ) {
495   if (snd_config_get_string(n, &tmp) < 0) {
496    return -EINVAL;
497   }
498   if ( (role = roar_str2role(tmp)) == -1 ) {
499    return -EINVAL;
500   }
501  } else {
502   return -EINVAL;
503  }
504 }
505
506 errno = ENOSYS;
507
508 if ( (self = malloc(sizeof(struct roar_alsa_pcm))) == NULL )
509  return -errno;
510
511 memset(self, 0, sizeof(struct roar_alsa_pcm));
512
513 self->stream_role = role;
514
515 errno = ENOSYS;
516 if ( roar_simple_connect(&(self->roar.con), (char*)server, "ALSA Plugin") == -1 ) {
517  free(self);
518  return -errno;
519 }
520
521 self->io.version      = SND_PCM_IOPLUG_VERSION;
522 self->io.name         = "RoarAudio Plugin";
523 self->io.poll_fd      =  -1;
524 self->io.poll_events  =  POLLOUT;
525 self->io.mmap_rw      =  0;
526 self->io.callback     = &roar_pcm_callback;
527 self->io.private_data =  self;
528
529 if ( (ret = snd_pcm_ioplug_create(&(self->io), name, stream, mode)) < 0 ) {
530  roar_disconnect(&(self->roar.con));
531  free(self);
532  return ret;
533 }
534
535 pthread_mutex_init(&(self->lock), NULL);
536 pthread_mutex_init(&(self->cond_lock), NULL);
537 pthread_cond_init(&(self->cond), NULL);
538
539
540 if ( (ret = roar_hw_constraint(self)) < 0 ) {
541  snd_pcm_ioplug_delete(&(self->io));
542  roar_disconnect(&(self->roar.con));
543  free(self);
544  return ret;
545 }
546
547 *pcmp = self->io.pcm;
548
549 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = 0");
550
551 return 0;
552}
553
554SND_PCM_PLUGIN_SYMBOL(roar);
555
556//ll
Note: See TracBrowser for help on using the repository browser.