alsalayer.cpp 26.4 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
3
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
4
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
5
 *  Author: Андрей Лухнов <aol.nnov@gmail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *  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
19
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
20 21 22 23 24 25 26 27 28 29 30
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
31 32 33
 */

#include "alsalayer.h"
34 35 36
#include "audio/dcblocker.h"
#include "eventthread.h"
#include "audio/samplerateconverter.h"
37
#include "logger.h"
38
#include "manager.h"
39
#include "noncopyable.h"
40
#include "dbus/configurationmanager.h"
41
#include <ctime>
42

43 44 45 46
#define SFL_ALSA_PERIOD_SIZE 160
#define SFL_ALSA_NB_PERIOD 8
#define SFL_ALSA_BUFFER_SIZE SFL_ALSA_PERIOD_SIZE*SFL_ALSA_NB_PERIOD

47
class AlsaThread : public ost::Thread {
48
    public:
49
        AlsaThread(AlsaLayer *alsa);
50

51
        ~AlsaThread() { ost::Thread::terminate(); }
52

53 54
        void initAudioLayer();

55
        virtual void run();
56 57

    private:
58
        NON_COPYABLE(AlsaThread);
59
        AlsaLayer* alsa_;
60 61
};

62
AlsaThread::AlsaThread(AlsaLayer *alsa)
63 64
    : ost::Thread(), alsa_(alsa)
{}
65

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
void AlsaThread::initAudioLayer(void)
{
    std::string pcmp;
    std::string pcmr;
    std::string pcmc;

    if (alsa_->audioPlugin_ == PCM_DMIX_DSNOOP) {
        pcmp = alsa_->buildDeviceTopo(PCM_DMIX, alsa_->indexOut_);
        pcmr = alsa_->buildDeviceTopo(PCM_DMIX, alsa_->indexRing_);
        pcmc = alsa_->buildDeviceTopo(PCM_DSNOOP, alsa_->indexIn_);
    } else {
        pcmp = alsa_->buildDeviceTopo(alsa_->audioPlugin_, alsa_->indexOut_);
        pcmr = alsa_->buildDeviceTopo(alsa_->audioPlugin_, alsa_->indexRing_);
        pcmc = alsa_->buildDeviceTopo(alsa_->audioPlugin_, alsa_->indexIn_);
    }

    if (not alsa_->is_capture_open_) {
        alsa_->is_capture_open_ = alsa_->openDevice(&alsa_->captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);

        if (not alsa_->is_capture_open_)
            Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_CAPTURE_DEVICE);
    }

    if (not alsa_->is_playback_open_) {
        alsa_->is_playback_open_ = alsa_->openDevice(&alsa_->playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK);

        if (not alsa_->is_playback_open_)
            Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_PLAYBACK_DEVICE);

        if (alsa_->getIndexPlayback() != alsa_->getIndexRingtone())
            if (!alsa_->openDevice(&alsa_->ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK))
                Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_PLAYBACK_DEVICE);
    }

    alsa_->prepareCaptureStream();
    alsa_->preparePlaybackStream();

    alsa_->startCaptureStream();
    alsa_->startPlaybackStream();

    alsa_->flushMain();
    alsa_->flushUrgent();
}

110 111 112
/**
 * Reimplementation of run()
 */
113
void AlsaThread::run()
114
{
115 116
    initAudioLayer();

117
    while (alsa_->isStarted_) {
118
        alsa_->audioCallback();
119
        ost::Thread::sleep(20 /* ms */);
120 121 122
    }
}

123
// Constructor
124
AlsaLayer::AlsaLayer(const AudioPreference &pref)
125 126 127
    : indexIn_(pref.getAlsaCardin())
    , indexOut_(pref.getAlsaCardout())
    , indexRing_(pref.getAlsaCardring())
128 129
    , watchdogTotalCount_(0)
    , watchdogTotalErr_(0)
130 131 132
    , playbackHandle_(NULL)
    , ringtoneHandle_(NULL)
    , captureHandle_(NULL)
133
    , audioPlugin_(pref.getAlsaPlugin())
