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