pulselayer.cpp 23.5 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>
Tristan Matthews's avatar
Tristan Matthews committed
5
 *  Author: Андрей Лухнов <aol.nnov@gmail.com>
6
 *  Author: Adrien Beraud <adrien.beraud@gmail.com>
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 *  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
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
21
22
23
24
25
26
27
28
29
30
31
 *
 *  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.
32
33
 */

34
#include <algorithm> // for std::find
35
#include <stdexcept>
36

37
#include "audiostream.h"
38
#include "pulselayer.h"
39
40
#include "audio/samplerateconverter.h"
#include "audio/dcblocker.h"
41
#include "logger.h"
42
#include "manager.h"
43

44
#include <unistd.h>
45
#include <cstdlib>
46
47
#include <fstream>

48
namespace {
49

50
void playback_callback(pa_stream * /*s*/, size_t /*bytes*/, void* userdata)
51
{
52
    static_cast<PulseLayer*>(userdata)->writeToSpeaker();
53
54
}

55
void capture_callback(pa_stream * /*s*/, size_t /*bytes*/, void* userdata)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
56
{
57
    static_cast<PulseLayer*>(userdata)->readFromMic();
58
}
59

60
void ringtone_callback(pa_stream * /*s*/, size_t /*bytes*/, void* userdata)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
61
{
62
    static_cast<PulseLayer*>(userdata)->ringtoneToSpeaker();
63
64
}

65
void stream_moved_callback(pa_stream *s, void *userdata UNUSED)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
66
{
67
    DEBUG("stream %d to %d", pa_stream_get_index(s), pa_stream_get_device_index(s));
68
69
}

70
} // end anonymous namespace
71

72
#ifdef RECTODISK
73
std::ofstream outfileResampled("testMicOuputResampled.raw", std::ifstream::binary);
74
75
76
std::ofstream outfile("testMicOuput.raw", std::ifstream::binary);
#endif

77
PulseLayer::PulseLayer(AudioPreference &pref)
78
    : playback_(0)
79
80
    , record_(0)
    , ringtone_(0)
81
82
    , sinkList_()
    , sourceList_()
Adrien Béraud's avatar
Adrien Béraud committed
83
    , mic_buffer_()
84
    , context_(0)
85
    , mainloop_(pa_threaded_mainloop_new())
86
87
88
    , enumeratingSinks_(false)
    , enumeratingSources_(false)
    , preference_(pref)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
89
{
90
91
    if (!mainloop_)
        throw std::runtime_error("Couldn't create pulseaudio mainloop");
92

93
#if PA_CHECK_VERSION(1, 0, 0)
94
95
96
97
    pa_proplist *pl = pa_proplist_new();
    pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "phone");

    context_ = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop_), "SFLphone", pl);
98

99
100
    if (pl)
        pa_proplist_free(pl);
101

102
103
104
105
#else
    setenv("PULSE_PROP_media.role", "phone", 1);
    context_ = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), "SFLphone");
#endif
106

107
108
    if (!context_)
        throw std::runtime_error("Couldn't create pulseaudio context");
109

110
    pa_context_set_state_callback(context_, context_state_callback, this);
111

112
113
    if (pa_context_connect(context_, NULL , PA_CONTEXT_NOAUTOSPAWN , NULL) < 0)
        throw std::runtime_error("Could not connect pulseaudio context to the server");
114

115
    pa_threaded_mainloop_lock(mainloop_);
116

117
118
    if (pa_threaded_mainloop_start(mainloop_) < 0)
        throw std::runtime_error("Failed to start pulseaudio mainloop");
119

120
    pa_threaded_mainloop_wait(mainloop_);
121

122
123
    if (pa_context_get_state(context_) != PA_CONTEXT_READY)
        throw std::runtime_error("Couldn't connect to pulse audio server");
124

125
126
127
    pa_threaded_mainloop_unlock(mainloop_);

    isStarted_ = true;
128
129
}

