alsalayer.cpp 21 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
4
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
5 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
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 20 21 22 23 24 25 26 27 28 29
 *
 *  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.
30 31 32
 */

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

42
class AlsaThread : public ost::Thread {
43
    public:
44
        AlsaThread(AlsaLayer *alsa);
45

46
        ~AlsaThread() { terminate(); }
47

48
        virtual void run();
49 50

    private:
51
        NON_COPYABLE(AlsaThread);
52
        AlsaLayer* alsa_;
53 54
};

55
AlsaThread::AlsaThread(AlsaLayer *alsa)
56 57
    : ost::Thread(), alsa_(alsa)
{}
58 59 60 61

/**
 * Reimplementation of run()
 */
62
void AlsaThread::run()
63
{
64
    while (alsa_->isStarted_) {
65
        alsa_->audioCallback();
66
        Thread::sleep(20);
67 68 69
    }
}

70
// Constructor
71 72 73 74 75 76 77 78
AlsaLayer::AlsaLayer()
    : indexIn_(audioPref.getCardin())
    , indexOut_(audioPref.getCardout())
    , indexRing_(audioPref.getCardring())
    , playbackHandle_(NULL)
    , ringtoneHandle_(NULL)
    , captureHandle_(NULL)
    , audioPlugin_(audioPref.getPlugin())
79
    // , IDSoundCards_()
80 81 82 83 84 85 86
    , 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)
87
{
88 89
    setCaptureGain(Manager::instance().audioPreference.getVolumemic());
    setPlaybackGain(Manager::instance().audioPreference.getVolumespkr());
90 91 92
}

// Destructor
93
AlsaLayer::~AlsaLayer()
94
{
95
    isStarted_ = false;
96
    delete audioThread_;
97

Emmanuel Milou's avatar
Emmanuel Milou committed
98
    /* Then close the audio devices */
99 100
    closeCaptureStream();
    closePlaybackStream();
101
}
102

103
// Retry approach taken from pa_linux_alsa.c, part of PortAudio
104
bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
105
{
106 107
    static const int MAX_RETRIES = 100;
    int err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
108

109 110
    // Retry if busy, since dmix plugin may not have released the device yet
    for (int tries = 0; tries < MAX_RETRIES and err == -EBUSY; ++tries) {
111 112
        const struct timespec req = {0, 10000000};
        nanosleep(&req, 0);
113 114 115
        err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
    }

116
    if (err < 0) {
117
        ERROR("Alsa: couldn't open device %s : %s",  dev.c_str(),
118
               snd_strerror(err));
119 120 121
        return false;
    }

122 123 124 125
    if (!alsa_set_params(*pcm)) {
        snd_pcm_close(*pcm);
        return false;
    }
126

127
    return true;
128 129
}

130
void
131
AlsaLayer::startStream()
132
{
133
    dcblocker_.reset();
134

Tristan Matthews's avatar
Tristan Matthews committed
135
    if (is_playback_running_ and is_capture_running_)
136 137
        return;

138 139 140
    std::string pcmp;
    std::string pcmr;
    std::string pcmc;
141

Tristan Matthews's avatar
Tristan Matthews committed
142
    if (audioPlugin_ == PCM_DMIX_DSNOOP) {
143 144 145
        pcmp = buildDeviceTopo(PCM_DMIX, indexOut_);
        pcmr = buildDeviceTopo(PCM_DMIX, indexRing_);
        pcmc = buildDeviceTopo(PCM_DSNOOP, indexIn_);
Julien Bonjean's avatar
Julien Bonjean committed
146
    } else {
147 148 149
        pcmp = buildDeviceTopo(audioPlugin_, indexOut_);
        pcmr = buildDeviceTopo(audioPlugin_, indexRing_);
        pcmc = buildDeviceTopo(audioPlugin_, indexIn_);
150
    }
151

152
    if (not is_capture_open_) {
153 154
        is_capture_open_ = openDevice(&captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);

155
        if (not is_capture_open_)
156 157 158 159
            Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_CAPTURE_DEVICE);
    }

    if (not is_playback_open_) {
160 161 162
        is_playback_open_ = openDevice(&playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK);

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

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

170 171
    prepareCaptureStream();
    preparePlaybackStream();
172

173 174
    startCaptureStream();
    startPlaybackStream();
Emmanuel Milou's avatar
Emmanuel Milou committed
175

176 177
    flushMain();
    flushUrgent();
178

Tristan Matthews's avatar
Tristan Matthews committed
179
    if (audioThread_ == NULL) {
180 181
        audioThread_ = new AlsaThread(this);
        audioThread_->start();
182 183
    }

184
    isStarted_ = true;
185
}
186