134 135 136 137 138 139 140
    , is_playback_prepared_(false)
    , is_capture_prepared_(false)
    , is_playback_running_(false)
    , is_capture_running_(false)
    , is_playback_open_(false)
    , is_capture_open_(false)
    , audioThread_(NULL)
141
{
142 143
    setCaptureGain(pref.getVolumemic());
    setPlaybackGain(pref.getVolumespkr());
144 145 146
}

// Destructor
147
AlsaLayer::~AlsaLayer()
148
{
149
    isStarted_ = false;
150
    delete audioThread_;
151

Emmanuel Milou's avatar
Emmanuel Milou committed
152
    /* Then close the audio devices */
153 154
    closeCaptureStream();
    closePlaybackStream();
155
}
156

157
// Retry approach taken from pa_linux_alsa.c, part of PortAudio
158
bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
159
{
160 161
    static const int MAX_RETRIES = 100;
    int err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
162

163 164
    // Retry if busy, since dmix plugin may not have released the device yet
    for (int tries = 0; tries < MAX_RETRIES and err == -EBUSY; ++tries) {
165
        const struct timespec req = {0, 100000000L};
166
        nanosleep(&req, 0);
167 168 169
        err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
    }

170
    if (err < 0) {
171
        ERROR("Alsa: couldn't open device %s : %s",  dev.c_str(),
172
               snd_strerror(err));
173 174 175
        return false;
    }

176 177 178 179
    if (!alsa_set_params(*pcm)) {
        snd_pcm_close(*pcm);
        return false;
    }
180

181
    return true;
182 183
}

184
void
185
AlsaLayer::startStream()
186
{
187
    dcblocker_.reset();
188

189
    if (is_playback_running_ and is_capture_running_)
190 191
        return;

192
    if (audioThread_ == NULL) {
193 194
        audioThread_ = new AlsaThread(this);
        audioThread_->start();
195
    }
196 197 198
    else if (!audioThread_->isRunning()) {
      audioThread_->start();
    }
199

200
    isStarted_ = true;
201
}
202

203
void
204
AlsaLayer::stopStream()
205
{
206
    isStarted_ = false;
207

208 209
    delete audioThread_;
    audioThread_ = NULL;
210

Rafaël Carré's avatar
Rafaël Carré committed
211 212
    closeCaptureStream();
    closePlaybackStream();
213

214 215 216
    playbackHandle_ = NULL;
    captureHandle_ = NULL;
    ringtoneHandle_ = NULL;
217

218
    /* Flush the ring buffers */
Rafaël Carré's avatar
Rafaël Carré committed
219 220
    flushUrgent();
    flushMain();
221 222 223 224 225
}

//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////   ALSA PRIVATE FUNCTIONS   ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
226

227 228 229 230 231 232 233 234 235
/*
 * GCC extension : statement expression
 *
 * ALSA_CALL(function_call, error_string) will:
 * 		call the function
 * 		display an error if the function failed
 * 		return the function return value
 */
#define ALSA_CALL(call, error) ({ \
236 237
			int err_code = call; \
			if (err_code < 0) \
238
				ERROR(error ": %s", snd_strerror(err_code)); \
239
			err_code; \
240 241
		})

242
void AlsaLayer::stopCaptureStream()
243
{
244 245 246
    if (captureHandle_ && ALSA_CALL(snd_pcm_drop(captureHandle_), "couldn't stop capture") >= 0) {
        is_capture_running_ = false;
        is_capture_prepared_ = false;
247 248
    }
}
249

250
void AlsaLayer::closeCaptureStream()
251
{
252
    if (is_capture_prepared_ and is_capture_running_)
253
        stopCaptureStream();
254

255 256
    if (is_capture_open_ && ALSA_CALL(snd_pcm_close(captureHandle_), "Couldn't close capture") >= 0)
        is_capture_open_ = false;
257 258
}

259
void AlsaLayer::startCaptureStream()
260
{
261
    if (captureHandle_ and not is_capture_running_)
262
        if (ALSA_CALL(snd_pcm_start(captureHandle_), "Couldn't start capture") >= 0)
263
            is_capture_running_ = true;
264 265
}

