~ubuntu-branches/ubuntu/raring/mumble/raring

« back to all changes in this revision

Viewing changes to .pc/debian-changes-1.2.3-1ubuntu2/src/mumble/PulseAudio.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-03-03 17:06:46 UTC
  • Revision ID: james.westby@ubuntu.com-20110303170646-18cv445x1pzc8ts3
Tags: 1.2.3-1ubuntu2
debian/patches/01_set_pulseaudio_role.patch: updated version, thanks Conor

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
 
2
 
 
3
   All rights reserved.
 
4
 
 
5
   Redistribution and use in source and binary forms, with or without
 
6
   modification, are permitted provided that the following conditions
 
7
   are met:
 
8
 
 
9
   - Redistributions of source code must retain the above copyright notice,
 
10
     this list of conditions and the following disclaimer.
 
11
   - Redistributions in binary form must reproduce the above copyright notice,
 
12
     this list of conditions and the following disclaimer in the documentation
 
13
     and/or other materials provided with the distribution.
 
14
   - Neither the name of the Mumble Developers nor the names of its
 
15
     contributors may be used to endorse or promote products derived from this
 
16
     software without specific prior written permission.
 
17
 
 
18
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
19
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
20
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
21
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 
22
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
25
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 
26
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 
27
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
28
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
*/
 
30
 
 
31
#include "PulseAudio.h"
 
32
#include "User.h"
 
33
#include "Global.h"
 
34
#include "MainWindow.h"
 
35
#include "Timer.h"
 
36
#include <sys/soundcard.h>
 
37
#include <fcntl.h>
 
38
#include <errno.h>
 
39
#include <sys/ioctl.h>
 
40
 
 
41
static PulseAudioSystem *pasys = NULL;
 
42
 
 
43
#define NBLOCKS 8
 
44
 
 
45
class PulseAudioInputRegistrar : public AudioInputRegistrar {
 
46
        public:
 
47
                PulseAudioInputRegistrar();
 
48
                virtual AudioInput *create();
 
49
                virtual const QList<audioDevice> getDeviceChoices();
 
50
                virtual void setDeviceChoice(const QVariant &, Settings &);
 
51
                virtual bool canEcho(const QString &) const;
 
52
};
 
53
 
 
54
 
 
55
class PulseAudioOutputRegistrar : public AudioOutputRegistrar {
 
56
        public:
 
57
                PulseAudioOutputRegistrar();
 
58
                virtual AudioOutput *create();
 
59
                virtual const QList<audioDevice> getDeviceChoices();
 
60
                virtual void setDeviceChoice(const QVariant &, Settings &);
 
61
};
 
62
 
 
63
class PulseAudioInit : public DeferInit {
 
64
        public:
 
65
                PulseAudioInputRegistrar *airPulseAudio;
 
66
                PulseAudioOutputRegistrar *aorPulseAudio;
 
67
                void initialize() {
 
68
                        pasys = new PulseAudioSystem();
 
69
                        pasys->qmWait.lock();
 
70
                        pasys->qwcWait.wait(&pasys->qmWait, 1000);
 
71
                        pasys->qmWait.unlock();
 
72
                        if (pasys->bPulseIsGood) {
 
73
                                airPulseAudio = new PulseAudioInputRegistrar();
 
74
                                aorPulseAudio = new PulseAudioOutputRegistrar();
 
75
                        } else {
 
76
                                airPulseAudio = NULL;
 
77
                                aorPulseAudio = NULL;
 
78
                                delete pasys;
 
79
                                pasys = NULL;
 
80
                        }
 
81
                };
 
82
                void destroy() {
 
83
                        delete airPulseAudio;
 
84
                        delete aorPulseAudio;
 
85
                        delete pasys;
 
86
                        pasys = NULL;
 
87
                };
 
88
};
 
89
 
 
90
static PulseAudioInit pulseinit;
 
