]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/alsa.c
65f0df7205528250578b69dde66f6acffb7190b4
[linphone] / linphone / mediastreamer2 / src / alsa.c
1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20
21
22 #include <alsa/asoundlib.h>
23
24
25 #include "mediastreamer2/msfilter.h"
26 #include "mediastreamer2/mssndcard.h"
27
28 //#define THREADED_VERSION
29
30 /*in case of troubles with a particular driver, try incrementing ALSA_PERIOD_SIZE
31 to 512, 1024, 2048, 4096...
32 then try incrementing the number of periods*/
33 #define ALSA_PERIODS 8
34 #define ALSA_PERIOD_SIZE 256
35
36 /*uncomment the following line if you have problems with an alsa driver
37 having sound quality trouble:*/
38 /*#define EPIPE_BUGFIX 1*/
39
40 static MSSndCard * alsa_card_new(int id);
41 static MSSndCard *alsa_card_duplicate(MSSndCard *obj);
42 static MSFilter * ms_alsa_read_new(const char *dev);
43 static MSFilter * ms_alsa_write_new(const char *dev);
44
45
46 struct _AlsaData{
47         char *pcmdev;
48         char *mixdev;
49 };
50
51 typedef struct _AlsaData AlsaData;
52
53
54 static int alsa_set_params(snd_pcm_t *pcm_handle, int rw, int bits, int stereo, int rate)
55 {
56         snd_pcm_hw_params_t *hwparams=NULL;
57         snd_pcm_sw_params_t *swparams=NULL;
58         int dir;
59         uint exact_uvalue;
60         unsigned long exact_ulvalue;
61         int channels;
62         int periods=ALSA_PERIODS;
63         int periodsize=ALSA_PERIOD_SIZE;
64         int err;
65         int format;
66         
67         /* Allocate the snd_pcm_hw_params_t structure on the stack. */
68         snd_pcm_hw_params_alloca(&hwparams);
69         
70         /* Init hwparams with full configuration space */
71         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
72                 ms_warning("alsa_set_params: Cannot configure this PCM device.");
73                 return -1;
74         }
75         
76         if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
77                 ms_warning("alsa_set_params: Error setting access.");
78                 return -1;
79         }
80         /* Set sample format */
81         format=SND_PCM_FORMAT_S16;
82         if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) {
83                 ms_warning("alsa_set_params: Error setting format.");
84                 return -1;
85         }
86         /* Set number of channels */
87         if (stereo) channels=2;
88         else channels=1;
89         if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) {
90                 ms_warning("alsa_set_params: Error setting channels.");
91                 return -1;
92         }
93         /* Set sample rate. If the exact rate is not supported */
94         /* by the hardware, use nearest possible rate.         */ 
95         exact_uvalue=rate;
96         dir=0;
97         if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_uvalue, &dir))<0){
98                 ms_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err));
99                 return -1;
100         }
101         if (dir != 0) {
102                 ms_warning("alsa_set_params: The rate %d Hz is not supported by your hardware.\n "
103                 "==> Using %d Hz instead.", rate, exact_uvalue);
104         }
105         /* choose greater period size when rate is high */
106         periodsize=periodsize*(rate/8000);      
107         
108         /* Set buffer size (in frames). The resulting latency is given by */
109         /* latency = periodsize * periods / (rate * bytes_per_frame)     */
110         /* set period size */
111         exact_ulvalue=periodsize;
112         dir=0;
113         if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_ulvalue, &dir) < 0) {
114                 ms_warning("alsa_set_params: Error setting period size.");
115                 return -1;
116         }
117         if (dir != 0) {
118                 ms_warning("alsa_set_params: The period size %d is not supported by your hardware.\n "
119                 "==> Using %d instead.", periodsize, (int)exact_ulvalue);
120         }
121         ms_warning("alsa_set_params: periodsize:%d Using %d", periodsize, (int)exact_ulvalue);
122         periodsize=exact_ulvalue;
123         /* Set number of periods. Periods used to be called fragments. */ 
124         exact_uvalue=periods;
125         dir=0;
126         if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_uvalue, &dir) < 0) {
127                 ms_warning("alsa_set_params: Error setting periods.");
128                 return -1;
129         }
130         ms_warning("alsa_set_params: period:%d Using %d", periods, exact_uvalue);
131         if (dir != 0) {
132                 ms_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n "
133                 "==> Using %d instead.", periods, exact_uvalue);
134         }
135         /* Apply HW parameter settings to */
136         /* PCM device and prepare device  */
137         if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
138                 ms_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err));
139                 return -1;
140         }
141         /*prepare sw params */
142         if (rw){
143                 snd_pcm_sw_params_alloca(&swparams);
144                 snd_pcm_sw_params_current(pcm_handle, swparams);
145                 if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){
146                         ms_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err));
147                 }
148                 if ((err=snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams,periodsize*periods ))<0){
149                         ms_warning("alsa_set_params: Error setting stop threshold:%s",snd_strerror(err));
150                 }
151                 if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){
152                         ms_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err));
153                         return -1;
154                 }
155         }
156         return 0;       
157 }
158
159 #ifdef EPIPE_BUGFIX
160 static void alsa_fill_w (snd_pcm_t *pcm_handle)
161 {
162         snd_pcm_hw_params_t *hwparams=NULL;
163         int channels;
164         snd_pcm_uframes_t buffer_size;
165         int buffer_size_bytes;
166         void *buffer;
167
168         /* Allocate the snd_pcm_hw_params_t structure on the stack. */
169         snd_pcm_hw_params_alloca(&hwparams);
170         snd_pcm_hw_params_current(pcm_handle, hwparams);
171
172         /* get channels */
173         snd_pcm_hw_params_get_channels (hwparams, &channels);
174
175         /* get buffer size */
176         snd_pcm_hw_params_get_buffer_size (hwparams, &buffer_size);
177
178         /* fill half */
179         buffer_size /= 2;
180
181         /* allocate buffer assuming 2 bytes per sample */
182         buffer_size_bytes = buffer_size * channels * 2;
183         buffer = alloca (buffer_size_bytes);
184         memset (buffer, 0, buffer_size_bytes);
185
186         /* write data */
187         snd_pcm_writei(pcm_handle, buffer, buffer_size);
188 }
189 #endif
190
191 static snd_pcm_t * alsa_open_r(const char *pcmdev,int bits,int stereo,int rate)
192 {
193         snd_pcm_t *pcm_handle;
194         int err;
195
196         ms_message("alsa_open_r: opening %s at %iHz, bits=%i, stereo=%i",pcmdev,rate,bits,stereo);
197
198
199 #ifndef THREADED_VERSION
200         if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) {
201                 ms_warning("alsa_open_r: Error opening PCM device %s",pcmdev );
202                 return NULL;
203         }
204 #else
205         /* want blocking mode for threaded version */
206         if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,0) < 0) {
207                 ms_warning("alsa_open_r: Error opening PCM device %s",pcmdev );
208                 return NULL;
209         }
210 #endif
211
212         {
213         struct timeval tv1;
214         struct timeval tv2;
215         struct timezone tz;
216         int diff = 0;
217         err = gettimeofday(&tv1, &tz);
218         while (1) { 
219                 if (!(alsa_set_params(pcm_handle,0,bits,stereo,rate)<0)){
220                         ms_message("alsa_open_r: Audio params set");
221                         break;
222                 }
223                 if (!gettimeofday(&tv2, &tz) && !err) {
224                         diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
225                 } else {
226                         diff = -1;
227                 }
228                 if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */
229                         ms_error("alsa_open_r: Error setting params for more than 3 seconds");
230                         snd_pcm_close(pcm_handle);
231                         return NULL;
232                 }
233                 ms_warning("alsa_open_r: Error setting params (for %d micros)", diff);
234                 usleep(200000);
235         }
236         }
237
238         err=snd_pcm_start(pcm_handle);
239         if (err<0){
240                 ms_warning("snd_pcm_start() failed: %s", snd_strerror(err));
241         }
242         return pcm_handle;
243 }
244
245 static snd_pcm_t * alsa_open_w(const char *pcmdev,int bits,int stereo,int rate)
246 {
247         snd_pcm_t *pcm_handle;
248         
249         if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) {
250                 ms_warning("alsa_open_w: Error opening PCM device %s",pcmdev );
251                 return NULL;
252         }
253         
254         {
255         struct timeval tv1;
256         struct timeval tv2;
257         struct timezone tz;
258         int diff = 0;
259         int err;
260         err = gettimeofday(&tv1, &tz);
261         while (1) { 
262                 if (!(alsa_set_params(pcm_handle,1,bits,stereo,rate)<0)){
263                         ms_message("alsa_open_w: Audio params set");
264                         break;
265                 }
266                 if (!gettimeofday(&tv2, &tz) && !err) {
267                         diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
268                 } else {
269                         diff = -1;
270                 }
271                 if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */
272                         ms_error("alsa_open_w: Error setting params for more than 3 seconds");
273                         snd_pcm_close(pcm_handle);
274                         return NULL;
275                 }
276                 ms_warning("alsa_open_w: Error setting params (for %d micros)", diff);
277                 usleep(200000);
278         }
279         }
280
281         return pcm_handle;
282 }
283
284 static int alsa_can_read(snd_pcm_t *dev)
285 {
286         snd_pcm_sframes_t avail;
287         int err;
288
289         avail = snd_pcm_avail_update(dev);
290         /* A buggy driver does not return an error while being in Xrun */
291         if (avail >= 0 && snd_pcm_state(dev) == SND_PCM_STATE_XRUN) avail=-EPIPE;
292         if (avail < 0) {
293                 ms_error("snd_pcm_avail_update: %s", snd_strerror(avail));      // most probably -EPIPE
294                 /* overrun occured, snd_pcm_state() would return SND_PCM_STATE_XRUN
295                  FIXME: handle other error conditions*/
296                 ms_error("*** alsa_can_read fixup, trying to recover");
297                 snd_pcm_drain(dev); /* Ignore possible error, at least -EAGAIN.*/
298                 err = snd_pcm_recover(dev, avail, 0);
299                 if (err){ 
300                         ms_error("snd_pcm_recover() failed with err %d: %s", err, snd_strerror(err));
301                         return -1;
302                 }
303                 err = snd_pcm_start(dev);
304                 if (err){ 
305                         ms_error("snd_pcm_start() failed with err %d: %s", err, snd_strerror(err)); 
306                         return -1; 
307                 }
308                 ms_message("Recovery done");
309         }
310         return avail;
311 }
312
313 static int alsa_read(snd_pcm_t *handle,unsigned char *buf,int nsamples)
314 {
315         int err;
316         err=snd_pcm_readi(handle,buf,nsamples);
317         if (err<0) {
318                 ms_warning("alsa_read: snd_pcm_readi() returned %i",err);
319                 if (err==-EPIPE){
320                         snd_pcm_prepare(handle);
321                         err=snd_pcm_readi(handle,buf,nsamples);
322                         if (err<0) ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
323                 }else if (err!=-EWOULDBLOCK){
324                         ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
325                 }
326         }else if (err==0){
327                 ms_warning("alsa_read: snd_pcm_readi() returned 0");
328         }
329         return err;
330 }
331
332
333 static int alsa_write(snd_pcm_t *handle,unsigned char *buf,int nsamples)
334 {
335         int err;
336         if ((err=snd_pcm_writei(handle,buf,nsamples))<0){
337                 if (err==-EPIPE){
338                         snd_pcm_prepare(handle);
339 #ifdef EPIPE_BUGFIX
340                         alsa_fill_w (handle);
341 #endif
342                         err=snd_pcm_writei(handle,buf,nsamples);
343                         if (err<0) ms_warning("alsa_card_write: Error writing sound buffer (nsamples=%i):%s",nsamples,snd_strerror(err));
344                 }else if (err!=-EWOULDBLOCK){
345                         ms_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err));
346                 }
347         }else if (err!=nsamples) {
348                 ms_debug("Only %i samples written instead of %i",err,nsamples);
349         }
350         return err;
351 }
352
353
354 static snd_mixer_t *alsa_mixer_open(const char *mixdev){
355         snd_mixer_t *mixer=NULL;
356         int err;
357         err=snd_mixer_open(&mixer,0);
358         if (err<0){
359                 ms_warning("Could not open alsa mixer: %s",snd_strerror(err));
360                 return NULL;
361         }
362         if ((err = snd_mixer_attach (mixer, mixdev)) < 0){
363                 ms_warning("Could not attach mixer to card: %s",snd_strerror(err));
364                 snd_mixer_close(mixer);
365                 return NULL;
366         }
367         if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){
368                 ms_warning("snd_mixer_selem_register: %s",snd_strerror(err));
369                 snd_mixer_close(mixer);
370                 return NULL;
371         }
372         if ((err = snd_mixer_load (mixer)) < 0){
373                 ms_warning("snd_mixer_load: %s",snd_strerror(err));
374                 snd_mixer_close(mixer);
375                 return NULL;
376         }
377         return mixer;
378 }
379
380 static void alsa_mixer_close(snd_mixer_t *mix){
381         snd_mixer_close(mix);
382 }
383
384 typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction;
385
386 static int get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){
387         long value=0;
388         const char *elemname;
389         snd_mixer_elem_t *elem;
390         int err;
391         long sndMixerPMin=0;
392         long sndMixerPMax=0;
393         long newvol=0;
394         elem=snd_mixer_first_elem(mixer);
395         while (elem!=NULL){
396                 elemname=snd_mixer_selem_get_name(elem);
397                 //ms_message("Found alsa mixer element %s.",elemname);
398                 if (strcmp(elemname,name)==0){
399                         switch (action){
400                                 case CAPTURE:
401                                 if (snd_mixer_selem_has_capture_volume(elem)){
402                                         snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
403                                         err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol);
404                                         newvol-=sndMixerPMin;
405                                         value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
406                                         if (err<0) ms_warning("Could not get capture volume for %s:%s",name,snd_strerror(err));
407                                         //else ms_message("Successfully get capture level for %s.",elemname);
408                                         break;
409                                 }
410                                 break;
411                                 case PLAYBACK:
412                                 if (snd_mixer_selem_has_playback_volume(elem)){
413                                         snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
414                                         err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol);
415                                         newvol-=sndMixerPMin;
416                                         value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
417                                         if (err<0) ms_warning("Could not get playback volume for %s:%s",name,snd_strerror(err));
418                                         //else ms_message("Successfully get playback level for %s.",elemname);
419                                         break;
420                                 }
421                                 break;
422                                 case CAPTURE_SWITCH:
423                                 
424                                 break;
425                                 case PLAYBACK_SWITCH:
426
427                                 break;
428                         }
429                 }
430                 elem=snd_mixer_elem_next(elem);
431         }
432         
433         return value;
434 }
435
436
437 static void set_mixer_element(snd_mixer_t *mixer,const char *name, int level,MixerAction action){
438         const char *elemname;
439         snd_mixer_elem_t *elem;
440         long sndMixerPMin=0;
441         long sndMixerPMax=0;
442         long newvol=0;
443         
444         elem=snd_mixer_first_elem(mixer);
445         
446         while (elem!=NULL){
447                 elemname=snd_mixer_selem_get_name(elem);
448                 //ms_message("Found alsa mixer element %s.",elemname);
449                 if (strcmp(elemname,name)==0){
450                         switch(action){
451                                 case CAPTURE:
452                                 if (snd_mixer_selem_has_capture_volume(elem)){
453                                         snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
454                                         newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
455                                         snd_mixer_selem_set_capture_volume_all(elem,newvol);
456                                         //ms_message("Successfully set capture level for %s.",elemname);
457                                         return;
458                                 }
459                                 break;
460                                 case PLAYBACK:
461                                 if (snd_mixer_selem_has_playback_volume(elem)){
462                                         snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
463                                         newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
464                                         snd_mixer_selem_set_playback_volume_all(elem,newvol);
465                                         //ms_message("Successfully set playback level for %s.",elemname);
466                                         return;
467                                 }
468                                 break;
469                                 case CAPTURE_SWITCH:
470                                 if (snd_mixer_selem_has_capture_switch(elem)){
471                                         snd_mixer_selem_set_capture_switch_all(elem,level);
472                                         //ms_message("Successfully set capture switch for %s.",elemname);
473                                 }
474                                 break;
475                                 case PLAYBACK_SWITCH:
476                                 if (snd_mixer_selem_has_playback_switch(elem)){
477                                         snd_mixer_selem_set_playback_switch_all(elem,level);
478                                         //ms_message("Successfully set capture switch for %s.",elemname);
479                                 }
480                                 break;
481
482                         }
483                 }
484                 elem=snd_mixer_elem_next(elem);
485         }
486
487         return ;
488 }
489
490
491 static void alsa_card_set_level(MSSndCard *obj,MSSndCardMixerElem e,int a)
492 {       
493         snd_mixer_t *mixer;
494         AlsaData *ad=(AlsaData*)obj->data;
495         mixer=alsa_mixer_open(ad->mixdev);
496         if (mixer==NULL) return ;
497         switch(e){
498                 case MS_SND_CARD_MASTER:
499                         set_mixer_element(mixer,"Master",a,PLAYBACK);
500                 break;
501                 case MS_SND_CARD_CAPTURE:
502                         set_mixer_element(mixer,"Capture",a,CAPTURE);
503                 break;
504                 case MS_SND_CARD_PLAYBACK:
505                         set_mixer_element(mixer,"PCM",a,PLAYBACK);
506                 break;
507                 default:
508                         ms_warning("alsa_card_set_level: unsupported command.");
509         }
510         alsa_mixer_close(mixer);
511 }
512
513 static int alsa_card_get_level(MSSndCard *obj, MSSndCardMixerElem e)
514 {
515         snd_mixer_t *mixer;
516         AlsaData *ad=(AlsaData*)obj->data;
517         int value = -1;
518         mixer=alsa_mixer_open(ad->mixdev);
519         if (mixer==NULL) return 0;
520         switch(e){
521                 case MS_SND_CARD_MASTER:
522                         value=get_mixer_element(mixer,"Master",PLAYBACK);
523                         break;
524                 case MS_SND_CARD_CAPTURE:
525                         value=get_mixer_element(mixer,"Capture",CAPTURE);
526                         break;
527                 case MS_SND_CARD_PLAYBACK:
528                         value=get_mixer_element(mixer,"PCM",PLAYBACK);
529                         break;
530                 default:
531                         ms_warning("alsa_card_set_level: unsupported command.");
532         }
533         alsa_mixer_close(mixer);
534         return value;
535 }
536
537 static void alsa_card_set_source(MSSndCard *obj,MSSndCardCapture source)
538 {
539         snd_mixer_t *mixer;
540         AlsaData *ad=(AlsaData*)obj->data;
541         mixer=alsa_mixer_open(ad->mixdev);
542         if (mixer==NULL) return;
543         switch (source){
544                 case MS_SND_CARD_MIC:
545                         set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH);
546                         set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
547                         break;
548                 case MS_SND_CARD_LINE:
549                         set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH);
550                         set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
551                         break;
552         }
553         alsa_mixer_close(mixer);
554 }
555
556 static MSFilter *alsa_card_create_reader(MSSndCard *card)
557 {
558         AlsaData *ad=(AlsaData*)card->data;
559         MSFilter *f=ms_alsa_read_new(ad->pcmdev);
560         return f;
561 }
562
563 static MSFilter *alsa_card_create_writer(MSSndCard *card)
564 {
565         AlsaData *ad=(AlsaData*)card->data;
566         MSFilter *f=ms_alsa_write_new(ad->pcmdev);
567         return f;
568 }
569
570
571 static void alsa_card_init(MSSndCard *obj){
572         AlsaData *ad=ms_new0(AlsaData,1);
573         obj->data=ad;
574 }
575
576 static void alsa_card_uninit(MSSndCard *obj){
577         AlsaData *ad=(AlsaData*)obj->data;
578         if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
579         if (ad->mixdev!=NULL) ms_free(ad->mixdev);
580         ms_free(ad);
581 }
582
583 static void alsa_card_detect(MSSndCardManager *m){
584         int i;
585         for (i=-1;i<10;i++){
586                 MSSndCard *card=alsa_card_new(i);
587                 if (card!=NULL)
588                         ms_snd_card_manager_add_card(m,card);
589         }
590 }
591
592 MSSndCardDesc alsa_card_desc={
593         .driver_type="ALSA",
594         .detect=alsa_card_detect,
595         .init=alsa_card_init,
596         .set_level=alsa_card_set_level,
597         .get_level=alsa_card_get_level,
598         .set_capture=alsa_card_set_source,
599         .set_control=NULL,
600         .get_control=NULL,
601         .create_reader=alsa_card_create_reader,
602         .create_writer=alsa_card_create_writer,
603         .uninit=alsa_card_uninit,
604         .duplicate=alsa_card_duplicate
605 };
606
607 static MSSndCard *alsa_card_duplicate(MSSndCard *obj){
608         MSSndCard *card=ms_snd_card_new(&alsa_card_desc);
609         AlsaData* dcard=(AlsaData*)card->data;
610         AlsaData* dobj=(AlsaData*)obj->data;
611         card->name=ms_strdup(obj->name);
612         card->id=ms_strdup(obj->id);
613         dcard->pcmdev=ms_strdup(dobj->pcmdev);
614         dcard->mixdev=ms_strdup(dobj->mixdev);
615         return card;
616 }
617
618 MSSndCard * ms_alsa_card_new_custom(const char *pcmdev, const char *mixdev){
619         MSSndCard * obj;
620         AlsaData *ad;
621         obj=ms_snd_card_new(&alsa_card_desc);
622         ad=(AlsaData*)obj->data;
623         obj->name=ms_strdup(pcmdev);
624         ad->pcmdev=ms_strdup(pcmdev);
625         ad->mixdev=ms_strdup(mixdev);
626         return obj;
627 }
628
629 static unsigned int get_card_capabilities(const char *devname){
630         snd_pcm_t *pcm_handle;
631         unsigned int ret=0;
632         if (snd_pcm_open(&pcm_handle,devname,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK)==0) {
633                 ret|=MS_SND_CARD_CAP_CAPTURE;
634                 snd_pcm_close(pcm_handle);
635         }
636         if (snd_pcm_open(&pcm_handle,devname,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK)==0) {
637                 ret|=MS_SND_CARD_CAP_PLAYBACK;
638                 snd_pcm_close(pcm_handle);
639         }
640         return ret;
641 }
642
643 static MSSndCard * alsa_card_new(int id)
644 {
645         MSSndCard * obj;
646         char *name=NULL;
647         AlsaData *ad;
648         int err;
649         
650         if (id!=-1){
651                 err=snd_card_get_name(id,&name);
652                 if (err<0) {
653                         return NULL;
654                 }
655         }
656         obj=ms_snd_card_new(&alsa_card_desc);
657         ad=(AlsaData*)obj->data;
658         if (id==-1) {
659                 /* the default pcm device */
660                 obj->name=ms_strdup("default device");
661                 ad->pcmdev=ms_strdup("default");
662                 ad->mixdev=ms_strdup("default");
663         }else{
664                 /* remove trailing spaces from card name */
665                 char *pos1, *pos2;
666                 pos1=ms_strdup(name);
667                 pos2=pos1+strlen(pos1)-1;
668                 for (; pos2>pos1 && *pos2==' '; pos2--) *pos2='\0';
669                 obj->name=pos1;
670                 ad->pcmdev=ms_strdup_printf("default:%i",id);
671                 ad->mixdev=ms_strdup_printf("default:%i",id);
672                 {
673                         snd_mixer_t *mixer;
674                         mixer = alsa_mixer_open(ad->mixdev);
675                         if (mixer==NULL) {
676                                 ms_free(ad->mixdev);
677                                 ad->mixdev=ms_strdup_printf("hw:%i",id);
678                         } else {
679                                 alsa_mixer_close(mixer);
680                         }
681                 }
682         }
683         /*check card capabilities: */
684         obj->capabilities=get_card_capabilities(ad->pcmdev);
685         if (obj->capabilities==0){
686                 ms_warning("Strange, sound card %s does not seems to be capable of anything, retrying with plughw...",obj->name);
687                 /*retry with plughw: this workarounds an alsa bug*/
688                 ms_free(ad->pcmdev);
689                 ad->pcmdev=ms_strdup_printf("plughw:%i",id);
690                 obj->capabilities=get_card_capabilities(ad->pcmdev);
691                 if (obj->capabilities==0){
692                         ms_warning("Strange, sound card %s seems totally unusable.",obj->name);
693                 }
694         }
695         free(name);
696         /*ms_message("alsa device %s found",obj->name);*/
697         return obj;
698 }
699
700 struct _AlsaReadData{
701         char *pcmdev;
702         snd_pcm_t *handle;
703         int rate;
704         int nchannels;
705
706 #ifdef THREADED_VERSION
707         ms_thread_t thread;
708         ms_mutex_t mutex;
709         MSBufferizer * bufferizer;
710         bool_t read_started;
711         bool_t write_started;
712 #endif
713 };
714
715 typedef struct _AlsaReadData AlsaReadData;
716
717 void alsa_read_init(MSFilter *obj){
718         AlsaReadData *ad=ms_new(AlsaReadData,1);
719         ad->pcmdev=NULL;
720         ad->handle=NULL;
721         ad->rate=8000;
722         ad->nchannels=1;
723         obj->data=ad;
724
725 #ifdef THREADED_VERSION
726         ad->read_started=FALSE;
727         ad->write_started=FALSE;
728         ad->bufferizer=ms_bufferizer_new();
729         ms_mutex_init(&ad->mutex,NULL);
730         ad->thread=0;
731 #endif
732 }
733
734 #ifdef THREADED_VERSION
735
736 static void * alsa_write_thread(void *p){
737         AlsaReadData *ad=(AlsaReadData*)p;
738         int samples=(160*ad->rate)/8000;
739         int err;
740         int count=0;
741         mblk_t *om=NULL;
742         struct timeval timeout;
743         if (ad->handle==NULL && ad->pcmdev!=NULL){
744                 ad->handle=alsa_open_r(ad->pcmdev,16,ad->nchannels==2,ad->rate);
745         }
746         if (ad->handle==NULL) return NULL;
747
748         while (ad->read_started)
749           {
750             count = alsa_can_read(ad->handle,samples);
751             if (count==24)
752               { /* keep this value for this driver */ }
753             else if (count<=0)
754               {
755                 count = samples;
756               }
757             else if (count>0)
758               {
759                 //ms_warning("%i count", count);
760                 //count = samples;
761               }
762
763             int size=count*2;
764             om=allocb(size,0);
765
766             if ((err=alsa_read(ad->handle,om->b_wptr,count))<=0)
767               {
768                 ms_warning("nothing to read");
769                 //ms_warning("Fail to read samples %i", count);
770                 freemsg(om); /* leak fixed */
771                 continue;
772               }
773             //ms_warning(" read %i", err);
774             
775             size=err*2;
776             om->b_wptr+=size;
777
778             ms_mutex_lock(&ad->mutex);
779             ms_bufferizer_put(ad->bufferizer,om);
780             ms_mutex_unlock(&ad->mutex);
781
782             if (count==24)
783               {
784                 timeout.tv_sec = 0;
785                 timeout.tv_usec = 2000;
786                 select(0, 0, NULL, NULL, &timeout );
787               }
788             else
789               {
790                 /* select will be less active than locking on "read" */
791                 timeout.tv_sec = 0;
792                 timeout.tv_usec = 5000;
793                 select(0, 0, NULL, NULL, &timeout );
794               }
795           }
796
797         if (ad->handle!=NULL) snd_pcm_close(ad->handle);
798         ad->handle=NULL;
799         return NULL;
800 }
801
802 static void alsa_start_r(AlsaReadData *d){
803         if (d->read_started==FALSE){
804                 d->read_started=TRUE;
805                 ms_thread_create(&d->thread,NULL,alsa_write_thread,d);
806         }else d->read_started=TRUE;
807 }
808
809 static void alsa_stop_r(AlsaReadData *d){
810         d->read_started=FALSE;
811         if (d->thread!=0)
812           {
813             ms_thread_join(d->thread,NULL);
814             d->thread=0;
815           }
816 }
817 #endif
818
819 #ifdef THREADED_VERSION
820 void alsa_read_preprocess(MSFilter *obj){
821         AlsaReadData *ad=(AlsaReadData*)obj->data;
822         alsa_start_r(ad);
823 }
824 #endif
825
826 void alsa_read_postprocess(MSFilter *obj){
827         AlsaReadData *ad=(AlsaReadData*)obj->data;
828 #ifdef THREADED_VERSION
829         alsa_stop_r(ad);
830 #endif
831         if (ad->handle!=NULL) snd_pcm_close(ad->handle);
832         ad->handle=NULL;
833 }
834
835 void alsa_read_uninit(MSFilter *obj){
836         AlsaReadData *ad=(AlsaReadData*)obj->data;
837 #ifdef THREADED_VERSION
838         alsa_stop_r(ad);
839 #endif
840         if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
841         if (ad->handle!=NULL) snd_pcm_close(ad->handle);
842 #ifdef THREADED_VERSION
843         ms_bufferizer_destroy(ad->bufferizer);
844         ms_mutex_destroy(&ad->mutex);
845 #endif
846         ms_free(ad);
847 }
848
849 #ifndef THREADED_VERSION
850 void alsa_read_process(MSFilter *obj){
851         AlsaReadData *ad=(AlsaReadData*)obj->data;
852         int samples=(128*ad->rate)/8000;
853         int err;
854         mblk_t *om=NULL;
855         if (ad->handle==NULL && ad->pcmdev!=NULL){
856                 ad->handle=alsa_open_r(ad->pcmdev,16,ad->nchannels==2,ad->rate);
857         }
858         if (ad->handle==NULL) return;
859         while (alsa_can_read(ad->handle)>=samples){
860           
861                 int size=samples*2;
862                 om=allocb(size,0);
863                 if ((err=alsa_read(ad->handle,om->b_wptr,samples))<=0) {
864                         ms_warning("Fail to read samples");
865                         freemsg(om);
866                         return;
867                 }
868                 size=err*2;
869                 om->b_wptr+=size;
870                 /*ms_message("alsa_read_process: Outputing %i bytes",size);*/
871                 ms_queue_put(obj->outputs[0],om);
872         }
873 }
874 #endif
875
876 #ifdef THREADED_VERSION
877 void alsa_read_process(MSFilter *obj){
878         AlsaReadData *ad=(AlsaReadData*)obj->data;
879         mblk_t *om=NULL;
880         int samples=(160*ad->rate)/8000;
881
882         ms_mutex_lock(&ad->mutex);
883         while (ms_bufferizer_get_avail(ad->bufferizer)>=samples*2){
884           
885           om=allocb(samples*2,0);
886           ms_bufferizer_read(ad->bufferizer,om->b_wptr,samples*2);        
887           om->b_wptr+=samples*2;
888           /*ms_message("alsa_read_process: Outputing %i bytes",size);*/
889           ms_queue_put(obj->outputs[0],om);
890         }
891         ms_mutex_unlock(&ad->mutex);
892 }
893 #endif
894
895 static int alsa_read_get_sample_rate(MSFilter *obj, void *param){
896         AlsaReadData *ad=(AlsaReadData*)obj->data;
897         *((int*)param)=ad->rate;
898         return 0;
899 }
900
901 static int alsa_read_set_sample_rate(MSFilter *obj, void *param){
902         AlsaReadData *ad=(AlsaReadData*)obj->data;
903         ad->rate=*((int*)param);
904         return 0;
905 }
906
907 static int alsa_read_set_nchannels(MSFilter *obj, void *param){
908         AlsaReadData *ad=(AlsaReadData*)obj->data;
909         ad->nchannels=*((int*)param);
910         return 0;
911 }
912
913 MSFilterMethod alsa_read_methods[]={
914         {MS_FILTER_GET_SAMPLE_RATE,     alsa_read_get_sample_rate},
915         {MS_FILTER_SET_SAMPLE_RATE, alsa_read_set_sample_rate},
916         {MS_FILTER_SET_NCHANNELS, alsa_read_set_nchannels},
917         {0,NULL}
918 };
919
920 MSFilterDesc alsa_read_desc={
921         .id=MS_ALSA_READ_ID,
922         .name="MSAlsaRead",
923         .text=N_("Alsa sound source"),
924         .category=MS_FILTER_OTHER,
925         .ninputs=0,
926         .noutputs=1,
927         .init=alsa_read_init,
928 #ifdef THREADED_VERSION
929         .preprocess=alsa_read_preprocess,
930 #endif
931         .process=alsa_read_process,
932         .postprocess=alsa_read_postprocess,
933         .uninit=alsa_read_uninit,
934         .methods=alsa_read_methods
935 };
936
937 static MSFilter * ms_alsa_read_new(const char *dev){
938         MSFilter *f=ms_filter_new_from_desc(&alsa_read_desc);
939         AlsaReadData *ad=(AlsaReadData*)f->data;
940         ad->pcmdev=ms_strdup(dev);
941         return f;
942 }
943
944 typedef struct _AlsaReadData AlsaWriteData;
945
946 void alsa_write_init(MSFilter *obj){
947         AlsaWriteData *ad=ms_new(AlsaWriteData,1);
948         ad->pcmdev=NULL;
949         ad->handle=NULL;
950         ad->rate=8000;
951         ad->nchannels=1;
952         obj->data=ad;
953 }
954
955 void alsa_write_postprocess(MSFilter *obj){
956         AlsaReadData *ad=(AlsaReadData*)obj->data;
957         if (ad->handle!=NULL) snd_pcm_close(ad->handle);
958         ad->handle=NULL;
959 }
960
961 void alsa_write_uninit(MSFilter *obj){
962         AlsaWriteData *ad=(AlsaWriteData*)obj->data;
963         if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
964         if (ad->handle!=NULL) snd_pcm_close(ad->handle);
965         ms_free(ad);
966 }
967
968 static int alsa_write_get_sample_rate(MSFilter *obj, void *data){
969         AlsaWriteData *ad=(AlsaWriteData*)obj->data;
970         *((int*)data)=ad->rate;
971         return 0;
972 }
973
974 int alsa_write_set_sample_rate(MSFilter *obj, void *data){
975         int *rate=(int*)data;
976         AlsaWriteData *ad=(AlsaWriteData*)obj->data;
977         ad->rate=*rate;
978         return 0;
979 }
980
981 int alsa_write_set_nchannels(MSFilter *obj, void *data){
982         int *n=(int*)data;
983         AlsaWriteData *ad=(AlsaWriteData*)obj->data;
984         ad->nchannels=*n;
985         return 0;
986 }
987
988 void alsa_write_process(MSFilter *obj){
989         AlsaWriteData *ad=(AlsaWriteData*)obj->data;
990         mblk_t *im=NULL;
991         int size;
992         int samples;
993         int err;
994         if (ad->handle==NULL && ad->pcmdev!=NULL){
995                 ad->handle=alsa_open_w(ad->pcmdev,16,ad->nchannels==2,ad->rate);
996 #ifdef EPIPE_BUGFIX
997                 alsa_fill_w (ad->pcmdev);
998 #endif
999         }
1000         if (ad->handle==NULL) {
1001                 ms_queue_flush(obj->inputs[0]);
1002                 return;
1003         }
1004         while ((im=ms_queue_get(obj->inputs[0]))!=NULL){
1005                 while((size=im->b_wptr-im->b_rptr)>0){
1006                         samples=size/(2*ad->nchannels);
1007                         err=alsa_write(ad->handle,im->b_rptr,samples);
1008                         if (err>0) {
1009                                 im->b_rptr+=err*(2*ad->nchannels);
1010                         }
1011                         else break;
1012                 }
1013                 freemsg(im);
1014         }
1015 }
1016
1017 MSFilterMethod alsa_write_methods[]={
1018         {MS_FILTER_GET_SAMPLE_RATE,     alsa_write_get_sample_rate},
1019         {MS_FILTER_SET_SAMPLE_RATE, alsa_write_set_sample_rate},
1020         {MS_FILTER_SET_NCHANNELS, alsa_write_set_nchannels},
1021         {0,NULL}
1022 };
1023
1024 MSFilterDesc alsa_write_desc={
1025         .id=MS_ALSA_WRITE_ID,
1026         .name="MSAlsaWrite",
1027         .text=N_("Alsa sound output"),
1028         .category=MS_FILTER_OTHER,
1029         .ninputs=1,
1030         .noutputs=0,
1031         .init=alsa_write_init,
1032         .process=alsa_write_process,
1033         .postprocess=alsa_write_postprocess,
1034         .uninit=alsa_write_uninit,
1035         .methods=alsa_write_methods
1036 };
1037
1038
1039 static MSFilter * ms_alsa_write_new(const char *dev){
1040         MSFilter *f=ms_filter_new_from_desc(&alsa_write_desc);
1041         AlsaWriteData *ad=(AlsaWriteData*)f->data;
1042         ad->pcmdev=ms_strdup(dev);
1043         return f;
1044 }
1045
1046
1047 MS_FILTER_DESC_EXPORT(alsa_write_desc)
1048
1049 MS_FILTER_DESC_EXPORT(alsa_read_desc)
1050