From cd71055f14b70284d8bd3eb35fadc15b36ba0780 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Mon, 13 Feb 2012 21:16:59 +0100 Subject: use PulseAudio instead of QAudioDevice --- fmrxthread.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 fmrxthread.cpp (limited to 'fmrxthread.cpp') diff --git a/fmrxthread.cpp b/fmrxthread.cpp new file mode 100644 index 0000000..0043754 --- /dev/null +++ b/fmrxthread.cpp @@ -0,0 +1,203 @@ +#include + +#include +#include + +#include "fmrxthread.h" + +FmRxThread::FmRxThread(QObject *parent) : + QThread(parent), m_quit(false), m_error(false), m_fd(-1), + m_mainloop(0), m_context(0), m_stream(0) +{ +} + +void FmRxThread::setInputFd(int fd) +{ + m_fd = fd; +} + +int FmRxThread::inputFd() const +{ + return m_fd; +} + +bool FmRxThread::error() const +{ + return m_error; +} + +void FmRxThread::run() +{ + m_quit = false; + m_error = false; + + if (!configure()) { + m_error = true; + deconfigure(); + return; + } + + if (!loop()) { + m_error = true; + } + + deconfigure(); +} + +void FmRxThread::stop() +{ + m_quit = true; + if (m_mainloop) { + pa_mainloop_wakeup(m_mainloop); + } +} + +bool FmRxThread::configure() +{ + pa_context_state_t context_state; + pa_stream_state_t stream_state; + + m_mainloop = pa_mainloop_new(); + if (!m_mainloop) { + qWarning("Could not create pa_mainloop"); + return false; + } + + qDebug("FmRx thread connecting to PulseAudio"); + + // Find the application name + QString appName = QCoreApplication::applicationName(); + if (appName.isEmpty()) { + // Set some default + appName = "FmRx MediaService"; + } + + // Setup the PulseAudio context + m_context = pa_context_new(pa_mainloop_get_api(m_mainloop), + appName.toUtf8().data()); + if (!m_context) { + qWarning("Could not create pa_mainloop"); + return false; + } + + if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) { + qWarning("Could not connect to PulseAudio"); + return false; + } + + do { + if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { + qWarning("pa_mainloop_iterate() failed"); + return false; + } + + context_state = pa_context_get_state(m_context); + if (context_state == PA_CONTEXT_FAILED) { + qWarning("Early pa_context connection fail"); + return false; + } + + if (m_quit) return true; + } while (context_state != PA_CONTEXT_READY); + + // Setup the stream + pa_sample_spec spec; + spec.format = PA_SAMPLE_S16LE; + spec.channels = 2; + spec.rate = 48000; + + m_stream = pa_stream_new(m_context, "FmRx Stream", &spec, NULL); + if (!m_stream) { + qWarning("Could not create pa_stream"); + return false; + } + + if (pa_stream_connect_playback(m_stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL) < 0) { + qWarning("Could not connect stream"); + return false; + } + + do { + if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { + qWarning("pa_mainloop_iterate() failed"); + return false; + } + + stream_state = pa_stream_get_state(m_stream); + if (stream_state == PA_STREAM_FAILED) { + qWarning("Early pa_stream termination"); + return false; + } + + if (m_quit) return true; + } while (stream_state != PA_STREAM_READY); + + return true; +} + +void FmRxThread::deconfigure() +{ + if (m_stream) { + pa_stream_disconnect(m_stream); + pa_stream_unref(m_stream); + m_stream = 0; + } + if (m_context) { + pa_context_disconnect(m_context); + pa_context_unref(m_context); + m_context = 0; + } + if (m_mainloop) { + pa_mainloop_free(m_mainloop); + m_mainloop = 0; + } + if (m_fd != -1) { + close(m_fd); + m_fd = -1; + } +} + +bool FmRxThread::loop() +{ + // Here comes the main loop + while (!m_quit) { + void *buffer; + size_t maxbytes = (size_t) -1; + ssize_t readbytes; + + if (pa_stream_begin_write(m_stream, &buffer, &maxbytes) < 0) { + qWarning("Failed to pa_stream_begin_write"); + return false; + } + + readbytes = read(m_fd, buffer, maxbytes); + if (readbytes < 0) { + qWarning("Failed to read from the FmRx pipe"); + return false; + } + + if (pa_stream_write(m_stream, buffer, readbytes, NULL, 0, PA_SEEK_RELATIVE) < 0) { + qWarning("Failed to pa_stream_write"); + return false; + } + + do { + if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { + qWarning("pa_mainloop_iterate() failed"); + return false; + } + if (m_quit) return true; + } while (pa_stream_writable_size(m_stream) == 0); + + if (pa_stream_get_state(m_stream) != PA_STREAM_READY) { + qWarning("pa_stream was lost"); + return false; + } + if (pa_context_get_state(m_context) != PA_CONTEXT_READY) { + qWarning("pa_context was lost"); + return false; + } + } + + return true; +} -- cgit v1.2.3