alsalayer.cpp 20.7 KB
Newer Older
1
/*
2
 *  Copyright (C) 2008 2009 Savoir-Faire Linux inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "alsalayer.h"

22
23
int framesPerBufferAlsa = 2048;

24
// Constructor
25
26
    AlsaLayer::AlsaLayer( ManagerImpl* manager ) 
    : AudioLayer( manager , ALSA ) 
Yun Liu's avatar
Yun Liu committed
27
28
29
30
    , _PlaybackHandle(NULL)
    , _CaptureHandle(NULL)
    , _periodSize()
    , _audioPlugin()
31
    , IDSoundCards() 
Emmanuel Milou's avatar
Emmanuel Milou committed
32
33
34
35
36
37
38
39
    , _is_prepared_playback (false)
    , _is_running_playback (false)
    , _is_open_playback (false)
    , _is_prepared_capture (false)
    , _is_running_capture (false)
    , _is_open_capture (false)
    , _trigger_request (false)
 
40
{
41
    _debug(" Constructor of AlsaLayer called\n");
42
43
    /* Instanciate the audio thread */
    _audioThread = new AudioThread (this);
44
45
46
47
48
}

// Destructor
AlsaLayer::~AlsaLayer (void) 
{ 
Emmanuel Milou's avatar
Emmanuel Milou committed
49
    _debug("Destructor of AlsaLayer called\n");
50
    closeLayer();
51
52
}

53
    void
