Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

audioalsa.C

Go to the documentation of this file.
00001 #include "audiodevice.h"
00002 #include "audioalsa.h"
00003 #include "bcsignals.h"
00004 #include "playbackconfig.h"
00005 #include "preferences.h"
00006 #include "recordconfig.h"
00007 
00008 #include <errno.h>
00009 
00010 #ifdef HAVE_ALSA
00011 
00012 AudioALSA::AudioALSA(AudioDevice *device)
00013  : AudioLowLevel(device)
00014 {
00015         samples_written = 0;
00016         timer = new Timer;
00017         delay = 0;
00018         timer_lock = new Mutex("AudioALSA::timer_lock");
00019         interrupted = 0;
00020 }
00021 
00022 AudioALSA::~AudioALSA()
00023 {
00024         delete timer_lock;
00025         delete timer;
00026 }
00027 
00028 void AudioALSA::list_devices(ArrayList<char*> *devices, int pcm_title)
00029 {
00030         snd_ctl_t *handle;
00031         int card, err, dev, idx;
00032         snd_ctl_card_info_t *info;
00033         snd_pcm_info_t *pcminfo;
00034         char string[BCTEXTLEN];
00035         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
00036         int error;
00037 
00038         snd_ctl_card_info_alloca(&info);
00039         snd_pcm_info_alloca(&pcminfo);
00040 
00041         card = -1;
00042 #define DEFAULT_DEVICE "default"
00043         char *result = new char[strlen(DEFAULT_DEVICE) + 1];
00044         devices->append(result);
00045         strcpy(result, DEFAULT_DEVICE);
00046 
00047         while(snd_card_next(&card) >= 0)
00048         {
00049                 char name[BCTEXTLEN];
00050                 if(card < 0) break;
00051                 sprintf(name, "hw:%i", card);
00052 
00053                 if((err = snd_ctl_open(&handle, name, 0)) < 0)
00054                 {
00055                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
00056                         continue;
00057                 }
00058 
00059                 if((err = snd_ctl_card_info(handle, info)) < 0)
00060                 {
00061                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
00062                         snd_ctl_close(handle);
00063                         continue;
00064                 }
00065 
00066                 dev = -1;
00067 
00068                 while(1)
00069                 {
00070                         unsigned int count;
00071                         if(snd_ctl_pcm_next_device(handle, &dev) < 0)
00072                                 printf("AudioALSA::list_devices: snd_ctl_pcm_next_device\n");
00073 
00074                         if (dev < 0)
00075                                 break;
00076 
00077                         snd_pcm_info_set_device(pcminfo, dev);
00078                         snd_pcm_info_set_subdevice(pcminfo, 0);
00079                         snd_pcm_info_set_stream(pcminfo, stream);
00080 
00081                         if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) 
00082                         {
00083                                 if(err != -ENOENT)
00084                                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
00085                                 continue;
00086                         }
00087 
00088                         if(pcm_title)
00089                         {
00090                                 sprintf(string, "plughw:%d,%d", card, dev);
00091 //                              strcpy(string, "cards.pcm.front");
00092                         }
00093                         else
00094                         {
00095                                 sprintf(string, "%s #%d", 
00096                                         snd_ctl_card_info_get_name(info), 
00097                                         dev);
00098                         }
00099 
00100                         char *result = devices->append(new char[strlen(string) + 1]);
00101                         strcpy(result, string);
00102                 }
00103 
00104                 snd_ctl_close(handle);
00105         }
00106 
00107 //      snd_ctl_card_info_free(info);
00108 //      snd_pcm_info_free(pcminfo);
00109 }
00110 
00111 void AudioALSA::translate_name(char *output, char *input)
00112 {
00113         ArrayList<char*> titles;
00114         ArrayList<char*> pcm_titles;
00115         
00116         list_devices(&titles, 0);
00117         list_devices(&pcm_titles, 1);
00118 
00119         sprintf(output, "default");     
00120         for(int i = 0; i < titles.total; i++)
00121         {
00122 //printf("AudioALSA::translate_name %s %s\n", titles.values[i], pcm_titles.values[i]);
00123                 if(!strcasecmp(titles.values[i], input))
00124                 {
00125                         strcpy(output, pcm_titles.values[i]);
00126                         break;
00127                 }
00128         }
00129         
00130         titles.remove_all_objects();
00131         pcm_titles.remove_all_objects();
00132 }
00133 
00134 snd_pcm_format_t AudioALSA::translate_format(int format)
00135 {
00136         switch(format)
00137         {
00138                 case 8:
00139                         return SND_PCM_FORMAT_S8;
00140                         break;
00141                 case 16:
00142                         return SND_PCM_FORMAT_S16_LE;
00143                         break;
00144                 case 24:
00145                         return SND_PCM_FORMAT_S24_LE;
00146                         break;
00147                 case 32:
00148                         return SND_PCM_FORMAT_S32_LE;
00149                         break;
00150         }
00151 }
00152 
00153 void AudioALSA::set_params(snd_pcm_t *dsp, 
00154         int channels, 
00155         int bits,
00156         int samplerate,
00157         int samples)
00158 {
00159         snd_pcm_hw_params_t *params;
00160         snd_pcm_sw_params_t *swparams;
00161         int err;
00162 
00163         snd_pcm_hw_params_alloca(&params);
00164         snd_pcm_sw_params_alloca(&swparams);
00165         err = snd_pcm_hw_params_any(dsp, params);
00166 
00167         if (err < 0) 
00168         {
00169                 printf("AudioALSA::set_params: no PCM configurations available\n");
00170                 return;
00171         }
00172 
00173         snd_pcm_hw_params_set_access(dsp, 
00174                 params,
00175                 SND_PCM_ACCESS_RW_INTERLEAVED);
00176         snd_pcm_hw_params_set_format(dsp, 
00177                 params, 
00178                 translate_format(bits));
00179         snd_pcm_hw_params_set_channels(dsp, 
00180                 params, 
00181                 channels);
00182         snd_pcm_hw_params_set_rate_near(dsp, 
00183                 params, 
00184                 (unsigned int*)&samplerate, 
00185                 (int*)0);
00186 
00187 // Buffers written must be equal to period_time
00188         int buffer_time;
00189         int period_time;
00190         if(device->r)
00191         {
00192                 buffer_time = 10000000;
00193                 period_time = (int)((int64_t)samples * 1000000 / samplerate);
00194         }
00195         else
00196         {
00197                 buffer_time = (int)((int64_t)samples * 1000000 * 2 / samplerate + 0.5);
00198                 period_time = samples * samplerate / 1000000;
00199         }
00200 
00201 
00202 //printf("AudioALSA::set_params 1 %d %d %d\n", samples, buffer_time, period_time);
00203         snd_pcm_hw_params_set_buffer_time_near(dsp, 
00204                 params,
00205                 (unsigned int*)&buffer_time, 
00206                 (int*)0);
00207         snd_pcm_hw_params_set_period_time_near(dsp, 
00208                 params,
00209                 (unsigned int*)&period_time, 
00210                 (int*)0);
00211 //printf("AudioALSA::set_params 5 %d %d\n", buffer_time, period_time);
00212         err = snd_pcm_hw_params(dsp, params);
00213         if(err < 0)
00214         {
00215                 printf("AudioALSA::set_params: hw_params failed\n");
00216                 return;
00217         }
00218 
00219         snd_pcm_uframes_t chunk_size = 1024;
00220         snd_pcm_uframes_t buffer_size = 262144;
00221         snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
00222         snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
00223 //printf("AudioALSA::set_params 10 %d %d\n", chunk_size, buffer_size);
00224 
00225         snd_pcm_sw_params_current(dsp, swparams);
00226         size_t xfer_align = 1 /* snd_pcm_sw_params_get_xfer_align(swparams) */;
00227         unsigned int sleep_min = 0;
00228         err = snd_pcm_sw_params_set_sleep_min(dsp, swparams, sleep_min);
00229         int n = chunk_size;
00230         err = snd_pcm_sw_params_set_avail_min(dsp, swparams, n);
00231         err = snd_pcm_sw_params_set_xfer_align(dsp, swparams, xfer_align);
00232         if(snd_pcm_sw_params(dsp, swparams) < 0)
00233         {
00234                 printf("AudioALSA::set_params: snd_pcm_sw_params failed\n");
00235         }
00236 
00237         device->device_buffer = samples * bits / 8 * channels;
00238 
00239 //printf("AudioALSA::set_params 100 %d %d\n", samples,  device->device_buffer);
00240 
00241 //      snd_pcm_hw_params_free(params);
00242 //      snd_pcm_sw_params_free(swparams);
00243 }
00244 
00245 int AudioALSA::open_input()
00246 {
00247         char pcm_name[BCTEXTLEN];
00248         snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
00249         int open_mode = 0;
00250         int err;
00251 
00252         device->in_channels = device->in_config->alsa_in_channels;
00253         device->in_bits = device->in_config->alsa_in_bits;
00254 
00255         translate_name(pcm_name, device->in_config->alsa_in_device);
00256 
00257         err = snd_pcm_open(&dsp_in, pcm_name, stream, open_mode);
00258 
00259         if(err < 0)
00260         {
00261                 printf("AudioALSA::open_input: %s\n", snd_strerror(err));
00262                 return 1;
00263         }
00264 
00265         set_params(dsp_in, 
00266                 device->in_config->alsa_in_channels, 
00267                 device->in_config->alsa_in_bits,
00268                 device->in_samplerate,
00269                 device->in_samples);
00270 
00271         return 0;
00272 }
00273 
00274 int AudioALSA::open_output()
00275 {
00276         char pcm_name[BCTEXTLEN];
00277         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
00278         int open_mode = 0;
00279         int err;
00280 
00281         device->out_channels = device->out_config->alsa_out_channels;
00282         device->out_bits = device->out_config->alsa_out_bits;
00283 
00284         translate_name(pcm_name, device->out_config->alsa_out_device);
00285 
00286         err = snd_pcm_open(&dsp_out, pcm_name, stream, open_mode);
00287 
00288         if(err < 0)
00289         {
00290                 printf("AudioALSA::open_output %s: %s\n", pcm_name, snd_strerror(err));
00291                 return 1;
00292         }
00293 
00294         set_params(dsp_out, 
00295                 device->out_config->alsa_out_channels, 
00296                 device->out_config->alsa_out_bits,
00297                 device->out_samplerate,
00298                 device->out_samples);
00299         timer->update();
00300         return 0;
00301 }
00302 
00303 int AudioALSA::open_duplex()
00304 {
00305 // ALSA always opens 2 devices
00306         return 0;
00307 }
00308 
00309 int AudioALSA::close_output()
00310 {
00311         if(device->w)
00312         {
00313                 snd_pcm_close(dsp_out);
00314         }
00315         return 0;
00316 }
00317 
00318 int AudioALSA::close_input()
00319 {
00320         if(device->r)
00321         {
00322 //              snd_pcm_reset(dsp_in);
00323                 snd_pcm_drop(dsp_in);
00324                 snd_pcm_drain(dsp_in);
00325                 snd_pcm_close(dsp_in);
00326         }
00327         return 0;
00328 }
00329 
00330 int AudioALSA::close_all()
00331 {
00332         close_input();
00333         close_output();
00334         if(device->d)
00335         {
00336                 snd_pcm_close(dsp_duplex);
00337         }
00338         samples_written = 0;
00339         delay = 0;
00340         interrupted = 0;
00341 }
00342 
00343 // Undocumented
00344 int64_t AudioALSA::device_position()
00345 {
00346         timer_lock->lock("AudioALSA::device_position");
00347         int64_t result = samples_written + 
00348                 timer->get_scaled_difference(device->out_samplerate) - 
00349                 delay;
00350 // printf("AudioALSA::device_position 1 %lld %lld %d %lld\n", 
00351 // samples_written,
00352 // timer->get_scaled_difference(device->out_samplerate),
00353 // delay,
00354 // samples_written + timer->get_scaled_difference(device->out_samplerate) - delay);
00355         timer_lock->unlock();
00356         return result;
00357 }
00358 
00359 int AudioALSA::read_buffer(char *buffer, int size)
00360 {
00361 //printf("AudioALSA::read_buffer 1\n");
00362         int attempts = 0;
00363         int done = 0;
00364         while(attempts < 1 && !done)
00365         {
00366                 if(snd_pcm_readi(get_input(), 
00367                         buffer, 
00368                         size / (device->in_bits / 8) / device->in_channels) < 0)
00369                 {
00370                         printf("AudioALSA::read_buffer overrun at sample %lld\n", 
00371                                 device->total_samples_read);
00372 //                      snd_pcm_resume(get_input());
00373                         close_input();
00374                         open_input();
00375                         attempts++;
00376                 }
00377                 else
00378                         done = 1;
00379         }
00380         return 0;
00381 }
00382 
00383 int AudioALSA::write_buffer(char *buffer, int size)
00384 {
00385 // Don't give up and drop the buffer on the first error.
00386         int attempts = 0;
00387         int done = 0;
00388         int samples = size / (device->out_bits / 8) / device->out_channels;
00389         while(attempts < 2 && !done && !interrupted)
00390         {
00391 // Buffers written must be equal to period_time
00392 // Update timing
00393                 snd_pcm_sframes_t delay;
00394                 snd_pcm_delay(get_output(), &delay);
00395                 snd_pcm_avail_update(get_output());
00396 
00397                 device->Thread::enable_cancel();
00398                 if(snd_pcm_writei(get_output(), 
00399                         buffer, 
00400                         samples) < 0)
00401                 {
00402                         device->Thread::disable_cancel();
00403                         printf("AudioALSA::write_buffer underrun at sample %lld\n",
00404                                 device->current_position());
00405 //                      snd_pcm_resume(get_output());
00406                         close_output();
00407                         open_output();
00408                         attempts++;
00409                 }
00410                 else
00411                 {
00412                         device->Thread::disable_cancel();
00413                         done = 1;
00414                 }
00415         }
00416 
00417         if(done)
00418         {
00419                 timer_lock->lock("AudioALSA::write_buffer");
00420                 this->delay = delay;
00421                 timer->update();
00422                 samples_written += samples;
00423                 timer_lock->unlock();
00424         }
00425         return 0;
00426 }
00427 
00428 int AudioALSA::flush_device()
00429 {
00430         if(get_output()) snd_pcm_drain(get_output());
00431         return 0;
00432 }
00433 
00434 int AudioALSA::interrupt_playback()
00435 {
00436         if(get_output()) 
00437         {
00438                 interrupted = 1;
00439 // Interrupts the playback but may not have caused snd_pcm_writei to exit.
00440 // With some soundcards it causes snd_pcm_writei to freeze for a few seconds.
00441                 if(!device->out_config->interrupt_workaround)
00442                         snd_pcm_drop(get_output());
00443 
00444 // Makes sure the current buffer finishes before stopping.
00445 //              snd_pcm_drain(get_output());
00446 
00447 // The only way to ensure snd_pcm_writei exits, but
00448 // got a lot of crashes when doing this.
00449 //              device->Thread::cancel();
00450         }
00451         return 0;
00452 }
00453 
00454 
00455 snd_pcm_t* AudioALSA::get_output()
00456 {
00457         if(device->w) return dsp_out;
00458         else
00459         if(device->d) return dsp_duplex;
00460         return 0;
00461 }
00462 
00463 snd_pcm_t* AudioALSA::get_input()
00464 {
00465         if(device->r) return dsp_in;
00466         else
00467         if(device->d) return dsp_duplex;
00468         return 0;
00469 }
00470 
00471 #endif

Generated on Sun Jan 8 13:38:50 2006 for Cinelerra-svn by  doxygen 1.4.4