source: mediastreamer2/linphone/mediastreamer2/src/alsa.c @ 173:dd88e7fefb9b

Last change on this file since 173:dd88e7fefb9b was 173:dd88e7fefb9b, checked in by smorlat <smorlat@…>, 5 years ago

plenty of improvements

git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@176 3f6dc0c8-ddfe-455d-9043-3cd528dc4637

File size: 26.8 KB
Line 
1/*
2mediastreamer2 library - modular sound and video processing and streaming
3Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
31to 512, 1024, 2048, 4096...
32then 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
37having sound quality trouble:*/
38/*#define EPIPE_BUGFIX 1*/
39
40static MSSndCard * alsa_card_new(int id);
41static MSSndCard *alsa_card_duplicate(MSSndCard *obj);
42static MSFilter * ms_alsa_read_new(const char *dev);
43static MSFilter * ms_alsa_write_new(const char *dev);
44
45
46struct _AlsaData{
47        char *pcmdev;
48        char *mixdev;
49};
50
51typedef struct _AlsaData AlsaData;
52
53
54static 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
160static 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
191static 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
245static 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
284static int alsa_can_read(snd_pcm_t *dev, int frames)
285{
286        snd_pcm_sframes_t avail;
287        int err;
288
289        avail = snd_pcm_avail_update(dev);
290        ms_debug("*** %s %d %d", __FUNCTION__, (long)avail, frames);
291        if (avail < 0) {
292                ms_error("snd_pcm_avail_update: %s", snd_strerror(avail));      // most probably -EPIPE
293                /* overrun occured, snd_pcm_state() would return SND_PCM_STATE_XRUN
294                 FIXME: handle other error conditions*/
295                ms_error("*** alsa_can_read fixup, trying to recover");
296                snd_pcm_drain(dev); /* Ignore possible error, at least -EAGAIN.*/
297                err = snd_pcm_recover(dev, avail, 0);
298                if (err){ 
299                        ms_error("snd_pcm_recover() failed with err %d: %s", err, snd_strerror(err));
300                        return -1;
301                }
302                err = snd_pcm_start(dev);
303                if (err){ 
304                        ms_error("snd_pcm_start() failed with err %d: %s", err, snd_strerror(err)); 
305                        return -1; 
306                }
307                ms_message("Recovery done");
308        }
309        return avail;
310}
311
312static int alsa_read(snd_pcm_t *handle,unsigned char *buf,int nsamples)
313{
314        int err;
315        err=snd_pcm_readi(handle,buf,nsamples);
316        if (err<0) {
317                ms_warning("alsa_read: snd_pcm_readi() returned %i",err);
318                if (err==-EPIPE){
319                        snd_pcm_prepare(handle);
320                        err=snd_pcm_readi(handle,buf,nsamples);
321                        if (err<0) ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
322                }else if (err!=-EWOULDBLOCK){
323                        ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
324                }
325        }else if (err==0){
326                ms_warning("alsa_read: snd_pcm_readi() returned 0");
327        }
328        return err;
329}
330
331
332static int alsa_write(snd_pcm_t *handle,unsigned char *buf,int nsamples)
333{
334        int err;
335        if ((err=snd_pcm_writei(handle,buf,nsamples))<0){
336                if (err==-EPIPE){
337                        snd_pcm_prepare(handle);
338#ifdef EPIPE_BUGFIX
339                        alsa_fill_w (handle);
340#endif
341                        err=snd_pcm_writei(handle,buf,nsamples);
342                        if (err<0) ms_warning("alsa_card_write: Error writing sound buffer (nsamples=%i):%s",nsamples,snd_strerror(err));
343                }else if (err!=-EWOULDBLOCK){
344                        ms_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err));
345                }
346        }else if (err!=nsamples) {
347                ms_debug("Only %i samples written instead of %i",err,nsamples);
348        }
349        return err;
350}
351
352
353static snd_mixer_t *alsa_mixer_open(const char *mixdev){
354        snd_mixer_t *mixer=NULL;
355        int err;
356        err=snd_mixer_open(&mixer,0);
357        if (err<0){
358                ms_warning("Could not open alsa mixer: %s",snd_strerror(err));
359                return NULL;
360        }
361        if ((err = snd_mixer_attach (mixer, mixdev)) < 0){
362                ms_warning("Could not attach mixer to card: %s",snd_strerror(err));
363                snd_mixer_close(mixer);
364                return NULL;
365        }
366        if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){
367                ms_warning("snd_mixer_selem_register: %s",snd_strerror(err));
368                snd_mixer_close(mixer);
369                return NULL;
370        }
371        if ((err = snd_mixer_load (mixer)) < 0){
372                ms_warning("snd_mixer_load: %s",snd_strerror(err));
373                snd_mixer_close(mixer);
374                return NULL;
375        }
376        return mixer;
377}
378
379static void alsa_mixer_close(snd_mixer_t *mix){
380        snd_mixer_close(mix);
381}
382
383typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction;
384
385static int get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){
386        long value=0;
387        const char *elemname;
388        snd_mixer_elem_t *elem;
389        int err;
390        long sndMixerPMin=0;
391        long sndMixerPMax=0;
392        long newvol=0;
393        elem=snd_mixer_first_elem(mixer);
394        while (elem!=NULL){
395                elemname=snd_mixer_selem_get_name(elem);
396                //ms_message("Found alsa mixer element %s.",elemname);
397                if (strcmp(elemname,name)==0){
398                        switch (action){
399                                case CAPTURE:
400                                if (snd_mixer_selem_has_capture_volume(elem)){
401                                        snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
402                                        err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol);
403                                        newvol-=sndMixerPMin;
404                                        value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
405                                        if (err<0) ms_warning("Could not get capture volume for %s:%s",name,snd_strerror(err));
406                                        //else ms_message("Successfully get capture level for %s.",elemname);
407                                        break;
408                                }
409                                break;
410                                case PLAYBACK:
411                                if (snd_mixer_selem_has_playback_volume(elem)){
412                                        snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
413                                        err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol);
414                                        newvol-=sndMixerPMin;
415                                        value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
416                                        if (err<0) ms_warning("Could not get playback volume for %s:%s",name,snd_strerror(err));
417                                        //else ms_message("Successfully get playback level for %s.",elemname);
418                                        break;
419                                }
420                                break;
421                                case CAPTURE_SWITCH:
422                               
423                                break;
424                                case PLAYBACK_SWITCH:
425
426                                break;
427                        }
428                }
429                elem=snd_mixer_elem_next(elem);
430        }
431       
432        return value;
433}
434
435
436static void set_mixer_element(snd_mixer_t *mixer,const char *name, int level,MixerAction action){
437        const char *elemname;
438        snd_mixer_elem_t *elem;
439        long sndMixerPMin=0;
440        long sndMixerPMax=0;
441        long newvol=0;
442       
443        elem=snd_mixer_first_elem(mixer);
444       
445        while (elem!=NULL){
446                elemname=snd_mixer_selem_get_name(elem);
447                //ms_message("Found alsa mixer element %s.",elemname);
448                if (strcmp(elemname,name)==0){
449                        switch(action){
450                                case CAPTURE:
451                                if (snd_mixer_selem_has_capture_volume(elem)){
452                                        snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
453                                        newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
454                                        snd_mixer_selem_set_capture_volume_all(elem,newvol);
455                                        //ms_message("Successfully set capture level for %s.",elemname);
456                                        return;
457                                }
458                                break;
459                                case PLAYBACK:
460                                if (snd_mixer_selem_has_playback_volume(elem)){
461                                        snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
462                                        newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
463                                        snd_mixer_selem_set_playback_volume_all(elem,newvol);
464                                        //ms_message("Successfully set playback level for %s.",elemname);
465                                        return;
466                                }
467                                break;
468                                case CAPTURE_SWITCH:
469                                if (snd_mixer_selem_has_capture_switch(elem)){
470                                        snd_mixer_selem_set_capture_switch_all(elem,level);
471                                        //ms_message("Successfully set capture switch for %s.",elemname);
472                                }
473                                break;
474                                case PLAYBACK_SWITCH:
475                                if (snd_mixer_selem_has_playback_switch(elem)){
476                                        snd_mixer_selem_set_playback_switch_all(elem,level);
477                                        //ms_message("Successfully set capture switch for %s.",elemname);
478                                }
479                                break;
480
481                        }
482                }
483                elem=snd_mixer_elem_next(elem);
484        }
485
486        return ;
487}
488
489
490static void alsa_card_set_level(MSSndCard *obj,MSSndCardMixerElem e,int a)
491{       
492        snd_mixer_t *mixer;
493        AlsaData *ad=(AlsaData*)obj->data;
494        mixer=alsa_mixer_open(ad->mixdev);
495        if (mixer==NULL) return ;
496        switch(e){
497                case MS_SND_CARD_MASTER:
498                        set_mixer_element(mixer,"Master",a,PLAYBACK);
499                break;
500                case MS_SND_CARD_CAPTURE:
501                        set_mixer_element(mixer,"Capture",a,CAPTURE);
502                break;
503                case MS_SND_CARD_PLAYBACK:
504                        set_mixer_element(mixer,"PCM",a,PLAYBACK);
505                break;
506                default:
507                        ms_warning("alsa_card_set_level: unsupported command.");
508        }
509        alsa_mixer_close(mixer);
510}
511
512static int alsa_card_get_level(MSSndCard *obj, MSSndCardMixerElem e)
513{
514        snd_mixer_t *mixer;
515        AlsaData *ad=(AlsaData*)obj->data;
516        int value = -1;
517        mixer=alsa_mixer_open(ad->mixdev);
518        if (mixer==NULL) return 0;
519        switch(e){
520                case MS_SND_CARD_MASTER:
521                        value=get_mixer_element(mixer,"Master",PLAYBACK);
522                        break;
523                case MS_SND_CARD_CAPTURE:
524                        value=get_mixer_element(mixer,"Capture",CAPTURE);
525                        break;
526                case MS_SND_CARD_PLAYBACK:
527                        value=get_mixer_element(mixer,"PCM",PLAYBACK);
528                        break;
529                default:
530                        ms_warning("alsa_card_set_level: unsupported command.");
531        }
532        alsa_mixer_close(mixer);
533        return value;
534}
535
536static void alsa_card_set_source(MSSndCard *obj,MSSndCardCapture source)
537{
538        snd_mixer_t *mixer;
539        AlsaData *ad=(AlsaData*)obj->data;
540        mixer=alsa_mixer_open(ad->mixdev);
541        if (mixer==NULL) return;
542        switch (source){
543                case MS_SND_CARD_MIC:
544                        set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH);
545                        set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
546                        break;
547                case MS_SND_CARD_LINE:
548                        set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH);
549                        set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
550                        break;
551        }
552        alsa_mixer_close(mixer);
553}
554
555static MSFilter *alsa_card_create_reader(MSSndCard *card)
556{
557        AlsaData *ad=(AlsaData*)card->data;
558        MSFilter *f=ms_alsa_read_new(ad->pcmdev);
559        return f;
560}
561
562static MSFilter *alsa_card_create_writer(MSSndCard *card)
563{
564        AlsaData *ad=(AlsaData*)card->data;
565        MSFilter *f=ms_alsa_write_new(ad->pcmdev);
566        return f;
567}
568
569
570static void alsa_card_init(MSSndCard *obj){
571        AlsaData *ad=ms_new0(AlsaData,1);
572        obj->data=ad;
573}
574
575static void alsa_card_uninit(MSSndCard *obj){
576        AlsaData *ad=(AlsaData*)obj->data;
577        if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
578        if (ad->mixdev!=NULL) ms_free(ad->mixdev);
579        ms_free(ad);
580}
581
582static void alsa_card_detect(MSSndCardManager *m){
583        int i;
584        for (i=-1;i<10;i++){
585                MSSndCard *card=alsa_card_new(i);
586                if (card!=NULL)
587                        ms_snd_card_manager_add_card(m,card);
588        }
589}
590
591MSSndCardDesc alsa_card_desc={
592        .driver_type="ALSA",
593        .detect=alsa_card_detect,
594        .init=alsa_card_init,
595        .set_level=alsa_card_set_level,
596        .get_level=alsa_card_get_level,
597        .set_capture=alsa_card_set_source,
598        .create_reader=alsa_card_create_reader,
599        .create_writer=alsa_card_create_writer,
600        .uninit=alsa_card_uninit,
601        .duplicate=alsa_card_duplicate
602};
603
604static MSSndCard *alsa_card_duplicate(MSSndCard *obj){
605        MSSndCard *card=ms_snd_card_new(&alsa_card_desc);
606        AlsaData* dcard=(AlsaData*)card->data;
607        AlsaData* dobj=(AlsaData*)obj->data;
608        card->name=ms_strdup(obj->name);
609        card->id=ms_strdup(obj->id);
610        dcard->pcmdev=ms_strdup(dobj->pcmdev);
611        dcard->mixdev=ms_strdup(dobj->mixdev);
612        return card;
613}
614
615MSSndCard * ms_alsa_card_new_custom(const char *pcmdev, const char *mixdev){
616        MSSndCard * obj;
617        AlsaData *ad;
618        obj=ms_snd_card_new(&alsa_card_desc);
619        ad=(AlsaData*)obj->data;
620        obj->name=ms_strdup(pcmdev);
621        ad->pcmdev=ms_strdup(pcmdev);
622        ad->mixdev=ms_strdup(mixdev);
623        return obj;
624}
625
626static MSSndCard * alsa_card_new(int id)
627{
628        MSSndCard * obj;
629        char *name=NULL;
630        AlsaData *ad;
631        int err;
632        snd_pcm_t *pcm_handle;
633        if (id!=-1){
634                err=snd_card_get_name(id,&name);
635                if (err<0) {
636                        return NULL;
637                }
638        }
639        obj=ms_snd_card_new(&alsa_card_desc);
640        ad=(AlsaData*)obj->data;
641        if (id==-1) {
642                /* the default pcm device */
643                obj->name=ms_strdup("default device");
644                ad->pcmdev=ms_strdup("default");
645                ad->mixdev=ms_strdup("default");
646        }else{
647                /* remove trailing spaces from card name */
648                char *pos1, *pos2;
649                pos1=ms_strdup(name);
650                pos2=pos1+strlen(pos1)-1;
651                for (; pos2>pos1 && *pos2==' '; pos2--) *pos2='\0';
652                obj->name=pos1;
653                ad->pcmdev=ms_strdup_printf("default:%i",id);
654                ad->mixdev=ms_strdup_printf("default:%i",id);
655        }
656        /*check card capabilities: */
657        obj->capabilities=0;
658        if (snd_pcm_open(&pcm_handle,ad->pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK)==0) {
659                obj->capabilities|=MS_SND_CARD_CAP_CAPTURE;
660                snd_pcm_close(pcm_handle);
661        }
662        if (snd_pcm_open(&pcm_handle,ad->pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK)==0) {
663                obj->capabilities|=MS_SND_CARD_CAP_PLAYBACK;
664                snd_pcm_close(pcm_handle);
665        }
666        if (obj->capabilities==0){
667                ms_warning("Strange, sound card %s does not seems to be capable of anything.",obj->name);
668                obj->capabilities=MS_SND_CARD_CAP_CAPTURE|MS_SND_CARD_CAP_PLAYBACK;
669        }
670        free(name);
671        /*ms_message("alsa device %s found",obj->name);*/
672        return obj;
673}
674
675struct _AlsaReadData{
676        char *pcmdev;
677        snd_pcm_t *handle;
678        int rate;
679        int nchannels;
680
681#ifdef THREADED_VERSION
682        ms_thread_t thread;
683        ms_mutex_t mutex;
684        MSBufferizer * bufferizer;
685        bool_t read_started;
686        bool_t write_started;
687#endif
688};
689
690typedef struct _AlsaReadData AlsaReadData;
691
692void alsa_read_init(MSFilter *obj){
693        AlsaReadData *ad=ms_new(AlsaReadData,1);
694        ad->pcmdev=NULL;
695        ad->handle=NULL;
696        ad->rate=8000;
697        ad->nchannels=1;
698        obj->data=ad;
699
700#ifdef THREADED_VERSION
701        ad->read_started=FALSE;
702        ad->write_started=FALSE;
703        ad->bufferizer=ms_bufferizer_new();
704        ms_mutex_init(&ad->mutex,NULL);
705        ad->thread=0;
706#endif
707}
708
709#ifdef THREADED_VERSION
710
711static void * alsa_write_thread(void *p){
712        AlsaReadData *ad=(AlsaReadData*)p;
713        int samples=(160*ad->rate)/8000;
714        int err;
715        int count=0;
716        mblk_t *om=NULL;
717        struct timeval timeout;
718        if (ad->handle==NULL && ad->pcmdev!=NULL){
719                ad->handle=alsa_open_r(ad->pcmdev,16,ad->nchannels==2,ad->rate);
720        }
721        if (ad->handle==NULL) return NULL;
722
723        while (ad->read_started)
724          {
725            count = alsa_can_read(ad->handle,samples);
726            if (count==24)
727              { /* keep this value for this driver */ }
728            else if (count<=0)
729              {
730                count = samples;
731              }
732            else if (count>0)
733              {
734                //ms_warning("%i count", count);
735                //count = samples;
736              }
737
738            int size=count*2;
739            om=allocb(size,0);
740
741            if ((err=alsa_read(ad->handle,om->b_wptr,count))<=0)
742              {
743                ms_warning("nothing to read");
744                //ms_warning("Fail to read samples %i", count);
745                freemsg(om); /* leak fixed */
746                continue;
747              }
748            //ms_warning(" read %i", err);
749           
750            size=err*2;
751            om->b_wptr+=size;
752
753            ms_mutex_lock(&ad->mutex);
754            ms_bufferizer_put(ad->bufferizer,om);
755            ms_mutex_unlock(&ad->mutex);
756
757            if (count==24)
758              {
759                timeout.tv_sec = 0;
760                timeout.tv_usec = 2000;
761                select(0, 0, NULL, NULL, &timeout );
762              }
763            else
764              {
765                /* select will be less active than locking on "read" */
766                timeout.tv_sec = 0;
767                timeout.tv_usec = 5000;
768                select(0, 0, NULL, NULL, &timeout );
769              }
770          }
771
772        if (ad->handle!=NULL) snd_pcm_close(ad->handle);
773        ad->handle=NULL;
774        return NULL;
775}
776
777static void alsa_start_r(AlsaReadData *d){
778        if (d->read_started==FALSE){
779                d->read_started=TRUE;
780                ms_thread_create(&d->thread,NULL,alsa_write_thread,d);
781        }else d->read_started=TRUE;
782}
783
784static void alsa_stop_r(AlsaReadData *d){
785        d->read_started=FALSE;
786        if (d->thread!=0)
787          {
788            ms_thread_join(d->thread,NULL);
789            d->thread=0;
790          }
791}
792#endif
793
794#ifdef THREADED_VERSION
795void alsa_read_preprocess(MSFilter *obj){
796        AlsaReadData *ad=(AlsaReadData*)obj->data;
797        alsa_start_r(ad);
798}
799#endif
800
801void alsa_read_postprocess(MSFilter *obj){
802        AlsaReadData *ad=(AlsaReadData*)obj->data;
803#ifdef THREADED_VERSION
804        alsa_stop_r(ad);
805#endif
806        if (ad->handle!=NULL) snd_pcm_close(ad->handle);
807        ad->handle=NULL;
808}
809
810void alsa_read_uninit(MSFilter *obj){
811        AlsaReadData *ad=(AlsaReadData*)obj->data;
812#ifdef THREADED_VERSION
813        alsa_stop_r(ad);
814#endif
815        if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
816        if (ad->handle!=NULL) snd_pcm_close(ad->handle);
817#ifdef THREADED_VERSION
818        ms_bufferizer_destroy(ad->bufferizer);
819        ms_mutex_destroy(&ad->mutex);
820#endif
821        ms_free(ad);
822}
823
824#ifndef THREADED_VERSION
825void alsa_read_process(MSFilter *obj){
826        AlsaReadData *ad=(AlsaReadData*)obj->data;
827        int samples=(128*ad->rate)/8000;
828        int err;
829        mblk_t *om=NULL;
830        if (ad->handle==NULL && ad->pcmdev!=NULL){
831                ad->handle=alsa_open_r(ad->pcmdev,16,ad->nchannels==2,ad->rate);
832        }
833        if (ad->handle==NULL) return;
834        while (alsa_can_read(ad->handle,samples)>=samples){
835         
836                int size=samples*2;
837                om=allocb(size,0);
838                if ((err=alsa_read(ad->handle,om->b_wptr,samples))<=0) {
839                        ms_warning("Fail to read samples");
840                        freemsg(om);
841                        return;
842                }
843                size=err*2;
844                om->b_wptr+=size;
845                /*ms_message("alsa_read_process: Outputing %i bytes",size);*/
846                ms_queue_put(obj->outputs[0],om);
847        }
848}
849#endif
850
851#ifdef THREADED_VERSION
852void alsa_read_process(MSFilter *obj){
853        AlsaReadData *ad=(AlsaReadData*)obj->data;
854        mblk_t *om=NULL;
855        int samples=(160*ad->rate)/8000;
856
857        ms_mutex_lock(&ad->mutex);
858        while (ms_bufferizer_get_avail(ad->bufferizer)>=samples*2){
859         
860          om=allocb(samples*2,0);
861          ms_bufferizer_read(ad->bufferizer,om->b_wptr,samples*2);       
862          om->b_wptr+=samples*2;
863          /*ms_message("alsa_read_process: Outputing %i bytes",size);*/
864          ms_queue_put(obj->outputs[0],om);
865        }
866        ms_mutex_unlock(&ad->mutex);
867}
868#endif
869
870static int alsa_read_set_sample_rate(MSFilter *obj, void *param){
871        AlsaReadData *ad=(AlsaReadData*)obj->data;
872        ad->rate=*((int*)param);
873        return 0;
874}
875
876static int alsa_read_set_nchannels(MSFilter *obj, void *param){
877        AlsaReadData *ad=(AlsaReadData*)obj->data;
878        ad->nchannels=*((int*)param);
879        return 0;
880}
881
882MSFilterMethod alsa_read_methods[]={
883        {MS_FILTER_SET_SAMPLE_RATE, alsa_read_set_sample_rate},
884        {MS_FILTER_SET_SAMPLE_RATE, alsa_read_set_nchannels},
885        {0,NULL}
886};
887
888MSFilterDesc alsa_read_desc={
889        .id=MS_ALSA_READ_ID,
890        .name="MSAlsaRead",
891        .text="Alsa sound source",
892        .category=MS_FILTER_OTHER,
893        .ninputs=0,
894        .noutputs=1,
895        .init=alsa_read_init,
896#ifdef THREADED_VERSION
897        .preprocess=alsa_read_preprocess,
898#endif
899        .process=alsa_read_process,
900        .postprocess=alsa_read_postprocess,
901        .uninit=alsa_read_uninit,
902        .methods=alsa_read_methods
903};
904
905static MSFilter * ms_alsa_read_new(const char *dev){
906        MSFilter *f=ms_filter_new_from_desc(&alsa_read_desc);
907        AlsaReadData *ad=(AlsaReadData*)f->data;
908        ad->pcmdev=ms_strdup(dev);
909        return f;
910}
911
912typedef struct _AlsaReadData AlsaWriteData;
913
914void alsa_write_init(MSFilter *obj){
915        AlsaWriteData *ad=ms_new(AlsaWriteData,1);
916        ad->pcmdev=NULL;
917        ad->handle=NULL;
918        ad->rate=8000;
919        ad->nchannels=1;
920        obj->data=ad;
921}
922
923void alsa_write_postprocess(MSFilter *obj){
924        AlsaReadData *ad=(AlsaReadData*)obj->data;
925        if (ad->handle!=NULL) snd_pcm_close(ad->handle);
926        ad->handle=NULL;
927}
928
929void alsa_write_uninit(MSFilter *obj){
930        AlsaWriteData *ad=(AlsaWriteData*)obj->data;
931        if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
932        if (ad->handle!=NULL) snd_pcm_close(ad->handle);
933        ms_free(ad);
934}
935
936int alsa_write_set_sample_rate(MSFilter *obj, void *data){
937        int *rate=(int*)data;
938        AlsaWriteData *ad=(AlsaWriteData*)obj->data;
939        ad->rate=*rate;
940        return 0;
941}
942
943int alsa_write_set_nchannels(MSFilter *obj, void *data){
944        int *n=(int*)data;
945        AlsaWriteData *ad=(AlsaWriteData*)obj->data;
946        ad->nchannels=*n;
947        return 0;
948}
949
950void alsa_write_process(MSFilter *obj){
951        AlsaWriteData *ad=(AlsaWriteData*)obj->data;
952        mblk_t *im=NULL;
953        int size;
954        int samples;
955        int err;
956        if (ad->handle==NULL && ad->pcmdev!=NULL){
957                ad->handle=alsa_open_w(ad->pcmdev,16,ad->nchannels==2,ad->rate);
958#ifdef EPIPE_BUGFIX
959                alsa_fill_w (ad->pcmdev);
960#endif
961        }
962        if (ad->handle==NULL) {
963                ms_queue_flush(obj->inputs[0]);
964                return;
965        }
966        while ((im=ms_queue_get(obj->inputs[0]))!=NULL){
967                while((size=im->b_wptr-im->b_rptr)>0){
968                        samples=size/(2*ad->nchannels);
969                        err=alsa_write(ad->handle,im->b_rptr,samples);
970                        if (err>0) {
971                                im->b_rptr+=err*(2*ad->nchannels);
972                        }
973                        else break;
974                }
975                freemsg(im);
976        }
977}
978
979MSFilterMethod alsa_write_methods[]={
980        {MS_FILTER_SET_SAMPLE_RATE, alsa_write_set_sample_rate},
981        {MS_FILTER_SET_NCHANNELS, alsa_write_set_nchannels},
982        {0,NULL}
983};
984
985MSFilterDesc alsa_write_desc={
986        .id=MS_ALSA_WRITE_ID,
987        .name="MSAlsaWrite",
988        .text="Alsa sound output",
989        .category=MS_FILTER_OTHER,
990        .ninputs=1,
991        .noutputs=0,
992        .init=alsa_write_init,
993        .process=alsa_write_process,
994        .postprocess=alsa_write_postprocess,
995        .uninit=alsa_write_uninit,
996        .methods=alsa_write_methods
997};
998
999
1000static MSFilter * ms_alsa_write_new(const char *dev){
1001        MSFilter *f=ms_filter_new_from_desc(&alsa_write_desc);
1002        AlsaWriteData *ad=(AlsaWriteData*)f->data;
1003        ad->pcmdev=ms_strdup(dev);
1004        return f;
1005}
1006
1007
1008MS_FILTER_DESC_EXPORT(alsa_write_desc)
1009
1010MS_FILTER_DESC_EXPORT(alsa_read_desc)
1011
Note: See TracBrowser for help on using the repository browser.