187
void
188
AlsaLayer::stopStream()
189
{
190
    isStarted_ = false;
191

192 193
    delete audioThread_;
    audioThread_ = NULL;
194

Rafaël Carré's avatar
Rafaël Carré committed
195 196
    closeCaptureStream();
    closePlaybackStream();
197

Tristan Matthews's avatar
Tristan Matthews committed
198 199 200
    playbackHandle_ = NULL;
    captureHandle_ = NULL;
    ringtoneHandle_ = NULL;
201

Emmanuel Milou's avatar
Emmanuel Milou committed
202
    /* Flush the ring buffers */
Rafaël Carré's avatar
Rafaël Carré committed
203 204
    flushUrgent();
    flushMain();
205 206 207 208 209
}

//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////   ALSA PRIVATE FUNCTIONS   ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
210

211 212 213 214 215 216 217 218 219
/*
 * 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) ({ \
220 221
			int err_code = call; \
			if (err_code < 0) \
222
				ERROR("ALSA: "error": %s", snd_strerror(err_code)); \
223
			err_code; \
224 225
		})

226
void AlsaLayer::stopCaptureStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
227
{
228 229 230
    if (captureHandle_ && ALSA_CALL(snd_pcm_drop(captureHandle_), "couldn't stop capture") >= 0) {
        is_capture_running_ = false;
        is_capture_prepared_ = false;
Emmanuel Milou's avatar
Emmanuel Milou committed
231 232
    }
}
233

234
void AlsaLayer::closeCaptureStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
235
{
Tristan Matthews's avatar
Tristan Matthews committed
236
    if (is_capture_prepared_ and is_capture_running_)
237
        stopCaptureStream();
238

239 240
    if (is_capture_open_ && ALSA_CALL(snd_pcm_close(captureHandle_), "Couldn't close capture") >= 0)
        is_capture_open_ = false;
241 242
}

243
void AlsaLayer::startCaptureStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
244
{
245
    if (captureHandle_ and not is_capture_running_)
246
        if (ALSA_CALL(snd_pcm_start(captureHandle_), "Couldn't start capture") >= 0)
Tristan Matthews's avatar
Tristan Matthews committed
247
            is_capture_running_ = true;
Emmanuel Milou's avatar
Emmanuel Milou committed
248 249
}

250
void AlsaLayer::stopPlaybackStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
251
{
252
    if (ringtoneHandle_ and is_playback_running_)
253
        ALSA_CALL(snd_pcm_drop(ringtoneHandle_), "Couldn't stop ringtone");
254

Tristan Matthews's avatar
Tristan Matthews committed
255
    if (playbackHandle_ and is_playback_running_) {
256
        if (ALSA_CALL(snd_pcm_drop(playbackHandle_), "Couldn't stop playback") >= 0) {
Tristan Matthews's avatar
Tristan Matthews committed
257 258 259
            is_playback_running_ = false;
            is_playback_prepared_ = false;
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
260 261 262 263
    }
}


264
void AlsaLayer::closePlaybackStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
265
{
Tristan Matthews's avatar
Tristan Matthews committed
266 267
    if (is_playback_prepared_ and is_playback_running_)
        stopPlaybackStream();
268

Tristan Matthews's avatar
Tristan Matthews committed
269
    if (is_playback_open_) {
270
        if (ringtoneHandle_)
271
            ALSA_CALL(snd_pcm_close(ringtoneHandle_), "Couldn't stop ringtone");
Emmanuel Milou's avatar
Emmanuel Milou committed
272

273
        if (ALSA_CALL(snd_pcm_close(playbackHandle_), "Coulnd't close playback") >= 0)
Tristan Matthews's avatar
Tristan Matthews committed
274
            is_playback_open_ = false;
275
    }
276

Emmanuel Milou's avatar
Emmanuel Milou committed
277 278
}

279
void AlsaLayer::startPlaybackStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
280
{
281
    if (playbackHandle_ and not is_playback_running_)
282
        if (ALSA_CALL(snd_pcm_start(playbackHandle_), "Couldn't start playback") >= 0)
Tristan Matthews's avatar
Tristan Matthews committed
283
            is_playback_running_ = true;
Emmanuel Milou's avatar
Emmanuel Milou committed
284 285
}

286
void AlsaLayer::prepareCaptureStream()
Rafaël Carré's avatar
Rafaël Carré committed
287
{
288
    if (is_capture_open_ and not is_capture_prepared_)
289
        if (ALSA_CALL(snd_pcm_prepare(captureHandle_), "Couldn't prepare capture") >= 0)
Rafaël Carré's avatar
Rafaël Carré committed
290 291 292
            is_capture_prepared_ = true;
}

293
void AlsaLayer::preparePlaybackStream()
Emmanuel Milou's avatar
Emmanuel Milou committed
294
{
295
    if (is_playback_open_ and not is_playback_prepared_)
296
        if (ALSA_CALL(snd_pcm_prepare(playbackHandle_), "Couldn't prepare playback") >= 0)
Tristan Matthews's avatar
Tristan Matthews committed
297
            is_playback_prepared_ = true;
298 299
}

300
bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
301
{
302
#define TRY(call, error) do { \
303
		if (ALSA_CALL(call, error) < 0) \
304 305 306 307 308 309 310 311 312 313
			return false; \
	} while(0)

    snd_pcm_hw_params_t *hwparams;
    snd_pcm_hw_params_alloca(&hwparams);

    snd_pcm_uframes_t periodSize = 160;
    unsigned int periods = 4;

#define HW pcm_handle, hwparams /* hardware parameters */
314 315 316 317 318 319 320 321
    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");
    TRY(snd_pcm_hw_params_set_rate_near(HW, &audioSampleRate_, NULL), "sample rate");
    TRY(snd_pcm_hw_params_set_channels(HW, 1), "channel count");
    TRY(snd_pcm_hw_params_set_period_size_near(HW, &periodSize, NULL), "period time");
    TRY(snd_pcm_hw_params_set_periods_near(HW, &periods, NULL), "periods number");
    TRY(snd_pcm_hw_params(HW), "hwparams");
