From ddfe5552b3297236effc42f0c73e8b69654c976c Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Tue, 19 Jul 2016 22:49:40 +0900 Subject: initial commit for ALS2016 Change-Id: If170e059c9be053117e31afd5ae4b7cfa810aa1b --- .gitignore | 1 + rtlfmradio.json | 4 + rtlfmradio.pro | 11 ++ rtlfmradioplugin.cpp | 34 +++++ rtlfmradioplugin.h | 27 ++++ rtlfmradioservice.cpp | 32 +++++ rtlfmradioservice.h | 28 ++++ rtlfmradiotunercontrol.cpp | 345 +++++++++++++++++++++++++++++++++++++++++++++ rtlfmradiotunercontrol.h | 68 +++++++++ 9 files changed, 550 insertions(+) create mode 100644 .gitignore create mode 100644 rtlfmradio.json create mode 100644 rtlfmradio.pro create mode 100644 rtlfmradioplugin.cpp create mode 100644 rtlfmradioplugin.h create mode 100644 rtlfmradioservice.cpp create mode 100644 rtlfmradioservice.h create mode 100644 rtlfmradiotunercontrol.cpp create mode 100644 rtlfmradiotunercontrol.h 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 RtlFmRadioPlugin::devices(const QByteArray &service) const +{ + Q_UNUSED(service) + return QList(); +} + +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 + +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 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 + +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 +#include +#include + +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 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 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 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 RtlFmRadioTunerControl::frequencyRange(QRadioTuner::Band band) const +{ + QPair ret; + switch (band) { + case QRadioTuner::AM: + ret = qMakePair(594000, 1242000); + break; + case QRadioTuner::FM: + ret = qMakePair(76100, 82500); + break; + default: + break; + } + return ret; +} + +void RtlFmRadioTunerControl::setFrequency(int frequency) +{ + QPair 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 + +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 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 -- cgit 1.2.3-korg