source: roaraudio/plugins/alsa/pcm_roar.c @ 4177:f5294d4360a5

Last change on this file since 4177:f5294d4360a5 was 4177:f5294d4360a5, checked in by phi, 14 years ago

some more debugging code

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