322 323
#undef HW

324
    DEBUG("ALSA: %s using sampling rate %dHz",
325 326
           (snd_pcm_stream(pcm_handle) == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
           audioSampleRate_);
327

328 329
    snd_pcm_sw_params_t *swparams = NULL;
    snd_pcm_sw_params_alloca(&swparams);
330

331
#define SW pcm_handle, swparams /* software parameters */
332 333 334
    snd_pcm_sw_params_current(SW);
    TRY(snd_pcm_sw_params_set_start_threshold(SW, periodSize * 2), "start threshold");
    TRY(snd_pcm_sw_params(SW), "sw parameters");
335
#undef SW
336

337
    return true;
338 339

#undef TRY
340 341
}

342
//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
* #6629  
Rafaël Carré committed
343
void
344
AlsaLayer::write(void* buffer, int length, snd_pcm_t * handle)
345
{
346 347 348
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames(handle, length);

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

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
350
    if (err >= 0)
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
        return;

    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;
        }

        default:
373
            ERROR("ALSA: unknown write error, dropping frames: %s", snd_strerror(err));
374 375 376
            stopPlaybackStream();
            break;
    }
377 378
}

379
int
380
AlsaLayer::read(void* buffer, int toCopy)
381
{
382 383 384
    if (snd_pcm_state(captureHandle_) == SND_PCM_STATE_XRUN) {
        prepareCaptureStream();
        startCaptureStream();
385
    }
386

387 388 389
    snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames(captureHandle_, toCopy);

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

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
391
    if (err >= 0)
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
        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();
                }

