r827: Fix a crash when no audio output device can be opened.
[cinelerra_cv.git] / cinelerra / audioalsa.C
blobb87bc1459774a80a208c79ca21b17c05c1f6d371
1 #include "audiodevice.h"
2 #include "audioalsa.h"
3 #include "bcsignals.h"
4 #include "playbackconfig.h"
5 #include "preferences.h"
6 #include "recordconfig.h"
8 #include <errno.h>
10 #ifdef HAVE_ALSA
12 AudioALSA::AudioALSA(AudioDevice *device)
13  : AudioLowLevel(device)
15         samples_written = 0;
16         timer = new Timer;
17         delay = 0;
18         timer_lock = new Mutex("AudioALSA::timer_lock");
19         interrupted = 0;
22 AudioALSA::~AudioALSA()
24         delete timer_lock;
25         delete timer;
28 void AudioALSA::list_devices(ArrayList<char*> *devices, int pcm_title)
30         snd_ctl_t *handle;
31         int card, err, dev, idx;
32         snd_ctl_card_info_t *info;
33         snd_pcm_info_t *pcminfo;
34         char string[BCTEXTLEN];
35         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
36         int error;
38         snd_ctl_card_info_alloca(&info);
39         snd_pcm_info_alloca(&pcminfo);
41         card = -1;
42 #define DEFAULT_DEVICE "default"
43         char *result = new char[strlen(DEFAULT_DEVICE) + 1];
44         devices->append(result);
45         devices->set_array_delete();     // since we are allocating by new[]
46         strcpy(result, DEFAULT_DEVICE);
48         while(snd_card_next(&card) >= 0)
49         {
50                 char name[BCTEXTLEN];
51                 if(card < 0) break;
52                 sprintf(name, "hw:%i", card);
54                 if((err = snd_ctl_open(&handle, name, 0)) < 0)
55                 {
56                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
57                         continue;
58                 }
60                 if((err = snd_ctl_card_info(handle, info)) < 0)
61                 {
62                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
63                         snd_ctl_close(handle);
64                         continue;
65                 }
67                 dev = -1;
69                 while(1)
70                 {
71                         unsigned int count;
72                         if(snd_ctl_pcm_next_device(handle, &dev) < 0)
73                                 printf("AudioALSA::list_devices: snd_ctl_pcm_next_device\n");
75                         if (dev < 0)
76                                 break;
78                         snd_pcm_info_set_device(pcminfo, dev);
79                         snd_pcm_info_set_subdevice(pcminfo, 0);
80                         snd_pcm_info_set_stream(pcminfo, stream);
82                         if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) 
83                         {
84                                 if(err != -ENOENT)
85                                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
86                                 continue;
87                         }
89                         if(pcm_title)
90                         {
91                                 sprintf(string, "plughw:%d,%d", card, dev);
92 //                              strcpy(string, "cards.pcm.front");
93                         }
94                         else
95                         {
96                                 sprintf(string, "%s #%d", 
97                                         snd_ctl_card_info_get_name(info), 
98                                         dev);
99                         }
101                         char *result = devices->append(new char[strlen(string) + 1]);
102                         strcpy(result, string);
103                 }
105                 snd_ctl_close(handle);
106         }
108 //      snd_ctl_card_info_free(info);
109 //      snd_pcm_info_free(pcminfo);
112 void AudioALSA::translate_name(char *output, char *input)
114         ArrayList<char*> titles;
115         ArrayList<char*> pcm_titles;
116         
117         list_devices(&titles, 0);
118         list_devices(&pcm_titles, 1);
120         sprintf(output, "default");     
121         for(int i = 0; i < titles.total; i++)
122         {
123 //printf("AudioALSA::translate_name %s %s\n", titles.values[i], pcm_titles.values[i]);
124                 if(!strcasecmp(titles.values[i], input))
125                 {
126                         strcpy(output, pcm_titles.values[i]);
127                         break;
128                 }
129         }
130         
131         titles.remove_all_objects();
132         pcm_titles.remove_all_objects();
135 snd_pcm_format_t AudioALSA::translate_format(int format)
137         switch(format)
138         {
139                 case 8:
140                         return SND_PCM_FORMAT_S8;
141                         break;
142                 case 16:
143                         return SND_PCM_FORMAT_S16_LE;
144                         break;
145                 case 24:
146                         return SND_PCM_FORMAT_S24_LE;
147                         break;
148                 case 32:
149                         return SND_PCM_FORMAT_S32_LE;
150                         break;
151         }
154 void AudioALSA::set_params(snd_pcm_t *dsp, 
155         int channels, 
156         int bits,
157         int samplerate,
158         int samples)
160         snd_pcm_hw_params_t *params;
161         snd_pcm_sw_params_t *swparams;
162         int err;
164         snd_pcm_hw_params_alloca(&params);
165         snd_pcm_sw_params_alloca(&swparams);
166         err = snd_pcm_hw_params_any(dsp, params);
168         if (err < 0) 
169         {
170                 printf("AudioALSA::set_params: no PCM configurations available\n");
171                 return;
172         }
174         snd_pcm_hw_params_set_access(dsp, 
175                 params,
176                 SND_PCM_ACCESS_RW_INTERLEAVED);
177         snd_pcm_hw_params_set_format(dsp, 
178                 params, 
179                 translate_format(bits));
180         snd_pcm_hw_params_set_channels(dsp, 
181                 params, 
182                 channels);
183         snd_pcm_hw_params_set_rate_near(dsp, 
184                 params, 
185                 (unsigned int*)&samplerate, 
186                 (int*)0);
188 // Buffers written must be equal to period_time
189         int buffer_time;
190         int period_time;
191         if(device->r)
192         {
193                 buffer_time = 10000000;
194                 period_time = (int)((int64_t)samples * 1000000 / samplerate);
195         }
196         else
197         {
198                 buffer_time = (int)((int64_t)samples * 1000000 * 2 / samplerate + 0.5);
199                 period_time = samples * samplerate / 1000000;
200         }
203 //printf("AudioALSA::set_params 1 %d %d %d\n", samples, buffer_time, period_time);
204         snd_pcm_hw_params_set_buffer_time_near(dsp, 
205                 params,
206                 (unsigned int*)&buffer_time, 
207                 (int*)0);
208         snd_pcm_hw_params_set_period_time_near(dsp, 
209                 params,
210                 (unsigned int*)&period_time, 
211                 (int*)0);
212 //printf("AudioALSA::set_params 5 %d %d\n", buffer_time, period_time);
213         err = snd_pcm_hw_params(dsp, params);
214         if(err < 0)
215         {
216                 printf("AudioALSA::set_params: hw_params failed\n");
217                 return;
218         }
220         snd_pcm_uframes_t chunk_size = 1024;
221         snd_pcm_uframes_t buffer_size = 262144;
222         snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
223         snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
224 //printf("AudioALSA::set_params 10 %d %d\n", chunk_size, buffer_size);
226         snd_pcm_sw_params_current(dsp, swparams);
227         size_t xfer_align = 1 /* snd_pcm_sw_params_get_xfer_align(swparams) */;
228         unsigned int sleep_min = 0;
229         err = snd_pcm_sw_params_set_sleep_min(dsp, swparams, sleep_min);
230         int n = chunk_size;
231         err = snd_pcm_sw_params_set_avail_min(dsp, swparams, n);
232         err = snd_pcm_sw_params_set_xfer_align(dsp, swparams, xfer_align);
233         if(snd_pcm_sw_params(dsp, swparams) < 0)
234         {
235                 printf("AudioALSA::set_params: snd_pcm_sw_params failed\n");
236         }
238         device->device_buffer = samples * bits / 8 * channels;
240 //printf("AudioALSA::set_params 100 %d %d\n", samples,  device->device_buffer);
242 //      snd_pcm_hw_params_free(params);
243 //      snd_pcm_sw_params_free(swparams);
246 int AudioALSA::open_input()
248         char pcm_name[BCTEXTLEN];
249         snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
250         int open_mode = 0;
251         int err;
253         device->in_channels = device->in_config->alsa_in_channels;
254         device->in_bits = device->in_config->alsa_in_bits;
256         translate_name(pcm_name, device->in_config->alsa_in_device);
258         err = snd_pcm_open(&dsp_in, pcm_name, stream, open_mode);
260         if(err < 0)
261         {
262                 printf("AudioALSA::open_input: %s\n", snd_strerror(err));
263                 return 1;
264         }
266         set_params(dsp_in, 
267                 device->in_config->alsa_in_channels, 
268                 device->in_config->alsa_in_bits,
269                 device->in_samplerate,
270                 device->in_samples);
272         return 0;
275 int AudioALSA::open_output()
277         char pcm_name[BCTEXTLEN];
278         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
279         int open_mode = 0;
280         int err;
282         device->out_channels = device->out_config->alsa_out_channels;
283         device->out_bits = device->out_config->alsa_out_bits;
285         translate_name(pcm_name, device->out_config->alsa_out_device);
287         err = snd_pcm_open(&dsp_out, pcm_name, stream, open_mode);
289         if(err < 0)
290         {
291                 printf("AudioALSA::open_output %s: %s\n", pcm_name, snd_strerror(err));
292                 return 1;
293         }
295         set_params(dsp_out, 
296                 device->out_config->alsa_out_channels, 
297                 device->out_config->alsa_out_bits,
298                 device->out_samplerate,
299                 device->out_samples);
300         timer->update();
301         return 0;
304 int AudioALSA::open_duplex()
306 // ALSA always opens 2 devices
307         return 0;
310 int AudioALSA::close_output()
312         if(device->w)
313         {
314                 snd_pcm_close(dsp_out);
315         }
316         return 0;
319 int AudioALSA::close_input()
321         if(device->r)
322         {
323 //              snd_pcm_reset(dsp_in);
324                 snd_pcm_drop(dsp_in);
325                 snd_pcm_drain(dsp_in);
326                 snd_pcm_close(dsp_in);
327         }
328         return 0;
331 int AudioALSA::close_all()
333         close_input();
334         close_output();
335         if(device->d)
336         {
337                 snd_pcm_close(dsp_duplex);
338         }
339         samples_written = 0;
340         delay = 0;
341         interrupted = 0;
344 // Undocumented
345 int64_t AudioALSA::device_position()
347         timer_lock->lock("AudioALSA::device_position");
348         int64_t result = samples_written + 
349                 timer->get_scaled_difference(device->out_samplerate) - 
350                 delay;
351 // printf("AudioALSA::device_position 1 %lld %lld %d %lld\n", 
352 // samples_written,
353 // timer->get_scaled_difference(device->out_samplerate),
354 // delay,
355 // samples_written + timer->get_scaled_difference(device->out_samplerate) - delay);
356         timer_lock->unlock();
357         return result;
360 int AudioALSA::read_buffer(char *buffer, int size)
362 //printf("AudioALSA::read_buffer 1\n");
363         int attempts = 0;
364         int done = 0;
365         while(attempts < 1 && !done)
366         {
367                 if(snd_pcm_readi(get_input(), 
368                         buffer, 
369                         size / (device->in_bits / 8) / device->in_channels) < 0)
370                 {
371                         printf("AudioALSA::read_buffer overrun at sample %lld\n", 
372                                 device->total_samples_read);
373 //                      snd_pcm_resume(get_input());
374                         close_input();
375                         open_input();
376                         attempts++;
377                 }
378                 else
379                         done = 1;
380         }
381         return 0;
384 int AudioALSA::write_buffer(char *buffer, int size)
386 // Don't give up and drop the buffer on the first error.
387         int attempts = 0;
388         int done = 0;
389         int samples = size / (device->out_bits / 8) / device->out_channels;
390         while(attempts < 2 && !done && !interrupted)
391         {
392 // Buffers written must be equal to period_time
393 // Update timing
394                 snd_pcm_sframes_t delay;
395                 snd_pcm_delay(get_output(), &delay);
396                 snd_pcm_avail_update(get_output());
398                 device->Thread::enable_cancel();
399                 if(snd_pcm_writei(get_output(), 
400                         buffer, 
401                         samples) < 0)
402                 {
403                         device->Thread::disable_cancel();
404                         printf("AudioALSA::write_buffer underrun at sample %lld\n",
405                                 device->current_position());
406 //                      snd_pcm_resume(get_output());
407                         close_output();
408                         open_output();
409                         attempts++;
410                 }
411                 else
412                 {
413                         device->Thread::disable_cancel();
414                         done = 1;
415                 }
416         }
418         if(done)
419         {
420                 timer_lock->lock("AudioALSA::write_buffer");
421                 this->delay = delay;
422                 timer->update();
423                 samples_written += samples;
424                 timer_lock->unlock();
425         }
426         return 0;
429 int AudioALSA::flush_device()
431         if(get_output()) snd_pcm_drain(get_output());
432         return 0;
435 int AudioALSA::interrupt_playback()
437         if(get_output()) 
438         {
439                 interrupted = 1;
440 // Interrupts the playback but may not have caused snd_pcm_writei to exit.
441 // With some soundcards it causes snd_pcm_writei to freeze for a few seconds.
442                 if(!device->out_config->interrupt_workaround)
443                         snd_pcm_drop(get_output());
445 // Makes sure the current buffer finishes before stopping.
446 //              snd_pcm_drain(get_output());
448 // The only way to ensure snd_pcm_writei exits, but
449 // got a lot of crashes when doing this.
450 //              device->Thread::cancel();
451         }
452         return 0;
456 snd_pcm_t* AudioALSA::get_output()
458         if(device->w) return dsp_out;
459         else
460         if(device->d) return dsp_duplex;
461         return 0;
464 snd_pcm_t* AudioALSA::get_input()
466         if(device->r) return dsp_in;
467         else
468         if(device->d) return dsp_duplex;
469         return 0;
472 #endif