CLAM-Development
1.1
|
00001 /* 00002 * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG) 00003 * UNIVERSITAT POMPEU FABRA 00004 * 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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; // write two silent buffers 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 * The functions which follow are taken from the latency test included 00222 * in the ALSA source distribution, with the following copyright note: 00223 * 00224 * Latency test program 00225 * 00226 * Author: Jaroslav Kysela <perex@suse.cz> 00227 * 00228 * Author of bandpass filter sweep effect: 00229 * Maarten de Boer <mdeboer@iua.upf.es> 00230 * 00231 * This small demo program can be used for measuring latency between 00232 * capture and playback. This latency is measured from driver (diff when 00233 * playback and capture was started). Scheduler is set to SCHED_RR. 00234 * 00235 * 00236 * This program is free software; you can redistribute it and/or modify 00237 * it under the terms of the GNU General Public License as published by 00238 * the Free Software Foundation; either version 2 of the License, or 00239 * (at your option) any later version. 00240 * 00241 * This program is distributed in the hope that it will be useful, 00242 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00243 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00244 * GNU General Public License for more details. 00245 * 00246 * You should have received a copy of the GNU General Public License 00247 * along with this program; if not, write to the Free Software 00248 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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; /* templates with rate, format and channels */ 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 // cat_error("read = %li\n", r); 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 // cat_error("r = %li, len = %li\n", r, len); 00504 } while (r >= 1 && len > 0); 00505 } 00506 // showstat(handle, 0); 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 // cat_error("write = %li\n", r); 00521 if (r < 0) 00522 return r; 00523 // showstat(handle, 0); 00524 buf += r * 4; 00525 len -= r; 00526 00527 } 00528 return 0; 00529 } 00530 00531 /* 00532 * Copyright (c) 2001-2002 MUSIC TECHNOLOGY GROUP (MTG) 00533 * UNIVERSITAT POMPEU FABRA 00534 * 00535 * 00536 * This program is free software; you can redistribute it and/or modify 00537 * it under the terms of the GNU General Public License as published by 00538 * the Free Software Foundation; either version 2 of the License, or 00539 * (at your option) any later version. 00540 * 00541 * This program is distributed in the hope that it will be useful, 00542 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00543 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00544 * GNU General Public License for more details. 00545 * 00546 * You should have received a copy of the GNU General Public License 00547 * along with this program; if not, write to the Free Software 00548 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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