91
 
 
92
PulseAudioSystem::PulseAudioSystem() {
 
93
        pasInput = pasOutput = pasSpeaker = NULL;
 
94
        bSourceDone=bSinkDone=bServerDone = false;
 
95
        iDelayCache = 0;
 
96
        bPositionalCache = false;
 
97
        bPulseIsGood = false;
 
98
 
 
99
        pam = pa_threaded_mainloop_new();
 
100
 
 
101
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
 
102
 
 
103
        pa_proplist     *proplist;
 
104
 
 
105
        proplist = pa_proplist_new ();
 
106
        pa_proplist_sets (proplist,
 
107
                              PA_PROP_APPLICATION_NAME,
 
108
                              "Mumble");
 
109
        pa_proplist_sets (proplist,
 
110
                              PA_PROP_APPLICATION_ID,
 
111
                              "net.sourceforge.mumble.mumble");
 
112
        pa_proplist_sets (proplist,
 
113
                              PA_PROP_APPLICATION_ICON_NAME,
 
114
                              "mumble");
 
115
        pa_proplist_sets (proplist,
 
116
                              PA_PROP_MEDIA_ROLE,
 
117
                              "phone");
 
118
 
 
119
        pacContext = pa_context_new_with_proplist (api, NULL, proplist);
 
120
        pa_proplist_free (proplist);
 
121
        
 
122
        pa_context_set_subscribe_callback(pacContext, subscribe_callback, this);
 
123
 
 
124
        pa_context_set_state_callback(pacContext, context_state_callback, this);
 
125
        pa_context_connect(pacContext, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
 
126
 
 
127
        pade = api->defer_new(api, defer_event_callback, this);
 
128
        api->defer_enable(pade, false);
 
129
 
 
130
        pa_threaded_mainloop_start(pam);
 
131
}
 
132
 
 
133
PulseAudioSystem::~PulseAudioSystem() {
 
134
        pa_threaded_mainloop_stop(pam);
 
135
        pa_context_disconnect(pacContext);
 
136
        pa_context_unref(pacContext);
 
137
        pa_threaded_mainloop_free(pam);
 
138
}
 
139
 
 
140
void PulseAudioSystem::wakeup() {
 
141
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
 
142
        api->defer_enable(pade, true);
 
143
}
 
144
 
 
145
void PulseAudioSystem::wakeup_lock() {
 
146
        pa_threaded_mainloop_lock(pam);
 
147
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
 
148
        api->defer_enable(pade, true);
 
149
        pa_threaded_mainloop_unlock(pam);
 
150
}
 
151
 
 
152
void PulseAudioSystem::defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
 
153
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
154
        pas->eventCallback(a, e);
 
155
}
 
