summaryrefslogtreecommitdiffstats
path: root/rtlfmradiotunercontrol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rtlfmradiotunercontrol.cpp')
-rw-r--r--rtlfmradiotunercontrol.cpp345
1 files changed, 345 insertions, 0 deletions
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);
+}