source: roaraudio/plugins/alsavs/pcm_roar.c @ 4170:5896bbcb14f5

Last change on this file since 4170:5896bbcb14f5 was 4170:5896bbcb14f5, checked in by phi, 14 years ago

update roar_pcm_pointer() function

File size: 14.2 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(*) = %i", ptr);
218 return ptr;
219}
220
221// TODO: FIXME: add support for reading data!
222//////////////////////////////////////////////////
223// Status: For writing, this should be complete.
224//////////////////////////////////////////////////
225static snd_pcm_sframes_t roar_pcm_transfer(snd_pcm_ioplug_t *io,
226  const snd_pcm_channel_area_t *areas,
227  snd_pcm_uframes_t offset,
228  snd_pcm_uframes_t size) {
229 struct roar_alsa_pcm * self = io->private_data;
230 char * buf;
231 size_t len = size * self->info.channels * self->info.bits / 8;
232 ssize_t ret;
233
234 ROAR_DBG("roar_pcm_transfer(*) = ?");
235 ROAR_DBG("roar_pcm_transfer(*): len=%lu", (long unsigned int) len);
236
237 // Weird ALSA stuff.
238 buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8;
239
240 ret = roar_write(self, buf, len);
241
242 if ( ret == -1 )
243  return -EIO;
244
245 ROAR_DBG("roar_pcm_transfer(*) = %lli", (long long int)size);
246 return size;
247}
248
249///////////////////////////////////////////////////////////////////
250// Status: Still missing proper delay measurements from the roar server.
251// Only uses a blind timer now. In ideal conditions, this will work well.
252///////////////////////////////////////////////////////////////////
253static int roar_pcm_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp) {
254 struct roar_alsa_pcm * self = io->private_data;
255
256 ROAR_DBG("roar_pcm_delay(*) = ?");
257
258 // TODO: We need to set *delayp the latency in frames.
259 pthread_mutex_lock(&(self->lock));
260 roar_drain(self);
261 *delayp = snd_pcm_bytes_to_frames(io->pcm, self->bytes_in_buffer);
262 pthread_mutex_unlock(&(self->lock));
263
264 return 0;
265}
266
267////////////////////
268// Status: Complete
269////////////////////
270static int roar_pcm_prepare(snd_pcm_ioplug_t *io) {
271 ROAR_DBG("roar_pcm_prepare(*) = ?");
272
273 return roar_pcm_start(io);
274}
275
276///////////////////////////////////////////////////////////////////////////////////////////////////////////////
277// Status: This should be mostly complete.
278// I'm not sure if hw_params can be called several times during one stream without stop() start() in between.
279// This will mean a memory leak, and possibly breakage should the buffer size change itself.
280///////////////////////////////////////////////////////////////////////////////////////////////////////////////
281static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
282 struct roar_alsa_pcm * self = io->private_data;
283
284 ROAR_DBG("roar_pcm_hw_params(*) = ?");
285
286 self->info.channels = io->channels;
287 self->info.rate     = io->rate;
288
289 switch (io->format) {
290  case SND_PCM_FORMAT_S8:
291   self->info.codec = ROAR_CODEC_PCM_U_LE;
292   self->info.bits  = 8;
293   break;
294  case SND_PCM_FORMAT_U8:
295   self->info.codec = ROAR_CODEC_PCM_U_LE;
296   self->info.bits  = 8;
297   break;
298  case SND_PCM_FORMAT_A_LAW:
299   self->info.codec = ROAR_CODEC_ALAW;
300   self->info.bits  = 8;
301   break;
302  case SND_PCM_FORMAT_MU_LAW:
303   self->info.codec = ROAR_CODEC_MULAW;
304   self->info.bits  = 8;
305   break;
306  case SND_PCM_FORMAT_S16_LE:
307   self->info.codec = ROAR_CODEC_PCM_S_LE;
308   self->info.bits  = 16;
309   break;
310  case SND_PCM_FORMAT_S16_BE:
311   self->info.codec = ROAR_CODEC_PCM_S_BE;
312   self->info.bits  = 16;
313   break;
314  case SND_PCM_FORMAT_U16_LE:
315   self->info.codec = ROAR_CODEC_PCM_U_LE;
316   self->info.bits  = 16;
317   break;
318  case SND_PCM_FORMAT_U16_BE:
319   self->info.codec = ROAR_CODEC_PCM_U_BE;
320   self->info.bits  = 16;
321   break;
322  case SND_PCM_FORMAT_S32_LE:
323   self->info.codec = ROAR_CODEC_PCM_S_LE;
324   self->info.bits  = 32;
325   break;
326  case SND_PCM_FORMAT_S32_BE:
327   self->info.codec = ROAR_CODEC_PCM_S_BE;
328   self->info.bits  = 32;
329   break;
330  case SND_PCM_FORMAT_U32_LE:
331   self->info.codec = ROAR_CODEC_PCM_U_LE;
332   self->info.bits  = 32;
333   break;
334  case SND_PCM_FORMAT_U32_BE:
335   self->info.codec = ROAR_CODEC_PCM_U_BE;
336   self->info.bits  = 32;
337   break;
338  case SND_PCM_FORMAT_S24_3LE:
339   self->info.codec = ROAR_CODEC_PCM_S_LE;
340   self->info.bits  = 24;
341   break;
342  case SND_PCM_FORMAT_S24_3BE:
343   self->info.codec = ROAR_CODEC_PCM_S_BE;
344   self->info.bits  = 24;
345   break;
346  case SND_PCM_FORMAT_U24_3LE:
347   self->info.codec = ROAR_CODEC_PCM_U_LE;
348   self->info.bits  = 24;
349   break;
350  case SND_PCM_FORMAT_U24_3BE:
351   self->info.codec = ROAR_CODEC_PCM_U_BE;
352   self->info.bits  = 24;
353   break;
354  default:
355   return -EINVAL;
356 }
357
358 snd_pcm_uframes_t buffersize;
359 int err;
360
361 if ((err = snd_pcm_hw_params_get_buffer_size(params, &buffersize) < 0))
362  return err;
363
364 //self->bufsize = snd_pcm_frames_to_bytes(io->pcm, buffersize);
365 self->bufsize = self->info.bits * self->info.channels * buffersize / 8;
366 self->buffer = malloc(self->bufsize);
367 if (self->buffer == NULL)
368  return -1;
369 self->bufptr = 0;
370
371 ROAR_DBG("roar_pcm_hw_params(*) = 0");
372 return 0;
373}
374
375///////////////////////////////////
376// Status: This should be complete.
377// This is the last cleanup function to be called by ALSA.
378///////////////////////////////////
379static int roar_pcm_close (snd_pcm_ioplug_t * io) {
380 struct roar_alsa_pcm * self = io->private_data;
381
382 ROAR_DBG("roar_pcm_close(*) = ?");
383
384 roar_disconnect(&(self->roar.con));
385
386 pthread_mutex_destroy(&(self->lock));
387 pthread_mutex_destroy(&(self->cond_lock));
388 pthread_cond_destroy(&(self->cond));
389
390 free(self->buffer);
391 free(self);
392
393 return 0;
394}
395
396static snd_pcm_ioplug_callback_t roar_pcm_callback = {
397 .start                  = roar_pcm_start,
398 .stop                   = roar_pcm_stop,
399 .pointer                = roar_pcm_pointer,
400 .transfer               = roar_pcm_transfer,
401 .delay                  = roar_pcm_delay,
402 .prepare                = roar_pcm_prepare,
403 .hw_params              = roar_pcm_hw_params,
404 .close                  = roar_pcm_close,
405};
406
407SND_PCM_PLUGIN_DEFINE_FUNC(roar) {
408 struct roar_alsa_pcm * self;
409 snd_config_iterator_t i, next;
410 snd_config_t * n;
411 const char   * para;
412 const char   * server = NULL;
413 int            ret;
414
415 (void)root;
416
417 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = ?");
418
419 snd_config_for_each(i, next, conf) {
420  n = snd_config_iterator_entry(i);
421  if ( snd_config_get_id(n, &para) < 0 )
422   continue;
423
424  if ( !strcmp(para, "type") || !strcmp(para, "comment") )
425   continue;
426
427  if ( !strcmp(para, "server") ) {
428   if (snd_config_get_string(n, &server) < 0) {
429    return -EINVAL;
430   }
431  } else {
432   return -EINVAL;
433  }
434 }
435
436 errno = ENOSYS;
437
438 if ( (self = malloc(sizeof(struct roar_alsa_pcm))) == NULL )
439  return -errno;
440
441 memset(self, 0, sizeof(struct roar_alsa_pcm));
442
443 errno = ENOSYS;
444 if ( roar_simple_connect(&(self->roar.con), (char*)server, "ALSA Plugin") == -1 ) {
445  free(self);
446  return -errno;
447 }
448
449 self->io.version      = SND_PCM_IOPLUG_VERSION;
450 self->io.name         = "RoarAudio Plugin";
451 self->io.poll_fd      =  -1;
452 self->io.poll_events  =  POLLOUT;
453 self->io.mmap_rw      =  0;
454 self->io.callback     = &roar_pcm_callback;
455 self->io.private_data =  self;
456
457 if ( (ret = snd_pcm_ioplug_create(&(self->io), name, stream, mode)) < 0 ) {
458  roar_disconnect(&(self->roar.con));
459  free(self);
460  return ret;
461 }
462
463 pthread_mutex_init(&(self->lock), NULL);
464 pthread_mutex_init(&(self->cond_lock), NULL);
465 pthread_cond_init(&(self->cond), NULL);
466
467
468 if ( (ret = roar_hw_constraint(self)) < 0 ) {
469  snd_pcm_ioplug_delete(&(self->io));
470  roar_disconnect(&(self->roar.con));
471  free(self);
472  return ret;
473 }
474
475 *pcmp = self->io.pcm;
476
477 ROAR_DBG("SND_PCM_PLUGIN_DEFINE_FUNC(roar) = 0");
478
479 return 0;
480}
481
482SND_PCM_PLUGIN_SYMBOL(roar);
483
484int __snd_pcm_roar_open_dlsym_pcm_001 (void) {
485 ROAR_DBG("__snd_pcm_roar_open_dlsym_pcm_001(void) = 0");
486 return 0;
487}
488
489//ll
Note: See TracBrowser for help on using the repository browser.