156
 
 
157
void PulseAudioSystem::eventCallback(pa_mainloop_api *api, pa_defer_event *) {
 
158
        api->defer_enable(pade, false);
 
159
 
 
160
        if (! bSourceDone || ! bSinkDone || ! bServerDone)
 
161
                return;
 
162
 
 
163
        AudioInputPtr ai = g.ai;
 
164
        AudioOutputPtr ao = g.ao;
 
165
        AudioInput *raw_ai = ai.get();
 
166
        AudioOutput *raw_ao = ao.get();
 
167
        PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(raw_ai);
 
168
        PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(raw_ao);
 
169
 
 
170
        if (raw_ao) {
 
171
                QString odev = g.s.qsPulseAudioOutput.isEmpty() ? qsDefaultOutput : g.s.qsPulseAudioOutput;
 
172
                pa_stream_state ost = pasOutput ? pa_stream_get_state(pasOutput) : PA_STREAM_TERMINATED;
 
173
                bool do_stop = false;
 
174
                bool do_start = false;
 
175
 
 
176
                if (! pao && (ost == PA_STREAM_READY)) {
 
177
                        do_stop = true;
 
178
                } else if (pao) {
 
179
                        switch (ost) {
 
180
                                case PA_STREAM_TERMINATED: {
 
181
                                                if (pasOutput)
 
182
                                                        pa_stream_unref(pasOutput);
 
183
                                                pa_sample_spec pss = qhSpecMap.value(odev);
 
184
                                                if (pss.rate == 0)
 
185
                                                        pss.rate = SAMPLE_RATE;
 
186
                                                if ((pss.channels == 0) || (! g.s.doPositionalAudio()))
 
187
                                                        pss.channels = 1;
 
188
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
 
189
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
 
190
                                                pasOutput = pa_stream_new(pacContext, "Mumble Speakers", &pss, NULL);
 
191
                                                pa_stream_set_state_callback(pasOutput, stream_callback, this);
 
192
                                                pa_stream_set_write_callback(pasOutput, write_callback, this);
 
193
                                        }
 
194
                                case PA_STREAM_UNCONNECTED:
 
195
                                        do_start = true;
 
196
                                        break;
 
197
                                case PA_STREAM_READY: {
 
198
                                                if (g.s.iOutputDelay != iDelayCache) {
 
199
                                                        do_stop = true;
 
200
                                                } else if (g.s.doPositionalAudio() != bPositionalCache) {
 
201
                                                        do_stop = true;
 
202
                                                } else if (odev != qsOutputCache) {
 
203
                                                        do_stop = true;
 
204
                                                }
 
205
                                                break;
 
206
                                        }
 
207
                                default:
 
208
                                        break;
 
209
                        }
 
210
                }
 
211
                if (do_stop) {
 
212
                        qWarning("PulseAudio: Stopping output");
 
213
                        pa_stream_disconnect(pasOutput);
 
214
                } else if (do_start) {
 
215
                        qWarning("PulseAudio: Starting output: %s", qPrintable(odev));
 
216
                        pa_buffer_attr buff;
 
217
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasOutput);
 
218
                        const unsigned int iBlockLen = ((pao->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
 
219
                        buff.tlength = iBlockLen * (g.s.iOutputDelay+1);
 
220
                        buff.minreq = iBlockLen;
 
221
                        buff.maxlength = -1;
 
222
                        buff.prebuf = -1;
 
223
                        buff.fragsize = iBlockLen;
 
224
 
 
225
                        iDelayCache = g.s.iOutputDelay;
 
226
                        bPositionalCache = g.s.doPositionalAudio();
 
227
                        qsOutputCache = odev;
 
228
 
 
229
                        pa_stream_connect_playback(pasOutput, qPrintable(odev), &buff, PA_STREAM_ADJUST_LATENCY, NULL, NULL);
 
230
                }
 
231
        }
 
232
 
 
233
        if (raw_ai) {
 
234
                QString idev = g.s.qsPulseAudioInput.isEmpty() ? qsDefaultInput : g.s.qsPulseAudioInput;
 
235
                pa_stream_state ist = pasInput ? pa_stream_get_state(pasInput) : PA_STREAM_TERMINATED;
 
236
                bool do_stop = false;
 
237
                bool do_start = false;
 
238
 
 
239
                if (! pai && (ist == PA_STREAM_READY)) {
 
240
                        do_stop = true;
 
241
                } else if (pai) {
 
242
                        switch (ist) {
 
243
                                case PA_STREAM_TERMINATED: {
 
244
                                                if (pasInput)
 
245
                                                        pa_stream_unref(pasInput);
 
246
 
 
247
                                                pa_sample_spec pss = qhSpecMap.value(idev);
 
248
                                                if (pss.rate == 0)
 
249
                                                        pss.rate = SAMPLE_RATE;
 
250
                                                pss.channels = 1;
 
251
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
 
252
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
 
253
 
 
254
                                                pasInput = pa_stream_new(pacContext, "Microphone", &pss, NULL);
 
255
                                                pa_stream_set_state_callback(pasInput, stream_callback, this);
 
256
                                                pa_stream_set_read_callback(pasInput, read_callback, this);
 
257
                                        }
 
258
                                case PA_STREAM_UNCONNECTED:
 
259
                                        do_start = true;
 
260
                                        break;
 
261
                                case PA_STREAM_READY: {
 
262
                                                if (idev != qsInputCache) {
 
263
                                                        do_stop = true;
 
264
                                                }
 
265
                                                break;
 
266
                                        }
 
267
                                default:
 
268
                                        break;
 
269
                        }
 
270
                }
 
271
                if (do_stop) {
 
272
                        qWarning("PulseAudio: Stopping input");
 
273
                        pa_stream_disconnect(pasInput);
 
274
                } else if (do_start) {
 
275
                        qWarning("PulseAudio: Starting input %s",qPrintable(idev));
 
276
                        pa_buffer_attr buff;
 
277
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasInput);
 
278
                        const unsigned int iBlockLen = ((pai->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
 
279
                        buff.tlength = iBlockLen;
 
280
                        buff.minreq = iBlockLen;
 
281
                        buff.maxlength = -1;
 
282
                        buff.prebuf = -1;
 
283
                        buff.fragsize = iBlockLen;
 
284
 
 
285
                        qsInputCache = idev;
 
286
 
 
287
                        pa_stream_connect_record(pasInput, qPrintable(idev), &buff, PA_STREAM_ADJUST_LATENCY);
 
288
                }
 
289
        }
 
290
 
 
291
        if (raw_ai) {
 
292
                QString odev = g.s.qsPulseAudioOutput.isEmpty() ? qsDefaultOutput : g.s.qsPulseAudioOutput;
 
293
                QString edev = qhEchoMap.value(odev);
 
294
                pa_stream_state est = pasSpeaker ? pa_stream_get_state(pasSpeaker) : PA_STREAM_TERMINATED;
 
295
                bool do_stop = false;
 
296
                bool do_start = false;
 
297
 
 
298
                if ((! pai || ! g.s.doEcho()) && (est == PA_STREAM_READY)) {
 
299
                        do_stop = true;
 
300
                } else if (pai && g.s.doEcho()) {
 
301
                        switch (est) {
 
302
                                case PA_STREAM_TERMINATED: {
 
303
                                                if (pasSpeaker)
 
304
                                                        pa_stream_unref(pasSpeaker);
 
305
 
 
306
                                                pa_sample_spec pss = qhSpecMap.value(edev);
 
307
                                                if (pss.rate == 0)
 
308
                                                        pss.rate = SAMPLE_RATE;
 
309
                                                pss.channels = 1;
 
310
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
 
311
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
 
312
                                                pasSpeaker = pa_stream_new(pacContext, "Mumble Speakers (Echo)", &pss, NULL);
 
313
                                                pa_stream_set_state_callback(pasSpeaker, stream_callback, this);
 
314
                                                pa_stream_set_read_callback(pasSpeaker, read_callback, this);
 
315
                                        }
 
316
                                case PA_STREAM_UNCONNECTED:
 
317
                                        do_start = true;
 
318
                                        break;
 
319
                                case PA_STREAM_READY: {
 
320
                                                if (edev != qsEchoCache) {
 
321
                                                        do_stop = true;
 
322
                                                }
 
323
                                                break;
 
324
                                        }
 
325
                                default:
 
326
                                        break;
 
327
                        }
 
328
                }
 
329
                if (do_stop) {
 
330
                        qWarning("PulseAudio: Stopping echo");
 
331
                        pa_stream_disconnect(pasSpeaker);
 
332
                } else if (do_start) {
 
333
                        qWarning("PulseAudio: Starting echo: %s", qPrintable(edev));
 
334
                        pa_buffer_attr buff;
 
335
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasSpeaker);
 
336
                        const unsigned int iBlockLen = ((pai->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
 
337
                        buff.tlength = iBlockLen;
 
338
                        buff.minreq = iBlockLen;
 
339
                        buff.maxlength = -1;
 
340
                        buff.prebuf = -1;
 
341
                        buff.fragsize = iBlockLen;
 
342
 
 
343
                        qsEchoCache = edev;
 
344
 
 
345
                        pa_stream_connect_record(pasSpeaker, qPrintable(edev), &buff, PA_STREAM_ADJUST_LATENCY);
 
346
                }
 
347
        }
 
348
}
 
349
 
 
350
void PulseAudioSystem::context_state_callback(pa_context *c, void *userdata) {
 
351
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
352
        pas->contextCallback(c);
 
353
}
 
354
 
 
355
void PulseAudioSystem::subscribe_callback(pa_context *, pa_subscription_event_type evt, unsigned int, void *userdata) {
 
356
        switch (evt & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
 
357
                case PA_SUBSCRIPTION_EVENT_NEW:
 
358
                case PA_SUBSCRIPTION_EVENT_REMOVE:
 
359
                        break;
 
360
                default:
 
361
                        return;
 
362
        }
 
363
        switch (evt & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
 
364
                case PA_SUBSCRIPTION_EVENT_SINK:
 
365
                case PA_SUBSCRIPTION_EVENT_SOURCE:
 
366
                        break;
 
367
                default:
 
368
                        return;
 
369
        }
 
370
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
371
        qWarning("PulseAudio: Sinks or inputs changed (inserted or removed sound card)");
 
372
        pas->query();
 
373
}
 
374
 
 
375
void PulseAudioSystem::sink_callback(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
 
376
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
377
        if (!i || eol) {
 
378
                pas->bSinkDone = true;
 
379
                pas->wakeup();
 
380
                return;
 
381
        }
 
382
 
 
383
        const QString name = QLatin1String(i->name);
 
384
 
 
385
        pas->qhIndexMap.insert(name, i->index);
 
386
        pas->qhSpecMap.insert(name, i->sample_spec);
 
387
        pas->qhOutput.insert(name, QLatin1String(i->description));
 
388
        pas->qhEchoMap.insert(name, QLatin1String(i->monitor_source_name));
 
389
}
 
390
 
 
391
void PulseAudioSystem::source_callback(pa_context *, const pa_source_info *i, int eol, void *userdata) {
 
392
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
393
        if (!i || eol) {
 
394
                pas->bSourceDone = true;
 
395
                pas->wakeup();
 
396
                return;
 
397
        }
 
398
 
 
399
        const QString name = QLatin1String(i->name);
 
400
 
 
401
        pas->qhIndexMap.insert(name, i->index);
 
402
        pas->qhSpecMap.insert(name, i->sample_spec);
 
403
 
 
404
        if (i->monitor_of_sink == PA_INVALID_INDEX)
 
405
                pas->qhInput.insert(QLatin1String(i->name), QLatin1String(i->description));
 
406
}
 
407
 
 
408
void PulseAudioSystem::server_callback(pa_context *, const pa_server_info *i, void *userdata) {
 
409
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
410
 
 
411
        pas->qsDefaultInput = QLatin1String(i->default_source_name);
 
412
        pas->qsDefaultOutput = QLatin1String(i->default_sink_name);
 
413
 
 
414
        pas->bServerDone = true;
 
415
        pas->wakeup();
 
416
}
 
417
 
 
418
void PulseAudioSystem::stream_callback(pa_stream *s, void *userdata) {
 
419
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
420
        switch (pa_stream_get_state(s)) {
 
421
                case PA_STREAM_FAILED:
 
422
                        qWarning("PulseAudio: Stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
 
423
                        break;
 
424
                default:
 
425
                        break;
 
426
        }
 
427
        pas->wakeup();
 
428
}
 
429
 
 
430
void PulseAudioSystem::read_callback(pa_stream *s, size_t bytes, void *userdata) {
 
431
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
432
 
 
433
        size_t length = bytes;
 
434
        const void *data;
 
435
        pa_stream_peek(s, &data, &length);
 
436
 
 
437
        AudioInputPtr ai = g.ai;
 
438
        PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(ai.get());
 
439
        if (! pai) {
 
440
                pa_stream_drop(s);
 
441
                pas->wakeup();
 
442
                return;
 
443
        }
 
444
 
 
445
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
 
446
 
 
447
        if (s == pas->pasInput) {
 
448
                if (!pa_sample_spec_equal(pss, &pai->pssMic)) {
 
449
                        pai->pssMic = *pss;
 
450
                        pai->iMicFreq = pss->rate;
 
451
                        pai->iMicChannels = pss->channels;
 
452
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
 
453
                                pai->eMicFormat = AudioInput::SampleFloat;
 
454
                        else
 
455
                                pai->eMicFormat = AudioInput::SampleShort;
 
456
                        pai->initializeMixer();
 
457
                }
 
458
                pai->addMic(data, length / pai->iMicSampleSize);
 
459
        } else if (s == pas->pasSpeaker) {
 
460
                if (!pa_sample_spec_equal(pss, &pai->pssEcho)) {
 
461
                        pai->pssEcho = *pss;
 
462
                        pai->iEchoFreq = pss->rate;
 
463
                        pai->iEchoChannels = pss->channels;
 
464
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
 
465
                                pai->eEchoFormat = AudioInput::SampleFloat;
 
466
                        else
 
467
                                pai->eEchoFormat = AudioInput::SampleShort;
 
468
                        pai->initializeMixer();
 
469
                }
 
470
                pai->addEcho(data, length / pai->iEchoSampleSize);
 
471
        }
 
472
 
 
473
        pa_stream_drop(s);
 
474
}
 
475
 
 
476
void PulseAudioSystem::write_callback(pa_stream *s, size_t bytes, void *userdata) {
 
477
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
 
478
        Q_ASSERT(s == pas->pasOutput);
 
479
 
 
480
        AudioOutputPtr ao = g.ao;
 
481
        PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(ao.get());
 
482
 
 
483
        unsigned char buffer[bytes];
 
484
 
 
485
        if (! pao) {
 
486
                // Transitioning, but most likely transitions back, so just zero.
 
487
                memset(buffer, 0, bytes);
 
488
                pa_stream_write(s, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
 
489
                pas->wakeup();
 
490
                return;
 
491
        }
 
492
 
 
493
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
 
494
        const pa_channel_map *pcm = pa_stream_get_channel_map(pas->pasOutput);
 
495
        if (!pa_sample_spec_equal(pss, &pao->pss) || !pa_channel_map_equal(pcm, &pao->pcm)) {
 
496
                pao->pss = *pss;
 
497
                pao->pcm = *pcm;
 
498
                if (pss->format == PA_SAMPLE_FLOAT32NE)
 
499
                        pao->eSampleFormat = AudioOutput::SampleFloat;
 
500
                else
 
501
                        pao->eSampleFormat = AudioOutput::SampleShort;
 
502
                pao->iMixerFreq = pss->rate;
 
503
                pao->iChannels = pss->channels;
 
504
                unsigned int chanmasks[pss->channels];
 
505
                for (int i=0;i<pss->channels;++i) {
 
506
                        unsigned int cm = 0;
 
507
                        switch (pcm->map[i]) {
 
508
                                case PA_CHANNEL_POSITION_LEFT:
 
509
                                        cm = SPEAKER_FRONT_LEFT;
 
510
                                        break;
 
511
                                case PA_CHANNEL_POSITION_RIGHT:
 
512
                                        cm = SPEAKER_FRONT_RIGHT;
 
513
                                        break;
 
514
                                case PA_CHANNEL_POSITION_CENTER:
 
515
                                        cm = SPEAKER_FRONT_CENTER;
 
516
                                        break;
 
517
                                case PA_CHANNEL_POSITION_REAR_LEFT:
 
518
                                        cm = SPEAKER_BACK_LEFT;
 
519
                                        break;
 
520
                                case PA_CHANNEL_POSITION_REAR_RIGHT:
 
521
                                        cm = SPEAKER_BACK_RIGHT;
 
522
                                        break;
 
523
                                case PA_CHANNEL_POSITION_REAR_CENTER:
 
524
                                        cm = SPEAKER_BACK_CENTER;
 
525
                                        break;
 
526
                                case PA_CHANNEL_POSITION_LFE:
 
527
                                        cm = SPEAKER_LOW_FREQUENCY;
 
528
                                        break;
 
529
                                case PA_CHANNEL_POSITION_SIDE_LEFT:
 
530
                                        cm = SPEAKER_SIDE_LEFT;
 
531
                                        break;
 
532
                                case PA_CHANNEL_POSITION_SIDE_RIGHT:
 
533
                                        cm = SPEAKER_SIDE_RIGHT;
 
534
                                        break;
 
535
                                case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
 
536
                                        cm = SPEAKER_FRONT_LEFT_OF_CENTER;
 
537
                                        break;
 
538
                                case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
 
539
                                        cm = SPEAKER_FRONT_RIGHT_OF_CENTER;
 
540
                                        break;
 
541
                                default:
 
542
                                        cm = 0;
 
543
                                        break;
 
544
                        }
 
545
                        chanmasks[i] = cm;
 
546
                }
 
547
                pao->initializeMixer(chanmasks);
 
548
        }
 
549
 
 
550
        const unsigned int iSampleSize = pao->iSampleSize;
 
551
        const unsigned int samples = bytes / iSampleSize;
 
552
 
 
553
        if (! pao->mix(buffer, samples))
 
554
                memset(buffer, 0, bytes);
 
555
        pa_stream_write(s, buffer, iSampleSize * samples, NULL, 0, PA_SEEK_RELATIVE);
 
556
}
 
557
 
 
558
void PulseAudioSystem::query() {
 
559
        bSourceDone=bSinkDone=bServerDone = false;
 
560
        qhInput.clear();
 
561
        qhOutput.clear();
 
562
        qhEchoMap.clear();
 
563
        qhInput.insert(QString(), tr("Default Input"));
 
564
        qhOutput.insert(QString(), tr("Default Output"));
 
565
        pa_operation_unref(pa_context_get_server_info(pacContext, server_callback, this));
 
566
        pa_operation_unref(pa_context_get_sink_info_list(pacContext, sink_callback, this));
 
567
        pa_operation_unref(pa_context_get_source_info_list(pacContext, source_callback, this));
 
568
        wakeup();
 
569
}
 
570
 
 
571
void PulseAudioSystem::contextCallback(pa_context *c) {
 
572
        Q_ASSERT(c == pacContext);
 
573
        switch (pa_context_get_state(c)) {
 
574
                case PA_CONTEXT_READY:
 
575
                        bPulseIsGood = true;
 
576
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SOURCE, NULL, this));
 
577
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SINK, NULL, this));
 
578
                        query();
 
579
                        break;
 
580
                case PA_CONTEXT_TERMINATED:
 
581
                        qWarning("PulseAudio: Forcibly disconnected from PulseAudio");
 
582
                        break;
 
583
                case PA_CONTEXT_FAILED:
 
584
                        qWarning("PulseAudio: Connection failure: %s", pa_strerror(pa_context_errno(c)));
 
585
                        break;
 
586
                default:
 
587
                        return;
 
588
        }
 
589
        qmWait.lock();
 
590
        qwcWait.wakeAll();
 
591
        qmWait.unlock();
 
592
}
 
593
 
 
594
PulseAudioInputRegistrar::PulseAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("PulseAudio"), 10) {
 
595
}
 
