diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | rtlfmradio.json | 4 | ||||
-rw-r--r-- | rtlfmradio.pro | 11 | ||||
-rw-r--r-- | rtlfmradioplugin.cpp | 34 | ||||
-rw-r--r-- | rtlfmradioplugin.h | 27 | ||||
-rw-r--r-- | rtlfmradioservice.cpp | 32 | ||||
-rw-r--r-- | rtlfmradioservice.h | 28 | ||||
-rw-r--r-- | rtlfmradiotunercontrol.cpp | 345 | ||||
-rw-r--r-- | rtlfmradiotunercontrol.h | 68 |
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 |