408
            ERROR("ALSA: XRUN capture ignored (%s)", snd_strerror(err));
409 410 411 412
            break;
        }

        case EPERM:
413
            ERROR("ALSA: Can't capture, EPERM (%s)", snd_strerror(err));
414 415 416 417 418 419
            prepareCaptureStream();
            startCaptureStream();
            break;
    }

    return 0;
420 421
}

422
std::string
423
AlsaLayer::buildDeviceTopo(const std::string &plugin, int card)
424
{
425
    std::stringstream ss;
Tristan Matthews's avatar
Tristan Matthews committed
426
    std::string pcm(plugin);
427

428
    if (pcm == PCM_DEFAULT)
429
        return pcm;
430

431
    ss << ":" << card;
432

433
    return pcm + ss.str();
434 435
}

436
std::vector<std::string>
437 438
AlsaLayer::getAudioDeviceList(AudioStreamDirection dir) const
{
439
    std::vector<HwIDPair> deviceMap(getAudioDeviceIndexMap(dir));
440

441 442 443
    std::vector<std::string> audioDeviceList;
    for (std::vector<HwIDPair>::const_iterator iter = deviceMap.begin();
         iter != deviceMap.end(); ++iter)
444 445 446 447 448 449 450 451
         audioDeviceList.push_back(iter->second);

    return audioDeviceList;
}


std::vector<HwIDPair>
AlsaLayer::getAudioDeviceIndexMap(AudioStreamDirection dir) const
452
{
453 454 455
    snd_ctl_t* handle;
    snd_ctl_card_info_t *info;
    snd_pcm_info_t* pcminfo;
456 457
    snd_ctl_card_info_alloca(&info);
    snd_pcm_info_alloca(&pcminfo);
458 459 460

    int numCard = -1 ;

461
    std::vector<HwIDPair> audioDevice;
462 463

    if (snd_card_next(&numCard) < 0 || numCard < 0)
464
        return audioDevice;
465

466
    do {
467 468
        std::stringstream ss;
        ss << numCard;
469
        std::string name = "hw:" + ss.str();
470

471 472 473
        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);
474
                snd_pcm_info_set_stream(pcminfo, (dir == AUDIO_STREAM_CAPTURE) ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
475

476
                if (snd_ctl_pcm_info(handle ,pcminfo) < 0) {
477
                    DEBUG(" Cannot get info");
478
                }
479
                else {
480
                    DEBUG("card %i : %s [%s]",
481 482 483 484 485 486
                           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));
487

488
                    // The number of the sound card is associated with a string description
489
                    audioDevice.push_back(HwIDPair(numCard , description));
490 491
                }
            }
492

493
            snd_ctl_close(handle);
494
        }
495
    } while (snd_card_next(&numCard) >= 0 && numCard >= 0);
496 497


498
    return audioDevice;
499 500 501
}


