20 #ifdef HAVE_PULSEAUDIO_SUPPORT 30 #include <QApplication> 33 #include <QLatin1Char> 38 #include <KLocalizedString> 52 #define TIMEOUT_WAIT_DEVICE_SCAN 10000 58 #define TIMEOUT_CONNECT_TO_SERVER 20000 64 #define TIMEOUT_CONNECT_PLAYBACK 10000 70 #define TIMEOUT_MIN_FLUSH 1000 76 #define TIMEOUT_MIN_DRAIN 3000 81 m_mainloop_thread(this, QVariant()),
86 m_bytes_per_sample(0),
91 m_pa_proplist(Q_NULLPTR),
92 m_pa_mainloop(Q_NULLPTR),
93 m_pa_context(Q_NULLPTR),
94 m_pa_stream(Q_NULLPTR),
110 Q_ASSERT(playback_plugin);
116 const pa_sink_info *info,
117 int eol,
void *userdata)
121 Q_ASSERT(playback_plugin);
122 if (playback_plugin) playback_plugin->
notifySinkInfo(c, info, eol);
130 Q_ASSERT(playback_plugin);
140 Q_ASSERT(playback_plugin);
141 if (playback_plugin) playback_plugin->
notifyWrite(p, nbytes);
151 Q_ASSERT(playback_plugin);
152 if (playback_plugin) playback_plugin->
notifySuccess(s, success);
159 switch (pa_context_get_state(c))
161 case PA_CONTEXT_UNCONNECTED:
164 case PA_CONTEXT_CONNECTING:
167 case PA_CONTEXT_AUTHORIZING:
170 case PA_CONTEXT_SETTING_NAME:
173 case PA_CONTEXT_READY:
177 case PA_CONTEXT_TERMINATED:
178 qWarning(
"PlayBackPulseAudio: PA_CONTEXT_TERMINATED");
181 case PA_CONTEXT_FAILED:
182 qWarning(
"PlayBackPulseAudio: PA_CONTEXT_FAILED");
190 const pa_sink_info *info,
197 qDebug(
"PlayBackPulseAudio: [%d] sink='%s' (%s) driver='%s'"\
206 for (
int p = 0; p < info->n_ports; p++) {
207 qDebug(
" [%2d] - '%s' (%s), prio=%d%s",
209 info->ports[p]->name,
210 info->ports[p]->description,
211 info->ports[p]->priority,
212 (info->ports[p] == info->active_port) ?
" <*>" :
"" 217 i.
m_name = QString::fromUtf8(info->name);
219 i.
m_driver = QString::fromUtf8(info->driver);
237 pa_stream_state_t state = pa_stream_get_state(stream);
254 case PA_STREAM_UNCONNECTED:
255 case PA_STREAM_CREATING:
257 case PA_STREAM_READY:
258 case PA_STREAM_FAILED:
259 case PA_STREAM_TERMINATED:
294 static int poll_func(
struct pollfd *ufds,
unsigned long nfds,
295 int timeout,
void *userdata)
307 unsigned long int nfds,
311 int retval = poll(ufds, nfds, timeout);
323 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
329 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_LANGUAGE,
332 UTF8(qApp->applicationName()));
333 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_ICON_NAME,
335 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_BINARY,
337 pa_proplist_setf(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_ID,
338 "%ld", static_cast<long int>(qApp->applicationPid()));
340 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_USER,
341 UTF8(user.loginName()));
343 UTF8(qApp->applicationVersion()));
345 pa_proplist_sets(
m_pa_proplist, PA_PROP_MEDIA_ROLE,
"production");
349 signal(SIGPIPE, SIG_IGN);
367 int error = pa_context_connect(
370 static_cast<pa_context_flags_t>(0),
375 qWarning(
"PlayBackPulseAudio: pa_contect_connect failed (%s)",
389 if (pa_context_get_state(
m_pa_context) == PA_CONTEXT_READY) {
390 qDebug(
"PlayBackPulseAudio: context is ready :-)");
397 qWarning(
"PlayBackPulseAudio: context FAILED (%s):-(",
407 QApplication::restoreOverrideCursor();
435 qDebug(
"PlayBackPulseAudio: mainloop freed");
448 unsigned int channels,
450 unsigned int bufbase)
452 #define SET_PROPERTY(__property__,__info__) \ 453 if (m_info.contains(__info__)) \ 454 pa_proplist_sets(_proplist, __property__, \ 455 m_info.get(__info__).toString().toUtf8().data()) 457 qDebug(
"PlayBackPulseAudio::open(device=%s,rate=%0.1f,channels=%u,"\
458 "bits=%u, bufbase=%u)",
459 DBG(device.split(
_(
"|")).at(0)), rate, channels,
465 return i18n(
"%1 channels are not supported, maximum is 255", channels);
472 return i18n(
"Connecting to the PulseAudio server failed.");
478 "The PulseAudio device '%1' is unknown or no longer connected",
479 device.section(QLatin1Char(
'|'), 0, 0));
494 #ifdef PA_PROP_MEDIA_COPYRIGHT 497 #ifdef PA_PROP_MEDIA_SOFTWARE 505 pa_sample_spec sample_spec;
506 #if (Q_BYTE_ORDER == Q_BIG_ENDIAN) 507 sample_spec.format = PA_SAMPLE_S24_32BE;
509 sample_spec.format = PA_SAMPLE_S24_32LE;
511 sample_spec.channels =
static_cast<uint8_t
>(channels);
512 sample_spec.rate =
static_cast<uint32_t
>(
m_rate);
522 name = i18n(
"playback...");
530 name.toUtf8().data(),
534 pa_proplist_free(_proplist);
538 return i18n(
"Failed to create a PulseAudio stream (%1).",
539 QString::fromLocal8Bit(
542 qDebug(
"PlayBackPulseAudio::open(...) - stream created as %p",
560 int result = pa_stream_connect_playback(
562 pa_device.length() ? pa_device.toUtf8().data() : Q_NULLPTR,
564 static_cast<pa_stream_flags_t
>(
565 PA_STREAM_INTERPOLATE_TIMING |
566 PA_STREAM_AUTO_TIMING_UPDATE),
572 if (pa_stream_get_state(
m_pa_stream) != PA_STREAM_READY)
580 return i18n(
"Failed to open a PulseAudio stream for playback (%1).",
581 QString::fromLocal8Bit(
620 qWarning(
"PlayBackPulseAudio: pa_stream_begin_write failed");
635 qWarning(
"PlayBackPulseAudio::write(): buffer overflow ?! (%u/%u)",
664 int ms = (!qFuzzyIsNull(
m_rate)) ?
666 int timeout = (ms + 1) * 16;
677 while (!(len = pa_stream_writable_size(
m_pa_stream))) {
678 if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(
m_pa_context)) ||
679 !PA_STREAM_IS_GOOD(pa_stream_get_state(
m_pa_stream)) ||
680 (static_cast<ssize_t>(len) == -1) )
682 qWarning(
"PlayBackPulseAudio::flush(): bad stream state");
687 qWarning(
"PlayBackPulseAudio::flush(): timed out after %u ms",
694 if (result < 0)
break;
700 result = pa_stream_write(
711 qWarning(
"PlayBackPulseAudio::flush(): pa_stream_write failed");
740 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
746 pa_operation *op = Q_NULLPTR;
750 if (!op) qWarning(
"pa_stream_drain() failed: '%s'", pa_strerror(
757 int ms = (!qFuzzyIsNull(
m_rate)) ?
759 int timeout = (ms + 1) * 4;
763 qDebug(
"PlayBackPulseAudio::flush(): waiting for drain to finish...");
764 while (op && (pa_operation_get_state(op) != PA_OPERATION_DONE)) {
765 if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(
m_pa_context)) ||
766 !PA_STREAM_IS_GOOD(pa_stream_get_state(
m_pa_stream))) {
767 qWarning(
"PlayBackPulseAudio::close(): bad stream state");
771 qWarning(
"PlayBackPulseAudio::flush(): timed out after %u ms",
788 QApplication::restoreOverrideCursor();
801 pa_operation *op_sink_info = pa_context_get_sink_info_list(
808 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
810 QApplication::restoreOverrideCursor();
815 QMap<QString, sink_info_t> list;
820 s.format = PA_SAMPLE_INVALID;
828 list[i18n(
"(Use server default)") +
_(
"|sound_note")] = i;
838 if (sn == sink)
continue;
846 if (!unique) description +=
_(
" [") + name +
_(
"]");
851 driver = f.baseName();
852 driver.replace(
_(
"-"),
_(
" "));
853 driver.replace(
_(
"_"),
_(
" "));
854 if (driver.toLower().startsWith(
_(
"module ")))
856 description.prepend(driver +
_(
"|sound_card||"));
860 description.append(
_(
"|sound_device"));
862 description.append(
_(
"|sound_note"));
884 if (!list.isEmpty()) list.prepend(
_(
"#TREE#"));
898 const QString &device
901 QList<unsigned int> list;
904 !pa_sample_spec_valid(&
m_device_list[device].m_sample_spec) )
bool contains(const FileProperty property) const
static void pa_context_notify_cb(pa_context *c, void *data)
virtual int close() Q_DECL_OVERRIDE
#define SET_PROPERTY(__property__, __info__)
virtual QStringList supportedDevices() Q_DECL_OVERRIDE
Kwave::WorkerThread m_mainloop_thread
unsigned int m_bytes_per_sample
QVariant get(FileProperty key) const
#define TIMEOUT_WAIT_DEVICE_SCAN
#define TIMEOUT_MIN_DRAIN
QWaitCondition m_mainloop_signal
void notifyStreamState(pa_stream *stream)
void disconnectFromServer()
static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
#define TIMEOUT_CONNECT_PLAYBACK
virtual ~PlayBackPulseAudio() Q_DECL_OVERRIDE
#define TIMEOUT_MIN_FLUSH
void notifyContext(pa_context *c)
PlayBackPulseAudio(const Kwave::FileInfo &info)
static void pa_sink_info_cb(pa_context *c, const pa_sink_info *info, int eol, void *userdata)
pa_sample_spec m_sample_spec
#define TIMEOUT_CONNECT_TO_SERVER
virtual QString open(const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) Q_DECL_OVERRIDE
virtual QString fileFilter() Q_DECL_OVERRIDE
pa_proplist * m_pa_proplist
static void pa_stream_state_cb(pa_stream *p, void *userdata)
const sample_t * constData() const
virtual int write(const Kwave::SampleArray &samples) Q_DECL_OVERRIDE
static void pa_write_cb(pa_stream *p, size_t nbytes, void *userdata)
virtual QList< unsigned int > supportedBits(const QString &device) Q_DECL_OVERRIDE
pa_mainloop * m_pa_mainloop
virtual int stop(unsigned int timeout=10000)
QMap< QString, sink_info_t > m_device_list
void notifySuccess(pa_stream *stream, int success)
virtual void run_wrapper(const QVariant ¶ms) Q_DECL_OVERRIDE
pa_context * m_pa_context
void notifyWrite(pa_stream *stream, size_t nbytes)
static void pa_stream_success_cb(pa_stream *s, int success, void *userdata)
int mainloopPoll(struct pollfd *ufds, unsigned long int nfds, int timeout)
virtual int detectChannels(const QString &device, unsigned int &min, unsigned int &max) Q_DECL_OVERRIDE
void notifySinkInfo(pa_context *c, const pa_sink_info *info, int eol)