596
 
 
597
AudioInput *PulseAudioInputRegistrar::create() {
 
598
        return new PulseAudioInput();
 
599
}
 
600
 
 
601
const QList<audioDevice> PulseAudioInputRegistrar::getDeviceChoices() {
 
602
        QList<audioDevice> qlReturn;
 
603
 
 
604
        QStringList qlInputDevs = pasys->qhInput.keys();
 
605
        qSort(qlInputDevs);
 
606
 
 
607
        if (qlInputDevs.contains(g.s.qsPulseAudioInput)) {
 
608
                qlInputDevs.removeAll(g.s.qsPulseAudioInput);
 
609
                qlInputDevs.prepend(g.s.qsPulseAudioInput);
 
610
        }
 
611
 
 
612
        foreach(const QString &dev, qlInputDevs) {
 
613
                qlReturn << audioDevice(pasys->qhInput.value(dev), dev);
 
614
        }
 
615
 
 
616
        return qlReturn;
 
617
}
 
618
 
 
619
void PulseAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
 
620
        s.qsPulseAudioInput = choice.toString();
 
621
}
 
622
 
 
623
bool PulseAudioInputRegistrar::canEcho(const QString &osys) const {
 
624
        return (osys == name);
 
625
}
 
626
 
 
627
PulseAudioOutputRegistrar::PulseAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("PulseAudio"), 10) {
 
628
}
 