54
55
AlsaLayer::closeLayer()
{
56
    _debugAlsa("Close ALSA streams\n");
Emmanuel Milou's avatar
Emmanuel Milou committed
57
58
59
60
61
62
63
        
    try {
        /* Stop the audio thread first */ 
        if (_audioThread)
        {
            delete _audioThread; _audioThread=NULL;
        }
64
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
65
66
67
68
69
70
    catch(...) {
        _debugException("! ARTP Exception: when stopping audiortp\n");
        throw;
    }
    
    /* Then close the audio devices */
71
72
    closeCaptureStream();
    closePlaybackStream();
Emmanuel Milou's avatar
Emmanuel Milou committed
73
74
75
    
    _CaptureHandle = 0;
    _PlaybackHandle = 0;
76
}
77

78
    bool 
79
80
AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin) 
{
Emmanuel Milou's avatar
Emmanuel Milou committed
81
82
    /* Close the devices before open it */  
    if (stream == SFL_PCM_BOTH && is_capture_open() == true && is_playback_open() == true)
83
    {
Emmanuel Milou's avatar
Emmanuel Milou committed
84
85
        closeCaptureStream();
        closePlaybackStream();
86
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
87
88
89
90
91
    else if ((stream == SFL_PCM_CAPTURE || stream == SFL_PCM_BOTH) && is_capture_open() == true)
        closeCaptureStream ();
    else if ((stream == SFL_PCM_PLAYBACK || stream == SFL_PCM_BOTH) && is_playback_open () == true)
        closePlaybackStream ();

92

93
94
95
96
97
98
99
100
101
102
    _indexIn = indexIn;
    _indexOut = indexOut;
    _sampleRate = sampleRate;
    _frameSize = frameSize;	
    _audioPlugin = plugin;

    _debugAlsa(" Setting AlsaLayer: device     in=%2d, out=%2d\n", _indexIn, _indexOut);
    _debugAlsa("                   : alsa plugin=%s\n", _audioPlugin.c_str());
    _debugAlsa("                   : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel);
    _debugAlsa("                   : sample rate=%5d, format=%s\n", _sampleRate, SFLDataFormatString);
103

104
    ost::MutexLock lock( _mutex );
105
    
106
107
    std::string pcmp = buildDeviceTopo( plugin , indexOut , 0);
    std::string pcmc = buildDeviceTopo( PCM_PLUGHW , indexIn , 0);
108

109
    return open_device( pcmp , pcmc , stream);
110
111
}

112
    void
113
114
AlsaLayer::startStream(void) 
{
Emmanuel Milou's avatar
Emmanuel Milou committed
115
116
117
118
    _debug ("Start ALSA streams\n");
    prepareCaptureStream ();
    startCaptureStream ();
    startPlaybackStream ();
Emmanuel Milou's avatar
Emmanuel Milou committed
119

120
121
} 

122
    void
123
124
AlsaLayer::stopStream(void) 
{
Emmanuel Milou's avatar
Emmanuel Milou committed
125
126
127
    _debug ("Stop ALSA streams\n");
    stopCaptureStream ();
    //stopPlaybackStream ();
128

Emmanuel Milou's avatar
Emmanuel Milou committed
129
    /* Flush the ring buffers */
Emmanuel Milou's avatar
Emmanuel Milou committed
130
131
132
    flushUrgent ();
    flushMain ();
    flushMic ();
133
134
}

135
    int
136
137
AlsaLayer::canGetMic()
{
Emmanuel Milou's avatar
Emmanuel Milou committed
138
139
    if(_CaptureHandle) 
        return _micRingBuffer.AvailForGet();
140
    else
Emmanuel Milou's avatar
Emmanuel Milou committed
141
        return 0;
142
143
}

144
    int 
145
146
AlsaLayer::getMic(void *buffer, int toCopy)
{
Emmanuel Milou's avatar
Emmanuel Milou committed
147
148
149
150
151
152
153
154
155
156
157
158
159
    if( _CaptureHandle ){
        return _micRingBuffer.Get(buffer, toCopy,100);
    } 
    else
        return 0;
}

bool AlsaLayer::isCaptureActive(void) {
    ost::MutexLock guard( _mutex );
    if( _CaptureHandle )
        return (snd_pcm_state( _CaptureHandle) == SND_PCM_STATE_RUNNING ? true : false); 
    else
        return false;
160
161
162
163
164
}

//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////   ALSA PRIVATE FUNCTIONS   ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
Emmanuel Milou's avatar
Emmanuel Milou committed
165
166
167
168
169
170
171
172
    
void AlsaLayer::stopCaptureStream (void)
{
    if(_CaptureHandle){
        snd_pcm_drop (_CaptureHandle);
        stop_capture ();
    }
}
173

Emmanuel Milou's avatar
Emmanuel Milou committed
174
175
176
177
178
179
180
181
void AlsaLayer::closeCaptureStream (void)
{
    if( is_capture_prepared() == true && is_capture_running() == true ) 
        stopCaptureStream ();
    if (is_capture_open())
        snd_pcm_close (_CaptureHandle);

    close_capture ();
182
183
}

Emmanuel Milou's avatar
Emmanuel Milou committed
184
185
186
void AlsaLayer::startCaptureStream (void)
{
    if(_CaptureHandle){
Emmanuel Milou's avatar
Emmanuel Milou committed
187
        _debug("Start the capture\n");
Emmanuel Milou's avatar
Emmanuel Milou committed
188
189
190
191
192
193
194
        snd_pcm_start (_CaptureHandle);
        start_capture();
    }
}

void AlsaLayer::prepareCaptureStream (void)
{
195
    if (is_capture_open() ) {
196
197
198
199
        if(snd_pcm_prepare (_CaptureHandle) < 0)    
            _debug("");
        else
            prepare_capture ();
200
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
}

void AlsaLayer::stopPlaybackStream (void)
{
    if( _PlaybackHandle){
        snd_pcm_drop (_PlaybackHandle);
        stop_playback ();
    }
}


void AlsaLayer::closePlaybackStream (void)
{
    if( is_playback_prepared() == true && is_playback_running() == true ) 
        stopPlaybackStream ();
    if (is_playback_open()) 
        snd_pcm_close (_PlaybackHandle);

    close_playback ();
}

void AlsaLayer::startPlaybackStream (void)
{
    if( _PlaybackHandle){
        snd_pcm_start (_PlaybackHandle);
        start_playback();
    }
}

void AlsaLayer::preparePlaybackStream (void)
{
232
233
234
235
    if(is_playback_open()){
        if( snd_pcm_prepare (_PlaybackHandle) < 0)  _debug("Error preparing the device\n");
        prepare_playback ();
    }
236
237
}

238
bool AlsaLayer::alsa_set_params( snd_pcm_t *pcm_handle, int type, int rate ){
239

240
241
242
243
244
245
246
247
248
249
250
251
252
    snd_pcm_hw_params_t *hwparams = NULL;
    snd_pcm_sw_params_t *swparams = NULL;
    unsigned int exact_ivalue;
    unsigned long exact_lvalue;
    int dir;
    int err;
    int format;
    int periods = 4;
    int periodsize = 1024;

    /* Allocate the snd_pcm_hw_params_t struct */
    snd_pcm_hw_params_malloc( &hwparams );

Yun Liu's avatar
Yun Liu committed
253
    _periodSize = 940;
254
255
256
257
258
259
    /* Full configuration space */
    if( (err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { 
        _debugAlsa(" Cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
        return false;
    }

260
    if( (err = snd_pcm_hw_params_set_access( pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
261
262
263
264
265
266
267
268
269
270
        _debugAlsa(" Cannot set access type (%s)\n", snd_strerror(err));
        return false;
    }

    /* Set sample format */
    format = SND_PCM_FORMAT_S16_LE;
    if( (err = snd_pcm_hw_params_set_format( pcm_handle, hwparams, (snd_pcm_format_t)format)) < 0) {
        _debugAlsa(" Cannot set sample format (%s)\n", snd_strerror(err));
        return false;
    }
Yun Liu's avatar
Yun Liu committed
271
    
272
273
274
275
276
277
278
279
280
281
282
    /* Set sample rate. If we can't set to the desired exact value, we set to the nearest acceptable */
    dir=0;
    rate = getSampleRate();
    exact_ivalue = rate;
    if( (err = snd_pcm_hw_params_set_rate_near( pcm_handle, hwparams, &exact_ivalue, &dir) < 0)) {
        _debugAlsa(" Cannot set sample rate (%s)\n", snd_strerror(err));
        return false;
    }
    if( dir!= 0 ){
        _debugAlsa(" (%i) The choosen rate %d Hz is not supported by your hardware.\nUsing %d Hz instead.\n ",type ,rate, exact_ivalue);
    }
283

284
285
286
287
288
    /* Set the number of channels */
    if( (err = snd_pcm_hw_params_set_channels( pcm_handle, hwparams, 1)) < 0){
        _debugAlsa(" Cannot set channel count (%s)\n", snd_strerror(err));
        return false;
    }
289

290
291
292
293
294
295
296
297
    /* Set the buffer size in frames */
    exact_lvalue = periodsize;
    dir=0;
    if( (err = snd_pcm_hw_params_set_period_size_near( pcm_handle, hwparams, &exact_lvalue , &dir)) < 0) {
        _debugAlsa(" Cannot set period time (%s)\n", snd_strerror(err));
        return false;
    }
    if(dir!=0) {
Emmanuel Milou's avatar
Emmanuel Milou committed
298
        _debugAlsa("(%i) The choosen period size %d bytes is not supported by your hardware.\nUsing %d instead.\n ", type, (int)periodsize, (int)exact_lvalue);
299
    }
Yun Liu's avatar
Yun Liu committed
300
    periodsize = exact_lvalue;
301
    _periodSize = exact_lvalue;
302
303
304
305
306
307
    /* Set the number of fragments */
    exact_ivalue = periods;
    dir=0;
    if( (err = snd_pcm_hw_params_set_periods_near( pcm_handle, hwparams, &exact_ivalue, &dir)) < 0) {
        _debugAlsa(" Cannot set periods number (%s)\n", snd_strerror(err));
        return false;
308
    }
309
310
311
312
    if(dir!=0) {
        _debugAlsa(" The choosen period number %i bytes is not supported by your hardware.\nUsing %i instead.\n ", periods, exact_ivalue);
    }
    periods=exact_ivalue;
313

314
315
316
317
318
    /* Set the hw parameters */
    if( (err = snd_pcm_hw_params( pcm_handle, hwparams )) < 0) {
        _debugAlsa(" Cannot set hw parameters (%s)\n", snd_strerror(err));
        return false;
    }
319

320
    snd_pcm_hw_params_free( hwparams );
321

322
    /* Set the sw parameters */
323
    snd_pcm_sw_params_malloc( &swparams );
324
    snd_pcm_sw_params_current( pcm_handle, swparams );
325

326
327
328
329
330
331
332
333
334
    /* Set the start threshold */
    if( (err = snd_pcm_sw_params_set_start_threshold( pcm_handle, swparams, 2700 /*periodsize*2*/ )) < 0 ) {
        _debugAlsa(" Cannot set start threshold (%s)\n", snd_strerror(err)); 
        return false;
    }
    if( (err = snd_pcm_sw_params( pcm_handle, swparams)) < 0 ) {
        _debugAlsa(" Cannot set sw parameters (%s)\n", snd_strerror(err)); 
        return false;
    }
335

336
337

    snd_pcm_sw_params_free( swparams );
338

339
340
341
342
343
344
345
346
347
    return true;
}


    bool 
AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag)
{

    int err;
348
    
349
350
    if(flag == SFL_PCM_BOTH || flag == SFL_PCM_PLAYBACK)
    {
351
        if((err = snd_pcm_open(&_PlaybackHandle, pcm_p.c_str(),  SND_PCM_STREAM_PLAYBACK, 0 )) < 0){
352
353
            _debugAlsa("Error while opening playback device %s\n",  pcm_p.c_str());
            setErrorMessage( ALSA_PLAYBACK_DEVICE );
354
            close_playback ();
355
356
357
358
359
            return false;
        }
        if(!alsa_set_params( _PlaybackHandle, 1, getSampleRate() )){
            _debug("playback failed\n");
            snd_pcm_close( _PlaybackHandle );
360
            close_playback ();
361
362
            return false;
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
363
364

        open_playback ();
365
366
367
368
369
370
371
    }

    if(flag == SFL_PCM_BOTH || flag == SFL_PCM_CAPTURE)
    {
        if( (err = snd_pcm_open(&_CaptureHandle,  pcm_c.c_str(),  SND_PCM_STREAM_CAPTURE, 0)) < 0){
            _debugAlsa("Error while opening capture device %s\n",  pcm_c.c_str());
            setErrorMessage( ALSA_CAPTURE_DEVICE );
372
            close_capture ();
373
374
            return false;
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
375
        if(!alsa_set_params( _CaptureHandle, 0, 8000 )){
376
377
            _debug("capture failed\n");
            snd_pcm_close( _CaptureHandle );
378
            close_capture ();
379
380
            return false;
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
381
382

        open_capture ();
Emmanuel Milou's avatar
Emmanuel Milou committed
383
        prepare_capture ();
384
385
    }

386
    /* Start the secondary audio thread for callbacks */
Emmanuel Milou's avatar
Emmanuel Milou committed
387
    try{
Emmanuel Milou's avatar
Emmanuel Milou committed
388
389
        _audioThread->start();
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
390
391
    catch (...) {
        _debugException("Fail to start audio thread\n");
Emmanuel Milou's avatar
Emmanuel Milou committed
392
    }
393
394

    return true;
395
396
397
}

//TODO	first frame causes broken pipe (underrun) because not enough data are send --> make the handle wait to be ready
398
    int
399
400
AlsaLayer::write(void* buffer, int length)
{
Emmanuel Milou's avatar
Emmanuel Milou committed
401
402
403
404
    if (_trigger_request == true)
    {
        _trigger_request = false;
        startPlaybackStream ();
405
406
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _PlaybackHandle, length);
    int err;
    if ((err=snd_pcm_writei( _PlaybackHandle , buffer , frames ))<0) 
    {
        switch(err) {
            case -EPIPE: 
            case -ESTRPIPE: 
            case -EIO: 
                //_debugAlsa(" XRUN playback ignored (%s)\n", snd_strerror(err));
                handle_xrun_playback();
                if (snd_pcm_writei( _PlaybackHandle , buffer , frames )<0)
                    _debugAlsa ("XRUN handling failed\n");
                _trigger_request = true;
                break;
            default:
                //_debugAlsa ("Write error unknown - dropping frames **********************************: %s\n", snd_strerror(err));
                stopPlaybackStream ();
                break;
        }
    }
427
428

    return ( err > 0 )? err : 0 ;
429
430
}

431
    int
432
433
AlsaLayer::read( void* buffer, int toCopy)
{
Emmanuel Milou's avatar
Emmanuel Milou committed
434
    //ost::MutexLock lock( _mutex );
435
    
Emmanuel Milou's avatar
Emmanuel Milou committed
436
    int samples;
437
438
439

    if(snd_pcm_state( _CaptureHandle ) == SND_PCM_STATE_XRUN)
    {
Emmanuel Milou's avatar
Emmanuel Milou committed
440
441
        prepareCaptureStream ();
        startCaptureStream ();
442
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
443
    
444
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _CaptureHandle, toCopy );
Emmanuel Milou's avatar
Emmanuel Milou committed
445
446
447
    if(( samples = snd_pcm_readi( _CaptureHandle, buffer, frames)) < 0 ) {
        switch (samples)
        {
448
            case -EPIPE:
Emmanuel Milou's avatar
Emmanuel Milou committed
449
450
            case -ESTRPIPE:
            case -EIO:
451
                _debugAlsa(" XRUN capture ignored (%s)\n", snd_strerror(samples));
452
                handle_xrun_capture();
Emmanuel Milou's avatar
Emmanuel Milou committed
453
454
455
456
457
458
459
                //samples = snd_pcm_readi( _CaptureHandle, buffer, frames);
                //if (samples<0)  samples=0;
                break;
            case EPERM:
                _debugAlsa(" Capture EPERM (%s)\n", snd_strerror(samples));
                prepareCaptureStream ();
                startCaptureStream ();
Emmanuel Milou's avatar
Emmanuel Milou committed
460
461
                break;
            default:
Emmanuel Milou's avatar
Emmanuel Milou committed
462
                _debugAlsa("%s\n", snd_strerror(samples));
463
464
465
                break;
        }
        return 0;
466
467
    }

468
    return toCopy;
469

Yun Liu's avatar
Yun Liu committed
470
}
471

472
    void
473
474
AlsaLayer::handle_xrun_capture( void )
{
475
476
477
478
479
480
    snd_pcm_status_t* status;
    snd_pcm_status_alloca( &status );

    int res = snd_pcm_status( _CaptureHandle, status );
    if( res <= 0){
        if(snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN ){
481
482
483
            stopCaptureStream ();
            prepareCaptureStream ();
            startCaptureStream ();
484
485
486
487
        }
    }
    else
        _debugAlsa(" Get status failed\n");
488
489
}

490
    void
491
492
AlsaLayer::handle_xrun_playback( void )
{
493
494
495
496
497
498
499
500
501
502
    int state; 
    snd_pcm_status_t* status;
    snd_pcm_status_alloca( &status );

    if( (state = snd_pcm_status( _PlaybackHandle, status )) < 0 )   _debugAlsa(" Error: Cannot get playback handle status (%s)\n" , snd_strerror( state ) );
    else 
    { 
        state = snd_pcm_status_get_state( status );
        if( state  == SND_PCM_STATE_XRUN )
        {
Emmanuel Milou's avatar
Emmanuel Milou committed
503
504
505
            stopPlaybackStream ();
            preparePlaybackStream ();
            _trigger_request = true;
506
        }
507
508
509
    }
}

510
    std::string
511
512
AlsaLayer::buildDeviceTopo( std::string plugin, int card, int subdevice )
{
513
514
515
516
517
518
519
520
521
522
523
524
    std::string pcm = plugin;
    std::stringstream ss,ss1;
    if( pcm == "default" || pcm == "pulse")
        return pcm;
    ss << card;
    pcm.append(":");
    pcm.append(ss.str());
    if( subdevice != 0 ){
        pcm.append(",");
        ss1 << subdevice;
        pcm.append(ss1.str());
    }
525
526
527
    return pcm;
}

528
    std::vector<std::string>
529
530
AlsaLayer::getSoundCardsInfo( int stream )
{
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
    std::vector<std::string> cards_id;
    HwIDPair p;

    snd_ctl_t* handle;
    snd_ctl_card_info_t *info;
    snd_pcm_info_t* pcminfo;
    snd_ctl_card_info_alloca( &info );
    snd_pcm_info_alloca( &pcminfo );

    int numCard = -1 ;
    std::string description;

    if(snd_card_next( &numCard ) < 0 || numCard < 0)
        return cards_id;

    while(numCard >= 0){
        std::stringstream ss;
        ss << numCard;
        std::string name= "hw:";
        name.append(ss.str());

        if( snd_ctl_open( &handle, name.c_str(), 0) == 0 ){
            if( snd_ctl_card_info( handle, info) == 0){
                snd_pcm_info_set_device( pcminfo , 0);
                snd_pcm_info_set_stream( pcminfo, ( stream == SFL_PCM_CAPTURE )? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK );
                if( snd_ctl_pcm_info ( handle ,pcminfo ) < 0) _debugAlsa(" Cannot get info\n");
                else{
                    _debugAlsa("card %i : %s [%s]\n", 
                            numCard, 
                            snd_ctl_card_info_get_id(info),
                            snd_ctl_card_info_get_name( info ));
                    description = snd_ctl_card_info_get_name( info );
                    description.append(" - ");
                    description.append(snd_pcm_info_get_name( pcminfo ));
                    cards_id.push_back( description );
                    // The number of the sound card is associated with a string description 
                    p = HwIDPair( numCard , description );
                    IDSoundCards.push_back( p );
                }
            }
            snd_ctl_close( handle );
        }
        if ( snd_card_next( &numCard ) < 0 ) {
            break;
        }
    }
577
578
579
580
581
    return cards_id;
}



582
    bool
583
584
AlsaLayer::soundCardIndexExist( int card , int stream )
{
585
586
587
588
589
590
591
592
593
594
595
596
597
    snd_ctl_t* handle;
    snd_pcm_info_t *pcminfo;
    snd_pcm_info_alloca( &pcminfo );
    std::string name = "hw:";
    std::stringstream ss;
    ss << card ;
    name.append(ss.str());
    if(snd_ctl_open( &handle, name.c_str(), 0) == 0 ){
        snd_pcm_info_set_stream( pcminfo , ( stream == SFL_PCM_PLAYBACK )? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE );
        if( snd_ctl_pcm_info( handle , pcminfo ) < 0) return false;
        else
            return true;
    }
598
    else
599
        return false;
600
601
}  

602
    int
603
604
AlsaLayer::soundCardGetIndex( std::string description )
{
605
606
607
608
609
610
611
612
613
    unsigned int i;
    for( i = 0 ; i < IDSoundCards.size() ; i++ )
    {
        HwIDPair p = IDSoundCards[i];
        if( p.second == description )
            return  p.first ;
    }
    // else return the default one
    return 0;
614
615
}

616
void AlsaLayer::audioCallback (void)
617
{
618

Emmanuel Milou's avatar
Emmanuel Milou committed
619
    int toGet, toPut, urgentAvail, normalAvail, micAvailAlsa, micAvailPut, maxBytes; 
620
621
622
623
    unsigned short spkrVolume, micVolume;
    AudioLoop *tone;

    SFLDataFormat *out;
Emmanuel Milou's avatar
Emmanuel Milou committed
624
    SFLDataFormat *in;
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665

    spkrVolume = _manager->getSpkrVolume();
    micVolume  = _manager->getMicVolume();

    // AvailForGet tell the number of chars inside the buffer
    // framePerBuffer are the number of data for one channel (left)
    urgentAvail = _urgentRingBuffer.AvailForGet();
    if (urgentAvail > 0) {
        // Urgent data (dtmf, incoming call signal) come first.     
        toGet = (urgentAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBufferAlsa * sizeof(SFLDataFormat);
        out =  (SFLDataFormat*)malloc(toGet * sizeof(SFLDataFormat) );
        _urgentRingBuffer.Get(out, toGet, spkrVolume);
        /* Play the sound */
        write( out , toGet );
        free(out); out=0;
        // Consume the regular one as well (same amount of bytes)
        _voiceRingBuffer.Discard(toGet);
    } else {
        tone = _manager->getTelephoneTone();
        toGet = 940  ; 
        maxBytes = toGet * sizeof(SFLDataFormat)  ;
        if ( tone != 0) {
            out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat));
            tone->getNext(out, toGet, spkrVolume);
            write (out , maxBytes);
        } else if ( (tone=_manager->getTelephoneFile()) != 0 ) {
            out =  (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat) );
            tone->getNext(out, toGet, spkrVolume);
            write (out , maxBytes);
        } else {
            // If nothing urgent, play the regular sound samples
            normalAvail = _voiceRingBuffer.AvailForGet();
            toGet = (normalAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? normalAvail : framesPerBufferAlsa * sizeof(SFLDataFormat);
            out =  (SFLDataFormat*)malloc(framesPerBufferAlsa * sizeof(SFLDataFormat));

            if (toGet) {
                _voiceRingBuffer.Get(out, toGet, spkrVolume);
                write (out, toGet);
            } else {
                bzero(out, framesPerBufferAlsa * sizeof(SFLDataFormat));
            }
666
        }
667
        free(out); out=0;
668
    }
669
670
    
    // Additionally handle the mic's audio stream 
Emmanuel Milou's avatar
Emmanuel Milou committed
671
672
673
674
675
676
677
678
679
680
681
682
683
    //if(is_capture_running()){  
    micAvailAlsa = snd_pcm_avail_update(_CaptureHandle);
    if(micAvailAlsa > 0) {
        micAvailPut = _micRingBuffer.AvailForPut();
        toPut = (micAvailAlsa <= micAvailPut) ? micAvailAlsa : micAvailPut;
        in = (SFLDataFormat*)malloc(toPut * sizeof(SFLDataFormat));
        toPut = read (in, toPut);
        if (in != 0)
        {   
            _micRingBuffer.Put(in, toPut, 100);
        }
        free(in); in=0;
    }
684
}
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703

void* AlsaLayer::adjustVolume( void* buffer , int len, int stream )
{
    int vol, i, size;
    SFLDataFormat *src = NULL;

    (stream == SFL_PCM_PLAYBACK)? vol = _manager->getSpkrVolume() : vol = _manager->getMicVolume();

    src = (SFLDataFormat*) buffer;

    if( vol != 100 )
    {
        size = len / sizeof(SFLDataFormat);
        for( i = 0 ; i < size ; i++ ){
            src[i] = src[i] * vol  / 100 ;
        }
    }
    return src ; 
}