266
void AlsaLayer::stopPlaybackStream()
267
{
268
    if (ringtoneHandle_ and is_playback_running_)
269
        ALSA_CALL(snd_pcm_drop(ringtoneHandle_), "Couldn't stop ringtone");
270

271
    if (playbackHandle_ and is_playback_running_) {
272
        if (ALSA_CALL(snd_pcm_drop(playbackHandle_), "Couldn't stop playback") >= 0) {
273 274 275
            is_playback_running_ = false;
            is_playback_prepared_ = false;
        }
276 277 278 279
    }
}


280
void AlsaLayer::closePlaybackStream()
281
{
282 283
    if (is_playback_prepared_ and is_playback_running_)
        stopPlaybackStream();
284

285
    if (is_playback_open_) {
286
        if (ringtoneHandle_)
287
            ALSA_CALL(snd_pcm_close(ringtoneHandle_), "Couldn't stop ringtone");
288

289
        if (ALSA_CALL(snd_pcm_close(playbackHandle_), "Coulnd't close playback") >= 0)
290
            is_playback_open_ = false;
291
    }
292

293 294
}

295
void AlsaLayer::startPlaybackStream()
296
{
297
    is_playback_running_ = true;
298 299
}

300
void AlsaLayer::prepareCaptureStream()
Rafaël Carré's avatar
Rafaël Carré committed
301
{
302
    if (is_capture_open_ and not is_capture_prepared_)
303
        if (ALSA_CALL(snd_pcm_prepare(captureHandle_), "Couldn't prepare capture") >= 0)
Rafaël Carré's avatar
Rafaël Carré committed
304 305 306
            is_capture_prepared_ = true;
}

307
void AlsaLayer::preparePlaybackStream()
308
{
309
    is_playback_prepared_ = true;
310 311
}

312
bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
313
{
314
#define TRY(call, error) do { \
315
		if (ALSA_CALL(call, error) < 0) \
316 317 318 319 320 321
			return false; \
	} while(0)

    snd_pcm_hw_params_t *hwparams;
    snd_pcm_hw_params_alloca(&hwparams);

322 323 324 325 326 327 328 329
    snd_pcm_uframes_t period_size = SFL_ALSA_PERIOD_SIZE;
    snd_pcm_uframes_t buffer_size = SFL_ALSA_BUFFER_SIZE;
    unsigned int periods = SFL_ALSA_NB_PERIOD;

    snd_pcm_uframes_t  period_size_min = 0;
    snd_pcm_uframes_t  period_size_max = 0;
    snd_pcm_uframes_t  buffer_size_min = 0;
    snd_pcm_uframes_t  buffer_size_max = 0;
330 331

#define HW pcm_handle, hwparams /* hardware parameters */
332 333 334
    TRY(snd_pcm_hw_params_any(HW), "hwparams init");
    TRY(snd_pcm_hw_params_set_access(HW, SND_PCM_ACCESS_RW_INTERLEAVED), "access type");
    TRY(snd_pcm_hw_params_set_format(HW, SND_PCM_FORMAT_S16_LE), "sample format");
335
    TRY(snd_pcm_hw_params_set_rate_near(HW, &sampleRate_, NULL), "sample rate");
336
    TRY(snd_pcm_hw_params_set_channels(HW, 1), "channel count");
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

    snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
    snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
    snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, NULL);
    snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, NULL);
    DEBUG("Buffer size range from %lu to %lu", buffer_size_min, buffer_size_max);
    DEBUG("Period size range from %lu to %lu", period_size_min, period_size_max);
    buffer_size = buffer_size > buffer_size_max ? buffer_size_max : buffer_size;
    buffer_size = buffer_size < buffer_size_min ? buffer_size_min : buffer_size;
    period_size = period_size > period_size_max ? period_size_max : period_size;
    period_size = period_size < period_size_min ? period_size_min : period_size;

    TRY(snd_pcm_hw_params_set_buffer_size_near(HW, &buffer_size), "Unable to set buffer size for playback");
    TRY(snd_pcm_hw_params_set_period_size_near(HW, &period_size, NULL), "Unable to set period size for playback");
    TRY(snd_pcm_hw_params_set_periods_near(HW, &periods, NULL), "Unable to set number of periods for playback");