629
 
 
630
AudioOutput *PulseAudioOutputRegistrar::create() {
 
631
        return new PulseAudioOutput();
 
632
}
 
633
 
 
634
const QList<audioDevice> PulseAudioOutputRegistrar::getDeviceChoices() {
 
635
        QList<audioDevice> qlReturn;
 
636
 
 
637
        QStringList qlOutputDevs = pasys->qhOutput.keys();
 
638
        qSort(qlOutputDevs);
 
639
 
 
640
        if (qlOutputDevs.contains(g.s.qsPulseAudioOutput)) {
 
641
                qlOutputDevs.removeAll(g.s.qsPulseAudioOutput);
 
642
                qlOutputDevs.prepend(g.s.qsPulseAudioOutput);
 
643
        }
 
644
 
 
645
        foreach(const QString &dev, qlOutputDevs) {
 
646
                qlReturn << audioDevice(pasys->qhOutput.value(dev), dev);
 
647
        }
 
648
 
 
649
        return qlReturn;
 
650
}
 
651
 
 
652
void PulseAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
 
653
        s.qsPulseAudioOutput = choice.toString();
 
654
}
 
655
 
 
656
PulseAudioInput::PulseAudioInput() {
 
657
        memset(&pssMic, 0, sizeof(pssMic));
 
658
        memset(&pssEcho, 0, sizeof(pssEcho));
 
659
        bRunning = true;
 
660
        if (pasys)
 
661
                pasys->wakeup_lock();
 
662
}
 
