CLAM-Development  1.4.0
SndPcm.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "SndPcm.hxx"
23 #include <stdarg.h>
24 
25 #include <stdarg.h>
26 
27 void SndPcm::cat_error(const char* fmt,...)
28 {
29  int len = strlen(error_str);
30  int rem = 1024 - len;
31  if (rem>0) {
32  va_list ap;
33  va_start(ap,fmt);
34  vsnprintf(error_str+len,rem,fmt,ap);
35  va_end(ap);
36  }
37 }
38 
39 
40 SndPcm::SndPcm(int irate,int ichannels_in,int ichannels_out,int ilatency,
41  const char* pdevice,const char* cdevice)
42 {
43  format = SND_PCM_FORMAT_S16;
44  rate = irate;
45  channels_in = ichannels_in;
46  channels_out = ichannels_out;
47  latency_min = ilatency;
48  latency_max = ilatency*2;
49  block = 0;
50  tick_time = 0;
51  tick_time_ok = 0;
52  error_str[0]=0;
53  int err;
54 
55  phandle = 0;
56  chandle = 0;
57 
58  if (channels_out)
59  {
60  if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
61  cat_error("SndPcm::SndPcm(...): Playback open error: %s\n", snd_strerror(err));
62  throw SndPcmError(error_str);
63  }
64  }
65 
66  if (channels_in)
67  {
68  if ((err = snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
69  cat_error("SndPcm::SndPcm(...): Record open error: %s\n", snd_strerror(err));
70  throw SndPcmError(error_str);
71  }
72  }
73 
74  latency = latency_min - 4;
75  if (setparams(phandle, chandle, &latency) < 0)
76  throw SndPcmError(error_str);
77 }
78 
80 {
81  if (phandle)
82  snd_pcm_hw_free(phandle);
83 
84  if (chandle)
85  snd_pcm_hw_free(chandle);
86 
87  if (phandle)
88  snd_pcm_close(phandle);
89 
90  if (chandle)
91  snd_pcm_close(chandle);
92 }
93 
94 void SndPcm::Start(void)
95 {
96  int err;
97  char buffer[1024];
98 
99  int nSilentBlockSamples = 0;
100  int nSilentBlockFrames = 0;
101  if (phandle)
102  {
103  nSilentBlockSamples = snd_pcm_bytes_to_samples(phandle,1024);
104  nSilentBlockFrames = snd_pcm_bytes_to_frames(phandle,1024);
105  }else{
106  nSilentBlockSamples = snd_pcm_bytes_to_samples(chandle,1024);
107  nSilentBlockFrames = snd_pcm_bytes_to_frames(chandle,1024);
108  }
109 
110  if (chandle && phandle)
111  {
112  if ((err = snd_pcm_link(chandle, phandle)) < 0) {
113  cat_error("Streams link error: %s\n", snd_strerror(err));
114  throw SndPcmError(error_str);
115  }
116  }
117  if (snd_pcm_format_set_silence(format, buffer, nSilentBlockSamples) < 0) {
118  cat_error("silence error\n");
119  throw SndPcmError(error_str);
120  }
121 
122  if (phandle)
123  {
124  int n = latency*2; // write two silent buffers
125  while (n>0)
126  {
127  int m = n;
128  if (m>nSilentBlockFrames) m = nSilentBlockFrames;
129  if (writebuf(phandle, buffer, m) < 0) {
130  cat_error("write error\n");
131  throw SndPcmError(error_str);
132  }
133  n -= m;
134  }
135  }
136 
137  if (chandle)
138  {
139  if ((err = snd_pcm_start(chandle)) < 0) {
140  cat_error("Go error: %s\n", snd_strerror(err));
141  throw SndPcmError(error_str);
142  }
143  }
144  else
145  {
146  if ((err = snd_pcm_start(phandle)) < 0) {
147  cat_error("Go error: %s\n", snd_strerror(err));
148  throw SndPcmError(error_str);
149  }
150  }
151 
152 }
153 
154 void SndPcm::Stop(void)
155 {
156  if (chandle)
157  snd_pcm_drop(chandle);
158  if (phandle)
159  {
160  snd_pcm_nonblock(phandle, 0);
161  snd_pcm_drain(phandle);
162  snd_pcm_nonblock(phandle, !block ? 1 : 0);
163  }
164  if (chandle)
165  snd_pcm_unlink(chandle);
166 }
167 
168 void SndPcm::RecoverXRun(short* data)
169 {
170  if (chandle) snd_pcm_drop(chandle);
171  if (phandle) snd_pcm_drop(phandle);
172 
173  putchar('.');
174 
175  latency = latency_min - 4;
176 
177  if (setparams(phandle, chandle, &latency) < 0)
178  throw SndPcmError(error_str);
179 
180  if (phandle)
181  {
182  if (writebuf(phandle,(char*) data, latency) < 0) {
183  cat_error("write error\n");
184  throw SndPcmError(error_str);
185  }
186  if (writebuf(phandle,(char*) data, latency) < 0) {
187  cat_error("write error\n");
188  throw SndPcmError(error_str);
189  }
190  }
191 
192 
193  if (chandle)
194  {
195  int err;
196 
197  if ((err = snd_pcm_start(chandle)) < 0) {
198  cat_error("Go error: %s\n", snd_strerror(err));
199  throw SndPcmError(error_str);
200  }
201  }
202  else
203  {
204  int err;
205 
206  if ((err = snd_pcm_start(phandle)) < 0) {
207  cat_error("Go error: %s\n", snd_strerror(err));
208  throw SndPcmError(error_str);
209  }
210  }
211 }
212 
213 void SndPcm::Poll(void)
214 {
215  if (chandle)
216  snd_pcm_wait(chandle, 1000);
217  else
218  snd_pcm_wait(phandle, 1000);
219 }
220 /*
221  * The functions which follow are taken from the latency test included
222  * in the ALSA source distribution, with the following copyright note:
223  *
224  * Latency test program
225  *
226  * Author: Jaroslav Kysela <perex@suse.cz>
227  *
228  * Author of bandpass filter sweep effect:
229  * Maarten de Boer <mdeboer@iua.upf.es>
230  *
231  * This small demo program can be used for measuring latency between
232  * capture and playback. This latency is measured from driver (diff when
233  * playback and capture was started). Scheduler is set to SCHED_RR.
234  *
235  *
236  * This program is free software; you can redistribute it and/or modify
237  * it under the terms of the GNU General Public License as published by
238  * the Free Software Foundation; either version 2 of the License, or
239  * (at your option) any later version.
240  *
241  * This program is distributed in the hope that it will be useful,
242  * but WITHOUT ANY WARRANTY; without even the implied warranty of
243  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
244  * GNU General Public License for more details.
245  *
246  * You should have received a copy of the GNU General Public License
247  * along with this program; if not, write to the Free Software
248  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
249  *
250  */
251 int SndPcm::setparams_stream(snd_pcm_t *handle,
252  snd_pcm_hw_params_t *params,
253  int channels,
254  const char *id)
255 {
256  int err;
257 
258  err = snd_pcm_hw_params_any(handle, params);
259  if (err < 0) {
260  cat_error("Broken configuration for %s PCM: no configurations available: %s\n", snd_strerror(err), id);
261  return err;
262  }
263  err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
264  if (err < 0) {
265  cat_error("Access type not available for %s: %s\n", id, snd_strerror(err));
266  return err;
267  }
268  err = snd_pcm_hw_params_set_format(handle, params, format);
269  if (err < 0) {
270  cat_error("Sample format not available for %s: %s\n", id, snd_strerror(err));
271  return err;
272  }
273  err = snd_pcm_hw_params_set_channels(handle, params, channels);
274  if (err < 0) {
275  cat_error("Channels count (%i) not available for %s: %s\n", channels, id, snd_strerror(err));
276  return err;
277  }
278 setrate:
279  err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
280  if (err < 0) {
281  cat_error("Rate %iHz not available for %s: %s\n", rate, id, snd_strerror(err));
282  return err;
283  }
284  if (err != rate) {
285  if (abs(err-rate)<3) { rate = err; goto setrate; }
286  cat_error("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
287  return -EINVAL;
288  }
289  return 0;
290 }
291 
292 int SndPcm::setparams_bufsize(snd_pcm_t *handle,
293  snd_pcm_hw_params_t *params,
294  snd_pcm_hw_params_t *tparams,
295  snd_pcm_uframes_t bufsize,
296  const char *id)
297 {
298  int err;
299  snd_pcm_uframes_t periodsize;
300 
301  snd_pcm_hw_params_copy(params, tparams);
302  err = snd_pcm_hw_params_set_buffer_size_near(handle, params, bufsize * 2);
303  if (err < 0) {
304  cat_error("Unable to set buffer size %li for %s: %s\n", bufsize * 2, id, snd_strerror(err));
305  return err;
306  }
307  periodsize = snd_pcm_hw_params_get_buffer_size(params) / 2;
308  err = snd_pcm_hw_params_set_period_size_near(handle, params, periodsize, 0);
309  if (err < 0) {
310  cat_error("Unable to set period size %li for %s: %s\n", periodsize, id, snd_strerror(err));
311  return err;
312  }
313  return 0;
314 }
315 
316 int SndPcm::setparams_set(snd_pcm_t *handle,
317  snd_pcm_hw_params_t *params,
318  snd_pcm_sw_params_t *swparams,
319  const char *id)
320 {
321  int err, val, sleep_min = 0;
322 
323  err = snd_pcm_hw_params(handle, params);
324  if (err < 0) {
325  cat_error("Unable to set hw params for %s: %s\n", id, snd_strerror(err));
326  return err;
327  }
328  err = snd_pcm_sw_params_current(handle, swparams);
329  if (err < 0) {
330  cat_error("Unable to determine current swparams for %s: %s\n", id, snd_strerror(err));
331  return err;
332  }
333  err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
334  if (err < 0) {
335  cat_error("Unable to set start threshold mode for %s: %s\n", id, snd_strerror(err));
336  return err;
337  }
338  tick_time_ok = 0;
339  if (tick_time > 0) {
340  int time, ttime;
341  time = snd_pcm_hw_params_get_period_time(params, NULL);
342  ttime = snd_pcm_hw_params_get_tick_time(params, NULL);
343  if (time < ttime) {
344  cat_error("Skipping to set minimal sleep: period time < tick time\n");
345  } else if (ttime <= 0) {
346  cat_error("Skipping to set minimal sleep: tick time <= 0 (%i)\n", ttime);
347  } else {
348  sleep_min = tick_time / ttime;
349  if (sleep_min <= 0)
350  sleep_min = 1;
351  err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min);
352  if (err < 0) {
353  cat_error("Unable to set minimal sleep %i for %s: %s\n", sleep_min, id, snd_strerror(err));
354  return err;
355  }
356  tick_time_ok = sleep_min * ttime;
357  }
358  }
359  val = !block ? 4 : snd_pcm_hw_params_get_period_size(params, NULL);
360  if (tick_time_ok > 0)
361  val = 16;
362  val = snd_pcm_hw_params_get_period_size(params, NULL);
363  err = snd_pcm_sw_params_set_avail_min(handle, swparams, val);
364  if (err < 0) {
365  cat_error("Unable to set avail min for %s: %s\n", id, snd_strerror(err));
366  return err;
367  }
368  val = !block ? 4 : 1;
369  err = snd_pcm_sw_params_set_xfer_align(handle, swparams, val);
370  if (err < 0) {
371  cat_error("Unable to set transfer align for %s: %s\n", id, snd_strerror(err));
372  return err;
373  }
374  err = snd_pcm_sw_params(handle, swparams);
375  if (err < 0) {
376  cat_error("Unable to set sw params for %s: %s\n", id, snd_strerror(err));
377  return err;
378  }
379  return 0;
380 }
381 
382 int SndPcm::setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize)
383 {
384  int err, last_bufsize = *bufsize;
385  snd_pcm_hw_params_t *pt_params = 0, *ct_params = 0; /* templates with rate, format and channels */
386  snd_pcm_hw_params_t *p_params = 0, *c_params = 0;
387  snd_pcm_sw_params_t *p_swparams = 0, *c_swparams = 0;
388  snd_pcm_sframes_t size;
389 
390  if (phandle)
391  {
392  snd_pcm_hw_params_alloca(&p_params);
393  snd_pcm_hw_params_alloca(&pt_params);
394  snd_pcm_sw_params_alloca(&p_swparams);
395  }
396 
397  if (chandle)
398  {
399  snd_pcm_hw_params_alloca(&c_params);
400  snd_pcm_hw_params_alloca(&ct_params);
401  snd_pcm_sw_params_alloca(&c_swparams);
402  }
403 
404  if (phandle && (err = setparams_stream(phandle, pt_params, channels_out, "playback")) < 0) {
405  cat_error("Unable to set parameters for playback stream: %s\n", snd_strerror(err));
406  return -1;;
407  }
408  if (chandle && (err = setparams_stream(chandle, ct_params, channels_in, "capture")) < 0) {
409  cat_error("Unable to set parameters for playback stream: %s\n", snd_strerror(err));
410  return -1;;
411  }
412  __again:
413  if (last_bufsize == *bufsize)
414  *bufsize += 4;
415  last_bufsize = *bufsize;
416  if (*bufsize > latency_max)
417  return -1;
418 
419  if (phandle && (err = setparams_bufsize(phandle, p_params, pt_params, *bufsize, "playback")) < 0) {
420  cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
421  return -1;;
422  }
423 
424  if (chandle && (err = setparams_bufsize(chandle, c_params, ct_params, *bufsize, "capture")) < 0) {
425  cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
426  return -1;;
427  }
428 
429  if (p_params)
430  {
431  size = snd_pcm_hw_params_get_period_size(p_params, NULL);
432  if (size > *bufsize)
433  *bufsize = size;
434  }
435 
436  if (c_params)
437  {
438  size = snd_pcm_hw_params_get_period_size(c_params, NULL);
439  if (size > *bufsize)
440  *bufsize = size;
441  }
442 
443  if (c_params && p_params)
444  if (snd_pcm_hw_params_get_period_time(p_params, NULL) !=
445  snd_pcm_hw_params_get_period_time(c_params, NULL))
446  goto __again;
447  if (p_params && snd_pcm_hw_params_get_period_size(p_params, NULL) * 2 < snd_pcm_hw_params_get_buffer_size(p_params))
448  goto __again;
449  if (c_params && snd_pcm_hw_params_get_period_size(c_params, NULL) * 2 < snd_pcm_hw_params_get_buffer_size(c_params))
450  goto __again;
451 
452  if (phandle && (err = setparams_set(phandle, p_params, p_swparams, "playback")) < 0) {
453  cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
454  return -1;;
455  }
456  if (chandle && (err = setparams_set(chandle, c_params, c_swparams, "capture")) < 0) {
457  cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
458  return -1;;
459  }
460 
461  if (phandle)
462  {
463  if ((err = snd_pcm_prepare(phandle)) < 0) {
464  cat_error("Prepare error: %s\n", snd_strerror(err));
465  return -1;;
466  }
467  }else if (chandle) {
468  if ((err = snd_pcm_prepare(chandle)) < 0) {
469  cat_error("Prepare error: %s\n", snd_strerror(err));
470  return -1;;
471  }
472  }
473 
474  fflush(stdout);
475  return 0;
476 }
477 
478 long SndPcm::readbuf(snd_pcm_t *handle, char *buf, long len)
479 {
480  long r;
481 
482  if (!block) {
483  do {
484  r = snd_pcm_readi(handle, buf, len);
485  } while (r == -EAGAIN);
486  if (r > 0) {
487 
488 
489 
490  }
491  // cat_error("read = %li\n", r);
492  } else {
493  int frame_bytes = (snd_pcm_format_width(format) / 8) * channels_in;
494  do {
495  r = snd_pcm_readi(handle, buf, len);
496  if (r > 0) {
497  buf += r * frame_bytes;
498  len -= r;
499 
500 
501 
502  }
503  // cat_error("r = %li, len = %li\n", r, len);
504  } while (r >= 1 && len > 0);
505  }
506  // showstat(handle, 0);
507  return r;
508 }
509 
510 long SndPcm::writebuf(snd_pcm_t *handle, char *buf, long len)
511 {
512  long r;
513 
514  while (len > 0) {
515  r = snd_pcm_writei(handle, buf, len);
516  if (r == -EAGAIN)
517  {
518  continue;
519  }
520  // cat_error("write = %li\n", r);
521  if (r < 0)
522  return r;
523  // showstat(handle, 0);
524  buf += r * 4;
525  len -= r;
526 
527  }
528  return 0;
529 }
530 
531 /*
532  * Copyright (c) 2001-2002 MUSIC TECHNOLOGY GROUP (MTG)
533  * UNIVERSITAT POMPEU FABRA
534  *
535  *
536  * This program is free software; you can redistribute it and/or modify
537  * it under the terms of the GNU General Public License as published by
538  * the Free Software Foundation; either version 2 of the License, or
539  * (at your option) any later version.
540  *
541  * This program is distributed in the hope that it will be useful,
542  * but WITHOUT ANY WARRANTY; without even the implied warranty of
543  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
544  * GNU General Public License for more details.
545  *
546  * You should have received a copy of the GNU General Public License
547  * along with this program; if not, write to the Free Software
548  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
549  *
550  */
551 
552 #ifdef TESTSNDPCM
553 
554 #include <sched.h>
555 
556 void setscheduler(void)
557 {
558  struct sched_param sched_param;
559 
560  if (sched_getparam(0, &sched_param) < 0) {
561  printf("Scheduler getparam failed...\n");
562  return;
563  }
564  sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
565  if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
566  printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority);
567  fflush(stdout);
568  return;
569  }
570  printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority);
571 }
572 
573 main()
574 {
575  short buf[1024];
576  try
577  {
578  SndPcm sndpcm(44099,2,"plughw:0,0","plughw:0,0");
579 
580  setscheduler();
581 
582  sndpcm.Start();
583 
584  for (int i=0;i<1000;i++)
585  {
586  sndpcm.Poll();
587  sndpcm.ReadBuf(buf);
588  sndpcm.WriteBuf(buf);
589  }
590  }
591  catch (SndPcmError e)
592  {
593  printf(e.str);
594  }
595 }
596 
597 #endif
598