SndPcm.cxx

Go to the documentation of this file.
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 
Generated by  doxygen 1.6.3