00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "SndPcm.hxx"
00023 #include <stdarg.h>
00024
00025 #include <stdarg.h>
00026
00027 void SndPcm::cat_error(const char* fmt,...)
00028 {
00029 int len = strlen(error_str);
00030 int rem = 1024 - len;
00031 if (rem>0) {
00032 va_list ap;
00033 va_start(ap,fmt);
00034 vsnprintf(error_str+len,rem,fmt,ap);
00035 va_end(ap);
00036 }
00037 }
00038
00039
00040 SndPcm::SndPcm(int irate,int ichannels_in,int ichannels_out,int ilatency,
00041 const char* pdevice,const char* cdevice)
00042 {
00043 format = SND_PCM_FORMAT_S16;
00044 rate = irate;
00045 channels_in = ichannels_in;
00046 channels_out = ichannels_out;
00047 latency_min = ilatency;
00048 latency_max = ilatency*2;
00049 block = 0;
00050 tick_time = 0;
00051 tick_time_ok = 0;
00052 error_str[0]=0;
00053 int err;
00054
00055 phandle = 0;
00056 chandle = 0;
00057
00058 if (channels_out)
00059 {
00060 if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
00061 cat_error("SndPcm::SndPcm(...): Playback open error: %s\n", snd_strerror(err));
00062 throw SndPcmError(error_str);
00063 }
00064 }
00065
00066 if (channels_in)
00067 {
00068 if ((err = snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
00069 cat_error("SndPcm::SndPcm(...): Record open error: %s\n", snd_strerror(err));
00070 throw SndPcmError(error_str);
00071 }
00072 }
00073
00074 latency = latency_min - 4;
00075 if (setparams(phandle, chandle, &latency) < 0)
00076 throw SndPcmError(error_str);
00077 }
00078
00079 SndPcm::~SndPcm()
00080 {
00081 if (phandle)
00082 snd_pcm_hw_free(phandle);
00083
00084 if (chandle)
00085 snd_pcm_hw_free(chandle);
00086
00087 if (phandle)
00088 snd_pcm_close(phandle);
00089
00090 if (chandle)
00091 snd_pcm_close(chandle);
00092 }
00093
00094 void SndPcm::Start(void)
00095 {
00096 int err;
00097 char buffer[1024];
00098
00099 int nSilentBlockSamples = 0;
00100 int nSilentBlockFrames = 0;
00101 if (phandle)
00102 {
00103 nSilentBlockSamples = snd_pcm_bytes_to_samples(phandle,1024);
00104 nSilentBlockFrames = snd_pcm_bytes_to_frames(phandle,1024);
00105 }else{
00106 nSilentBlockSamples = snd_pcm_bytes_to_samples(chandle,1024);
00107 nSilentBlockFrames = snd_pcm_bytes_to_frames(chandle,1024);
00108 }
00109
00110 if (chandle && phandle)
00111 {
00112 if ((err = snd_pcm_link(chandle, phandle)) < 0) {
00113 cat_error("Streams link error: %s\n", snd_strerror(err));
00114 throw SndPcmError(error_str);
00115 }
00116 }
00117 if (snd_pcm_format_set_silence(format, buffer, nSilentBlockSamples) < 0) {
00118 cat_error("silence error\n");
00119 throw SndPcmError(error_str);
00120 }
00121
00122 if (phandle)
00123 {
00124 int n = latency*2;
00125 while (n>0)
00126 {
00127 int m = n;
00128 if (m>nSilentBlockFrames) m = nSilentBlockFrames;
00129 if (writebuf(phandle, buffer, m) < 0) {
00130 cat_error("write error\n");
00131 throw SndPcmError(error_str);
00132 }
00133 n -= m;
00134 }
00135 }
00136
00137 if (chandle)
00138 {
00139 if ((err = snd_pcm_start(chandle)) < 0) {
00140 cat_error("Go error: %s\n", snd_strerror(err));
00141 throw SndPcmError(error_str);
00142 }
00143 }
00144 else
00145 {
00146 if ((err = snd_pcm_start(phandle)) < 0) {
00147 cat_error("Go error: %s\n", snd_strerror(err));
00148 throw SndPcmError(error_str);
00149 }
00150 }
00151
00152 }
00153
00154 void SndPcm::Stop(void)
00155 {
00156 if (chandle)
00157 snd_pcm_drop(chandle);
00158 if (phandle)
00159 {
00160 snd_pcm_nonblock(phandle, 0);
00161 snd_pcm_drain(phandle);
00162 snd_pcm_nonblock(phandle, !block ? 1 : 0);
00163 }
00164 if (chandle)
00165 snd_pcm_unlink(chandle);
00166 }
00167
00168 void SndPcm::RecoverXRun(short* data)
00169 {
00170 if (chandle) snd_pcm_drop(chandle);
00171 if (phandle) snd_pcm_drop(phandle);
00172
00173 putchar('.');
00174
00175 latency = latency_min - 4;
00176
00177 if (setparams(phandle, chandle, &latency) < 0)
00178 throw SndPcmError(error_str);
00179
00180 if (phandle)
00181 {
00182 if (writebuf(phandle,(char*) data, latency) < 0) {
00183 cat_error("write error\n");
00184 throw SndPcmError(error_str);
00185 }
00186 if (writebuf(phandle,(char*) data, latency) < 0) {
00187 cat_error("write error\n");
00188 throw SndPcmError(error_str);
00189 }
00190 }
00191
00192
00193 if (chandle)
00194 {
00195 int err;
00196
00197 if ((err = snd_pcm_start(chandle)) < 0) {
00198 cat_error("Go error: %s\n", snd_strerror(err));
00199 throw SndPcmError(error_str);
00200 }
00201 }
00202 else
00203 {
00204 int err;
00205
00206 if ((err = snd_pcm_start(phandle)) < 0) {
00207 cat_error("Go error: %s\n", snd_strerror(err));
00208 throw SndPcmError(error_str);
00209 }
00210 }
00211 }
00212
00213 void SndPcm::Poll(void)
00214 {
00215 if (chandle)
00216 snd_pcm_wait(chandle, 1000);
00217 else
00218 snd_pcm_wait(phandle, 1000);
00219 }
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 int SndPcm::setparams_stream(snd_pcm_t *handle,
00252 snd_pcm_hw_params_t *params,
00253 int channels,
00254 const char *id)
00255 {
00256 int err;
00257
00258 err = snd_pcm_hw_params_any(handle, params);
00259 if (err < 0) {
00260 cat_error("Broken configuration for %s PCM: no configurations available: %s\n", snd_strerror(err), id);
00261 return err;
00262 }
00263 err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
00264 if (err < 0) {
00265 cat_error("Access type not available for %s: %s\n", id, snd_strerror(err));
00266 return err;
00267 }
00268 err = snd_pcm_hw_params_set_format(handle, params, format);
00269 if (err < 0) {
00270 cat_error("Sample format not available for %s: %s\n", id, snd_strerror(err));
00271 return err;
00272 }
00273 err = snd_pcm_hw_params_set_channels(handle, params, channels);
00274 if (err < 0) {
00275 cat_error("Channels count (%i) not available for %s: %s\n", channels, id, snd_strerror(err));
00276 return err;
00277 }
00278 setrate:
00279 err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
00280 if (err < 0) {
00281 cat_error("Rate %iHz not available for %s: %s\n", rate, id, snd_strerror(err));
00282 return err;
00283 }
00284 if (err != rate) {
00285 if (abs(err-rate)<3) { rate = err; goto setrate; }
00286 cat_error("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
00287 return -EINVAL;
00288 }
00289 return 0;
00290 }
00291
00292 int SndPcm::setparams_bufsize(snd_pcm_t *handle,
00293 snd_pcm_hw_params_t *params,
00294 snd_pcm_hw_params_t *tparams,
00295 snd_pcm_uframes_t bufsize,
00296 const char *id)
00297 {
00298 int err;
00299 snd_pcm_uframes_t periodsize;
00300
00301 snd_pcm_hw_params_copy(params, tparams);
00302 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, bufsize * 2);
00303 if (err < 0) {
00304 cat_error("Unable to set buffer size %li for %s: %s\n", bufsize * 2, id, snd_strerror(err));
00305 return err;
00306 }
00307 periodsize = snd_pcm_hw_params_get_buffer_size(params) / 2;
00308 err = snd_pcm_hw_params_set_period_size_near(handle, params, periodsize, 0);
00309 if (err < 0) {
00310 cat_error("Unable to set period size %li for %s: %s\n", periodsize, id, snd_strerror(err));
00311 return err;
00312 }
00313 return 0;
00314 }
00315
00316 int SndPcm::setparams_set(snd_pcm_t *handle,
00317 snd_pcm_hw_params_t *params,
00318 snd_pcm_sw_params_t *swparams,
00319 const char *id)
00320 {
00321 int err, val, sleep_min = 0;
00322
00323 err = snd_pcm_hw_params(handle, params);
00324 if (err < 0) {
00325 cat_error("Unable to set hw params for %s: %s\n", id, snd_strerror(err));
00326 return err;
00327 }
00328 err = snd_pcm_sw_params_current(handle, swparams);
00329 if (err < 0) {
00330 cat_error("Unable to determine current swparams for %s: %s\n", id, snd_strerror(err));
00331 return err;
00332 }
00333 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
00334 if (err < 0) {
00335 cat_error("Unable to set start threshold mode for %s: %s\n", id, snd_strerror(err));
00336 return err;
00337 }
00338 tick_time_ok = 0;
00339 if (tick_time > 0) {
00340 int time, ttime;
00341 time = snd_pcm_hw_params_get_period_time(params, NULL);
00342 ttime = snd_pcm_hw_params_get_tick_time(params, NULL);
00343 if (time < ttime) {
00344 cat_error("Skipping to set minimal sleep: period time < tick time\n");
00345 } else if (ttime <= 0) {
00346 cat_error("Skipping to set minimal sleep: tick time <= 0 (%i)\n", ttime);
00347 } else {
00348 sleep_min = tick_time / ttime;
00349 if (sleep_min <= 0)
00350 sleep_min = 1;
00351 err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min);
00352 if (err < 0) {
00353 cat_error("Unable to set minimal sleep %i for %s: %s\n", sleep_min, id, snd_strerror(err));
00354 return err;
00355 }
00356 tick_time_ok = sleep_min * ttime;
00357 }
00358 }
00359 val = !block ? 4 : snd_pcm_hw_params_get_period_size(params, NULL);
00360 if (tick_time_ok > 0)
00361 val = 16;
00362 val = snd_pcm_hw_params_get_period_size(params, NULL);
00363 err = snd_pcm_sw_params_set_avail_min(handle, swparams, val);
00364 if (err < 0) {
00365 cat_error("Unable to set avail min for %s: %s\n", id, snd_strerror(err));
00366 return err;
00367 }
00368 val = !block ? 4 : 1;
00369 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, val);
00370 if (err < 0) {
00371 cat_error("Unable to set transfer align for %s: %s\n", id, snd_strerror(err));
00372 return err;
00373 }
00374 err = snd_pcm_sw_params(handle, swparams);
00375 if (err < 0) {
00376 cat_error("Unable to set sw params for %s: %s\n", id, snd_strerror(err));
00377 return err;
00378 }
00379 return 0;
00380 }
00381
00382 int SndPcm::setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize)
00383 {
00384 int err, last_bufsize = *bufsize;
00385 snd_pcm_hw_params_t *pt_params = 0, *ct_params = 0;
00386 snd_pcm_hw_params_t *p_params = 0, *c_params = 0;
00387 snd_pcm_sw_params_t *p_swparams = 0, *c_swparams = 0;
00388 snd_pcm_sframes_t size;
00389
00390 if (phandle)
00391 {
00392 snd_pcm_hw_params_alloca(&p_params);
00393 snd_pcm_hw_params_alloca(&pt_params);
00394 snd_pcm_sw_params_alloca(&p_swparams);
00395 }
00396
00397 if (chandle)
00398 {
00399 snd_pcm_hw_params_alloca(&c_params);
00400 snd_pcm_hw_params_alloca(&ct_params);
00401 snd_pcm_sw_params_alloca(&c_swparams);
00402 }
00403
00404 if (phandle && (err = setparams_stream(phandle, pt_params, channels_out, "playback")) < 0) {
00405 cat_error("Unable to set parameters for playback stream: %s\n", snd_strerror(err));
00406 return -1;;
00407 }
00408 if (chandle && (err = setparams_stream(chandle, ct_params, channels_in, "capture")) < 0) {
00409 cat_error("Unable to set parameters for playback stream: %s\n", snd_strerror(err));
00410 return -1;;
00411 }
00412 __again:
00413 if (last_bufsize == *bufsize)
00414 *bufsize += 4;
00415 last_bufsize = *bufsize;
00416 if (*bufsize > latency_max)
00417 return -1;
00418
00419 if (phandle && (err = setparams_bufsize(phandle, p_params, pt_params, *bufsize, "playback")) < 0) {
00420 cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
00421 return -1;;
00422 }
00423
00424 if (chandle && (err = setparams_bufsize(chandle, c_params, ct_params, *bufsize, "capture")) < 0) {
00425 cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
00426 return -1;;
00427 }
00428
00429 if (p_params)
00430 {
00431 size = snd_pcm_hw_params_get_period_size(p_params, NULL);
00432 if (size > *bufsize)
00433 *bufsize = size;
00434 }
00435
00436 if (c_params)
00437 {
00438 size = snd_pcm_hw_params_get_period_size(c_params, NULL);
00439 if (size > *bufsize)
00440 *bufsize = size;
00441 }
00442
00443 if (c_params && p_params)
00444 if (snd_pcm_hw_params_get_period_time(p_params, NULL) !=
00445 snd_pcm_hw_params_get_period_time(c_params, NULL))
00446 goto __again;
00447 if (p_params && snd_pcm_hw_params_get_period_size(p_params, NULL) * 2 < snd_pcm_hw_params_get_buffer_size(p_params))
00448 goto __again;
00449 if (c_params && snd_pcm_hw_params_get_period_size(c_params, NULL) * 2 < snd_pcm_hw_params_get_buffer_size(c_params))
00450 goto __again;
00451
00452 if (phandle && (err = setparams_set(phandle, p_params, p_swparams, "playback")) < 0) {
00453 cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
00454 return -1;;
00455 }
00456 if (chandle && (err = setparams_set(chandle, c_params, c_swparams, "capture")) < 0) {
00457 cat_error("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err));
00458 return -1;;
00459 }
00460
00461 if (phandle)
00462 {
00463 if ((err = snd_pcm_prepare(phandle)) < 0) {
00464 cat_error("Prepare error: %s\n", snd_strerror(err));
00465 return -1;;
00466 }
00467 }else if (chandle) {
00468 if ((err = snd_pcm_prepare(chandle)) < 0) {
00469 cat_error("Prepare error: %s\n", snd_strerror(err));
00470 return -1;;
00471 }
00472 }
00473
00474 fflush(stdout);
00475 return 0;
00476 }
00477
00478 long SndPcm::readbuf(snd_pcm_t *handle, char *buf, long len)
00479 {
00480 long r;
00481
00482 if (!block) {
00483 do {
00484 r = snd_pcm_readi(handle, buf, len);
00485 } while (r == -EAGAIN);
00486 if (r > 0) {
00487
00488
00489
00490 }
00491
00492 } else {
00493 int frame_bytes = (snd_pcm_format_width(format) / 8) * channels_in;
00494 do {
00495 r = snd_pcm_readi(handle, buf, len);
00496 if (r > 0) {
00497 buf += r * frame_bytes;
00498 len -= r;
00499
00500
00501
00502 }
00503
00504 } while (r >= 1 && len > 0);
00505 }
00506
00507 return r;
00508 }
00509
00510 long SndPcm::writebuf(snd_pcm_t *handle, char *buf, long len)
00511 {
00512 long r;
00513
00514 while (len > 0) {
00515 r = snd_pcm_writei(handle, buf, len);
00516 if (r == -EAGAIN)
00517 {
00518 continue;
00519 }
00520
00521 if (r < 0)
00522 return r;
00523
00524 buf += r * 4;
00525 len -= r;
00526
00527 }
00528 return 0;
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552 #ifdef TESTSNDPCM
00553
00554 #include <sched.h>
00555
00556 void setscheduler(void)
00557 {
00558 struct sched_param sched_param;
00559
00560 if (sched_getparam(0, &sched_param) < 0) {
00561 printf("Scheduler getparam failed...\n");
00562 return;
00563 }
00564 sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
00565 if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
00566 printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority);
00567 fflush(stdout);
00568 return;
00569 }
00570 printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority);
00571 }
00572
00573 main()
00574 {
00575 short buf[1024];
00576 try
00577 {
00578 SndPcm sndpcm(44099,2,"plughw:0,0","plughw:0,0");
00579
00580 setscheduler();
00581
00582 sndpcm.Start();
00583
00584 for (int i=0;i<1000;i++)
00585 {
00586 sndpcm.Poll();
00587 sndpcm.ReadBuf(buf);
00588 sndpcm.WriteBuf(buf);
00589 }
00590 }
00591 catch (SndPcmError e)
00592 {
00593 printf(e.str);
00594 }
00595 }
00596
00597 #endif
00598