130
PulseLayer::~PulseLayer()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
131
{
132
133
134
135
136
#ifdef RECTODISK
    outfile.close();
    outfileResampled.close();
#endif

137
    disconnectAudioStream();
138

139
    if (mainloop_)
140
        pa_threaded_mainloop_stop(mainloop_);
Emmanuel Milou's avatar
Emmanuel Milou committed
141

142
    if (context_) {
143
144
        pa_context_disconnect(context_);
        pa_context_unref(context_);
145
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
146

147
    if (mainloop_)
148
        pa_threaded_mainloop_free(mainloop_);
Emmanuel Milou's avatar
Emmanuel Milou committed
149
150
}

151
void PulseLayer::context_state_callback(pa_context* c, void *user_data)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
152
{
153
    PulseLayer *pulse = static_cast<PulseLayer*>(user_data);
Tristan Matthews's avatar
Tristan Matthews committed
154
155
    assert(c and pulse and pulse->mainloop_);
    const pa_subscription_mask_t mask = (pa_subscription_mask_t)
156
                                        (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE);
157

158
    switch (pa_context_get_state(c)) {
159
160
161
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
162
            DEBUG("Waiting....");
163
            break;
164

165
        case PA_CONTEXT_READY:
166
            DEBUG("Connection to PulseAudio server established");
167
            pa_threaded_mainloop_signal(pulse->mainloop_, 0);
Tristan Matthews's avatar
Tristan Matthews committed
168
            pa_context_subscribe(c, mask, NULL, pulse);
169
            pa_context_set_subscribe_callback(c, context_changed_callback, pulse);
170
            pulse->updateSinkList();
171
            pulse->updateSourceList();
172
            break;
173

174
175
        case PA_CONTEXT_TERMINATED:
            break;
176

177
178
        case PA_CONTEXT_FAILED:
        default:
179
            ERROR("%s" , pa_strerror(pa_context_errno(c)));
180
            pa_threaded_mainloop_signal(pulse->mainloop_, 0);
181
            pulse->disconnectAudioStream();
182
183
            break;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
184
185
}

186

187
void PulseLayer::updateSinkList()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
188
{
189
    sinkList_.clear();
190
    enumeratingSinks_ = true;
191
    pa_operation *op = pa_context_get_sink_info_list(context_, sink_input_info_callback, this);
192

193
194
    if (op != NULL)
        pa_operation_unref(op);
195
196
}

197
void PulseLayer::updateSourceList()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
198
{
199
    sourceList_.clear();
200
    enumeratingSources_ = true;
201
    pa_operation *op = pa_context_get_source_info_list(context_, source_input_info_callback, this);
202

203
204
    if (op != NULL)
        pa_operation_unref(op);
205
206
}

Adrien Béraud's avatar
Adrien Béraud committed
207
bool PulseLayer::inSinkList(const std::string &deviceName)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
208
{
209
    const bool found = std::find_if(sinkList_.begin(), sinkList_.end(), PaDeviceInfos::nameComparator(deviceName)) != sinkList_.end();
Adrien Béraud's avatar
Adrien Béraud committed
210

211
    DEBUG("seeking for %s in sinks. %s found", deviceName.c_str(), found ? "" : "NOT");
212
    return found;
213
214
}

Adrien Béraud's avatar
Adrien Béraud committed
215
bool PulseLayer::inSourceList(const std::string &deviceName)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
216
{
217
    const bool found = std::find_if(sourceList_.begin(), sourceList_.end(), PaDeviceInfos::nameComparator(deviceName)) != sourceList_.end();
Adrien Béraud's avatar
Adrien Béraud committed
218

219
    DEBUG("seeking for %s in sources. %s found", deviceName.c_str(), found ? "" : "NOT");
220
    return found;
221
222
}

223
std::vector<std::string> PulseLayer::getCaptureDeviceList() const
224
{
Adrien Béraud's avatar
Adrien Béraud committed
225
226
    const unsigned n = sourceList_.size();
    std::vector<std::string> names(n);
Tristan Matthews's avatar
Tristan Matthews committed
227
228

    for (unsigned i = 0; i < n; i++)
Adrien Béraud's avatar
Adrien Béraud committed
229
        names[i] = sourceList_[i].name;
Tristan Matthews's avatar
Tristan Matthews committed
230

Adrien Béraud's avatar
Adrien Béraud committed
231
    return names;
232
233
234
235
}

std::vector<std::string> PulseLayer::getPlaybackDeviceList() const
{
Adrien Béraud's avatar
Adrien Béraud committed
236
237
    const unsigned n = sinkList_.size();
    std::vector<std::string> names(n);
Tristan Matthews's avatar
Tristan Matthews committed
238
239

    for (unsigned i = 0; i < n; i++)
Adrien Béraud's avatar
Adrien Béraud committed
240
        names[i] = sinkList_[i].name;
Tristan Matthews's avatar
Tristan Matthews committed
241

Adrien Béraud's avatar
Adrien Béraud committed
242
    return names;
243
}
244

245
246
int PulseLayer::getAudioDeviceIndex(const std::string& name) const
{
247
    int index = std::distance(sourceList_.begin(), std::find_if(sourceList_.begin(), sourceList_.end(), PaDeviceInfos::nameComparator(name)));
248

249
    if (index == std::distance(sourceList_.begin(), sourceList_.end())) {
250
        index = std::distance(sinkList_.begin(), std::find_if(sinkList_.begin(), sinkList_.end(), PaDeviceInfos::nameComparator(name)));
251
    }
252

253
254
255
    return index;
}

256
257
258
const PaDeviceInfos* PulseLayer::getDeviceInfos(const std::vector<PaDeviceInfos>& list, const std::string& name) const
{
    std::vector<PaDeviceInfos>::const_iterator dev_info = std::find_if(list.begin(), list.end(), PaDeviceInfos::nameComparator(name));
Tristan Matthews's avatar
Tristan Matthews committed
259
260
261

    if (dev_info == list.end()) return NULL;

262
263
    return &(*dev_info);
}
Adrien Béraud's avatar
Adrien Béraud committed
264

265
266
std::string PulseLayer::getAudioDeviceName(int index, PCMType type) const
{
267

268
269
270
    switch (type) {
        case SFL_PCM_PLAYBACK:
        case SFL_PCM_RINGTONE:
271
            if (index < 0 or static_cast<size_t>(index) >= sinkList_.size()) {
272
273
274
                ERROR("Index %d out of range", index);
                return "";
            }
Tristan Matthews's avatar
Tristan Matthews committed
275

Adrien Béraud's avatar
Adrien Béraud committed
276
            return sinkList_[index].name;
277

278
        case SFL_PCM_CAPTURE:
279
            if (index < 0 or static_cast<size_t>(index) >= sourceList_.size()) {
280
281
282
                ERROR("Index %d out of range", index);
                return "";
            }
Tristan Matthews's avatar
Tristan Matthews committed
283

Adrien Béraud's avatar
Adrien Béraud committed
284
            return sourceList_[index].name;
285

286
287
288
289
290
        default:
            return "";
    }
}

291
void PulseLayer::createStreams(pa_context* c)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
292
{
293
    while (enumeratingSinks_ or enumeratingSources_)
294
        usleep(20000); // 20 ms
295

Tristan Matthews's avatar
Tristan Matthews committed
296
297
298
    std::string playbackDevice(preference_.getPulseDevicePlayback());
    std::string captureDevice(preference_.getPulseDeviceRecord());
    std::string ringtoneDevice(preference_.getPulseDeviceRingtone());
299
    std::string defaultDevice = "";
300

301
    DEBUG("Devices: playback: %s record: %s ringtone: %s",
Tristan Matthews's avatar
Tristan Matthews committed
302
          playbackDevice.c_str(), captureDevice.c_str(), ringtoneDevice.c_str());
303

304
305
    // Create playback stream
    const PaDeviceInfos* dev_infos = getDeviceInfos(sinkList_, playbackDevice);
Tristan Matthews's avatar
Tristan Matthews committed
306
307

    if (dev_infos == NULL) {
308
309
310
        dev_infos = &sinkList_[0];
        DEBUG("Prefered playback device not found in device list, selecting %s instead.", dev_infos->name.c_str());
    }
Tristan Matthews's avatar
Tristan Matthews committed
311

312
    playback_ = new AudioStream(c, mainloop_, "SFLphone playback", PLAYBACK_STREAM, sampleRate_, dev_infos);
313

314
315
    pa_stream_set_write_callback(playback_->pulseStream(), playback_callback, this);
    pa_stream_set_moved_callback(playback_->pulseStream(), stream_moved_callback, this);
Emmanuel Milou's avatar
Emmanuel Milou committed
316

317
318
    // Create capture stream
    dev_infos = getDeviceInfos(sourceList_, captureDevice);
Tristan Matthews's avatar
Tristan Matthews committed
319
320

    if (dev_infos == NULL) {
321
322
323
        dev_infos = &sourceList_[0];
        DEBUG("Prefered capture device not found in device list, selecting %s instead.", dev_infos->name.c_str());
    }
Tristan Matthews's avatar
Tristan Matthews committed
324

325
    record_ = new AudioStream(c, mainloop_, "SFLphone capture", CAPTURE_STREAM, sampleRate_, dev_infos);
326

327
328
    pa_stream_set_read_callback(record_->pulseStream() , capture_callback, this);
    pa_stream_set_moved_callback(record_->pulseStream(), stream_moved_callback, this);
329

330
331
    // Create ringtone stream
    dev_infos = getDeviceInfos(sinkList_, ringtoneDevice);
Tristan Matthews's avatar
Tristan Matthews committed
332
333

    if (dev_infos == NULL) {
334
335
336
        dev_infos = &sinkList_[0];
        DEBUG("Prefered ringtone device not found in device list, selecting %s instead.", dev_infos->name.c_str());
    }
Tristan Matthews's avatar
Tristan Matthews committed
337

338
    ringtone_ = new AudioStream(c, mainloop_, "SFLphone ringtone", RINGTONE_STREAM, sampleRate_, dev_infos);
339

340
341
    pa_stream_set_write_callback(ringtone_->pulseStream(), ringtone_callback, this);
    pa_stream_set_moved_callback(ringtone_->pulseStream(), stream_moved_callback, this);
342

343
    pa_threaded_mainloop_signal(mainloop_, 0);
344

345
346
    flushMain();
    flushUrgent();
347
348
}

349
namespace {
350
351
352
353
354
355
356
// Delete stream and zero out its pointer
void
cleanupStream(AudioStream *&stream)
{
    delete stream;
    stream = 0;
}
357
}
358

359

360
361
362
363
364
void PulseLayer::disconnectAudioStream()
{
    cleanupStream(playback_);
    cleanupStream(ringtone_);
    cleanupStream(record_);
Emmanuel Milou's avatar
Emmanuel Milou committed
365
366
}

367
void PulseLayer::startStream()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
368
{
369
    // Create Streams
370
    if (!playback_ or !record_)
371
        createStreams(context_);
372

Emmanuel Milou's avatar
Emmanuel Milou committed
373
    // Flush outside the if statement: every time start stream is
374
    // called is to notify a new event
375
376
    flushUrgent();
    flushMain();
Emmanuel Milou's avatar
Emmanuel Milou committed
377
378
}

379

380
void
381
PulseLayer::stopStream()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
382
{
383
    pa_threaded_mainloop_lock(mainloop_);
384

385
    if (playback_)
386
        pa_stream_flush(playback_->pulseStream(), NULL, NULL);
387

388
    if (record_)
389
        pa_stream_flush(record_->pulseStream(), NULL, NULL);
390

391
    pa_threaded_mainloop_unlock(mainloop_);
392

393
    disconnectAudioStream();
Emmanuel Milou's avatar
Emmanuel Milou committed
394
395
}

396
void PulseLayer::writeToSpeaker()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
397
{
398
    if (!playback_ or !playback_->isReady())
399
        return;
400

401
    pa_stream *s = playback_->pulseStream();
Adrien Béraud's avatar
Adrien Béraud committed
402
403
404
    const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(s);
    size_t sample_size = pa_frame_size(sample_spec);
    const unsigned n_channels = sample_spec->channels;
405

406
    // available bytes to be written in pulseaudio internal buffer
407
    int ret = pa_stream_writable_size(s);
408

409
410
    if (ret < 0) {
        ERROR("Playback error : %s", pa_strerror(ret));
Tristan Matthews's avatar
Tristan Matthews committed
411
        return;
412
    } else if (ret == 0)
413
        return;
414

415
    size_t writableBytes = ret;
Tristan Matthews's avatar
Tristan Matthews committed
416
    const size_t writableSamples = writableBytes / sample_size;
417

418
    notifyIncomingCall();
419

Adrien Béraud's avatar
Adrien Béraud committed
420
    size_t urgentSamples = urgentRingBuffer_.availableForGet(MainBuffer::DEFAULT_ID);
Tristan Matthews's avatar
Tristan Matthews committed
421
    size_t urgentBytes = urgentSamples * sample_size;
422

Adrien Béraud's avatar
Adrien Béraud committed
423
424
    if (urgentSamples > writableSamples) {
        urgentSamples = writableSamples;
Tristan Matthews's avatar
Tristan Matthews committed
425
        urgentBytes = urgentSamples * sample_size;
Adrien Béraud's avatar
Adrien Béraud committed
426
    }
427

Adrien Béraud's avatar
Adrien Béraud committed
428
    SFLAudioSample *data = 0;
429

430
    if (urgentBytes) {
Adrien Béraud's avatar
Adrien Béraud committed
431
432
        AudioBuffer linearbuff(urgentSamples, n_channels);
        pa_stream_begin_write(s, (void**)&data, &urgentBytes);
433
        urgentRingBuffer_.get(linearbuff, MainBuffer::DEFAULT_ID); // retrive only the first sample_spec->channels channels
434
        linearbuff.applyGain(playbackGain_);
Adrien Béraud's avatar
Adrien Béraud committed
435
        linearbuff.interleave(data);
436
        pa_stream_write(s, data, urgentBytes, NULL, 0, PA_SEEK_RELATIVE);
Adrien Béraud's avatar
Adrien Béraud committed
437
438
        // Consume the regular one as well (same amount of samples)
        Manager::instance().getMainBuffer().discard(urgentSamples, MainBuffer::DEFAULT_ID);
439
        return;
440
    }
441

442
443
    // FIXME: not thread safe! we only lock the mutex when we get the
    // pointer, we have no guarantee that it will stay safe to use
Rafaël Carré's avatar
Rafaël Carré committed
444
    AudioLoop *toneToPlay = Manager::instance().getTelephoneTone();
445

446
    if (toneToPlay) {
447
        if (playback_->isReady()) {
Adrien Béraud's avatar
Adrien Béraud committed
448
449
            pa_stream_begin_write(s, (void**)&data, &writableBytes);
            AudioBuffer linearbuff(writableSamples, n_channels);
450
            toneToPlay->getNext(linearbuff, playbackGain_); // retrive only n_channels
Adrien Béraud's avatar
Adrien Béraud committed
451
            linearbuff.interleave(data);
452
            pa_stream_write(s, data, writableBytes, NULL, 0, PA_SEEK_RELATIVE);
453
454
455
        }

        return;
456
    }
457

458
459
    flushUrgent(); // flush remaining samples in _urgentRingBuffer

Adrien Béraud's avatar
Adrien Béraud committed
460
    size_t availSamples = Manager::instance().getMainBuffer().availableForGet(MainBuffer::DEFAULT_ID);
461
462

    if (availSamples == 0) {
Adrien Béraud's avatar
Adrien Béraud committed
463
        pa_stream_begin_write(s, (void**)&data, &writableBytes);
464
465
        memset(data, 0, writableBytes);
        pa_stream_write(s, data, writableBytes, NULL, 0, PA_SEEK_RELATIVE);
466
467
468
        return;
    }

469
470
    // how many samples we want to read from the buffer
    size_t readableSamples = writableSamples;
471
472
473

    double resampleFactor = 1.;

474
    unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer().getInternalSamplingRate();
475
    bool resample = sampleRate_ != mainBufferSampleRate;
476

477
    if (resample) {
478
479
        resampleFactor = (double) sampleRate_ / mainBufferSampleRate;
        readableSamples = (double) readableSamples / resampleFactor;
480
481
    }

Adrien Béraud's avatar
Adrien Béraud committed
482
483
484
485
486
    readableSamples = std::min(readableSamples, availSamples);
    size_t nResampled = (double) readableSamples * resampleFactor;
    size_t resampledBytes =  nResampled * sample_size;

    pa_stream_begin_write(s, (void**)&data, &resampledBytes);
487

Adrien Béraud's avatar
Adrien Béraud committed
488
    AudioBuffer linearbuff(readableSamples, n_channels);
489
    Manager::instance().getMainBuffer().getData(linearbuff, MainBuffer::DEFAULT_ID);
490
491

    if (resample) {
Adrien Béraud's avatar
Adrien Béraud committed
492
        AudioBuffer rsmpl_out(nResampled, 1, sampleRate_);
493
        converter_.resample(linearbuff, rsmpl_out);
494
        rsmpl_out.applyGain(playbackGain_);
Adrien Béraud's avatar
Adrien Béraud committed
495
496
        rsmpl_out.interleave(data);
        pa_stream_write(s, data, resampledBytes, NULL, 0, PA_SEEK_RELATIVE);
497
    } else {
498
        linearbuff.applyGain(playbackGain_);
Adrien Béraud's avatar
Adrien Béraud committed
499
500
        linearbuff.interleave(data);
        pa_stream_write(s, data, resampledBytes, NULL, 0, PA_SEEK_RELATIVE);
501
    }
502
}
503

504
void PulseLayer::readFromMic()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
505
{
506
    if (!record_ or !record_->isReady())
507
        return;
508

509
    const char *data = NULL;
510
    size_t bytes;
511

512
513
    const size_t sample_size = record_->sampleSize();
    const uint8_t channels = record_->channels();
Adrien Béraud's avatar
Adrien Béraud committed
514

515
    if (pa_stream_peek(record_->pulseStream() , (const void**) &data , &bytes) < 0 or !data)
516
        return;
517

Adrien Béraud's avatar
Adrien Béraud committed
518
519
520
521
#ifdef RECTODISK
    outfile.write((const char *)data, bytes);
#endif

522
523
524
    assert(channels);
    assert(sample_size);
    const size_t samples = bytes / sample_size / channels;
Adrien Béraud's avatar
Adrien Béraud committed
525
526

    AudioBuffer in(samples, channels, sampleRate_);
527
    in.deinterleave((SFLAudioSample*)data, samples, channels);
Adrien Béraud's avatar
Adrien Béraud committed
528

529
    unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer().getInternalSamplingRate();
530
    bool resample = sampleRate_ != mainBufferSampleRate;
531

Adrien Béraud's avatar
Adrien Béraud committed
532
    AudioBuffer * out = &in;
533

534
    if (resample) {
Adrien Béraud's avatar
Adrien Béraud committed
535
        mic_buffer_.setSampleRate(mainBufferSampleRate);
536
        converter_.resample(in, mic_buffer_);
Adrien Béraud's avatar
Adrien Béraud committed
537
        out = &mic_buffer_;
538
    }
Rafaël Carré's avatar
* #6629    
Rafaël Carré committed
539

540
    dcblocker_.process(*out);
541
    out->applyGain(playbackGain_);
542
    Manager::instance().getMainBuffer().putData(*out, MainBuffer::DEFAULT_ID);
Adrien Béraud's avatar
Adrien Béraud committed
543

544
#ifdef RECTODISK
545
    outfileResampled.write((const char *)out->getChannel(0), out->samples() * sizeof(SFLAudioSample));
546
#endif
547
548

    if (pa_stream_drop(record_->pulseStream()) < 0)
549
        ERROR("Capture stream drop failed: %s" , pa_strerror(pa_context_errno(context_)));
550
551
}

552

553
void PulseLayer::ringtoneToSpeaker()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
554
{
555
    if (!ringtone_ or !ringtone_->isReady())
556
        return;
557
558

    pa_stream *s = ringtone_->pulseStream();
Adrien Béraud's avatar
Adrien Béraud committed
559
    size_t sample_size = ringtone_->sampleSize();
560

561
562
    int writable = pa_stream_writable_size(s);

563
    if (writable < 0)
564
        ERROR("Ringtone error : %s", pa_strerror(writable));
565

566
    if (writable <= 0)
Rafaël Carré's avatar
* #6629    
Rafaël Carré committed
567
        return;
568

569
    size_t bytes = writable;
570
    void *data;
571

572
    pa_stream_begin_write(s, &data, &bytes);
Rafaël Carré's avatar
* #6629    
Rafaël Carré committed
573
    AudioLoop *fileToPlay = Manager::instance().getTelephoneFile();
574

575
    if (fileToPlay) {
Tristan Matthews's avatar
Tristan Matthews committed
576
        const unsigned samples = (bytes / sample_size) / ringtone_->channels();
Adrien Béraud's avatar
Adrien Béraud committed
577
        AudioBuffer tmp(samples, ringtone_->channels());
578
        fileToPlay->getNext(tmp, playbackGain_);
Tristan Matthews's avatar
Tristan Matthews committed
579
        tmp.interleave((SFLAudioSample*) data);
580
    } else {
581
        memset(data, 0, bytes);
582
    }
583

584
    pa_stream_write(s, data, bytes, NULL, 0, PA_SEEK_RELATIVE);
585
}
586

587
588
589
590
void
PulseLayer::context_changed_callback(pa_context* c,
                                     pa_subscription_event_type_t type,
                                     uint32_t idx UNUSED, void *userdata)
591
{
592
    PulseLayer *context = static_cast<PulseLayer*>(userdata);
593

594
    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
595
            pa_operation *op;
596

597
        case PA_SUBSCRIPTION_EVENT_SINK:
598
599
600
601
602
603
            switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
                case PA_SUBSCRIPTION_EVENT_NEW:
                case PA_SUBSCRIPTION_EVENT_REMOVE:
                    DEBUG("Updating sink list");
                    context->sinkList_.clear();
                    op = pa_context_get_sink_info_list(c, sink_input_info_callback, userdata);
604

605
606
                    if (op != NULL)
                        pa_operation_unref(op);
607

608
609
610
                default:
                    break;
            }
611

612
            break;
613

614
        case PA_SUBSCRIPTION_EVENT_SOURCE:
615
616
617
618
619
620
            switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
                case PA_SUBSCRIPTION_EVENT_NEW:
                case PA_SUBSCRIPTION_EVENT_REMOVE:
                    DEBUG("Updating source list");
                    context->sourceList_.clear();
                    op = pa_context_get_source_info_list(c, source_input_info_callback, userdata);
621

622
623
                    if (op != NULL)
                        pa_operation_unref(op);
624

625
626
627
                default:
                    break;
            }
628

629
            break;
630

631
        default:
632
633
            DEBUG("Unhandled event type 0x%x", type);
            break;
634
635
636
    }
}

