fixes for checkin 2 times ago
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / audioalsa.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "audiodevice.h"
23 #include "audioalsa.h"
24 #include "bcsignals.h"
25 #include "language.h"
26 #include "mutex.h"
27 #include "playbackconfig.h"
28 #include "preferences.h"
29 #include "recordconfig.h"
30
31 #include <errno.h>
32
33 #ifdef HAVE_ALSA
34
35 AudioALSA::AudioALSA(AudioDevice *device)
36  : AudioLowLevel(device)
37 {
38         buffer_position = 0;
39         samples_written = 0;
40         timer = new Timer;
41         delay = 0;
42         period_size = 0;
43         timer_lock = new Mutex("AudioALSA::timer_lock");
44         interrupted = 0;
45         dsp_in = 0;
46         dsp_out = 0;
47 }
48
49 AudioALSA::~AudioALSA()
50 {
51         delete timer_lock;
52         delete timer;
53 }
54
55 // leak checking
56 static class alsa_leaks
57 {
58 public:
59 // This is required in the top thread for Alsa to work
60         alsa_leaks() {
61                 ArrayList<char*> *alsa_titles = new ArrayList<char*>;
62                 alsa_titles->set_array_delete();
63                 AudioALSA::list_devices(alsa_titles, 0, MODEPLAY);
64                 alsa_titles->remove_all_objects();
65                 delete alsa_titles;
66         }
67         ~alsa_leaks() { snd_config_update_free_global(); }
68 } alsa_leak;
69
70 void AudioALSA::list_devices(ArrayList<char*> *names, ArrayList<char*> *pcm_names, int mode)
71 {
72         snd_ctl_t *handle;
73         int card, err, dev;
74         snd_ctl_card_info_t *info;
75         snd_pcm_info_t *pcminfo;
76         char string[BCTEXTLEN];
77         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
78
79         switch(mode)
80         {
81                 case MODERECORD:
82                         stream = SND_PCM_STREAM_CAPTURE;
83                         break;
84                 case MODEPLAY:
85                         stream = SND_PCM_STREAM_PLAYBACK;
86                         break;
87         }
88
89
90         snd_ctl_card_info_alloca(&info);
91         snd_pcm_info_alloca(&pcminfo);
92
93         card = -1;
94 #define DEFAULT_DEVICE "default"
95         if( names )
96                 names->append(cstrdup(DEFAULT_DEVICE));
97         if( pcm_names )
98                 pcm_names->append(cstrdup(DEFAULT_DEVICE));
99
100         while(snd_card_next(&card) >= 0) {
101                 char name[BCTEXTLEN];
102                 if(card < 0) break;
103                 sprintf(name, "hw:%i", card);
104
105                 if((err = snd_ctl_open(&handle, name, 0)) < 0) {
106                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
107                         continue;
108                 }
109
110                 if((err = snd_ctl_card_info(handle, info)) < 0) {
111                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
112                         snd_ctl_close(handle);
113                         continue;
114                 }
115
116                 dev = -1;
117
118                 while(1) {
119                         if(snd_ctl_pcm_next_device(handle, &dev) < 0)
120                                 printf("AudioALSA::list_devices: snd_ctl_pcm_next_device\n");
121
122                         if (dev < 0) break;
123
124                         snd_pcm_info_set_device(pcminfo, dev);
125                         snd_pcm_info_set_subdevice(pcminfo, 0);
126                         snd_pcm_info_set_stream(pcminfo, stream);
127
128                         if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
129                                 if(err != -ENOENT)
130                                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
131                                 continue;
132                         }
133
134                         if( pcm_names ) {
135                                 sprintf(string, "plughw:%d,%d", card, dev);
136 //                              strcpy(string, "cards.pcm.front");
137                                 pcm_names->append(cstrdup(string));
138                         }
139                         if( names ) {
140                                 sprintf(string, "%s #%d",
141                                         snd_ctl_card_info_get_name(info), dev);
142                                 names->append(cstrdup(string));
143                         }
144                 }
145
146                 snd_ctl_close(handle);
147         }
148 }
149
150 void AudioALSA::translate_name(char *output, char *input, int mode)
151 {
152         ArrayList<char*> titles;
153         titles.set_array_delete();
154
155         ArrayList<char*> pcm_titles;
156         pcm_titles.set_array_delete();
157
158         list_devices(&titles, &pcm_titles, mode);
159
160 // attempt to add pulseaudio "monitor" devices
161         add_pulse_devices(mode, &titles, &pcm_titles);
162
163         sprintf(output, "default");
164         for(int i = 0; i < titles.total; i++)
165         {
166 //printf("AudioALSA::translate_name %s %s\n", titles.values[i], pcm_titles.values[i]);
167                 if(!strcasecmp(titles.values[i], input))
168                 {
169                         strcpy(output, pcm_titles.values[i]);
170                         break;
171                 }
172         }
173
174         titles.remove_all_objects();
175         pcm_titles.remove_all_objects();
176 }
177
178 snd_pcm_format_t AudioALSA::translate_format(int format)
179 {
180         switch(format)
181         {
182         case  8: return SND_PCM_FORMAT_S8;
183         case 16: return SND_PCM_FORMAT_S16_LE;
184         case 24: return SND_PCM_FORMAT_S24_LE;
185         case 32: return SND_PCM_FORMAT_S32_LE;
186         }
187         return SND_PCM_FORMAT_UNKNOWN;
188 }
189
190 int AudioALSA::set_params(snd_pcm_t *dsp, int mode,
191         int channels, int bits, int samplerate, int samples)
192 {
193         snd_pcm_hw_params_t *params;
194         snd_pcm_sw_params_t *swparams;
195         int err;
196
197         snd_pcm_hw_params_alloca(&params);
198         snd_pcm_sw_params_alloca(&swparams);
199         err = snd_pcm_hw_params_any(dsp, params);
200
201         if (err < 0) {
202                 fprintf(stderr, "AudioALSA::set_params: ");
203                 fprintf(stderr, _("no PCM configurations available\n"));
204                 return 1;
205         }
206
207         err=snd_pcm_hw_params_set_access(dsp,
208                 params,
209                 SND_PCM_ACCESS_RW_INTERLEAVED);
210         if(err) {
211                 fprintf(stderr, "AudioALSA::set_params: ");
212                 fprintf(stderr, _("failed to set up interleaved device access.\n"));
213                 return 1;
214         }
215
216         err=snd_pcm_hw_params_set_format(dsp,
217                 params,
218                 translate_format(bits));
219         if(err) {
220                 fprintf(stderr, "AudioALSA::set_params: ");
221                 fprintf(stderr, _("failed to set output format.\n"));
222                 return 1;
223         }
224
225         err=snd_pcm_hw_params_set_channels(dsp,
226                 params,
227                 channels);
228         if(err) {
229                 fprintf(stderr, "AudioALSA::set_params: ");
230                 fprintf(stderr, _("Configured ALSA device does not support %d channel operation.\n"),
231                         channels);
232                 return 1;
233         }
234
235         err=snd_pcm_hw_params_set_rate_near(dsp,
236                 params,
237                 (unsigned int*)&samplerate,
238                 (int*)0);
239         if(err) {
240                 fprintf(stderr, "AudioALSA::set_params: ");
241                 fprintf(stderr, _(" Configured ALSA device does not support %u Hz playback.\n"),
242                         (unsigned int)samplerate);
243                 return 1;
244         }
245
246 // Buffers written must be equal to period_time
247         int buffer_time = 0;
248         int period_time = (int)(1000000 * (double)samples / samplerate);
249         switch( mode ) {
250         case MODERECORD:
251                 buffer_time = 10000000;
252                 break;
253         case MODEPLAY:
254                 buffer_time = 2 * period_time;
255                 break;
256         }
257
258 //printf("AudioALSA::set_params 1 %d %d %d\n", samples, buffer_time, period_time);
259         snd_pcm_hw_params_set_buffer_time_near(dsp,
260                 params,
261                 (unsigned int*)&buffer_time,
262                 (int*)0);
263         snd_pcm_hw_params_set_period_time_near(dsp,
264                 params,
265                 (unsigned int*)&period_time,
266                 (int*)0);
267 //printf("AudioALSA::set_params 5 %d %d\n", buffer_time, period_time);
268         err = snd_pcm_hw_params(dsp, params);
269         if(err < 0) {
270                 fprintf(stderr, "AudioALSA::set_params: hw_params failed\n");
271                 return 1;
272         }
273
274         snd_pcm_uframes_t chunk_size = 1024;
275         snd_pcm_uframes_t buffer_size = 262144;
276         snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
277         snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
278 //printf("AudioALSA::set_params 10 %d %d\n", chunk_size, buffer_size);
279
280         snd_pcm_sw_params_current(dsp, swparams);
281         //snd_pcm_uframes_t xfer_align = 1;
282         //snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align);
283         //unsigned int sleep_min = 0;
284         //err = snd_pcm_sw_params_set_sleep_min(dsp, swparams, sleep_min);
285         period_size = chunk_size;
286         err = snd_pcm_sw_params_set_avail_min(dsp, swparams, period_size);
287         //err = snd_pcm_sw_params_set_xfer_align(dsp, swparams, xfer_align);
288         if(snd_pcm_sw_params(dsp, swparams) < 0) {
289                 /* we can continue staggering along even if this fails */
290                 fprintf(stderr, "AudioALSA::set_params: snd_pcm_sw_params failed\n");
291         }
292
293         device->device_buffer = samples * bits / 8 * channels;
294         period_size /= 2;
295 //printf("AudioALSA::set_params 100 %d %d\n", samples,  device->device_buffer);
296
297 //      snd_pcm_hw_params_free(params);
298 //      snd_pcm_sw_params_free(swparams);
299         return 0;
300 }
301
302 int AudioALSA::open_input()
303 {
304         char pcm_name[BCTEXTLEN];
305         snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
306         int open_mode = 0;
307         int err;
308
309         device->in_channels = device->get_ichannels();
310         device->in_bits = device->in_config->alsa_in_bits;
311
312         translate_name(pcm_name, device->in_config->alsa_in_device,MODERECORD);
313 //printf("AudioALSA::open_input %s\n", pcm_name);
314
315         err = snd_pcm_open(&dsp_in, pcm_name, stream, open_mode);
316
317         if(err < 0) {
318                 dsp_in = 0;
319                 printf("AudioALSA::open_input: %s\n", snd_strerror(err));
320                 return 1;
321         }
322
323         err = set_params(dsp_in, MODERECORD,
324                 device->get_ichannels(),
325                 device->in_config->alsa_in_bits,
326                 device->in_samplerate,
327                 device->in_samples);
328         if(err) {
329                 fprintf(stderr, "AudioALSA::open_input: set_params failed.  Aborting sampling.\n");
330                 close_input();
331                 return 1;
332         }
333
334         return 0;
335 }
336
337 int AudioALSA::open_output()
338 {
339         char pcm_name[BCTEXTLEN];
340         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
341         int open_mode = SND_PCM_NONBLOCK;
342         int err;
343
344         device->out_channels = device->get_ochannels();
345         device->out_bits = device->out_config->alsa_out_bits;
346
347 //printf("AudioALSA::open_output out_device %s\n", device->out_config->alsa_out_device);
348         translate_name(pcm_name, device->out_config->alsa_out_device,MODEPLAY);
349 //printf("AudioALSA::open_output pcm_name %s\n", pcm_name);
350
351         err = snd_pcm_open(&dsp_out, pcm_name, stream, open_mode);
352
353         if(err < 0)
354         {
355                 dsp_out = 0;
356                 printf("AudioALSA::open_output %s: %s\n", pcm_name, snd_strerror(err));
357                 return 1;
358         }
359
360         err = set_params(dsp_out, MODEPLAY,
361                 device->get_ochannels(),
362                 device->out_config->alsa_out_bits,
363                 device->out_samplerate,
364                 device->out_samples);
365         if(err) {
366                 fprintf(stderr, "AudioALSA::open_output: set_params failed.  Aborting playback.\n");
367                 close_output();
368                 return 1;
369         }
370
371         timer->update();
372         return 0;
373 }
374
375 int AudioALSA::stop_output()
376 {
377 //printf("AudioALSA::stop_output\n");
378         if(!device->out_config->interrupt_workaround)
379         {
380                 if( get_output() )
381                         snd_pcm_drop(get_output());
382         }
383         else
384                 flush_device();
385         return 0;
386 }
387
388 int AudioALSA::close_output()
389 {
390 //printf("AudioALSA::close_output\n");
391         if(device->w && dsp_out) {
392                 stop_output();
393                 snd_pcm_close(dsp_out);
394                 dsp_out = 0;
395         }
396         return 0;
397 }
398
399 int AudioALSA::close_input()
400 {
401 //printf("AudioALSA::close_input\n");
402         if(device->r && dsp_in) {
403 //              snd_pcm_reset(dsp_in);
404                 snd_pcm_drop(dsp_in);
405                 snd_pcm_drain(dsp_in);
406                 snd_pcm_close(dsp_in);
407                 dsp_in = 0;
408         }
409         return 0;
410 }
411
412 int AudioALSA::close_all()
413 {
414 //printf("AudioALSA::close_all\n");
415         close_input();
416         close_output();
417         buffer_position = 0;
418         samples_written = 0;
419         delay = 0;
420         interrupted = 0;
421         return 0;
422 }
423
424 // Undocumented
425 int64_t AudioALSA::device_position()
426 {
427         timer_lock->lock("AudioALSA::device_position");
428         int64_t delta = timer->get_scaled_difference(device->out_samplerate);
429         int64_t result = buffer_position - delay + delta;
430 //printf("AudioALSA::device_position 1 w=%jd dt=%jd dly=%d pos=%jd\n",
431 // buffer_position, delta, delay, result);
432         timer_lock->unlock();
433         return result;
434 }
435
436 int AudioALSA::read_buffer(char *buffer, int size)
437 {
438 //printf("AudioALSA::read_buffer 1\n");
439         int attempts = 0;
440         int done = 0;
441         int frame_size = (device->in_bits / 8) * device->get_ichannels();
442         int result = 0;
443
444         if(!get_input())
445         {
446                 sleep(1);
447                 return 0;
448         }
449
450         while(attempts < 1 && !done)
451         {
452                 snd_pcm_uframes_t frames = size / frame_size;
453                 result = snd_pcm_readi(get_input(), buffer, frames);
454                 if( result < 0)
455                 {
456                         printf("AudioALSA::read_buffer overrun at sample %jd\n",
457                                 device->total_samples_read);
458 //                      snd_pcm_resume(get_input());
459                         close_input();  open_input();
460                         attempts++;
461                 }
462                 else
463                         done = 1;
464 //printf("AudioALSA::read_buffer %d result=%d done=%d\n", __LINE__, result, done);
465         }
466         return 0;
467 }
468
469 int AudioALSA::write_buffer(char *buffer, int size)
470 {
471 //printf("AudioALSA::write_buffer %d\n",size);
472 // Don't give up and drop the buffer on the first error.
473         int attempts = 0;
474         int done = 0;
475         int sample_size = (device->out_bits / 8) * device->get_ochannels();
476         int samples = size / sample_size;
477 //printf("AudioALSA::write_buffer %d\n",samples);
478         int count = samples;
479         snd_pcm_sframes_t delay = 0;
480
481 // static FILE *debug_fd = 0;
482 // if(!debug_fd)
483 // {
484 //      debug_fd = fopen("/tmp/debug.pcm", "w");
485 // }
486 // fwrite(buffer, size, 1, debug_fd);
487 // fflush(debug_fd);
488
489
490         if(!get_output()) return 0;
491         if( buffer_position == 0 )
492                 timer->update();
493
494         AudioThread *audio_out = device->audio_out;
495         while( count > 0 && attempts < 2 && !done ) {
496                 if( device->playback_interrupted ) break;
497 // Buffers written must be equal to period_time
498                 audio_out->Thread::enable_cancel();
499                 int ret = snd_pcm_avail_update(get_output());
500                 if( ret >= period_size ) {
501                         if( ret > count ) ret = count;
502 //FILE *alsa_fp = 0;
503 //if( !alsa_fp ) alsa_fp = fopen("/tmp/alsa.raw","w");
504 //if( alsa_fp ) fwrite(buffer, sample_size, ret, alsa_fp);
505 //printf("AudioALSA::snd_pcm_writei start %d\n",count);
506                         ret = snd_pcm_writei(get_output(),buffer,ret);
507 //printf("AudioALSA::snd_pcm_writei done %d\n", ret);
508                 }
509                 else if( ret >= 0 || ret == -EAGAIN ) {
510                         ret = snd_pcm_wait(get_output(),15);
511                         if( ret > 0 ) ret = 0;
512                 }
513                 audio_out->Thread::disable_cancel();
514                 if( device->playback_interrupted ) break;
515                 if( ret == 0 ) continue;
516
517                 if( ret > 0 ) {
518                         samples_written += ret;
519                         if( (count-=ret) > 0 ) {
520                                 buffer += ret * sample_size;
521                                 attempts = 0;
522                         }
523                         else
524                                 done = 1;
525                 }
526                 else {
527                         printf(_("AudioALSA::write_buffer err %d(%s) at sample %jd\n"),
528                                 ret, snd_strerror(ret), device->current_position());
529                         Timer::delay(50);
530 //                      snd_pcm_resume(get_output());
531                         snd_pcm_recover(get_output(), ret, 1);
532 //                      close_output();  open_output();
533                         attempts++;
534                 }
535         }
536
537         if( !interrupted && device->playback_interrupted )
538         {
539                 interrupted = 1;
540 //printf("AudioALSA::write_buffer interrupted\n");
541                 stop_output();
542         }
543
544         if(done)
545         {
546                 timer_lock->lock("AudioALSA::write_buffer");
547                 snd_pcm_delay(get_output(), &delay);
548                 this->delay = delay;
549                 timer->update();
550                 buffer_position += samples;
551 //printf("AudioALSA::write_buffer ** wrote %d, delay %d\n",samples,(int)delay);
552                 timer_lock->unlock();
553         }
554         return 0;
555 }
556
557 //this delay seems to prevent a problem where the sound system outputs
558 //a lot of silence while waiting for the device drain to happen.
559 int AudioALSA::output_wait()
560 {
561         snd_pcm_sframes_t delay = 0;
562         snd_pcm_delay(get_output(), &delay);
563         if( delay <= 0 ) return 0;
564         int64_t udelay = 1e6 * delay / device->out_samplerate;
565         // don't allow more than 10 seconds
566         if( udelay > 10000000 ) udelay = 10000000;
567         while( udelay > 0 && !device->playback_interrupted ) {
568                 int64_t usecs = udelay;
569                 if( usecs > 100000 ) usecs = 100000;
570                 usleep(usecs);
571                 udelay -= usecs;
572         }
573         if( device->playback_interrupted &&
574             !device->out_config->interrupt_workaround )
575                 snd_pcm_drop(get_output());
576         return 0;
577 }
578
579 int AudioALSA::flush_device()
580 {
581 //printf("AudioALSA::flush_device\n");
582         if(get_output())
583         {
584                 output_wait();
585                 //this causes the output to stutter.
586                 //snd_pcm_nonblock(get_output(), 0);
587                 snd_pcm_drain(get_output());
588                 //snd_pcm_nonblock(get_output(), 1);
589         }
590         return 0;
591 }
592
593 int AudioALSA::interrupt_playback()
594 {
595 //printf("AudioALSA::interrupt_playback *********\n");
596 //      if(get_output())
597 //      {
598 // Interrupts the playback but may not have caused snd_pcm_writei to exit.
599 // With some soundcards it causes snd_pcm_writei to freeze for a few seconds.
600 //              if(!device->out_config->interrupt_workaround)
601 //                      snd_pcm_drop(get_output());
602
603 // Makes sure the current buffer finishes before stopping.
604 //              snd_pcm_drain(get_output());
605
606 // The only way to ensure snd_pcm_writei exits, but
607 // got a lot of crashes when doing this.
608 //              device->Thread::cancel();
609 //      }
610         return 0;
611 }
612
613
614 snd_pcm_t* AudioALSA::get_output()
615 {
616         return dsp_out;
617 }
618
619 snd_pcm_t* AudioALSA::get_input()
620 {
621         return dsp_in;
622 }
623
624 void AudioALSA::add_pulse_devices(int mode,
625                 ArrayList<char*> *names, ArrayList<char*> *pcm_names)
626 {
627 #ifdef HAVE_PACTL
628 //  run: pactl list <sources>|<sinks>
629 //   scan output for <Source/Sink> #n,  Name: <device>
630 //   build alsa device config and add to alsa snd_config
631         const char *arg = 0;
632         switch( mode ) {
633         case MODERECORD:
634                 arg = "source";
635                 break;
636         case MODEPLAY:
637                 arg = "sink";
638                 break;
639         }
640         FILE *pactl = 0;
641         char line[BCTEXTLEN];
642         if( arg ) {
643                 sprintf(line, "LANGUAGE=en_US.UTF-8 pactl list %ss", arg);
644                 pactl = popen(line,"r");
645         }
646         if( pactl ) {
647                 snd_config_update();
648                 char name[BCTEXTLEN], pa_name[BCTEXTLEN], device[BCTEXTLEN];
649                 name[0] = pa_name[0] = device[0] = 0;
650                 int arg_len = strlen(arg);
651                 while( fgets(line, sizeof(line), pactl) ) {
652                         if( !strncasecmp(line, arg, arg_len) ) {
653                                 char *sp = name, *id = pa_name;
654                                 for( char *cp=line; *cp && *cp!='\n'; *sp++=*cp++ )
655                                         *id++ = (*cp>='A' && *cp<='Z') ||
656                                                 (*cp>='a' && *cp<='z') ||
657                                                 (*cp>='0' && *cp<='9') ? *cp : '_';
658                                 *sp++ = 0;  *id = 0;
659                                 if( names )
660                                         names->append(cstrdup(name));
661                                 continue;
662                         }
663                         if( sscanf(line, " Name: %s", device) != 1 ) continue;
664                         int len = strlen(pa_name);
665                         if( pcm_names )
666                                 pcm_names->append(cstrdup(pa_name));
667                         char alsa_config[BCTEXTLEN];
668                         len = snprintf(alsa_config, sizeof(alsa_config),
669                                 "pcm.!%s {\n type pulse\n device %s\n}\n"
670                                 "ctl.!%s {\n type pulse\n device %s\n}\n",
671                                 pa_name, device, pa_name, device);
672                         FILE *fp = fmemopen(alsa_config,len,"r");
673                         snd_input_t *inp;
674                         snd_input_stdio_attach(&inp, fp, 1);
675                         snd_config_load(snd_config, inp);
676                         name[0] = pa_name[0] = device[0] = 0;
677                         snd_input_close(inp);
678                 }
679                 pclose(pactl);
680         }
681 #endif
682 }
683
684 #endif