663
 
 
664
PulseAudioInput::~PulseAudioInput() {
 
665
        bRunning = false;
 
666
        qmMutex.lock();
 
667
        qwcWait.wakeAll();
 
668
        qmMutex.unlock();
 
669
        wait();
 
670
        if (pasys)
 
671
                pasys->wakeup_lock();
 
672
}
 
673
 
 
674
void PulseAudioInput::run() {
 
675
        qmMutex.lock();
 
676
        while (bRunning)
 
677
                qwcWait.wait(&qmMutex);
 
678
        qmMutex.unlock();
 
679
}
 
680
 
 
681
PulseAudioOutput::PulseAudioOutput() {
 
682
        memset(&pss, 0, sizeof(pss));
 
683
        memset(&pcm, 0, sizeof(pcm));
 
684
        bRunning = true;
 
685
        if (pasys)
 
686
                pasys->wakeup_lock();
 
687
}
 
688
 
 
689
PulseAudioOutput::~PulseAudioOutput() {
 
690
        bRunning = false;
 
691
        qmMutex.lock();
 
692
        qwcWait.wakeAll();
 
693
        qmMutex.unlock();
 
694
        wait();
 
695
        if (pasys)
 
696
                pasys->wakeup_lock();
 
697
}
 
698
 
 
699
void PulseAudioOutput::run() {
 
700
        qmMutex.lock();
 
701
        while (bRunning)
 
702
                qwcWait.wait(&qmMutex);
 
703
        qmMutex.unlock();
 
704
}