summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier S. Pedro <javier@javispedro.com>2012-02-13 21:16:59 +0100
committerJavier S. Pedro <javier@javispedro.com>2012-02-13 21:16:59 +0100
commitcd71055f14b70284d8bd3eb35fadc15b36ba0780 (patch)
tree79fe0693f0bb3e031e51cb1657f29927bdebcb38
parent140d795d27e19dd7ffb3d58a404e601eef8b5f55 (diff)
downloadfmrx-qt-cd71055f14b70284d8bd3eb35fadc15b36ba0780.tar.gz
fmrx-qt-cd71055f14b70284d8bd3eb35fadc15b36ba0780.zip
use PulseAudio instead of QAudioDevice
-rw-r--r--fmrx-qt.pro8
-rw-r--r--fmrxcontrol.cpp29
-rw-r--r--fmrxcontrol.h4
-rw-r--r--fmrxproxy.cpp5
-rw-r--r--fmrxproxy.h3
-rw-r--r--fmrxservice.cpp110
-rw-r--r--fmrxservice.h10
-rw-r--r--fmrxthread.cpp203
-rw-r--r--fmrxthread.h38
-rw-r--r--qtc_packaging/debian_harmattan/changelog7
-rw-r--r--qtc_packaging/debian_harmattan/control3
11 files changed, 343 insertions, 77 deletions
diff --git a/fmrx-qt.pro b/fmrx-qt.pro
index 0ed54da..5c4ba4a 100644
--- a/fmrx-qt.pro
+++ b/fmrx-qt.pro
@@ -11,7 +11,7 @@ TARGET = fmrx-qt
TEMPLATE = lib
CONFIG += plugin mobility link_pkgconfig
MOBILITY += multimedia
-PKGCONFIG += dbus-glib-1
+PKGCONFIG += dbus-glib-1 libpulse
contains(MEEGO_EDITION,harmattan) {
target.path = /usr/lib/qt4/plugins/mediaservice
@@ -23,12 +23,14 @@ INSTALLS += target
SOURCES += fmrxserviceplugin.cpp \
fmrxservice.cpp \
fmrxcontrol.cpp \
- fmrxproxy.cpp
+ fmrxproxy.cpp \
+ fmrxthread.cpp
HEADERS += fmrxserviceplugin.h \
fmrxservice.h \
fmrxcontrol.h \
- fmrxproxy.h
+ fmrxproxy.h \
+ fmrxthread.h
OTHER_FILES += \
qtc_packaging/debian_harmattan/rules \
diff --git a/fmrxcontrol.cpp b/fmrxcontrol.cpp
index 8116021..a3c21b5 100644
--- a/fmrxcontrol.cpp
+++ b/fmrxcontrol.cpp
@@ -7,6 +7,7 @@ FmRxControl::FmRxControl(FmRxService *parent) :
connect(m_service, SIGNAL(started()), this, SLOT(handleStarted()));
connect(m_service, SIGNAL(stopped()), this, SLOT(handleStopped()));
connect(m_service, SIGNAL(tuned(double)), this, SLOT(handleTuned(double)));
+ connect(m_service, SIGNAL(signalLevelChanged(ushort)), this, SLOT(handleSignalLevelChanged(ushort)));
}
bool FmRxControl::isAvailable() const
@@ -48,6 +49,7 @@ int FmRxControl::frequency() const
int FmRxControl::frequencyStep(QRadioTuner::Band b) const
{
+ // 0.1 Mhz
return 100 * 1000;
}
@@ -66,7 +68,8 @@ void FmRxControl::setFrequency(int frequency)
bool FmRxControl::isStereo() const
{
- return true; // TODO
+ // The driver does not support reporting this
+ return true;
}
QRadioTuner::StereoMode FmRxControl::stereoMode() const
@@ -81,16 +84,17 @@ void FmRxControl::setStereoMode(QRadioTuner::StereoMode mode)
int FmRxControl::signalStrength() const
{
- return 0; // TODO
+ return convertSignalLevel(m_service->signalLevel());
}
int FmRxControl::volume() const
{
- return 100; // Best not to have volume control in Harmattan.
+ return 100; // See comment in setVolume
}
void FmRxControl::setVolume(int volume)
{
- // Best not to have volume control in Harmattan.
+ // It is IMHO best not to have a separate volume control in Harmattan.
+ Q_UNUSED(volume);
}
bool FmRxControl::isMuted() const
@@ -100,17 +104,20 @@ bool FmRxControl::isMuted() const
void FmRxControl::setMuted(bool muted)
{
- // Best not to have volume control in Harmattan.
+ // Set comment in setVolume
+ Q_UNUSED(muted);
}
bool FmRxControl::isSearching() const
{
+ // Searches are quite fast.
return false;
}
void FmRxControl::cancelSearch()
{
- // TODO
+ // I am not sure this driver supports cancelling a search.
+ qWarning("FmRxControl: cannot cancel a search");
}
void FmRxControl::searchForward()
@@ -143,6 +150,11 @@ QString FmRxControl::errorString() const
return QString();
}
+int FmRxControl::convertSignalLevel(int v4l_level)
+{
+ return (v4l_level * 100) / 65535;
+}
+
void FmRxControl::handleStarted()
{
emit stateChanged(QRadioTuner::ActiveState);
@@ -157,3 +169,8 @@ void FmRxControl::handleTuned(double frequency)
{
emit frequencyChanged(frequency * 1000000.0);
}
+
+void FmRxControl::handleSignalLevelChanged(ushort level)
+{
+ emit signalStrengthChanged(convertSignalLevel(level));
+}
diff --git a/fmrxcontrol.h b/fmrxcontrol.h
index 20247da..a24e92d 100644
--- a/fmrxcontrol.h
+++ b/fmrxcontrol.h
@@ -52,10 +52,14 @@ public:
QRadioTuner::Error error() const;
QString errorString() const;
+private:
+ static int convertSignalLevel(int v4l_level);
+
private slots:
void handleStarted();
void handleStopped();
void handleTuned(double frequency);
+ void handleSignalLevelChanged(ushort level);
};
#endif // FMRXCONTROL_H
diff --git a/fmrxproxy.cpp b/fmrxproxy.cpp
index 77fcfb1..57956b0 100644
--- a/fmrxproxy.cpp
+++ b/fmrxproxy.cpp
@@ -126,6 +126,11 @@ DBusHandlerResult FmRxProxy::bus_message_filter(DBusConnection *,
}
} else if (dbus_message_is_signal(m, BUS_INTERFACE, "Stopped")) {
emit self->Stopped();
+ } else if (dbus_message_is_signal(m, BUS_INTERFACE, "SignalLevelChanged")) {
+ quint16 level;
+ if (dbus_message_get_args(m, NULL, DBUS_TYPE_UINT16, &level, DBUS_TYPE_INVALID)) {
+ emit self->SignalLevelChanged(level);
+ }
} else if (dbus_message_is_signal(m, BUS_INTERFACE, "PiReceived")) {
quint16 pi;
if (dbus_message_get_args(m, NULL, DBUS_TYPE_UINT16, &pi, DBUS_TYPE_INVALID)) {
diff --git a/fmrxproxy.h b/fmrxproxy.h
index ccd544a..de16df6 100644
--- a/fmrxproxy.h
+++ b/fmrxproxy.h
@@ -5,6 +5,8 @@
#include <QtCore/QString>
#include <dbus/dbus.h>
+/** This is the Proxy class that connects to the D-Bus service. */
+
class FmRxProxy : public QObject
{
Q_OBJECT
@@ -20,6 +22,7 @@ public:
signals:
void Tuned(double frequency);
void Stopped();
+ void SignalLevelChanged(ushort level);
void PiReceived(ushort pi);
void PsReceived(const QString &ps);
void RtReceived(const QString &rt);
diff --git a/fmrxservice.cpp b/fmrxservice.cpp
index 3a990bd..750ae8f 100644
--- a/fmrxservice.cpp
+++ b/fmrxservice.cpp
@@ -1,90 +1,73 @@
-#include <QtMultimedia/QAudioDeviceInfo>
-#include <QtMultimedia/QAudioOutput>
#include <QtCore/QFile>
#include <QtDebug>
-#include "fmrxservice.h"
#include "fmrxcontrol.h"
#include "fmrxproxy.h"
+#include "fmrxthread.h"
+#include "fmrxservice.h"
// Again, this would be incredibly shorter if QAudioOuput could be used.
// Unfortunately, it is completely useless in the Harmattan version.
struct FmRxPriv
{
- QAudioOutput *out;
- QFile *in;
- int fd;
+ FmRxThread *thread;
+ FmRxProxy *proxy;
+ FmRxControl *control;
+ FmRxRds *rds;
bool active;
double frequency;
+ quint16 signalLevel;
};
FmRxService::FmRxService(QObject *parent) :
QMediaService(parent)
{
- m_proxy = new FmRxProxy(this);
- m_control = new FmRxControl(this);
- m_priv = new FmRxPriv;
-
- QAudioFormat format;
- format.setFrequency(48000);
- format.setChannels(2);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(16);
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setCodec("audio/pcm");
-
- QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice();
- if (!info.isFormatSupported(format)) {
- qWarning("FM radio format not supported by audio output");
- }
- qDebug("Starting output to %s", qPrintable(info.deviceName()));
-
- m_priv->out = new QAudioOutput(info, format, this);
- m_priv->in = new QFile(this);
- m_priv->active = false;
-
- connect(m_proxy, SIGNAL(Tuned(double)), this, SLOT(handleTuned(double)));
- connect(m_proxy, SIGNAL(Stopped()), this, SLOT(handleStopped()));
- connect(m_proxy, SIGNAL(PiReceived(ushort)), this, SIGNAL(piReceived(ushort)));
- connect(m_proxy, SIGNAL(PsReceived(QString)), this, SIGNAL(psReceived(QString)));
- connect(m_proxy, SIGNAL(RtReceived(QString)), this, SIGNAL(rtReceived(QString)));
+ d = new FmRxPriv;
+ d->thread = new FmRxThread(this);
+ d->proxy = new FmRxProxy(this);
+ d->control = new FmRxControl(this);
+ d->active = false;
- connect(m_priv->out, SIGNAL(stateChanged(QAudio::State)),
- this, SLOT(handleOutState(QAudio::State)));
+ connect(d->proxy, SIGNAL(Tuned(double)), this, SLOT(handleTuned(double)));
+ connect(d->proxy, SIGNAL(Stopped()), this, SLOT(handleStopped()));
+ connect(d->proxy, SIGNAL(SignalLevelChanged(ushort)), this, SLOT(handleSignalLevelChanged(ushort)));
+ connect(d->proxy, SIGNAL(PiReceived(ushort)), this, SIGNAL(piReceived(ushort)));
+ connect(d->proxy, SIGNAL(PsReceived(QString)), this, SIGNAL(psReceived(QString)));
+ connect(d->proxy, SIGNAL(RtReceived(QString)), this, SIGNAL(rtReceived(QString)));
}
FmRxService::~FmRxService()
{
- delete m_priv;
+ delete d;
}
QMediaControl *FmRxService::requestControl(const char *name)
{
if (qstrcmp(name, QRadioTunerControl_iid) == 0)
- return m_control;
+ return d->control;
return 0;
}
void FmRxService::releaseControl(QMediaControl *control)
{
- // Do nothing
+ // Nothing to do; the control instance will be free when the service is freed
}
void FmRxService::start()
{
- m_priv->fd = m_proxy->Connect();
- m_priv->in->open(m_priv->fd, QIODevice::ReadOnly);
- m_priv->out->start(m_priv->in);
- qDebug("Starting fmrx service");
+ if (d->thread->isRunning()) {
+ qWarning("FmRxService already started");
+ return;
+ }
+ d->thread->setInputFd(d->proxy->Connect());
+ d->thread->start();
}
void FmRxService::stop()
{
- m_priv->out->stop();
- m_priv->in->close();
- close(m_priv->fd);
+ d->thread->stop();
}
bool FmRxService::isAvailable() const
@@ -100,55 +83,58 @@ QtMultimediaKit::AvailabilityError FmRxService::availabilityError() const
bool FmRxService::isActive() const
{
- return m_priv->active;
+ return d->active;
}
void FmRxService::handleTuned(double frequency)
{
- if (!qFuzzyCompare(m_priv->frequency, frequency)) {
- m_priv->frequency = frequency;
+ if (!qFuzzyCompare(d->frequency, frequency)) {
+ d->frequency = frequency;
emit tuned(frequency);
}
- if (!m_priv->active) {
- m_priv->active = true;
+ if (!d->active) {
+ d->active = true;
emit started();
}
}
void FmRxService::handleStopped()
{
- if (m_priv->active) {
- m_priv->active = false;
+ if (d->active) {
+ d->active = false;
emit stopped();
}
}
-void FmRxService::handleOutState(QAudio::State state)
+void FmRxService::handleSignalLevelChanged(ushort level)
{
- qDebug() << "New out state = " << state;
- if (state == QAudio::IdleState &&
- m_priv->out->error() == QAudio::UnderrunError) {
- qDebug() << "Restarting";
- m_priv->out->start(m_priv->in);
+ d->signalLevel = level;
+ if (d->active) {
+ emit signalLevelChanged(level);
}
}
double FmRxService::frequency()
{
- return m_priv->frequency;
+ return d->frequency;
}
void FmRxService::setFrequency(double frequency)
{
- m_proxy->Tune(frequency);
+ d->proxy->Tune(frequency);
}
void FmRxService::searchForward()
{
- m_proxy->SearchForward();
+ d->proxy->SearchForward();
}
void FmRxService::searchBackward()
{
- m_proxy->SearchBackward();
+ d->proxy->SearchBackward();
+}
+
+ushort FmRxService::signalLevel() const
+{
+ return d->signalLevel;
}
diff --git a/fmrxservice.h b/fmrxservice.h
index 1f9e278..75afe38 100644
--- a/fmrxservice.h
+++ b/fmrxservice.h
@@ -35,10 +35,13 @@ public:
void searchForward();
void searchBackward();
+ ushort signalLevel() const;
+
signals:
void tuned(double frequency);
void started();
void stopped();
+ void signalLevelChanged(ushort level);
void piReceived(ushort pi);
void psReceived(const QString &ps);
void rtReceived(const QString &rt);
@@ -46,13 +49,10 @@ signals:
private slots:
void handleTuned(double frequency);
void handleStopped();
- void handleOutState(QAudio::State state);
+ void handleSignalLevelChanged(ushort level);
private:
- FmRxPriv *m_priv;
- FmRxProxy *m_proxy;
- FmRxControl *m_control;
- FmRxRds *m_rds;
+ FmRxPriv *d;
};
#endif // FMRXSERVICE_H
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 <unistd.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#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;
+}
diff --git a/fmrxthread.h b/fmrxthread.h
new file mode 100644
index 0000000..357f4c4
--- /dev/null
+++ b/fmrxthread.h
@@ -0,0 +1,38 @@
+#ifndef FMRXTHREAD_H
+#define FMRXTHREAD_H
+
+#include <QThread>
+
+#include <pulse/pulseaudio.h>
+
+class FmRxThread : public QThread
+{
+ Q_OBJECT
+public:
+ explicit FmRxThread(QObject *parent = 0);
+
+ void setInputFd(int fd);
+ int inputFd() const;
+
+ bool error() const;
+
+ void run();
+ void stop();
+
+signals:
+
+private:
+ bool configure();
+ void deconfigure();
+ bool loop();
+
+private:
+ bool m_quit;
+ bool m_error;
+ int m_fd;
+ pa_mainloop *m_mainloop;
+ pa_context *m_context;
+ pa_stream *m_stream;
+};
+
+#endif // FMRXTHREAD_H
diff --git a/qtc_packaging/debian_harmattan/changelog b/qtc_packaging/debian_harmattan/changelog
index e727a60..eb45c7f 100644
--- a/qtc_packaging/debian_harmattan/changelog
+++ b/qtc_packaging/debian_harmattan/changelog
@@ -1,3 +1,10 @@
+fmrx-qt (0.2.0) unstable; urgency=low
+
+ * Use PulseAudio directly instead of QAudioOutput.
+ * Report signalStrength if daemon version 0.2.
+
+ -- Javier S. Pedro <maemo@javispedro.com> Mon, 13 Feb 2012 21:13:44 +0100
+
fmrx-qt (0.0.1) unstable; urgency=low
* Initial Release.
diff --git a/qtc_packaging/debian_harmattan/control b/qtc_packaging/debian_harmattan/control
index e28bdf5..b45d17e 100644
--- a/qtc_packaging/debian_harmattan/control
+++ b/qtc_packaging/debian_harmattan/control
@@ -3,7 +3,7 @@ Section: sound
Priority: optional
Maintainer: Javier S. Pedro <maemo@javispedro.com>
Build-Depends: debhelper (>= 5), libqt4-dev, libqtm-multimedia-dev,
- libglib2.0-dev, libdbus-glib-1-dev, libdbus-1-dev
+ libpulse-dev, libglib2.0-dev, libdbus-glib-1-dev, libdbus-1-dev
Standards-Version: 3.7.3
Homepage: https://gitorious.org/n950-fmrx/fmrx-qt
@@ -16,3 +16,4 @@ Description: FmRx Qt MediaService plugin
.
If this plugin is installed, QRadioTuner can be used in
Harmattan apps.
+XSBC-Maemo-Display-Name: FmRx Qt MediaService plugin