637

638
void PulseLayer::source_input_info_callback(pa_context *c UNUSED, const pa_source_info *i, int eol, void *userdata)
639
640
{
    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
641
    PulseLayer *context = static_cast<PulseLayer*>(userdata);
642

643
    if (eol) {
644
        context->enumeratingSources_ = false;
645
        return;
646
    }
647

648
    DEBUG("Source %u\n"
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
          "    Name: %s\n"
          "    Driver: %s\n"
          "    Description: %s\n"
          "    Sample Specification: %s\n"
          "    Channel Map: %s\n"
          "    Owner Module: %u\n"
          "    Volume: %s\n"
          "    Monitor if Sink: %u\n"
          "    Latency: %0.0f usec\n"
          "    Flags: %s%s%s\n",
          i->index,
          i->name,
          i->driver,
          i->description,
          pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
          pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
          i->owner_module,
          i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
          i->monitor_of_sink,
          (double) i->latency,
          i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
          i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
          i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : "");
672

673
674
675
676
    if (not context->inSourceList(i->name)) {
        PaDeviceInfos ep_infos(i->index, i->name, i->sample_spec, i->channel_map);
        context->sourceList_.push_back(ep_infos);
    }
677
678
}

679
void PulseLayer::sink_input_info_callback(pa_context *c UNUSED, const pa_sink_info *i, int eol, void *userdata)
680
681
{
    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
682
    PulseLayer *context = static_cast<PulseLayer*>(userdata);
683

684
    if (eol) {
685
        context->enumeratingSinks_ = false;
686
        return;
687
    }