r726: Implementing ability to add textural info to the labels
[cinelerra_cv/mob.git] / cinelerra / audioalsa.C
blobb9529557bf94a584f54b4d6f58fd0368f8e4ded1
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         strcpy(result, DEFAULT_DEVICE);
47         while(snd_card_next(&card) >= 0)
48         {
49                 char name[BCTEXTLEN];
50                 if(card < 0) break;
51                 sprintf(name, "hw:%i", card);
53                 if((err = snd_ctl_open(&handle, name, 0)) < 0)
54                 {
55                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
56                         continue;
57                 }
59                 if((err = snd_ctl_card_info(handle, info)) < 0)
60                 {
61                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
62                         snd_ctl_close(handle);
63                         continue;
64                 }
66                 dev = -1;
68                 while(1)
69                 {
70                         unsigned int count;
71                         if(snd_ctl_pcm_next_device(handle, &dev) < 0)
72                                 printf("AudioALSA::list_devices: snd_ctl_pcm_next_device\n");
74                         if (dev < 0)
75                                 break;
77                         snd_pcm_info_set_device(pcminfo, dev);
78                         snd_pcm_info_set_subdevice(pcminfo, 0);
79                         snd_pcm_info_set_stream(pcminfo, stream);
81                         if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) 
82                         {
83                                 if(err != -ENOENT)
84                                         printf("AudioALSA::list_devices (%i): %s\n", card, snd_strerror(err));
85                                 continue;
86                         }
88                         if(pcm_title)
89                         {
90                                 sprintf(string, "plughw:%d,%d", card, dev);
91 //                              strcpy(string, "cards.pcm.front");
92                         }
93                         else
94                         {
95                                 sprintf(string, "%s #%d", 
96                                         snd_ctl_card_info_get_name(info), 
97                                         dev);
98                         }
100                         char *result = devices->append(new char[strlen(string) + 1]);
101                         strcpy(result, string);
102                 }
104                 snd_ctl_close(handle);
105         }
107 //      snd_ctl_card_info_free(info);
108 //      snd_pcm_info_free(pcminfo);
111 void AudioALSA::translate_name(char *output, char *input)
113         ArrayList<char*> titles;
114         ArrayList<char*> pcm_titles;
115         
116         list_devices(&titles, 0);
117         list_devices(&pcm_titles, 1);
119         sprintf(output, "default");     
120         for(int i = 0; i < titles.total; i++)
121         {
122 //printf("AudioALSA::translate_name %s %s\n", titles.values[i], pcm_titles.values[i]);
123                 if(!strcasecmp(titles.values[i], input))
124                 {
125                         strcpy(output, pcm_titles.values[i]);
126                         break;
127                 }
128         }
129         
130         titles.remove_all_objects();
131         pcm_titles.remove_all_objects();
134 snd_pcm_format_t AudioALSA::translate_format(int format)
136         switch(format)
137         {
138                 case 8:
139                         return SND_PCM_FORMAT_S8;
140                         break;
141                 case 16:
142                         return SND_PCM_FORMAT_S16_LE;
143                         break;
144                 case 24:
145                         return SND_PCM_FORMAT_S24_LE;
146                         break;
147                 case 32:
148                         return SND_PCM_FORMAT_S32_LE;
149                         break;
150         }
153 void AudioALSA::set_params(snd_pcm_t *dsp, 
154         int channels, 
155         int bits,
156         int samplerate,
157         int samples)
159         snd_pcm_hw_params_t *params;
160         snd_pcm_sw_params_t *swparams;
161         int err;
163         snd_pcm_hw_params_alloca(&params);
164         snd_pcm_sw_params_alloca(&swparams);
165         err = snd_pcm_hw_params_any(dsp, params);
167         if (err < 0) 
168         {
169                 printf("AudioALSA::set_params: no PCM configurations available\n");
170                 return;
171         }
173         snd_pcm_hw_params_set_access(dsp, 
174                 params,
175                 SND_PCM_ACCESS_RW_INTERLEAVED);
176         snd_pcm_hw_params_set_format(dsp, 
177                 params, 
178                 translate_format(bits));
179         snd_pcm_hw_params_set_channels(dsp, 
180                 params, 
181                 channels);
182         snd_pcm_hw_params_set_rate_near(dsp, 
183                 params, 
184                 (unsigned int*)&samplerate, 
185                 (int*)0);
187 // Buffers written must be equal to period_time
188         int buffer_time;
189         int period_time;
190         if(device->r)
191         {
192                 buffer_time = 10000000;
193                 period_time = (int)((int64_t)samples * 1000000 / samplerate);
194         }
195         else
196         {
197                 buffer_time = (int)((int64_t)samples * 1000000 * 2 / samplerate + 0.5);
198                 period_time = samples * samplerate / 1000000;
199         }
202 //printf("AudioALSA::set_params 1 %d %d %d\n", samples, buffer_time, period_time);
203         snd_pcm_hw_params_set_buffer_time_near(dsp, 
204                 params,
205                 (unsigned int*)&buffer_time, 
206                 (int*)0);
207         snd_pcm_hw_params_set_period_time_near(dsp, 
208                 params,
209                 (unsigned int*)&period_time, 
210                 (int*)0);
211 //printf("AudioALSA::set_params 5 %d %d\n", buffer_time, period_time);
212         err = snd_pcm_hw_params(dsp, params);
213         if(err < 0)
214         {
215                 printf("AudioALSA::set_params: hw_params failed\n");
216                 return;
217         }
219         snd_pcm_uframes_t chunk_size = 1024;
220         snd_pcm_uframes_t buffer_size = 262144;
221         snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
222         snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
223 //printf("AudioALSA::set_params 10 %d %d\n", chunk_size, buffer_size);
225         snd_pcm_sw_params_current(dsp, swparams);
226         size_t xfer_align = 1 /* snd_pcm_sw_params_get_xfer_align(swparams) */;
227         unsigned int sleep_min = 0;
228         err = snd_pcm_sw_params_set_sleep_min(dsp, swparams, sleep_min);
229         int n = chunk_size;
230         err = snd_pcm_sw_params_set_avail_min(dsp, swparams, n);
231         err = snd_pcm_sw_params_set_xfer_align(dsp, swparams, xfer_align);
232         if(snd_pcm_sw_params(dsp, swparams) < 0)
233         {
234                 printf("AudioALSA::set_params: snd_pcm_sw_params failed\n");
235         }
237         device->device_buffer = samples * bits / 8 * channels;
239 //printf("AudioALSA::set_params 100 %d %d\n", samples,  device->device_buffer);
241 //      snd_pcm_hw_params_free(params);
242 //      snd_pcm_sw_params_free(swparams);
245 int AudioALSA::open_input()
247         char pcm_name[BCTEXTLEN];
248         snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
249         int open_mode = 0;
250         int err;
252         device->in_channels = device->in_config->alsa_in_channels;
253         device->in_bits = device->in_config->alsa_in_bits;
255         translate_name(pcm_name, device->in_config->alsa_in_device);
257         err = snd_pcm_open(&dsp_in, pcm_name, stream, open_mode);
259         if(err < 0)
260         {
261                 printf("AudioALSA::open_input: %s\n", snd_strerror(err));
262                 return 1;
263         }
265         set_params(dsp_in, 
266                 device->in_config->alsa_in_channels, 
267                 device->in_config->alsa_in_bits,
268                 device->in_samplerate,
269                 device->in_samples);
271         return 0;
274 int AudioALSA::open_output()
276         char pcm_name[BCTEXTLEN];
277         snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
278         int open_mode = 0;
279         int err;
281         device->out_channels = device->out_config->alsa_out_channels;
282         device->out_bits = device->out_config->alsa_out_bits;
284         translate_name(pcm_name, device->out_config->alsa_out_device);
286         err = snd_pcm_open(&dsp_out, pcm_name, stream, open_mode);
288         if(err < 0)
289         {
290                 printf("AudioALSA::open_output %s: %s\n", pcm_name, snd_strerror(err));
291                 return 1;
292         }
294         set_params(dsp_out, 
295                 device->out_config->alsa_out_channels, 
296                 device->out_config->alsa_out_bits,
297                 device->out_samplerate,
298                 device->out_samples);
299         timer->update();
300         return 0;
303 int AudioALSA::open_duplex()
305 // ALSA always opens 2 devices
306         return 0;
309 int AudioALSA::close_output()
311         if(device->w)
312         {
313                 snd_pcm_close(dsp_out);
314         }
315         return 0;
318 int AudioALSA::close_input()
320         if(device->r)
321         {
322 //              snd_pcm_reset(dsp_in);
323                 snd_pcm_drop(dsp_in);
324                 snd_pcm_drain(dsp_in);
325                 snd_pcm_close(dsp_in);
326         }
327         return 0;
330 int AudioALSA::close_all()
332         close_input();
333         close_output();
334         if(device->d)
335         {
336                 snd_pcm_close(dsp_duplex);
337         }
338         samples_written = 0;
339         delay = 0;
340         interrupted = 0;
343 // Undocumented
344 int64_t AudioALSA::device_position()
346         timer_lock->lock("AudioALSA::device_position");
347         int64_t result = samples_written + 
348                 timer->get_scaled_difference(device->out_samplerate) - 
349                 delay;
350 // printf("AudioALSA::device_position 1 %lld %lld %d %lld\n", 
351 // samples_written,
352 // timer->get_scaled_difference(device->out_samplerate),
353 // delay,
354 // samples_written + timer->get_scaled_difference(device->out_samplerate) - delay);
355         timer_lock->unlock();
356         return result;
359 int AudioALSA::read_buffer(char *buffer, int size)
361 //printf("AudioALSA::read_buffer 1\n");
362         int attempts = 0;
363         int done = 0;
364         while(attempts < 1 && !done)
365         {
366                 if(snd_pcm_readi(get_input(), 
367                         buffer, 
368                         size / (device->in_bits / 8) / device->in_channels) < 0)
369                 {
370                         printf("AudioALSA::read_buffer overrun at sample %lld\n", 
371                                 device->total_samples_read);
372 //                      snd_pcm_resume(get_input());
373                         close_input();
374                         open_input();
375                         attempts++;
376                 }
377                 else
378                         done = 1;
379         }
380         return 0;
383 int AudioALSA::write_buffer(char *buffer, int size)
385 // Don't give up and drop the buffer on the first error.
386         int attempts = 0;
387         int done = 0;
388         int samples = size / (device->out_bits / 8) / device->out_channels;
389         while(attempts < 2 && !done && !interrupted)
390         {
391 // Buffers written must be equal to period_time
392 // Update timing
393                 snd_pcm_sframes_t delay;
394                 snd_pcm_delay(get_output(), &delay);
395                 snd_pcm_avail_update(get_output());
397                 device->Thread::enable_cancel();
398                 if(snd_pcm_writei(get_output(), 
399                         buffer, 
400                         samples) < 0)
401                 {
402                         device->Thread::disable_cancel();
403                         printf("AudioALSA::write_buffer underrun at sample %lld\n",
404                                 device->current_position());
405 //                      snd_pcm_resume(get_output());
406                         close_output();
407                         open_output();
408                         attempts++;
409                 }
410                 else
411                 {
412                         device->Thread::disable_cancel();
413                         done = 1;
414                 }
415         }
417         if(done)
418         {
419                 timer_lock->lock("AudioALSA::write_buffer");
420                 this->delay = delay;
421                 timer->update();
422                 samples_written += samples;
423                 timer_lock->unlock();
424         }
425         return 0;
428 int AudioALSA::flush_device()
430         if(get_output()) snd_pcm_drain(get_output());
431         return 0;
434 int AudioALSA::interrupt_playback()
436         if(get_output()) 
437         {
438                 interrupted = 1;
439 // Interrupts the playback but may not have caused snd_pcm_writei to exit.
440 // With some soundcards it causes snd_pcm_writei to freeze for a few seconds.
441                 if(!device->out_config->interrupt_workaround)
442                         snd_pcm_drop(get_output());
444 // Makes sure the current buffer finishes before stopping.
445 //              snd_pcm_drain(get_output());
447 // The only way to ensure snd_pcm_writei exits, but
448 // got a lot of crashes when doing this.
449 //              device->Thread::cancel();
450         }
451         return 0;
455 snd_pcm_t* AudioALSA::get_output()
457         if(device->w) return dsp_out;
458         else
459         if(device->d) return dsp_duplex;
460         return 0;
463 snd_pcm_t* AudioALSA::get_input()
465         if(device->r) return dsp_in;
466         else
467         if(device->d) return dsp_duplex;
468         return 0;
471 #endif