352
    TRY(snd_pcm_hw_params(HW), "hwparams");
353 354 355 356 357 358 359 360 361 362

    snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
    snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL);
    DEBUG("Was set period_size = %lu", period_size);
    DEBUG("Was set buffer_size = %lu", buffer_size);

    if (2*period_size > buffer_size) {
        ERROR("buffer to small, could not use");
        return false;
    }
363 364
#undef HW

365
    DEBUG("%s using sampling rate %dHz",
366
           (snd_pcm_stream(pcm_handle) == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
367
           sampleRate_);
368

369 370
    snd_pcm_sw_params_t *swparams = NULL;
    snd_pcm_sw_params_alloca(&swparams);
371

372
#define SW pcm_handle, swparams /* software parameters */
373
    snd_pcm_sw_params_current(SW);
374
    TRY(snd_pcm_sw_params_set_start_threshold(SW, period_size * 2), "start threshold");
375
    TRY(snd_pcm_sw_params(SW), "sw parameters");
376
#undef SW
377

378
    return true;
379 380

#undef TRY
381 382
}

383
//TODO first frame causes broken pipe (underrun) because not enough data are send --> make the handle wait to be ready
Rafaël Carré's avatar
Rafaël Carré committed
384
void
385
AlsaLayer::write(void* buffer, int length, snd_pcm_t * handle)
386
{
387 388 389 390
    //Do not waste CPU cycle to handle void
    if (!length)
       return;

391
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames(handle, length);
392
    watchdogTotalCount_++;
393 394

    int err = snd_pcm_writei(handle, buffer , frames);
395

Rafaël Carré's avatar
Rafaël Carré committed
396
    if (err >= 0)
397 398
        return;

399 400
    watchdogTotalErr_++;

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    switch (err) {

        case -EPIPE:
        case -ESTRPIPE:
        case -EIO: {
            snd_pcm_status_t* status;
            snd_pcm_status_alloca(&status);

            if (ALSA_CALL(snd_pcm_status(handle, status), "Cannot get playback handle status") >= 0)
                if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
                    stopPlaybackStream();
                    preparePlaybackStream();
                    startPlaybackStream();
                }

            ALSA_CALL(snd_pcm_writei(handle, buffer , frames), "XRUN handling failed");
            break;
        }

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
        case -EBADFD: {
            snd_pcm_status_t* status;
            snd_pcm_status_alloca(&status);

            if (ALSA_CALL(snd_pcm_status(handle, status), "Cannot get playback handle status") >= 0) {
                if (snd_pcm_status_get_state(status) == SND_PCM_STATE_SETUP) {
                    ERROR("Writing in state SND_PCM_STATE_SETUP, should be "
                          "SND_PCM_STATE_PREPARED or SND_PCM_STATE_RUNNING");
                    int error = snd_pcm_prepare(handle);
                    if (error < 0) {
                        ERROR("Failed to prepare handle: %s", snd_strerror(error));
                        stopPlaybackStream();
                    }
                }
            }
            break;
        }

438
        default:
439
            ERROR("Unknown write error, dropping frames: %s", snd_strerror(err));
440 441 442
            stopPlaybackStream();
            break;
    }
443 444 445 446 447 448 449 450

    //Detect when something is going wrong. This can be caused by alsa bugs or faulty encoder on the other side
    //TODO do something useful instead of just warning and flushing buffers
    if (watchdogTotalErr_ > 0 && watchdogTotalCount_ / watchdogTotalErr_ >=4 && watchdogTotalCount_ > 50) {
        ERROR("Alsa: too many errors (%d error on %d frame)",watchdogTotalErr_,watchdogTotalCount_);
        flushUrgent();
        flushMain();
    }