502
bool
503
AlsaLayer::soundCardIndexExists(int card, int stream)
504
{
505 506
    snd_ctl_t* handle;
    snd_pcm_info_t *pcminfo;
507 508
    snd_pcm_info_alloca(&pcminfo);
    std::string name("hw:");
509
    std::stringstream ss;
510 511
    ss << card;
    name.append(ss.str());
512

513
    if (snd_ctl_open(&handle, name.c_str(), 0) != 0)
514
        return false;
515

516
    snd_pcm_info_set_stream(pcminfo , (stream == SFL_PCM_PLAYBACK) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
517 518 519
    bool ret = snd_ctl_pcm_info(handle , pcminfo) >= 0;
    snd_ctl_close(handle);
    return ret;
520
}
521

522
int
523
AlsaLayer::getAudioDeviceIndex(const std::string &description) const
524
{
525 526
    std::vector<HwIDPair> audioDeviceIndexMap;

527 528 529 530 531 532 533
    std::vector<HwIDPair> captureDevice = getAudioDeviceIndexMap(AUDIO_STREAM_CAPTURE);
    std::vector<HwIDPair> playbackDevice = getAudioDeviceIndexMap(AUDIO_STREAM_PLAYBACK);

    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)
Tristan Matthews's avatar
Tristan Matthews committed
534 535
        if (iter->second == description)
            return iter->first;
536

537 538
    // else return the default one
    return 0;
539 540
}

541
void AlsaLayer::capture()
542
{
Rafaël Carré's avatar
Rafaël Carré committed
543
    unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate();
544
    bool resample = audioSampleRate_ != mainBufferSampleRate;
545

546 547
    int toGetSamples = snd_pcm_avail_update(captureHandle_);

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
548
    if (toGetSamples < 0)
549
        ERROR("Audio: Mic error: %s", snd_strerror(toGetSamples));
550

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
551 552 553 554
    if (toGetSamples <= 0)
        return;

    const int framesPerBufferAlsa = 2048;
555

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
556
    if (toGetSamples > framesPerBufferAlsa)
557
        toGetSamples = framesPerBufferAlsa;
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
558 559

    int toGetBytes = toGetSamples * sizeof(SFLDataFormat);
560 561
    SFLDataFormat* in = (SFLDataFormat*) malloc(toGetBytes);

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
562
    if (read(in, toGetBytes) != toGetBytes) {
563
        ERROR("ALSA MIC : Couldn't read!");
564
        goto end;
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
565
    }
566

567
    AudioLayer::applyGain(in, toGetSamples, getCaptureGain());
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
568 569

    if (resample) {
570 571 572 573
        int outSamples = toGetSamples * ((double) audioSampleRate_ / mainBufferSampleRate);
        int outBytes = outSamples * sizeof(SFLDataFormat);
        SFLDataFormat* rsmpl_out = (SFLDataFormat*) malloc(outBytes);
        converter_->resample((SFLDataFormat*) in, rsmpl_out, mainBufferSampleRate, audioSampleRate_, toGetSamples);
574
        dcblocker_.process(rsmpl_out, rsmpl_out, outSamples);
575 576
        Manager::instance().getMainBuffer()->putData(rsmpl_out, outBytes);
        free(rsmpl_out);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
577
    } else {
578
        dcblocker_.process(in, in, toGetSamples);
579
        Manager::instance().getMainBuffer()->putData(in, toGetBytes);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
580 581 582
    }

end:
583
    free(in);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
584
}
585

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
586 587
void AlsaLayer::playback(int maxSamples)
{
Emmanuel Milou's avatar
Emmanuel Milou committed
588

Rafaël Carré's avatar
Rafaël Carré committed
589
    unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate();
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
590 591
    bool resample = audioSampleRate_ != mainBufferSampleRate;

Rafaël Carré's avatar
Rafaël Carré committed
592
    int toGet = Manager::instance().getMainBuffer()->availForGet();
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
593 594 595 596 597
    int toPut = maxSamples * sizeof(SFLDataFormat);

    if (toGet <= 0) {    	// no audio available, play tone or silence
        AudioLoop *tone = Manager::instance().getTelephoneTone();
        AudioLoop *file_tone = Manager::instance().getTelephoneFile();
598

599
        SFLDataFormat *out = (SFLDataFormat *) malloc(toPut);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
600

601 602 603 604 605 606
        if (tone) {
            tone->getNext(out, maxSamples, getPlaybackGain());
        }
        else if (file_tone && !ringtoneHandle_) {
            file_tone->getNext(out, maxSamples, getPlaybackGain());
        }
607 608
        else
            memset(out, 0, toPut);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
609

610 611 612
        write(out, toPut, playbackHandle_);
        free(out);
        return;
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
613
    }
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630

    // play the regular sound samples

    int maxNbBytesToGet = toPut;
    // Compute maximal value to get from the ring buffer
    double resampleFactor = 1.0;

    if (resample) {
        resampleFactor = (double) audioSampleRate_ / mainBufferSampleRate;
        maxNbBytesToGet = (double) toGet / resampleFactor;
    }

    if (toGet > maxNbBytesToGet)
        toGet = maxNbBytesToGet;

    SFLDataFormat *out = (SFLDataFormat*) malloc(toGet);
    Manager::instance().getMainBuffer()->getData(out, toGet);
631
    AudioLayer::applyGain(out, toGet / sizeof(SFLDataFormat), getPlaybackGain());
632 633 634 635 636 637 638 639 640 641 642 643 644

    if (resample) {
        int inSamples = toGet / sizeof(SFLDataFormat);
        int outSamples = inSamples * resampleFactor;
        SFLDataFormat *rsmpl_out = (SFLDataFormat*) malloc(outSamples * sizeof(SFLDataFormat));
        converter_->resample(out, rsmpl_out, mainBufferSampleRate, audioSampleRate_, inSamples);
        write(rsmpl_out, outSamples * sizeof(SFLDataFormat), playbackHandle_);
        free(rsmpl_out);
    } else {
        write(out, toGet, playbackHandle_);
    }

    free(out);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
645 646
}

