summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--rtlfmradio.json4
-rw-r--r--rtlfmradio.pro11
-rw-r--r--rtlfmradioplugin.cpp34
-rw-r--r--rtlfmradioplugin.h27
-rw-r--r--rtlfmradioservice.cpp32
-rw-r--r--rtlfmradioservice.h28
-rw-r--r--rtlfmradiotunercontrol.cpp345
-rw-r--r--rtlfmradiotunercontrol.h68
9 files changed, 550 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..89f64c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pro.*
diff --git a/rtlfmradio.json b/rtlfmradio.json
new file mode 100644
index 0000000..f58f57b
--- /dev/null
+++ b/rtlfmradio.json
@@ -0,0 +1,4 @@
+{
+ "Keys": ["rtlfmradio"],
+ "Services": ["org.qt-project.qt.radio"]
+}
diff --git a/rtlfmradio.pro b/rtlfmradio.pro
new file mode 100644
index 0000000..0b8d406
--- /dev/null
+++ b/rtlfmradio.pro
@@ -0,0 +1,11 @@
+TEMPLATE = lib
+CONFIG += plugin c++11
+TARGET = rtlfmradio
+QT = multimedia
+
+HEADERS = rtlfmradioplugin.h rtlfmradioservice.h rtlfmradiotunercontrol.h
+SOURCES = rtlfmradioplugin.cpp rtlfmradioservice.cpp rtlfmradiotunercontrol.cpp
+DISTFILES += rtlfmradio.json
+
+target.path = $$[QT_INSTALL_PLUGINS]/mediaservice
+INSTALLS += target
diff --git a/rtlfmradioplugin.cpp b/rtlfmradioplugin.cpp
new file mode 100644
index 0000000..2a44774
--- /dev/null
+++ b/rtlfmradioplugin.cpp
@@ -0,0 +1,34 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "rtlfmradioplugin.h"
+#include "rtlfmradioservice.h"
+
+QMediaService* RtlFmRadioPlugin::create(QString const& key)
+{
+ if (key == QLatin1String(Q_MEDIASERVICE_RADIO))
+ return new RtlFmRadioService;
+
+ return 0;
+}
+
+void RtlFmRadioPlugin::release(QMediaService *service)
+{
+ delete service;
+}
+
+QList<QByteArray> RtlFmRadioPlugin::devices(const QByteArray &service) const
+{
+ Q_UNUSED(service)
+ return QList<QByteArray>();
+}
+
+QString RtlFmRadioPlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
+{
+ Q_UNUSED(service)
+ Q_UNUSED(device)
+ return QString();
+}
diff --git a/rtlfmradioplugin.h b/rtlfmradioplugin.h
new file mode 100644
index 0000000..9941337
--- /dev/null
+++ b/rtlfmradioplugin.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RTLFMRADIOPLUGIN_H
+#define RTLFMRADIOPLUGIN_H
+
+#include <QMediaServiceProviderPlugin>
+
+class RtlFmRadioPlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceProviderFactoryInterface)
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ Q_PLUGIN_METADATA(IID QMediaServiceProviderFactoryInterface_iid FILE "rtlfmradio.json")
+
+public:
+ QMediaService* create(QString const& key);
+ void release(QMediaService *service);
+
+ QList<QByteArray> devices(const QByteArray &service) const;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device);
+};
+
+#endif // RTLFMRADIOPLUGIN_H
diff --git a/rtlfmradioservice.cpp b/rtlfmradioservice.cpp
new file mode 100644
index 0000000..12d1419
--- /dev/null
+++ b/rtlfmradioservice.cpp
@@ -0,0 +1,32 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "rtlfmradioservice.h"
+#include "rtlfmradiotunercontrol.h"
+
+RtlFmRadioService::RtlFmRadioService(QObject *parent)
+ : QMediaService(parent)
+ , m_control(new RtlFmRadioTunerControl(this))
+{
+}
+
+RtlFmRadioService::~RtlFmRadioService()
+{
+}
+
+QMediaControl *RtlFmRadioService::requestControl(const char *name)
+{
+ if (qstrcmp(name,QRadioTunerControl_iid) == 0)
+ return m_control;
+
+ return 0;
+}
+
+void RtlFmRadioService::releaseControl(QMediaControl *control)
+{
+ delete control;
+ m_control = 0;
+}
diff --git a/rtlfmradioservice.h b/rtlfmradioservice.h
new file mode 100644
index 0000000..929dbf0
--- /dev/null
+++ b/rtlfmradioservice.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RTLFMRADIOSERVICE_H
+#define RTLFMRADIOSERVICE_H
+
+#include <QtMultimedia/QMediaService>
+
+class QRadioTunerControl;
+
+class RtlFmRadioService : public QMediaService
+{
+ Q_OBJECT
+public:
+ RtlFmRadioService(QObject *parent = 0);
+ ~RtlFmRadioService();
+
+ QMediaControl *requestControl(const char *name);
+ void releaseControl(QMediaControl *);
+
+private:
+ QRadioTunerControl *m_control;
+};
+
+#endif // RTLFMRADIOSERVICE_H
diff --git a/rtlfmradiotunercontrol.cpp b/rtlfmradiotunercontrol.cpp
new file mode 100644
index 0000000..83096b6
--- /dev/null
+++ b/rtlfmradiotunercontrol.cpp
@@ -0,0 +1,345 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "rtlfmradiotunercontrol.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QTimer>
+#include <QtCore/QProcess>
+
+class RtlFmRadioTunerControl::Private
+{
+public:
+ Private(RtlFmRadioTunerControl *parent);
+ ~Private();
+
+private:
+ RtlFmRadioTunerControl *q;
+
+public:
+ QRadioTuner::State state;
+ QRadioTuner::Band band;
+ int frequency;
+ QRadioTuner::StereoMode stereoMode;
+ int signalStrength;
+ int volume;
+ bool muted;
+ bool searching;
+ QRadioTuner::Error error;
+ QString errorString;
+ QTimer timer;
+ bool searchOne;
+ int step;
+
+ QProcess rtlFm;
+ QProcess aplay;
+
+private:
+ QMap<int, QString> amFrequenciesTokyo = {
+ { 594000, QString::fromUtf8("NHK Tokyo #1") }
+ , { 693000, QString::fromUtf8("NHK Tokyo #2") }
+ , { 810000, QString::fromUtf8("Eagle 810 (AFN TOKYO)") }
+ , { 954000, QString::fromUtf8("TBS Radio Tokyo") }
+ , { 1134000, QString::fromUtf8("Nippon Cultural Broadcasting") }
+ , { 1242000, QString::fromUtf8("Nippon Broadcasting") }
+ };
+
+ QMap<int, QString> fmFrequenciesTokyo = {
+ { 76100, QString::fromUtf8("Inter FM") }
+ , { 77100, QString::fromUtf8("The Open University of Japan") }
+ , { 80000, QString::fromUtf8("TOKYO FM") }
+ , { 81300, QString::fromUtf8("J-WAVE") }
+ , { 82500, QString::fromUtf8("NHK FM Tokyo") }
+ };
+};
+
+RtlFmRadioTunerControl::Private::Private(RtlFmRadioTunerControl *parent)
+ : q(parent)
+ , state(QRadioTuner::StoppedState)
+ , band(QRadioTuner::FM)
+ , frequency(76100)
+ , stereoMode(QRadioTuner::Auto)
+ , signalStrength(100)
+ , volume(50)
+ , muted(false)
+ , searching(false)
+ , error(QRadioTuner::NoError)
+ , searchOne(true)
+ , step(0)
+{
+ connect(q, &RtlFmRadioTunerControl::stationFound, [this](int frequency, const QString &name) {
+ qDebug() << frequency << name;
+ if (rtlFm.state() != QProcess::NotRunning)
+ q->start();
+ });
+ connect(q, &RtlFmRadioTunerControl::frequencyChanged, [this](int frequency) {
+ QMap<int, QString> knownBands;
+ switch (band) {
+ case QRadioTuner::AM:
+ knownBands = amFrequenciesTokyo;
+ break;
+ case QRadioTuner::FM:
+ knownBands = fmFrequenciesTokyo;
+ break;
+ default:
+ qFatal("%s:%d %d not supported", Q_FUNC_INFO, __LINE__, band);
+ break;
+ }
+ if (knownBands.contains(frequency)) {
+ emit q->stationFound(frequency, knownBands.value(frequency));
+ if (timer.isActive() && searchOne)
+ timer.stop();
+ }
+ });
+ connect(&timer, &QTimer::timeout, [this]() {
+ q->setFrequency(frequency + step);
+ });
+
+ connect(&rtlFm, &QProcess::readyReadStandardOutput, [this]() {
+ aplay.write(rtlFm.readAllStandardOutput());
+ });
+ connect(&rtlFm, &QProcess::readyReadStandardError, [this]() {
+ qDebug() << rtlFm.readAllStandardError();
+ });
+}
+
+RtlFmRadioTunerControl::Private::~Private()
+{
+ aplay.kill();
+ aplay.waitForFinished();
+ rtlFm.kill();
+ rtlFm.waitForFinished();
+}
+
+RtlFmRadioTunerControl::RtlFmRadioTunerControl(QObject *parent)
+ : QRadioTunerControl(parent)
+ , d(new Private(this))
+{
+}
+
+RtlFmRadioTunerControl::~RtlFmRadioTunerControl()
+{
+ delete d;
+}
+
+QRadioTuner::State RtlFmRadioTunerControl::state() const
+{
+ return d->state;
+}
+
+QRadioTuner::Band RtlFmRadioTunerControl::band() const
+{
+ return d->band;
+}
+
+void RtlFmRadioTunerControl::setBand(QRadioTuner::Band band)
+{
+ if (d->band == band) return;
+ d->band = band;
+ emit bandChanged(band);
+}
+
+bool RtlFmRadioTunerControl::isBandSupported(QRadioTuner::Band band) const
+{
+ switch (band) {
+ case QRadioTuner::AM:
+ case QRadioTuner::FM:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+int RtlFmRadioTunerControl::frequency() const
+{
+ return d->frequency;
+}
+
+int RtlFmRadioTunerControl::frequencyStep(QRadioTuner::Band band) const
+{
+ int ret = 0;
+ switch (band) {
+ case QRadioTuner::AM:
+ ret = 1000; // 1kHz
+ break;
+ case QRadioTuner::FM:
+ ret = 100; // 100Hz
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+QPair<int,int> RtlFmRadioTunerControl::frequencyRange(QRadioTuner::Band band) const
+{
+ QPair<int,int> ret;
+ switch (band) {
+ case QRadioTuner::AM:
+ ret = qMakePair<int,int>(594000, 1242000);
+ break;
+ case QRadioTuner::FM:
+ ret = qMakePair<int,int>(76100, 82500);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void RtlFmRadioTunerControl::setFrequency(int frequency)
+{
+ QPair<int,int> range = frequencyRange(d->band);
+ frequency = qBound(range.first, frequency, range.second);
+ if (d->frequency == frequency) {
+ if (d->timer.isActive())
+ d->timer.stop();
+ return;
+ }
+ d->frequency = frequency;
+ emit frequencyChanged(frequency);
+}
+
+bool RtlFmRadioTunerControl::isStereo() const
+{
+ bool ret = false;
+ switch (d->stereoMode) {
+ case QRadioTuner::ForceStereo:
+ case QRadioTuner::Auto:
+ ret = true;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+QRadioTuner::StereoMode RtlFmRadioTunerControl::stereoMode() const
+{
+ return d->stereoMode;
+}
+
+void RtlFmRadioTunerControl::setStereoMode(QRadioTuner::StereoMode stereoMode)
+{
+ if (d->stereoMode == stereoMode) return;
+ bool prevStereo = isStereo();
+ d->stereoMode = stereoMode;
+ bool currentStereo = isStereo();
+ if (prevStereo != currentStereo)
+ emit stereoStatusChanged(currentStereo);
+}
+
+int RtlFmRadioTunerControl::signalStrength() const
+{
+ return d->signalStrength;
+}
+
+int RtlFmRadioTunerControl::volume() const
+{
+ return d->volume;
+}
+
+void RtlFmRadioTunerControl::setVolume(int volume)
+{
+ volume = qBound(0, volume, 100);
+ if (d->volume == volume) return;
+ d->volume = volume;
+ emit volumeChanged(volume);
+}
+
+bool RtlFmRadioTunerControl::isMuted() const
+{
+ return d->muted;
+}
+
+void RtlFmRadioTunerControl::setMuted(bool muted)
+{
+ if (d->muted == muted) return;
+ d->muted = muted;
+ emit mutedChanged(muted);
+}
+
+bool RtlFmRadioTunerControl::isSearching() const
+{
+ return d->searching;
+}
+
+void RtlFmRadioTunerControl::searchForward()
+{
+ d->searchOne = true;
+ d->step = frequencyStep(d->band);
+ d->timer.start(100);
+ setSearching(true);
+}
+
+void RtlFmRadioTunerControl::searchBackward()
+{
+ d->searchOne = true;
+ d->step = -frequencyStep(d->band);
+ d->timer.start(250);
+ setSearching(true);
+}
+
+void RtlFmRadioTunerControl::searchAllStations(QRadioTuner::SearchMode searchMode)
+{
+ Q_UNUSED(searchMode)
+ d->searchOne = false;
+ d->frequency = frequencyRange(d->band).first;
+ d->step = frequencyStep(d->band);
+ d->timer.start(250);
+ setSearching(true);
+}
+
+void RtlFmRadioTunerControl::cancelSearch()
+{
+ d->timer.stop();
+ setSearching(false);
+}
+
+void RtlFmRadioTunerControl::setSearching(bool searching)
+{
+ if (d->searching == searching) return;
+ d->searching = searching;
+ emit searchingChanged(searching);
+}
+
+void RtlFmRadioTunerControl::start()
+{
+ stop();
+ d->aplay.start(QStringLiteral("aplay"), QStringList() << QStringLiteral("-r") << QStringLiteral("48000") << QStringLiteral("-f") << QStringLiteral("S16_LE"));
+ d->aplay.waitForStarted();
+// d->rtlFm.start(QStringLiteral("sudo"), QStringList() << QStringLiteral("rtl_fm -f %1 -M wbfm -s 200000 -r 48000").arg(d->frequency * 1000).split(" "));
+ d->rtlFm.start(QStringLiteral("rtl_fm"), QStringList() << QStringLiteral("-f %1 -M wbfm -s 200000 -r 48000").arg(d->frequency * 1000).split(" "));
+ d->rtlFm.waitForStarted();
+ d->state = QRadioTuner::ActiveState;
+}
+
+void RtlFmRadioTunerControl::stop()
+{
+ d->aplay.kill();
+ d->aplay.waitForFinished();
+ d->rtlFm.kill();
+ d->rtlFm.waitForFinished();
+ d->state = QRadioTuner::StoppedState;
+}
+
+QRadioTuner::Error RtlFmRadioTunerControl::error() const
+{
+ return d->error;
+}
+
+QString RtlFmRadioTunerControl::errorString() const
+{
+ return d->errorString;
+}
+
+void RtlFmRadioTunerControl::setError(QRadioTuner::Error error, const QString &errorString)
+{
+ d->error = error;
+ d->errorString = errorString;
+ emit QRadioTunerControl::error(error);
+}
diff --git a/rtlfmradiotunercontrol.h b/rtlfmradiotunercontrol.h
new file mode 100644
index 0000000..f00bfa4
--- /dev/null
+++ b/rtlfmradiotunercontrol.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RTLFMRADIOTUNERCONTROL_H
+#define RTLFMRADIOTUNERCONTROL_H
+
+#include <QtMultimedia/QRadioTunerControl>
+
+class RtlFmRadioTunerControl : public QRadioTunerControl
+{
+ Q_OBJECT
+public:
+ RtlFmRadioTunerControl(QObject *parent = 0);
+ ~RtlFmRadioTunerControl();
+
+ QRadioTuner::State state() const;
+
+ QRadioTuner::Band band() const;
+ void setBand(QRadioTuner::Band band);
+ bool isBandSupported(QRadioTuner::Band band) const;
+
+ int frequency() const;
+ int frequencyStep(QRadioTuner::Band band) const;
+ QPair<int,int> frequencyRange(QRadioTuner::Band band) const;
+ void setFrequency(int frequency);
+
+ bool isStereo() const;
+ QRadioTuner::StereoMode stereoMode() const;
+ void setStereoMode(QRadioTuner::StereoMode stereoMode);
+
+ int signalStrength() const;
+
+ int volume() const;
+ void setVolume(int volume);
+
+ bool isMuted() const;
+ void setMuted(bool muted);
+
+ bool isSearching() const;
+
+ bool isAntennaConnected() const { return true; }
+
+ void searchForward();
+ void searchBackward();
+ void searchAllStations(QRadioTuner::SearchMode searchMode = QRadioTuner::SearchFast);
+ void cancelSearch();
+private:
+ void setSearching(bool searching);
+
+public:
+ void start();
+ void stop();
+
+ QRadioTuner::Error error() const;
+ QString errorString() const;
+
+private:
+ void setError(QRadioTuner::Error error, const QString &errorString);
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif // RTLFMRADIOTUNERCONTROL_H