1 /*************************************************************************
2   Record-PulseAudio.cpp  -  device for audio recording via PulesAudio
3                              -------------------
4     begin                : Sun Okt 20 2013
5     copyright            : (C) 2014 by Joerg-Christian Boehme
6     email                : joerg@chaosdorf.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "config.h"
19 #ifdef HAVE_PULSEAUDIO_SUPPORT
20 
21 #include <errno.h>
22 #include <signal.h>
23 #include <unistd.h>
24 
25 #include <limits>
26 
27 #include <pulse/thread-mainloop.h>
28 
29 #include <QApplication>
30 #include <QCursor>
31 #include <QFileInfo>
32 #include <QLatin1Char>
33 #include <QLocale>
34 #include <QString>
35 #include <QVariant>
36 #include <QtGlobal>
37 
38 #include <KLocalizedString>
39 #include <KUser>
40 
41 #include "libkwave/Compression.h"
42 #include "libkwave/SampleFormat.h"
43 #include "libkwave/String.h"
44 #include "libkwave/Utils.h"
45 #include "libkwave/memcpy.h"
46 
47 #include "Record-PulseAudio.h"
48 
49 /** helper macro: returns the number of elements in an array */
50 #define ELEMENTS_OF(__array__) (sizeof(__array__) / sizeof(__array__[0]))
51 
52 /**
53  * timeout for the device scan [ms]
54  * @see scanDevices()
55  */
56 #define TIMEOUT_WAIT_DEVICE_SCAN 10000
57 
58 /**
59  * timeout to wait for the connection to the server [ms]
60  * @see connectToServer()
61  */
62 #define TIMEOUT_CONNECT_TO_SERVER 20000
63 
64 /**
65  * timeout to wait for record [ms]
66  * @see open()
67  */
68 #define TIMEOUT_CONNECT_RECORD 10000
69 
70 /**
71  * timeout to wait for disconnecting the recording stream [ms]
72  * @see close()
73  */
74 #define TIMEOUT_DISCONNECT_STREAM 10000
75 
76 /**
77  * Global list of all known sample formats.
78  * @note this list should be sorted so that the most preferable formats
79  *       come first in the list. When searching for a format that matches
80  *       a given set of parameters, the first entry is taken.
81  *
82  *       The sort order should be:
83  *       - compression:      none -> ulaw -> alaw -> adpcm -> mpeg ...
84  *       - bits per sample:  ascending
85  *       - sample format:    signed -> unsigned -> float -> double ...
86  *       - endianness:       cpu -> little -> big
87  *       - bytes per sample: ascending
88  */
89 static const pa_sample_format_t _known_formats[] =
90 {
91     /* 8 bit */
92     PA_SAMPLE_U8,
93 
94     /* 16 bit */
95     PA_SAMPLE_S16LE, PA_SAMPLE_S16BE,
96 
97     /* 24 bit */
98     PA_SAMPLE_S24LE, PA_SAMPLE_S24BE,
99 
100     /* 24 bit in LSB of 32 bit */
101     PA_SAMPLE_S24_32LE, PA_SAMPLE_S24_32BE,
102 
103     /* 32 bit */
104     PA_SAMPLE_S32LE, PA_SAMPLE_S32BE,
105 
106     /* float 32 bit */
107     PA_SAMPLE_FLOAT32LE, PA_SAMPLE_FLOAT32BE,
108 
109     /* ULAW */
110     PA_SAMPLE_ULAW,
111 
112     /* ALAW */
113     PA_SAMPLE_ALAW
114 };
115 
116 //***************************************************************************
117 /** find out the SampleFormat of an PulseAudio format */
sample_format_of(pa_sample_format_t fmt)118 static Kwave::SampleFormat::Format sample_format_of(pa_sample_format_t fmt)
119 {
120     Kwave::SampleFormat::Format sampleFormat = Kwave::SampleFormat::Unknown;
121     switch (fmt) {
122 	case PA_SAMPLE_FLOAT32LE: /* FALLTHROUGH */
123 	case PA_SAMPLE_FLOAT32BE:
124 	    sampleFormat = Kwave::SampleFormat::Float;
125 	    break;
126 	case PA_SAMPLE_U8:
127 	    sampleFormat = Kwave::SampleFormat::Unsigned;
128 	    break;
129 	default:
130 	    sampleFormat = Kwave::SampleFormat::Signed;
131 	    break;
132     }
133     return sampleFormat;
134 }
135 
136 //***************************************************************************
137 /** find out the endianness of an PulseAudio format */
endian_of(pa_sample_format_t fmt)138 static Kwave::byte_order_t endian_of(pa_sample_format_t fmt)
139 {
140     if (pa_sample_format_is_le(fmt) == 1)
141 	return Kwave::LittleEndian;
142     if (pa_sample_format_is_be(fmt) == 1)
143 	return Kwave::BigEndian;
144     return Kwave::CpuEndian;
145 }
146 
147 //***************************************************************************
compression_of(pa_sample_format_t fmt)148 static Kwave::Compression::Type compression_of(pa_sample_format_t fmt)
149 {
150     Kwave::Compression::Type compression = Kwave::Compression::NONE;
151     switch (fmt) {
152 	case PA_SAMPLE_ULAW:
153 	    compression = Kwave::Compression::G711_ULAW;
154 	    break;
155 	case PA_SAMPLE_ALAW:
156 	    compression = Kwave::Compression::G711_ALAW;
157 	    break;
158 	default:
159 	    compression = Kwave::Compression::NONE;
160 	    break;
161     }
162     return compression;
163 }
164 
165 //***************************************************************************
bits_of(pa_sample_format_t fmt)166 static int bits_of(pa_sample_format_t fmt)
167 {
168     int bits = 0;
169     switch (fmt) {
170 	case PA_SAMPLE_ULAW:    /* FALLTHROUGH */
171 	case PA_SAMPLE_ALAW:    /* FALLTHROUGH */
172 	case PA_SAMPLE_U8:
173 	    bits = 8;
174 	    break;
175 	case PA_SAMPLE_S16LE:    /* FALLTHROUGH */
176 	case PA_SAMPLE_S16BE:
177 	    bits = 16;
178 	    break;
179 	case PA_SAMPLE_S24LE:    /* FALLTHROUGH */
180 	case PA_SAMPLE_S24BE:    /* FALLTHROUGH */
181 	case PA_SAMPLE_S24_32LE: /* FALLTHROUGH */
182 	case PA_SAMPLE_S24_32BE:
183 	    bits = 24;
184 	    break;
185 	case PA_SAMPLE_S32LE:     /* FALLTHROUGH */
186 	case PA_SAMPLE_S32BE:     /* FALLTHROUGH */
187 	case PA_SAMPLE_FLOAT32LE: /* FALLTHROUGH */
188 	case PA_SAMPLE_FLOAT32BE:
189 	    bits = 32;
190 	    break;
191 	default:
192 	    bits = 0;
193 	    break;
194     }
195     return bits;
196 }
197 
198 //***************************************************************************
RecordPulseAudio()199 Kwave::RecordPulseAudio::RecordPulseAudio()
200     :Kwave::RecordDevice(),
201     m_mainloop_thread(this, QVariant()),
202     m_mainloop_lock(),
203     m_mainloop_signal(),
204     m_sample_format(Kwave::SampleFormat::Unknown),
205     m_tracks(0),
206     m_rate(0.0),
207     m_compression(Kwave::Compression::NONE),
208     m_bits_per_sample(0),
209     m_supported_formats(),
210     m_initialized(false),
211     m_pa_proplist(Q_NULLPTR),
212     m_pa_mainloop(Q_NULLPTR),
213     m_pa_context(Q_NULLPTR),
214     m_pa_stream(Q_NULLPTR),
215     m_pa_device(),
216     m_name(i18n("Kwave record")),
217     m_device_list()
218 {
219 }
220 
221 //***************************************************************************
~RecordPulseAudio()222 Kwave::RecordPulseAudio::~RecordPulseAudio()
223 {
224     disconnectFromServer();
225     m_device_list.clear();
226 }
227 
228 //***************************************************************************
detectSupportedFormats(const QString & device)229 void Kwave::RecordPulseAudio::detectSupportedFormats(const QString &device)
230 {
231     // start with an empty list
232     m_supported_formats.clear();
233 
234     // lookup in the device list
235     if (!m_device_list.contains(device))
236 	return;
237 
238     const pa_sample_spec     &sampleSpec = m_device_list[device].m_sample_spec;
239     const pa_sample_format_t &formatSpec = sampleSpec.format;
240 
241     // try all known formats
242     qDebug("--- list of supported formats --- ");
243     for(unsigned int i = 0; i < ELEMENTS_OF(_known_formats); ++i) {
244 	const pa_sample_format_t &fmt = _known_formats[i];
245 
246 	if (formatSpec < _known_formats[i])
247 	    continue;
248 
249 	// TODO: avoid duplicate entries that differ only in endianness,
250 	//       prefer our own (native) endianness
251 
252 	Kwave::Compression t(compression_of(fmt));
253 	Kwave::SampleFormat::Map sf;
254 	qDebug("#%2u, %2u bit [%u byte], %s, '%s', '%s'",
255 	    i,
256 	    bits_of(fmt),
257 	    (bits_of(fmt) + 7) >> 3,
258 	    endian_of(fmt) == Kwave::CpuEndian ? "CPU" :
259 	    (endian_of(fmt) == Kwave::LittleEndian ? "LE " : "BE "),
260 	    DBG(sf.description(sf.findFromData(sample_format_of(fmt)), true)),
261 	    DBG(t.name())
262 	);
263 
264 	m_supported_formats.append(fmt);
265     }
266     qDebug("--------------------------------- ");
267 }
268 
269 //***************************************************************************
pa_read_cb(pa_stream * p,size_t nbytes,void * userdata)270 void Kwave::RecordPulseAudio::pa_read_cb(pa_stream *p, size_t nbytes,
271                                          void *userdata)
272 {
273     Kwave::RecordPulseAudio *record_plugin =
274 	reinterpret_cast<Kwave::RecordPulseAudio *>(userdata);
275     Q_ASSERT(record_plugin);
276     if (record_plugin) record_plugin->notifyRead(p, nbytes);
277 }
278 
279 //***************************************************************************
notifyRead(pa_stream * stream,size_t nbytes)280 void Kwave::RecordPulseAudio::notifyRead(pa_stream *stream, size_t nbytes)
281 {
282     Q_UNUSED(nbytes)
283     Q_ASSERT(stream);
284 
285     if (!stream || (stream != m_pa_stream)) return;
286 
287     m_mainloop_signal.wakeAll();
288 }
289 
290 //***************************************************************************
pa_stream_state_cb(pa_stream * p,void * userdata)291 void Kwave::RecordPulseAudio::pa_stream_state_cb(pa_stream *p, void *userdata)
292 {
293     Kwave::RecordPulseAudio *record_plugin =
294 	reinterpret_cast<Kwave::RecordPulseAudio *>(userdata);
295     Q_ASSERT(record_plugin);
296     if (record_plugin) record_plugin->notifyStreamState(p);
297 }
298 
299 //***************************************************************************
notifyStreamState(pa_stream * stream)300 void Kwave::RecordPulseAudio::notifyStreamState(pa_stream* stream)
301 {
302     Q_ASSERT(stream);
303     if (!stream || (stream != m_pa_stream)) return;
304 
305     pa_stream_state_t state = pa_stream_get_state(stream);
306 
307 #ifdef DEBUG
308     #define DBG_CASE(x) case x: qDebug("RecordPulseAudio -> " #x ); break
309     switch (state)
310     {
311 	DBG_CASE(PA_STREAM_CREATING);
312 	DBG_CASE(PA_STREAM_UNCONNECTED);
313 	DBG_CASE(PA_STREAM_FAILED);
314 	DBG_CASE(PA_STREAM_TERMINATED);
315 	DBG_CASE(PA_STREAM_READY);
316     }
317     #undef DBG_CASE
318 #endif /* DEBUG */
319 
320     switch (state) {
321 	case PA_STREAM_CREATING:
322 	    break;
323 	case PA_STREAM_UNCONNECTED: /* FALLTHROUGH */
324 	case PA_STREAM_FAILED:      /* FALLTHROUGH */
325 	case PA_STREAM_TERMINATED:  /* FALLTHROUGH */
326 	case PA_STREAM_READY:
327 	    m_mainloop_signal.wakeAll();
328 	    break;
329 	default:
330 	    Q_ASSERT(0 && "?");
331 	    break;
332     }
333 }
334 
335 //***************************************************************************
pa_context_notify_cb(pa_context * c,void * userdata)336 void Kwave::RecordPulseAudio::pa_context_notify_cb(pa_context* c, void* userdata)
337 {
338     Kwave::RecordPulseAudio *record_plugin =
339 	reinterpret_cast<Kwave::RecordPulseAudio *>(userdata);
340     Q_ASSERT(record_plugin);
341     if (record_plugin) record_plugin->notifyContext(c);
342 }
343 
344 //***************************************************************************
notifyContext(pa_context * c)345 void Kwave::RecordPulseAudio::notifyContext(pa_context *c)
346 {
347     Q_ASSERT(c == m_pa_context);
348     const pa_context_state_t state = pa_context_get_state(c);
349 
350 #ifdef DEBUG
351     #define DBG_CASE(x) case x: qDebug("RecordPulseAudio -> " #x ); break
352     switch (state)
353     {
354 	DBG_CASE(PA_CONTEXT_UNCONNECTED);
355 	DBG_CASE(PA_CONTEXT_CONNECTING);
356 	DBG_CASE(PA_CONTEXT_AUTHORIZING);
357 	DBG_CASE(PA_CONTEXT_SETTING_NAME);
358 	DBG_CASE(PA_CONTEXT_READY);
359 	DBG_CASE(PA_CONTEXT_TERMINATED);
360 	DBG_CASE(PA_CONTEXT_FAILED);
361     }
362     #undef DBG_CASE
363 #endif /* DEBUG */
364 
365     switch (state)
366     {
367 	case PA_CONTEXT_UNCONNECTED: /* FALLTHROUGH */
368 	case PA_CONTEXT_CONNECTING:  /* FALLTHROUGH */
369 	case PA_CONTEXT_AUTHORIZING: /* FALLTHROUGH */
370 	case PA_CONTEXT_SETTING_NAME:
371 	    break;
372 	case PA_CONTEXT_READY:       /* FALLTHROUGH */
373 	case PA_CONTEXT_TERMINATED:  /* FALLTHROUGH */
374 	case PA_CONTEXT_FAILED:
375 	    m_mainloop_signal.wakeAll();
376 	    break;
377     }
378 }
379 
380 //***************************************************************************
mode2format(int compression,int bits,Kwave::SampleFormat::Format sample_format)381 pa_sample_format_t Kwave::RecordPulseAudio::mode2format(
382     int compression, int bits, Kwave::SampleFormat::Format sample_format)
383 {
384     // loop over all supported formats and keep only those that are
385     // compatible with the given compression, bits and sample format
386     foreach (const pa_sample_format_t &fmt, m_supported_formats)
387     {
388 	if (compression_of(fmt) != compression) continue;
389 	if (bits_of(fmt) != bits) continue;
390 	if (!(sample_format_of(fmt) == sample_format)) continue;
391 
392 	// mode is compatible
393 	// As the list of known formats is already sorted so that
394 	// the simplest formats come first, we don't have a lot
395 	// of work -> just take the first entry ;-)
396 	return fmt;
397     }
398 
399     qWarning("RecordPulesAudio::mode2format -> no match found !?");
400     return PA_SAMPLE_INVALID;
401 }
402 
403 //***************************************************************************
endianness()404 Kwave::byte_order_t Kwave::RecordPulseAudio::endianness()
405 {
406     pa_sample_format_t fmt = mode2format(m_compression, m_bits_per_sample,
407                                          m_sample_format);
408     return (fmt != PA_SAMPLE_INVALID) ?
409 	endian_of(fmt) : Kwave::UnknownEndian;
410 }
411 
412 //***************************************************************************
sampleFormat()413 Kwave::SampleFormat::Format Kwave::RecordPulseAudio::sampleFormat()
414 {
415     return m_sample_format;
416 }
417 
418 //***************************************************************************
setSampleFormat(Kwave::SampleFormat::Format new_format)419 int Kwave::RecordPulseAudio::setSampleFormat(
420     Kwave::SampleFormat::Format new_format)
421 {
422     if (m_sample_format == new_format)
423 	return 0;
424     close();
425     m_sample_format = new_format;
426     return 0;
427 }
428 
429 //***************************************************************************
detectSampleFormats()430 QList<Kwave::SampleFormat::Format> Kwave::RecordPulseAudio::detectSampleFormats()
431 {
432     QList<Kwave::SampleFormat::Format> list;
433 
434     // try all known sample formats
435     foreach (const pa_sample_format_t &fmt, m_supported_formats)
436     {
437 	const Kwave::SampleFormat::Format sample_format = sample_format_of(fmt);
438 
439 	// only accept bits/sample if compression types
440 	// and bits per sample match
441 	if (compression_of(fmt) != m_compression) continue;
442 	if (bits_of(fmt) != Kwave::toInt(m_bits_per_sample))
443 	    continue;
444 
445 	// do not produce duplicates
446 	if (list.contains(sample_format)) continue;
447 
448 	list.append(sample_format);
449     }
450 
451     return list;
452 }
453 
454 //***************************************************************************
bitsPerSample()455 int Kwave::RecordPulseAudio::bitsPerSample()
456 {
457     return m_bits_per_sample;
458 }
459 
460 //***************************************************************************
setBitsPerSample(unsigned int new_bits)461 int Kwave::RecordPulseAudio::setBitsPerSample(unsigned int new_bits)
462 {
463     if (m_bits_per_sample == new_bits)
464 	return 0;
465     close();
466     m_bits_per_sample = new_bits;
467     return 0;
468 }
469 
470 //***************************************************************************
supportedBits()471 QList< unsigned int > Kwave::RecordPulseAudio::supportedBits()
472 {
473     QList<unsigned int> list;
474 
475     // try all known sample formats
476     foreach(const pa_sample_format_t &fmt, m_supported_formats)
477     {
478 	const unsigned int bits = bits_of(fmt);
479 
480 	// 0  bits means invalid/does not apply
481 	if (!bits) continue;
482 
483 	// only accept bits/sample if compression matches
484 	if (compression_of(fmt) != m_compression) continue;
485 
486 	// do not produce duplicates
487 	if (list.contains(bits)) continue;
488 
489 	list.append(bits);
490     }
491 
492     return list;
493 }
494 
495 //***************************************************************************
compression()496 Kwave::Compression::Type Kwave::RecordPulseAudio::compression()
497 {
498     return m_compression;
499 }
500 
501 //***************************************************************************
setCompression(Kwave::Compression::Type new_compression)502 int Kwave::RecordPulseAudio::setCompression(
503     Kwave::Compression::Type new_compression
504 )
505 {
506     if (m_compression != new_compression) {
507 	close();
508 	m_compression = new_compression;
509     }
510     return 0;
511 }
512 
513 //***************************************************************************
detectCompressions()514 QList<Kwave::Compression::Type> Kwave::RecordPulseAudio::detectCompressions()
515 {
516     QList<Kwave::Compression::Type> list;
517 
518     // try all known sample formats
519     foreach (const pa_sample_format_t &fmt, m_supported_formats)
520     {
521 	Kwave::Compression::Type compression = compression_of(fmt);
522 
523 	// do not produce duplicates
524 	if (list.contains(compression)) continue;
525 
526 	Kwave::Compression t(compression);
527 	list.append(compression);
528     }
529 
530     return list;
531 }
532 
533 //***************************************************************************
sampleRate()534 double Kwave::RecordPulseAudio::sampleRate()
535 {
536     return m_rate;
537 }
538 
539 //***************************************************************************
setSampleRate(double & new_rate)540 int Kwave::RecordPulseAudio::setSampleRate(double& new_rate)
541 {
542     if (qFuzzyCompare(new_rate, m_rate))
543 	return 0;
544     close();
545     m_rate = new_rate;
546     return 0;
547 }
548 
549 //***************************************************************************
detectSampleRates()550 QList< double > Kwave::RecordPulseAudio::detectSampleRates()
551 {
552     QList<double> list;
553 
554     static const unsigned int known_rates[] = {
555 	  1,
556 	  1000, // (just for testing)
557 	  2000, // (just for testing)
558 	  4000, // standard OSS
559 	  5125, // seen in Harmony driver (HP712, 715/new)
560 	  5510, // seen in AD1848 driver
561 	  5512, // seen in ES1370 driver
562 	  6215, // seen in ES188X driver
563 	  6615, // seen in Harmony driver (HP712, 715/new)
564 	  6620, // seen in AD1848 driver
565 	  7350, // seen in AWACS and Burgundy sound driver
566 	  8000, // standard OSS
567 	  8820, // seen in AWACS and Burgundy sound driver
568 	  9600, // seen in AD1848 driver
569 	 11025, // soundblaster
570 	 14700, // seen in AWACS and Burgundy sound driver
571 	 16000, // standard OSS
572 	 17640, // seen in AWACS and Burgundy sound driver
573 	 18900, // seen in Harmony driver (HP712, 715/new)
574 	 22050, // soundblaster
575 	 24000, // seen in NM256 driver
576 	 27428, // seen in Harmony driver (HP712, 715/new)
577 	 29400, // seen in AWACS and Burgundy sound driver
578 	 32000, // standard OSS
579 	 32768, // seen in CS4299 driver
580 	 33075, // seen in Harmony driver (HP712, 715/new)
581 	 37800, // seen in Harmony driver (HP712, 715/new)
582 	 44100, // soundblaster
583 	 48000, // AC97
584 	 64000, // AC97
585 	 88200, // seen in RME96XX driver
586 	 96000, // AC97
587 	128000, // (just for testing)
588 	192000 // AC97
589     };
590 
591     pa_sample_spec sampleSpec = m_device_list[m_device].m_sample_spec;
592     uint32_t rate = sampleSpec.rate;
593     for (unsigned int i = 0; i < ELEMENTS_OF(known_rates); i++) {
594 	if(known_rates[i] <= rate) {
595 	    list.append(known_rates[i]);
596 	}
597     }
598 
599     return list;
600 }
601 
602 //***************************************************************************
tracks()603 int Kwave::RecordPulseAudio::tracks()
604 {
605     return m_tracks;
606 }
607 
608 //***************************************************************************
setTracks(unsigned int & tracks)609 int Kwave::RecordPulseAudio::setTracks(unsigned int &tracks)
610 {
611     const quint8 max_tracks = std::numeric_limits<quint8>::max();
612 
613     if (tracks > max_tracks) {
614 	tracks = max_tracks;
615 	return -1;
616     }
617 
618     if (tracks == m_tracks)
619 	return 0;
620 
621     close();
622     m_tracks = static_cast<quint8>(tracks);
623 
624     return 0;
625 }
626 
627 //***************************************************************************
detectTracks(unsigned int & min,unsigned int & max)628 int Kwave::RecordPulseAudio::detectTracks(unsigned int &min, unsigned int &max)
629 {
630     pa_sample_spec sampleSpec = m_device_list[m_device].m_sample_spec;
631     unsigned int channels = sampleSpec.channels;
632 
633     min = 1;
634     max = qBound<unsigned int>(min, channels, PA_CHANNELS_MAX);
635     return 0;
636 }
637 
638 //***************************************************************************
close()639 int Kwave::RecordPulseAudio::close()
640 {
641     if (m_pa_stream) {
642 	pa_stream_drop(m_pa_stream);
643 
644 	m_mainloop_lock.lock();
645 	pa_stream_disconnect(m_pa_stream);
646 	qDebug("RecordPulseAudio::close() - waiting for stream disconnect...");
647 	m_mainloop_signal.wait(&m_mainloop_lock, TIMEOUT_DISCONNECT_STREAM);
648 	m_mainloop_lock.unlock();
649 	qDebug("RecordPulseAudio::close() - stream disconnect DONE");
650 
651 	pa_stream_unref(m_pa_stream);
652     }
653     m_pa_stream = Q_NULLPTR;
654 
655     // we need to re-initialize the next time
656     m_initialized = false;
657     return 0;
658 }
659 
660 //***************************************************************************
read(QByteArray & buffer,unsigned int offset)661 int Kwave::RecordPulseAudio::read(QByteArray& buffer, unsigned int offset)
662 {
663     if (buffer.isNull() || buffer.isEmpty())
664 	return 0; // no buffer, nothing to do
665 
666     unsigned int length = buffer.size();
667 
668     // we configure our device at a late stage, not on the fly like in OSS
669     if (!m_initialized) {
670 	int err = initialize(length);
671 	if (err < 0) return err;
672     }
673 
674     m_mainloop_lock.lock();
675 
676     size_t freeBytes = length - offset;
677     size_t readableSize = pa_stream_readable_size(m_pa_stream);
678     if (readableSize > freeBytes) {
679 	size_t additional_size = readableSize - freeBytes;
680 	buffer.resize(static_cast<int>(length + additional_size));
681     }
682 
683     size_t readLength = 0;
684     if (readableSize > 0) {
685         const void *audioBuffer = Q_NULLPTR;
686 	pa_stream_peek(m_pa_stream, &audioBuffer, &readLength);
687 
688 	if (offset + readLength > Kwave::toUint(buffer.length())) {
689 	    pa_stream_drop(m_pa_stream);
690 	    m_mainloop_lock.unlock();
691 	    return -EIO; // peek returned invalid length
692 	}
693 
694 	char *data = buffer.data() + offset;
695 	if (audioBuffer) {
696 	    MEMCPY(data, audioBuffer, readLength); // real data
697 	} else {
698 	    memset(data, 0x00, readLength); // there was a gap
699 	}
700 
701 	pa_stream_drop(m_pa_stream);
702 
703     } else {
704 	m_mainloop_lock.unlock();
705 	return -EAGAIN;
706     }
707     m_mainloop_lock.unlock();
708 
709     return Kwave::toInt(readLength);
710 }
711 
712 //***************************************************************************
initialize(uint32_t buffer_size)713 int Kwave::RecordPulseAudio::initialize(uint32_t buffer_size)
714 {
715     Q_ASSERT(!m_initialized);
716 
717     // make sure that we are connected to the sound server
718     if (!connectToServer()) {
719 	qWarning("Connecting to the PulseAudio server failed!");
720 	return -1;
721     }
722 
723     pa_sample_format_t fmt = mode2format(m_compression, m_bits_per_sample,
724                                          m_sample_format);
725     if (fmt == PA_SAMPLE_INVALID) {
726 	Kwave::SampleFormat::Map sf;
727 
728 	qWarning("format: no matching format for compression '%s', "
729 	    "%d bits/sample, format '%s'",
730 	    DBG(sf.description(sf.findFromData(m_sample_format), true)),
731 	    m_bits_per_sample,
732 	    DBG(Kwave::Compression(m_compression).name())
733 	);
734 	return -EINVAL;
735     }
736 
737     pa_sample_spec sample_spec;
738     sample_spec.channels = m_tracks;
739     sample_spec.format = fmt;
740     sample_spec.rate = static_cast<quint32>(m_rate);
741 
742     if(!pa_sample_spec_valid(&sample_spec)) {
743 	Kwave::SampleFormat::Map sf;
744 
745 	qWarning("no valid pulse audio format: %d, rate: %0.3g, channels: %d",
746 		 static_cast<int>(fmt), m_rate, m_tracks);
747 	return -EINVAL;
748     }
749 
750     // run with mainloop locked from here on...
751     m_mainloop_lock.lock();
752 
753     pa_channel_map channel_map;
754     pa_channel_map_init_extend(&channel_map, sample_spec.channels,
755                                PA_CHANNEL_MAP_DEFAULT);
756 
757     if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
758         qWarning("Channel map doesn't match sample specification!");
759     }
760 
761     // create a new stream
762     m_pa_stream = pa_stream_new(
763 	m_pa_context,
764 	m_name.toUtf8().constData(),
765 	&sample_spec,
766 	&channel_map);
767 
768     if (!m_pa_stream) {
769 	m_mainloop_lock.unlock();
770 	qWarning("Failed to create a PulseAudio stream %s",
771 	         pa_strerror(pa_context_errno(m_pa_context)));
772 	return -1;
773     }
774 
775     pa_stream_set_state_callback(m_pa_stream, pa_stream_state_cb, this);
776     pa_stream_set_read_callback(m_pa_stream, pa_read_cb, this);
777 
778     pa_buffer_attr attr;
779     attr.fragsize  = buffer_size;
780     attr.maxlength = static_cast<uint32_t>(-1);
781     attr.minreq    = static_cast<uint32_t>(-1);
782     attr.prebuf    = static_cast<uint32_t>(-1);
783     attr.tlength   = static_cast<uint32_t>(-1);
784     int flags      = PA_STREAM_ADJUST_LATENCY;
785 
786     // connect the stream in record mode
787     int result = pa_stream_connect_record(
788 	m_pa_stream,
789 	m_pa_device.toUtf8().constData(),
790 	&attr,
791 	static_cast<pa_stream_flags_t>(flags));
792 
793     if (result >= 0) {
794 	m_mainloop_signal.wait(&m_mainloop_lock, TIMEOUT_CONNECT_RECORD);
795 	if (pa_stream_get_state(m_pa_stream) != PA_STREAM_READY)
796 	    result = -1;
797     }
798 
799     m_mainloop_lock.unlock();
800 
801     if (result < 0) {
802 	pa_stream_unref(m_pa_stream);
803         m_pa_stream = Q_NULLPTR;
804 	qWarning("Failed to open a PulseAudio stream for record %s",
805 	         pa_strerror(pa_context_errno(m_pa_context)));
806 	return -1;
807     }
808 
809     m_initialized = true;
810     return 0;
811 }
812 
813 //***************************************************************************
open(const QString & device)814 QString Kwave::RecordPulseAudio::open(const QString& device)
815 {
816     // close the previous device
817     if (m_pa_stream) close();
818 
819     QString pa_device;
820     if (m_device_list.contains(device))
821 	pa_device = m_device_list[device].m_name;
822 
823     if (!pa_device.length())
824 	return QString::number(ENODEV);
825 
826     m_pa_device = pa_device;
827     m_device    = device;
828 
829     // detect all formats the device knows
830     detectSupportedFormats(device);
831 
832     return QString();
833 }
834 
835 //***************************************************************************
supportedDevices()836 QStringList Kwave::RecordPulseAudio::supportedDevices()
837 {
838     QStringList list;
839 
840     // re-validate the list if necessary
841     scanDevices();
842 
843     if (!m_pa_mainloop || !m_pa_context) return list;
844 
845     list = m_device_list.keys();
846     if (!list.isEmpty()) list.prepend(_("#TREE#"));
847 
848     return list;
849 }
850 
851 //***************************************************************************
run_wrapper(const QVariant & params)852 void Kwave::RecordPulseAudio::run_wrapper(const QVariant &params)
853 {
854     Q_UNUSED(params)
855     m_mainloop_lock.lock();
856     pa_mainloop_run(m_pa_mainloop, Q_NULLPTR);
857     m_mainloop_lock.unlock();
858     qDebug("RecordPulseAudio::run_wrapper - done.");
859 }
860 
861 //***************************************************************************
poll_func(struct pollfd * ufds,unsigned long nfds,int timeout,void * userdata)862 static int poll_func(struct pollfd *ufds, unsigned long nfds,
863                      int timeout, void *userdata)
864 {
865     Kwave::RecordPulseAudio *dev =
866 	static_cast<Kwave::RecordPulseAudio *>(userdata);
867     Q_ASSERT(dev);
868     if (!dev) return -1;
869 
870     return dev->mainloopPoll(ufds, nfds, timeout);
871 }
872 
873 //***************************************************************************
mainloopPoll(struct pollfd * ufds,unsigned long int nfds,int timeout)874 int Kwave::RecordPulseAudio::mainloopPoll(struct pollfd *ufds,
875                                             unsigned long int nfds,
876                                             int timeout)
877 {
878     m_mainloop_lock.unlock();
879     int retval = poll(ufds, nfds, timeout);
880     m_mainloop_lock.lock();
881 
882     return retval;
883 }
884 
885 //***************************************************************************
connectToServer()886 bool Kwave::RecordPulseAudio::connectToServer()
887 {
888     if (m_pa_context) return true; // already connected
889 
890     // set hourglass cursor, we are waiting...
891     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
892 
893     // create a property list for this application
894     m_pa_proplist = pa_proplist_new();
895     Q_ASSERT(m_pa_proplist);
896 
897     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_LANGUAGE,
898                      UTF8(QLocale::system().name()));
899     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_NAME,
900                      UTF8(qApp->applicationName()));
901     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_ICON_NAME,
902                      "kwave");
903     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_PROCESS_BINARY,
904                      "kwave");
905     pa_proplist_setf(m_pa_proplist, PA_PROP_APPLICATION_PROCESS_ID,
906                     "%ld", static_cast<long int>(qApp->applicationPid()));
907     KUser user;
908     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_PROCESS_USER,
909                      UTF8(user.loginName()));
910     pa_proplist_sets(m_pa_proplist, PA_PROP_APPLICATION_VERSION,
911                      UTF8(qApp->applicationVersion()));
912 
913     pa_proplist_sets(m_pa_proplist, PA_PROP_MEDIA_ROLE, "production");
914 
915     // ignore SIGPIPE in this context
916 #ifdef HAVE_SIGNAL_H
917     signal(SIGPIPE, SIG_IGN);
918 #endif
919 
920     m_pa_mainloop = pa_mainloop_new();
921     Q_ASSERT(m_pa_mainloop);
922     pa_mainloop_set_poll_func(m_pa_mainloop, poll_func, this);
923 
924     m_pa_context = pa_context_new_with_proplist(
925 	pa_mainloop_get_api(m_pa_mainloop),
926 	"Kwave",
927 	m_pa_proplist
928     );
929 
930     // set the callback for getting informed about the context state
931     pa_context_set_state_callback(m_pa_context, pa_context_notify_cb, this);
932 
933     // connect to the pulse audio server server
934     bool failed = false;
935     int error = pa_context_connect(
936 	m_pa_context,                       // context
937         Q_NULLPTR,                          // server
938 	static_cast<pa_context_flags_t>(0), // flags
939 	Q_NULLPTR                           // API
940     );
941     if (error < 0)
942     {
943 	qWarning("RecordPulseAudio: pa_contect_connect failed (%s)",
944 	          pa_strerror(pa_context_errno(m_pa_context)));
945 	failed = true;
946     }
947 
948     if (!failed) {
949 	m_mainloop_lock.lock();
950 	m_mainloop_thread.start();
951 
952 	// wait until the context state is either connected or failed
953 	failed = true;
954 	if ( m_mainloop_signal.wait(&m_mainloop_lock,
955 	                            TIMEOUT_CONNECT_TO_SERVER) )
956 	{
957 	    if (pa_context_get_state(m_pa_context) == PA_CONTEXT_READY) {
958 		failed = false;
959 	    }
960 	}
961 	m_mainloop_lock.unlock();
962 
963 	if (failed) {
964 	    qWarning("RecordPulseAudio: context FAILED (%s):-(",
965 	             pa_strerror(pa_context_errno(m_pa_context)));
966 	}
967     }
968 
969     // if the connection failed, clean up
970     if (failed) {
971 	disconnectFromServer();
972     }
973 
974     QApplication::restoreOverrideCursor();
975 
976     return !failed;
977 }
978 
979 //***************************************************************************
disconnectFromServer()980 void Kwave::RecordPulseAudio::disconnectFromServer()
981 {
982     close();
983 
984     // stop the main loop
985     m_mainloop_thread.isInterruptionRequested();
986     if (m_pa_mainloop) {
987 	m_mainloop_lock.lock();
988 	pa_mainloop_quit(m_pa_mainloop, 0);
989 	m_mainloop_lock.unlock();
990     }
991     m_mainloop_thread.stop();
992 
993     // disconnect the pulse context
994     if (m_pa_context) {
995 	pa_context_disconnect(m_pa_context);
996 	pa_context_unref(m_pa_context);
997         m_pa_context  = Q_NULLPTR;
998     }
999 
1000     // stop and free the main loop
1001     if (m_pa_mainloop) {
1002 	pa_mainloop_free(m_pa_mainloop);
1003         m_pa_mainloop = Q_NULLPTR;
1004     }
1005 
1006     // release the property list
1007     if (m_pa_proplist) {
1008 	pa_proplist_free(m_pa_proplist);
1009         m_pa_proplist = Q_NULLPTR;
1010     }
1011 
1012 }
1013 
1014 //***************************************************************************
pa_source_info_cb(pa_context * c,const pa_source_info * info,int eol,void * userdata)1015 void Kwave::RecordPulseAudio::pa_source_info_cb(pa_context *c,
1016                                                 const pa_source_info *info,
1017                                                 int eol, void *userdata)
1018 {
1019     Kwave::RecordPulseAudio *record_plugin =
1020 	reinterpret_cast<Kwave::RecordPulseAudio *>(userdata);
1021     Q_ASSERT(record_plugin);
1022     if (record_plugin) record_plugin->notifySourceInfo(c, info, eol);
1023 }
1024 
1025 //***************************************************************************
notifySourceInfo(pa_context * c,const pa_source_info * info,int eol)1026 void Kwave::RecordPulseAudio::notifySourceInfo(pa_context *c,
1027                                                const pa_source_info *info,
1028                                                int eol)
1029 {
1030     Q_UNUSED(c)
1031     Q_ASSERT(c == m_pa_context);
1032 
1033     if (eol == 0) {
1034 	source_info_t i;
1035 	i.m_name        = QString::fromUtf8(info->name);
1036 	i.m_description = QString::fromUtf8(info->description);
1037 	i.m_driver      = QString::fromUtf8(info->driver);
1038 	i.m_card        = info->card;
1039 	i.m_sample_spec = info->sample_spec;
1040 
1041 	QString name    = QString::number(m_device_list.count());
1042 	m_device_list[name] = i;
1043     } else {
1044 	m_mainloop_signal.wakeAll();
1045     }
1046 }
1047 
1048 //***************************************************************************
scanDevices()1049 void Kwave::RecordPulseAudio::scanDevices()
1050 {
1051     if (!m_pa_context) connectToServer();
1052     if (!m_pa_context) return;
1053 
1054     // fetch the device list from the PulseAudio server
1055     m_mainloop_lock.lock();
1056     m_device_list.clear();
1057     pa_operation *op_source_info = pa_context_get_source_info_list(
1058 	m_pa_context,
1059 	pa_source_info_cb,
1060 	this
1061     );
1062     if (op_source_info) {
1063 	// set hourglass cursor, we have a long timeout...
1064 	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1065 	m_mainloop_signal.wait(&m_mainloop_lock, TIMEOUT_WAIT_DEVICE_SCAN);
1066 	QApplication::restoreOverrideCursor();
1067     }
1068 
1069     // create a list with final names
1070     QMap<QString, source_info_t> list;
1071     for (QMap<QString, source_info_t>::const_iterator it =
1072          m_device_list.constBegin();
1073          it != m_device_list.constEnd(); ++it)
1074     {
1075 	const QString       &source     = it.key();
1076 	const source_info_t &inf        = it.value();
1077 	const QString       &name       = inf.m_name;
1078 	QString             description = inf.m_description;
1079 	QString             driver      = inf.m_driver;
1080 
1081 	// if the name is not unique, add the internal source name
1082 	for (QMap<QString, source_info_t>::const_iterator it2 =
1083 	     m_device_list.constBegin();
1084 	     it2 != m_device_list.constEnd(); ++it2)
1085 	{
1086 	    const QString       &s = it2.key();
1087 	    const source_info_t &i = it2.value();
1088 	    if (s == source) continue;
1089 	    if ((i.m_description == description) &&
1090 		(i.m_driver      == driver))
1091 	    {
1092 		// not unique
1093 		description += _(" [") + name + _("]");
1094 		break;
1095 	    }
1096 	}
1097 
1098 	// mangle the driver name, e.g.
1099 	// "module-alsa-sink.c" -> "alsa sink"
1100 	QFileInfo f(driver);
1101 	driver = f.baseName();
1102 	driver.replace(_("-"), _(" "));
1103 	driver.replace(_("_"), _(" "));
1104 	if (driver.startsWith(_("module "), Qt::CaseInsensitive))
1105 	    driver.remove(0, 7);
1106 	description.prepend(driver + _("|sound_card||"));
1107 
1108 	// add the leaf node
1109 	if (inf.m_card != PA_INVALID_INDEX)
1110 	    description.append(_("|sound_device"));
1111 	else
1112 	    description.append(_("|sound_note"));
1113 
1114 	list.insert(description, *it);
1115     }
1116 
1117     m_device_list.clear();
1118     m_device_list = list;
1119     m_mainloop_lock.unlock();
1120 }
1121 
1122 #endif /* HAVE_PULSEAUDIO_SUPPORT */
1123 
1124 //***************************************************************************
1125 //***************************************************************************
1126