647
void AlsaLayer::audioCallback()
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
648
{
Tristan Matthews's avatar
Tristan Matthews committed
649
    if (!playbackHandle_ or !captureHandle_)
Julien Bonjean's avatar
Julien Bonjean committed
650
        return;
651

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
652 653
    notifyincomingCall();

654
    snd_pcm_wait(playbackHandle_, 20);
Alexandre Savard's avatar
Alexandre Savard committed
655

656 657
    int playbackAvailSmpl = snd_pcm_avail_update(playbackHandle_);
    int playbackAvailBytes = playbackAvailSmpl * sizeof(SFLDataFormat);
Alexandre Savard's avatar
Alexandre Savard committed
658

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
659
    int toGet = urgentRingBuffer_.AvailForGet();
660

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
661
    if (toGet > 0) {
662
        // Urgent data (dtmf, incoming call signal) come first.
663
        if (toGet > playbackAvailBytes)
664 665 666 667
            toGet = playbackAvailBytes;

        SFLDataFormat *out = (SFLDataFormat*) malloc(toGet);
        urgentRingBuffer_.Get(out, toGet);
668
        AudioLayer::applyGain(out, toGet / sizeof(SFLDataFormat), getPlaybackGain());
669

670 671
        write(out, toGet, playbackHandle_);
        free(out);
672
        // Consume the regular one as well (same amount of bytes)
673
        Manager::instance().getMainBuffer()->discard(toGet);
674
    } else {
675 676
        // regular audio data
        playback(playbackAvailSmpl);
677
    }
678

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
679 680
    if (ringtoneHandle_) {
        AudioLoop *file_tone = Manager::instance().getTelephoneFile();
681
        int ringtoneAvailSmpl = snd_pcm_avail_update(ringtoneHandle_);
682
        int ringtoneAvailBytes = ringtoneAvailSmpl * sizeof(SFLDataFormat);
683

684
        std::vector<SFLDataFormat> out(ringtoneAvailSmpl, 0);
685

686 687
        if (file_tone) {
            DEBUG("playback gain %d", getPlaybackGain());
688 689
            file_tone->getNext(&(*out.begin()), ringtoneAvailSmpl,
                               getPlaybackGain());
690
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
691

692
        write(&(*out.begin()), ringtoneAvailBytes, ringtoneHandle_);
693 694
    }

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
695 696
    // Additionally handle the mic's audio stream
    if (is_capture_running_)
697
        capture();
698
}