451 452
}

453
int
454
AlsaLayer::read(void* buffer, int toCopy)
455
{
456 457 458
    if (snd_pcm_state(captureHandle_) == SND_PCM_STATE_XRUN) {
        prepareCaptureStream();
        startCaptureStream();
459
    }
460

461 462 463
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames(captureHandle_, toCopy);

    int err = snd_pcm_readi(captureHandle_, buffer, frames);
464

Rafaël Carré's avatar
Rafaël Carré committed
465
    if (err >= 0)
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
        return snd_pcm_frames_to_bytes(captureHandle_, frames);

    switch (err) {
        case -EPIPE:
        case -ESTRPIPE:
        case -EIO: {
            snd_pcm_status_t* status;
            snd_pcm_status_alloca(&status);

            if (ALSA_CALL(snd_pcm_status(captureHandle_, status), "Get status failed") >= 0)
                if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
                    stopCaptureStream();
                    prepareCaptureStream();
                    startCaptureStream();
                }

482
            ERROR("XRUN capture ignored (%s)", snd_strerror(err));
483 484 485 486
            break;
        }

        case EPERM:
487
            ERROR("Can't capture, EPERM (%s)", snd_strerror(err));
488 489 490 491 492 493
            prepareCaptureStream();
            startCaptureStream();
            break;
    }

    return 0;
494 495
}

496
std::string
497
AlsaLayer::buildDeviceTopo(const std::string &plugin, int card)
498
{
499
    std::stringstream ss;
500
    std::string pcm(plugin);
501

502
    if (pcm == PCM_DEFAULT)
503
        return pcm;
504

505
    ss << ":" << card;
506

507
    return pcm + ss.str();
508 509
}

510
namespace {
511 512 513 514 515 516 517 518 519 520 521 522 523
bool safeUpdate(snd_pcm_t *handle, int &samples)
{
    samples = snd_pcm_avail_update(handle);
    if (samples < 0) {
        samples = snd_pcm_recover(handle, samples, 0);
        if (samples < 0) {
            ERROR("Got unrecoverable error from snd_pcm_avail_update: %s", snd_strerror(samples));
            return false;
        }
    }
    return true;
}

524
std::vector<std::string>
525
getValues(const std::vector<HwIDPair> &deviceMap)
526
{
527 528
    std::vector<std::string> audioDeviceList;
    for (std::vector<HwIDPair>::const_iterator iter = deviceMap.begin();
529 530
            iter != deviceMap.end(); ++iter)
        audioDeviceList.push_back(iter->second);
531 532
    return audioDeviceList;
}
533
}
534

535 536 537 538 539 540 541 542 543 544 545
std::vector<std::string>
AlsaLayer::getCaptureDeviceList() const
{
    return getValues(getAudioDeviceIndexMap(true));
}

std::vector<std::string>
AlsaLayer::getPlaybackDeviceList() const
{
    return getValues(getAudioDeviceIndexMap(false));
}
546 547

std::vector<HwIDPair>
548
AlsaLayer::getAudioDeviceIndexMap(bool getCapture) const
549
{
550 551 552
    snd_ctl_t* handle;
    snd_ctl_card_info_t *info;
    snd_pcm_info_t* pcminfo;
553 554
    snd_ctl_card_info_alloca(&info);
    snd_pcm_info_alloca(&pcminfo);
555

556
    int numCard = -1;
557

558
    std::vector<HwIDPair> audioDevice;
559 560

    if (snd_card_next(&numCard) < 0 || numCard < 0)
561
        return audioDevice;
562

563
    do {
564 565
        std::stringstream ss;
        ss << numCard;
566
        std::string name = "hw:" + ss.str();
567

568 569 570
        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);
571
                snd_pcm_info_set_stream(pcminfo, getCapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
572

573
                if (snd_ctl_pcm_info(handle ,pcminfo) < 0) {
574
                    DEBUG(" Cannot get info");
575
                }
576
                else {
577
                    DEBUG("card %i : %s [%s]",
578 579 580 581 582 583
                           numCard,
                           snd_ctl_card_info_get_id(info),
                           snd_ctl_card_info_get_name(info));
                    std::string description = snd_ctl_card_info_get_name(info);
                    description.append(" - ");
                    description.append(snd_pcm_info_get_name(pcminfo));
584

585
                    // The number of the sound card is associated with a string description
586
                    audioDevice.push_back(HwIDPair(numCard , description));
587 588
                }
            }
589

590
            snd_ctl_close(handle);
591
        }
592
    } while (snd_card_next(&numCard) >= 0 && numCard >= 0);
593 594


595
    return audioDevice;
596 597 598
}


599
bool
600
AlsaLayer::soundCardIndexExists(int card, PCMType stream)
601
{
602
    snd_pcm_info_t *pcminfo;
603 604
    snd_pcm_info_alloca(&pcminfo);
    std::string name("hw:");
605
    std::stringstream ss;
606 607
    ss << card;
    name.append(ss.str());
608

609
    snd_ctl_t* handle;
610
    if (snd_ctl_open(&handle, name.c_str(), 0) != 0)
611
        return false;
612

613
    snd_pcm_info_set_stream(pcminfo , (stream == SFL_PCM_PLAYBACK) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
614 615 616
    bool ret = snd_ctl_pcm_info(handle , pcminfo) >= 0;
    snd_ctl_close(handle);
    return ret;
617
}
618

619
int
620
AlsaLayer::getAudioDeviceIndex(const std::string &description) const
621
{
622 623
    std::vector<HwIDPair> captureDevice(getAudioDeviceIndexMap(true));
    std::vector<HwIDPair> playbackDevice(getAudioDeviceIndexMap(false));
624

625
    std::vector<HwIDPair> audioDeviceIndexMap;
626 627 628 629
    audioDeviceIndexMap.insert(audioDeviceIndexMap.end(), captureDevice.begin(), captureDevice.end());
    audioDeviceIndexMap.insert(audioDeviceIndexMap.end(), playbackDevice.begin(), playbackDevice.end());

    for (std::vector<HwIDPair>::const_iterator iter = audioDeviceIndexMap.begin(); iter != audioDeviceIndexMap.end(); ++iter)
630 631
        if (iter->second == description)
            return iter->first;
632

633 634
    // else return the default one
    return 0;
635 636
}

637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
std::string
AlsaLayer::getAudioDeviceName(int index, PCMType type) const
{
    // a bit ugly and wrong.. i do not know how to implement it better in alsalayer.
    // in addition, for now it is used in pulselayer only due to alsa and pulse layers api differences.
    // but after some tweaking in alsalayer, it could be used in it too.
    switch (type) {
        case SFL_PCM_PLAYBACK:
        case SFL_PCM_RINGTONE:
            return getPlaybackDeviceList().at(index);
        case SFL_PCM_CAPTURE:
            return getCaptureDeviceList().at(index);
        default:
            ERROR("Unexpected type %d", type);
            return "";
    }
}

655
void AlsaLayer::capture()
656
{
657
    unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer().getInternalSamplingRate();
658
    bool resample = sampleRate_ != mainBufferSampleRate;
659

660 661
    int toGetSamples = snd_pcm_avail_update(captureHandle_);

Rafaël Carré's avatar
Rafaël Carré committed
662
    if (toGetSamples < 0)
663
        ERROR("Audio: Mic error: %s", snd_strerror(toGetSamples));
664

Rafaël Carré's avatar
Rafaël Carré committed
665 666 667 668
    if (toGetSamples <= 0)
        return;

    const int framesPerBufferAlsa = 2048;
669
    toGetSamples = std::min(framesPerBufferAlsa, toGetSamples);
Rafaël Carré's avatar
Rafaël Carré committed
670

671
    std::vector<SFLDataFormat> in(toGetSamples);
672
    SFLDataFormat * const in_ptr = &(*in.begin());
673

674
    const int toGetBytes = in.size() * sizeof(in[0]);
675
    if (read(in_ptr, toGetBytes) != toGetBytes) {
676
        ERROR("ALSA MIC : Couldn't read!");
677
        return;
Rafaël Carré's avatar
Rafaël Carré committed
678
    }
679

680
    AudioLayer::applyGain(in_ptr, toGetSamples, getCaptureGain());
Rafaël Carré's avatar
Rafaël Carré committed
681 682

    if (resample) {
683
        int outSamples = toGetSamples * (static_cast<double>(sampleRate_) / mainBufferSampleRate);
684
        std::vector<SFLDataFormat> rsmpl_out(outSamples);
685 686
        SFLDataFormat * const rsmpl_out_ptr = &(*rsmpl_out.begin());
        converter_.resample(in_ptr, rsmpl_out_ptr,
687 688
                rsmpl_out.size(), mainBufferSampleRate, sampleRate_,
                toGetSamples);
689
        dcblocker_.process(rsmpl_out_ptr, rsmpl_out_ptr, outSamples);
690
        Manager::instance().getMainBuffer().putData(rsmpl_out_ptr,
691
                rsmpl_out.size() * sizeof(rsmpl_out[0]), MainBuffer::DEFAULT_ID);
Rafaël Carré's avatar
Rafaël Carré committed
692
    } else {
693
        dcblocker_.process(in_ptr, in_ptr, toGetSamples);
694
        Manager::instance().getMainBuffer().putData(in_ptr, toGetBytes,
695
                                                     MainBuffer::DEFAULT_ID);
Rafaël Carré's avatar
Rafaël Carré committed
696 697
    }
}
698

Rafaël Carré's avatar
Rafaël Carré committed
699 700
void AlsaLayer::playback(int maxSamples)
{
701
    size_t bytesToGet = Manager::instance().getMainBuffer().availableForGet(MainBuffer::DEFAULT_ID);
702

703 704 705
    const size_t bytesToPut = maxSamples * sizeof(SFLDataFormat);
    // no audio available, play tone or silence
    if (bytesToGet <= 0) {
Rafaël Carré's avatar
Rafaël Carré committed
706 707
        AudioLoop *tone = Manager::instance().getTelephoneTone();
        AudioLoop *file_tone = Manager::instance().getTelephoneFile();
708

709 710 711 712 713 714
        std::vector<SFLDataFormat> out(maxSamples, 0);
        SFLDataFormat * const out_ptr = &(*out.begin());
        if (tone)
            tone->getNext(out_ptr, out.size(), getPlaybackGain());
        else if (file_tone && !ringtoneHandle_)
            file_tone->getNext(out_ptr, out.size(), getPlaybackGain());
715

716 717 718
        write(out_ptr, bytesToPut, playbackHandle_);
    } else {
        // play the regular sound samples
719

720
        const size_t mainBufferSampleRate = Manager::instance().getMainBuffer().getInternalSamplingRate();
721
        const bool resample = sampleRate_ != mainBufferSampleRate;
722

723 724 725 726 727 728
        double resampleFactor = 1.0;
        size_t maxNbBytesToGet = bytesToPut;
        if (resample) {
            resampleFactor = static_cast<double>(sampleRate_) / mainBufferSampleRate;
            maxNbBytesToGet = bytesToGet / resampleFactor;
        }
729

730 731 732 733 734
        bytesToGet = std::min(maxNbBytesToGet, bytesToGet);

        const size_t samplesToGet = bytesToGet / sizeof(SFLDataFormat);
        std::vector<SFLDataFormat> out(samplesToGet, 0);
        SFLDataFormat * const out_ptr = &(*out.begin());
735
        Manager::instance().getMainBuffer().getData(out_ptr, bytesToGet, MainBuffer::DEFAULT_ID);
736 737 738 739 740 741 742 743 744 745 746 747 748
        AudioLayer::applyGain(out_ptr, samplesToGet, getPlaybackGain());

        if (resample) {
            const size_t outSamples = samplesToGet * resampleFactor;
            const size_t outBytes = outSamples * sizeof(SFLDataFormat);
            std::vector<SFLDataFormat> rsmpl_out(outSamples);
            SFLDataFormat * const rsmpl_out_ptr = &(*rsmpl_out.begin());
            converter_.resample(out_ptr, rsmpl_out_ptr, rsmpl_out.size(),
                    mainBufferSampleRate, sampleRate_, samplesToGet);
            write(rsmpl_out_ptr, outBytes, playbackHandle_);
        } else {
            write(out_ptr, bytesToGet, playbackHandle_);
        }
749
    }
Rafaël Carré's avatar
Rafaël Carré committed
750 751
}

752
void AlsaLayer::audioCallback()
Rafaël Carré's avatar
Rafaël Carré committed
753
{
754
    if (!playbackHandle_ or !captureHandle_)
Julien Bonjean's avatar
Julien Bonjean committed
755
        return;
756

757
    notifyIncomingCall();
Rafaël Carré's avatar
Rafaël Carré committed
758

759
    snd_pcm_wait(playbackHandle_, 20);
760

761 762 763 764
    int playbackAvailSmpl = 0;
    if (not safeUpdate(playbackHandle_, playbackAvailSmpl))
        return;
    const size_t playbackAvailBytes = playbackAvailSmpl * sizeof(SFLDataFormat);
765

766
    size_t bytesToGet = urgentRingBuffer_.availableForGet(MainBuffer::DEFAULT_ID);
767

768
    if (bytesToGet > 0) {
769
        // Urgent data (dtmf, incoming call signal) come first.
770 771 772 773 774 775 776 777
        bytesToGet = std::min(bytesToGet, playbackAvailBytes);
        const size_t samplesToGet = bytesToGet / sizeof(SFLDataFormat);
        std::vector<SFLDataFormat> out(samplesToGet);
        SFLDataFormat * const out_ptr = &(*out.begin());
        urgentRingBuffer_.get(out_ptr, bytesToGet, MainBuffer::DEFAULT_ID);
        AudioLayer::applyGain(out_ptr, samplesToGet, getPlaybackGain());

        write(out_ptr, bytesToGet, playbackHandle_);
778
        // Consume the regular one as well (same amount of bytes)
779
        Manager::instance().getMainBuffer().discard(bytesToGet, MainBuffer::DEFAULT_ID);
780
    } else {
781 782
        // regular audio data
        playback(playbackAvailSmpl);
783
    }
784

Rafaël Carré's avatar
Rafaël Carré committed
785 786
    if (ringtoneHandle_) {
        AudioLoop *file_tone = Manager::instance().getTelephoneFile();
787 788 789
        int ringtoneAvailSmpl = 0;
        if (not safeUpdate(ringtoneHandle_, ringtoneAvailSmpl))
            return;
790
        int ringtoneAvailBytes = ringtoneAvailSmpl * sizeof(SFLDataFormat);
791

792
        std::vector<SFLDataFormat> out(ringtoneAvailSmpl, 0);
793
        SFLDataFormat * const out_ptr = &(*out.begin());
794

795 796
        if (file_tone) {
            DEBUG("playback gain %d", getPlaybackGain());
797
            file_tone->getNext(out_ptr, ringtoneAvailSmpl,
798
                               getPlaybackGain());
799
        }
800

801
        write(out_ptr, ringtoneAvailBytes, ringtoneHandle_);
802 803
    }

Rafaël Carré's avatar
Rafaël Carré committed
804 805
    // Additionally handle the mic's audio stream
    if (is_capture_running_)
806
        capture();
807
}
808 809 810 811 812

void AlsaLayer::updatePreference(AudioPreference &preference, int index, PCMType type)
{
    switch (type) {
        case SFL_PCM_PLAYBACK:
813
            preference.setAlsaCardout(index);
814 815
            break;
        case AudioLayer::SFL_PCM_CAPTURE:
816
            preference.setAlsaCardin(index);
817 818
            break;
        case AudioLayer::SFL_PCM_RINGTONE:
819
            preference.setAlsaCardring(index);
820 821 822 823 824
            break;
        default:
            break;
    }
}