Merge branch 'ct' of git.pipapo.org:cinelerra-ct into ct
[cinelerra_cv/ct.git] / cinelerra / audioalsa.C
blobaeaf910f4ed64e5b774f7a705c7f9ec5427a34f7
1 #include "audiodevice.h"
2 #include "audioalsa.h"
3 #include "bcsignals.h"
4 #include "mutex.h"
5 #include "playbackconfig.h"
6 #include "preferences.h"
7 #include "recordconfig.h"
9 #include <errno.h>
11 #ifdef HAVE_ALSA
13 AudioALSA::AudioALSA(AudioDevice *device)
14  : AudioLowLevel(device)
16         samples_written = 0;
17         timer = new Timer;
18         delay = 0;
19         timer_lock = new Mutex("AudioALSA::timer_lock");
20         interrupted = 0;
21         dsp_out = 0;
24 AudioALSA::~AudioALSA()
26         delete timer_lock;
27         delete timer;
30 void AudioALSA::list_devices(ArrayList<char*> *devices, int pcm_title, int mode)
32         snd_ctl_t *handle;
33         int card, err, dev, idx;
34         snd_ctl_card_info_t *info;
35         snd_pcm_info_t *pcminfo;
36         char string[BCTEXTLEN];
37         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
38         int error;
39         switch(mode)
40         {
41                 case MODERECORD:
42                         stream = SND_PCM_STREAM_CAPTURE;
43                         break;
44                 case MODEPLAY:
45                         stream = SND_PCM_STREAM_PLAYBACK;
46                         break;
47         }
49         snd_ctl_card_info_alloca(&info);
50         snd_pcm_info_alloca(&pcminfo);
52         card = -1;
53 #define DEFAULT_DEVICE "default"
54         char *result = new char[strlen(DEFAULT_DEVICE) + 1];
55         devices->append(result);
56         devices->set_array_delete();     // since we are allocating by new[]
57         strcpy(result, DEFAULT_DEVICE);
59         while(snd_card_next(&card) >= 0)
60         {
61                 char name[BCTEXTLEN];
62                 if(card < 0) break;
63                 sprintf(name, "hw:%i", card);
65                 if((err = snd_ctl_open(&handle, name, 0)) < 0)
66                 {
67                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
68                         continue;
69                 }
71                 if((err = snd_ctl_card_info(handle, info)) < 0)
72                 {
73                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
74                         snd_ctl_close(handle);
75                         continue;
76                 }
78                 dev = -1;
80                 while(1)
81                 {
82                         unsigned int count;
83                         if(snd_ctl_pcm_next_device(handle, &dev) < 0)
84                                 printf("AudioALSA::list_devices: snd_ctl_pcm_next_device\n");
86                         if (dev < 0)
87                                 break;
89                         snd_pcm_info_set_device(pcminfo, dev);
90                         snd_pcm_info_set_subdevice(pcminfo, 0);
91                         snd_pcm_info_set_stream(pcminfo, stream);
93                         if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) 
94                         {
95                                 if(err != -ENOENT)
96                                         printf("AudioALSA::list_devices card=%i: %s\n", card, snd_strerror(err));
97                                 continue;
98                         }
100                         if(pcm_title)
101                         {
102                                 sprintf(string, "plughw:%d,%d", card, dev);
103 //                              strcpy(string, "cards.pcm.front");
104                         }
105                         else
106                         {
107                                 sprintf(string, "%s #%d", 
108                                         snd_ctl_card_info_get_name(info), 
109                                         dev);
110                         }
112                         char *result = devices->append(new char[strlen(string) + 1]);
113                         strcpy(result, string);
114                 }
116                 snd_ctl_close(handle);
117         }
119 //      snd_ctl_card_info_free(info);
120 //      snd_pcm_info_free(pcminfo);
123 void AudioALSA::translate_name(char *output, char *input)
125         ArrayList<char*> titles;
126         ArrayList<char*> pcm_titles;
127         int mode;
128         if(device->r) mode = MODERECORD;
129         else
130         if(device->w) mode = MODEPLAY;
131         
132         list_devices(&titles, 0, mode);
133         list_devices(&pcm_titles, 1, mode);
135         sprintf(output, "default");     
136         for(int i = 0; i < titles.total; i++)
137         {
138 //printf("AudioALSA::translate_name %s %s\n", titles.values[i], pcm_titles.values[i]);
139                 if(!strcasecmp(titles.values[i], input))
140                 {
141                         strcpy(output, pcm_titles.values[i]);
142                         break;
143                 }
144         }
145         
146         titles.remove_all_objects();
147         pcm_titles.remove_all_objects();
150 snd_pcm_format_t AudioALSA::translate_format(int format)
152         switch(format)
153         {
154                 case 8:
155                         return SND_PCM_FORMAT_S8;
156                         break;
157                 case 16:
158                         return SND_PCM_FORMAT_S16_LE;
159                         break;
160                 case 24:
161                         return SND_PCM_FORMAT_S24_LE;
162                         break;
163                 case 32:
164                         return SND_PCM_FORMAT_S32_LE;
165                         break;
166         }
169 void AudioALSA::set_params(snd_pcm_t *dsp, 
170         int channels, 
171         int bits,
172         int samplerate,
173         int samples)
175         snd_pcm_hw_params_t *params;
176         snd_pcm_sw_params_t *swparams;
177         int err;
179         snd_pcm_hw_params_alloca(&params);
180         snd_pcm_sw_params_alloca(&swparams);
181         err = snd_pcm_hw_params_any(dsp, params);
183         if (err < 0) 
184         {
185                 printf("AudioALSA::set_params: no PCM configurations available\n");
186                 return;
187         }
189         snd_pcm_hw_params_set_access(dsp, 
190                 params,
191                 SND_PCM_ACCESS_RW_INTERLEAVED);
192         snd_pcm_hw_params_set_format(dsp, 
193                 params, 
194                 translate_format(bits));
195         snd_pcm_hw_params_set_channels(dsp, 
196                 params, 
197                 channels);
198         snd_pcm_hw_params_set_rate_near(dsp, 
199                 params, 
200                 (unsigned int*)&samplerate, 
201                 (int*)0);
203 // Buffers written must be equal to period_time
204         int buffer_time;
205         int period_time;
206         if(device->r)
207         {
208                 buffer_time = 10000000;
209                 period_time = (int)((int64_t)samples * 1000000 / samplerate);
210         }
211         else
212         {
213                 buffer_time = (int)((int64_t)samples * 1000000 * 2 / samplerate + 0.5);
214                 period_time = samples * samplerate / 1000000;
215         }
218 //printf("AudioALSA::set_params 1 %d %d %d\n", samples, buffer_time, period_time);
219         snd_pcm_hw_params_set_buffer_time_near(dsp, 
220                 params,
221                 (unsigned int*)&buffer_time, 
222                 (int*)0);
223         snd_pcm_hw_params_set_period_time_near(dsp, 
224                 params,
225                 (unsigned int*)&period_time, 
226                 (int*)0);
227 //printf("AudioALSA::set_params 5 %d %d\n", buffer_time, period_time);
228         err = snd_pcm_hw_params(dsp, params);
229         if(err < 0)
230         {
231                 printf("AudioALSA::set_params: hw_params failed\n");
232                 return;
233         }
235         snd_pcm_uframes_t chunk_size = 1024;
236         snd_pcm_uframes_t buffer_size = 262144;
237         snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
238         snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
239 //printf("AudioALSA::set_params 10 %d %d\n", chunk_size, buffer_size);
241         snd_pcm_sw_params_current(dsp, swparams);
242         size_t xfer_align = 1 /* snd_pcm_sw_params_get_xfer_align(swparams) */;
243         unsigned int sleep_min = 0;
244         err = snd_pcm_sw_params_set_sleep_min(dsp, swparams, sleep_min);
245         int n = chunk_size;
246         err = snd_pcm_sw_params_set_avail_min(dsp, swparams, n);
247         err = snd_pcm_sw_params_set_xfer_align(dsp, swparams, xfer_align);
248         if(snd_pcm_sw_params(dsp, swparams) < 0)
249         {
250                 printf("AudioALSA::set_params: snd_pcm_sw_params failed\n");
251         }
253         device->device_buffer = samples * bits / 8 * channels;
255 //printf("AudioALSA::set_params 100 %d %d\n", samples,  device->device_buffer);
257 //      snd_pcm_hw_params_free(params);
258 //      snd_pcm_sw_params_free(swparams);
261 int AudioALSA::open_input()
263         char pcm_name[BCTEXTLEN];
264         snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
265         int open_mode = 0;
266         int err;
268         device->in_channels = device->get_ichannels();
269         device->in_bits = device->in_config->alsa_in_bits;
271         translate_name(pcm_name, device->in_config->alsa_in_device);
272 //printf("AudioALSA::open_input %s\n", pcm_name);
274 //      err = snd_pcm_open(&dsp_in, pcm_name, stream, open_mode);
275         err = snd_pcm_open(&dsp_in, device->in_config->alsa_in_device, stream, open_mode);
277         if(err < 0)
278         {
279                 printf("AudioALSA::open_input: %s\n", snd_strerror(err));
280                 return 1;
281         }
283         set_params(dsp_in, 
284                 device->get_ichannels(), 
285                 device->in_config->alsa_in_bits,
286                 device->in_samplerate,
287                 device->in_samples);
289         return 0;
292 int AudioALSA::open_output()
294         char pcm_name[BCTEXTLEN];
295         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
296         int open_mode = 0;
297         int err;
299         device->out_channels = device->get_ochannels();
300         device->out_bits = device->out_config->alsa_out_bits;
302         translate_name(pcm_name, device->out_config->alsa_out_device);
304 //      err = snd_pcm_open(&dsp_out, pcm_name, stream, open_mode);
305         err = snd_pcm_open(&dsp_out, device->out_config->alsa_out_device, stream, open_mode);
307         if(err < 0)
308         {
309                 dsp_out = 0;
310                 printf("AudioALSA::open_output %s: %s\n", pcm_name, snd_strerror(err));
311                 return 1;
312         }
314         set_params(dsp_out, 
315                 device->get_ochannels(), 
316                 device->out_config->alsa_out_bits,
317                 device->out_samplerate,
318                 device->out_samples);
319         timer->update();
320         return 0;
323 int AudioALSA::open_duplex()
325 // ALSA always opens 2 devices
326         return 0;
329 int AudioALSA::close_output()
331         if(device->w && dsp_out)
332         {
333                 snd_pcm_close(dsp_out);
334         }
335         return 0;
338 int AudioALSA::close_input()
340         if(device->r)
341         {
342 //              snd_pcm_reset(dsp_in);
343                 snd_pcm_drop(dsp_in);
344                 snd_pcm_drain(dsp_in);
345                 snd_pcm_close(dsp_in);
346         }
347         return 0;
350 int AudioALSA::close_all()
352         close_input();
353         close_output();
354         if(device->d)
355         {
356                 snd_pcm_close(dsp_duplex);
357         }
358         samples_written = 0;
359         delay = 0;
360         interrupted = 0;
363 // Undocumented
364 int64_t AudioALSA::device_position()
366         timer_lock->lock("AudioALSA::device_position");
367         int64_t result = samples_written + 
368                 timer->get_scaled_difference(device->out_samplerate) - 
369                 delay;
370 // printf("AudioALSA::device_position 1 %lld %lld %d %lld\n", 
371 // samples_written,
372 // timer->get_scaled_difference(device->out_samplerate),
373 // delay,
374 // samples_written + timer->get_scaled_difference(device->out_samplerate) - delay);
375         timer_lock->unlock();
376         return result;
379 int AudioALSA::read_buffer(char *buffer, int size)
381 //printf("AudioALSA::read_buffer 1\n");
382         int attempts = 0;
383         int done = 0;
385         if(!get_input())
386         {
387                 sleep(1);
388                 return 0;
389         }
391         while(attempts < 1 && !done)
392         {
393                 if(snd_pcm_readi(get_input(), 
394                         buffer, 
395                         size / (device->in_bits / 8) / device->get_ichannels()) < 0)
396                 {
397                         printf("AudioALSA::read_buffer overrun at sample %lld\n", 
398                                 device->total_samples_read);
399 //                      snd_pcm_resume(get_input());
400                         close_input();
401                         open_input();
402                         attempts++;
403                 }
404                 else
405                         done = 1;
406         }
407         return 0;
410 int AudioALSA::write_buffer(char *buffer, int size)
412 // Don't give up and drop the buffer on the first error.
413         int attempts = 0;
414         int done = 0;
415         int samples = size / (device->out_bits / 8) / device->get_ochannels();
417         if(!get_output()) return 0;
419         while(attempts < 2 && !done && !interrupted)
420         {
421 // Buffers written must be equal to period_time
422 // Update timing
423                 snd_pcm_sframes_t delay;
424                 snd_pcm_delay(get_output(), &delay);
425                 snd_pcm_avail_update(get_output());
427                 device->Thread::enable_cancel();
428                 if(snd_pcm_writei(get_output(), 
429                         buffer, 
430                         samples) < 0)
431                 {
432                         device->Thread::disable_cancel();
433                         printf("AudioALSA::write_buffer underrun at sample %lld\n",
434                                 device->current_position());
435 //                      snd_pcm_resume(get_output());
436                         close_output();
437                         open_output();
438                         attempts++;
439                 }
440                 else
441                 {
442                         device->Thread::disable_cancel();
443                         done = 1;
444                 }
445         }
447         if(done)
448         {
449                 timer_lock->lock("AudioALSA::write_buffer");
450                 this->delay = delay;
451                 timer->update();
452                 samples_written += samples;
453                 timer_lock->unlock();
454         }
455         return 0;
458 int AudioALSA::flush_device()
460         if(get_output()) snd_pcm_drain(get_output());
461         return 0;
464 int AudioALSA::interrupt_playback()
466         if(get_output()) 
467         {
468                 interrupted = 1;
469 // Interrupts the playback but may not have caused snd_pcm_writei to exit.
470 // With some soundcards it causes snd_pcm_writei to freeze for a few seconds.
471                 if(!device->out_config->interrupt_workaround)
472                         snd_pcm_drop(get_output());
474 // Makes sure the current buffer finishes before stopping.
475 //              snd_pcm_drain(get_output());
477 // The only way to ensure snd_pcm_writei exits, but
478 // got a lot of crashes when doing this.
479 //              device->Thread::cancel();
480         }
481         return 0;
485 snd_pcm_t* AudioALSA::get_output()
487         if(device->w) return dsp_out;
488         else
489         if(device->d) return dsp_duplex;
490         return 0;
493 snd_pcm_t* AudioALSA::get_input()
495         if(device->r) return dsp_in;
496         else
497         if(device->d) return dsp_duplex;
498         return 0;
501 #endif
503 //      Local Variables:
504 //      mode: C++
505 //      c-file-style: "linux"
506 //      End: