summaryrefslogtreecommitdiffstats
path: root/rtlfmradiotunercontrol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rtlfmradiotunercontrol.cpp')
-rw-r--r--rtlfmradiotunercontrol.cpp225
1 files changed, 131 insertions, 94 deletions
diff --git a/rtlfmradiotunercontrol.cpp b/rtlfmradiotunercontrol.cpp
index 863cb06..1fdf8d3 100644
--- a/rtlfmradiotunercontrol.cpp
+++ b/rtlfmradiotunercontrol.cpp
@@ -1,14 +1,19 @@
-/* Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+/*
+ * Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved.
+ * Copyright (C) 2016, Scott Murray <scott.murray@konsulko.com>
*
* 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/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
#include "rtlfmradiotunercontrol.h"
+#include "radio_output.h"
+#include "rtl_fm.h"
#include <QtCore/QDebug>
#include <QtCore/QTimer>
-#include <QtCore/QProcess>
+#include <QtCore/QSettings>
class RtlFmRadioTunerControl::Private
{
@@ -26,97 +31,112 @@ public:
QRadioTuner::StereoMode stereoMode;
int signalStrength;
int volume;
- bool muted;
+ static bool muted;
bool searching;
QRadioTuner::Error error;
QString errorString;
QTimer timer;
bool searchOne;
int step;
-
- QProcess rtlFm;
- QProcess aplay;
+ bool present;
+ QPair<int, int> fmBandPlan;
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<QString, QPair<int, int> > knownFmBandPlans = {
+ { QString::fromUtf8("US"), qMakePair(87900000, 107900000) },
+ { QString::fromUtf8("JP"), qMakePair(76100000, 89900000) }
};
- QMap<int, QString> fmFrequenciesTokyo = {
- { 76100000, QString::fromUtf8("Inter FM") }
- , { 77100000, QString::fromUtf8("The Open University of Japan") }
- , { 80000000, QString::fromUtf8("TOKYO FM") }
- , { 81300000, QString::fromUtf8("J-WAVE") }
- , { 82500000, QString::fromUtf8("NHK FM Tokyo") }
- };
+ static void output_thread_fn(int16_t *result, int result_len, void *ctx);
+ static RadioOutputImplementation *mRadioOutput;
};
+bool RtlFmRadioTunerControl::Private::muted;
+RadioOutputImplementation *RtlFmRadioTunerControl::Private::mRadioOutput;
+
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)
-{
+ : q(parent),
+ state(QRadioTuner::StoppedState),
+ band(QRadioTuner::FM),
+ stereoMode(QRadioTuner::Auto),
+ signalStrength(100),
+ volume(50),
+ searching(false),
+ error(QRadioTuner::NoError),
+ searchOne(true),
+ step(0)
+{
+ mRadioOutput = NULL;
+ muted = false;
+
+ // Initialize output
+ // Note that the optional ALSA support is currently not entirely
+ // functional and needs further debugging.
+ char *impl_env = getenv("RADIO_OUTPUT");
+ if (impl_env) {
+ if (strcasecmp(impl_env, "Pulse") == 0)
+ mRadioOutput = new RadioOutputPulse();
+ if (strcasecmp(impl_env, "Alsa") == 0)
+ mRadioOutput = new RadioOutputAlsa();
+ }
+ if (!mRadioOutput) {
+ mRadioOutput = new RadioOutputPulse();
+ if (!mRadioOutput->works)
+ mRadioOutput = new RadioOutputAlsa();
+ }
+
+ // Initialize RTL-SDR dongle
+ present = false;
+ if(rtl_fm_init(frequency, 200000, 48000, output_thread_fn, NULL) < 0) {
+ qDebug("%s: no RTL USB adapter?", Q_FUNC_INFO);
+ } else {
+ present = true;
+ }
+
+ // Initialize FM band plan
+ QSettings settings("AGL", "qtmultimedia-rtlfm-radio-plugin");
+ if(!settings.contains("fmbandplan"))
+ settings.setValue("fmbandplan", QString::fromUtf8("US"));
+ if(!knownFmBandPlans.contains(settings.value("fmbandplan").toString()))
+ settings.setValue("fmbandplan", QString::fromUtf8("US"));
+ fmBandPlan = knownFmBandPlans.value(settings.value("fmbandplan").toString());
+
+ // Initialize frequency to lower bound of band plan
+ frequency = fmBandPlan.first;
+
connect(q, &RtlFmRadioTunerControl::stationFound, [this](int frequency, const QString &name) {
- if (rtlFm.state() != QProcess::NotRunning)
- q->start();
+ qDebug() << frequency << name;
});
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)) {
- QMetaObject::invokeMethod(q, "stationFound", Qt::QueuedConnection, Q_ARG(int, frequency), Q_ARG(QString, knownBands.value(frequency)));
- if (timer.isActive() && searchOne)
- timer.stop();
- }
+ // Note that the following effectively disables any attempt at seeking.
+ // If seeking support is required some logic will need to be added back
+ // here (and elsewhere).
+ if (searching) {
+ emit q->stationFound(frequency, QString("%1 MHz").arg((double) frequency / 1000000, 0, 'f', 1));
+ 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();
- });
+void RtlFmRadioTunerControl::Private::output_thread_fn(int16_t *result, int result_len, void *ctx)
+{
+ if (!muted)
+ mRadioOutput->play((void*) result, result_len);
}
RtlFmRadioTunerControl::Private::~Private()
{
- aplay.kill();
- aplay.waitForFinished();
- rtlFm.kill();
- rtlFm.waitForFinished();
+ delete mRadioOutput;
+ rtl_fm_cleanup();
}
RtlFmRadioTunerControl::RtlFmRadioTunerControl(QObject *parent)
- : QRadioTunerControl(parent)
- , d(new Private(this))
+ : QRadioTunerControl(parent),
+ d(new Private(this))
{
}
@@ -137,15 +157,20 @@ QRadioTuner::Band RtlFmRadioTunerControl::band() const
void RtlFmRadioTunerControl::setBand(QRadioTuner::Band band)
{
- if (d->band == band) return;
+ // We only support FM, so this is a no-op, but leaving the code below
+ // #if'ed out for reference.
+#if 0
+ if (d->band == band)
+ return;
d->band = band;
emit bandChanged(band);
+#endif
}
bool RtlFmRadioTunerControl::isBandSupported(QRadioTuner::Band band) const
{
+ // We only support FM.
switch (band) {
- case QRadioTuner::AM:
case QRadioTuner::FM:
return true;
default:
@@ -162,9 +187,10 @@ int RtlFmRadioTunerControl::frequency() const
int RtlFmRadioTunerControl::frequencyStep(QRadioTuner::Band band) const
{
int ret = 0;
+
switch (band) {
case QRadioTuner::AM:
- ret = 1000; // 1kHz
+ ret = 1000; // 1 kHz
break;
case QRadioTuner::FM:
ret = 100000; // 0.1 MHz
@@ -178,12 +204,13 @@ int RtlFmRadioTunerControl::frequencyStep(QRadioTuner::Band band) const
QPair<int,int> RtlFmRadioTunerControl::frequencyRange(QRadioTuner::Band band) const
{
QPair<int,int> ret;
+
switch (band) {
case QRadioTuner::AM:
ret = qMakePair<int,int>(531000, 1602000);
break;
case QRadioTuner::FM:
- ret = qMakePair<int,int>(76000000, 107900000);
+ ret = QPair<int,int>(d->fmBandPlan);
break;
default:
break;
@@ -193,6 +220,9 @@ QPair<int,int> RtlFmRadioTunerControl::frequencyRange(QRadioTuner::Band band) co
void RtlFmRadioTunerControl::setFrequency(int frequency)
{
+ if(!d->present)
+ return;
+
QPair<int,int> range = frequencyRange(d->band);
frequency = qBound(range.first, frequency, range.second);
if (d->frequency == frequency) {
@@ -201,12 +231,14 @@ void RtlFmRadioTunerControl::setFrequency(int frequency)
return;
}
d->frequency = frequency;
+ rtl_fm_set_freq(frequency);
emit frequencyChanged(frequency);
}
bool RtlFmRadioTunerControl::isStereo() const
{
bool ret = false;
+
switch (d->stereoMode) {
case QRadioTuner::ForceStereo:
case QRadioTuner::Auto:
@@ -225,7 +257,8 @@ QRadioTuner::StereoMode RtlFmRadioTunerControl::stereoMode() const
void RtlFmRadioTunerControl::setStereoMode(QRadioTuner::StereoMode stereoMode)
{
- if (d->stereoMode == stereoMode) return;
+ if (d->stereoMode == stereoMode)
+ return;
bool prevStereo = isStereo();
d->stereoMode = stereoMode;
bool currentStereo = isStereo();
@@ -246,7 +279,8 @@ int RtlFmRadioTunerControl::volume() const
void RtlFmRadioTunerControl::setVolume(int volume)
{
volume = qBound(0, volume, 100);
- if (d->volume == volume) return;
+ if (d->volume == volume)
+ return;
d->volume = volume;
emit volumeChanged(volume);
}
@@ -258,7 +292,8 @@ bool RtlFmRadioTunerControl::isMuted() const
void RtlFmRadioTunerControl::setMuted(bool muted)
{
- if (d->muted == muted) return;
+ if (d->muted == muted)
+ return;
d->muted = muted;
emit mutedChanged(muted);
}
@@ -302,41 +337,43 @@ void RtlFmRadioTunerControl::cancelSearch()
void RtlFmRadioTunerControl::setSearching(bool searching)
{
- if (d->searching == searching) return;
+ 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(" "));
- QString modulation;
- switch (d->band) {
- case QRadioTuner::AM:
- modulation = QStringLiteral("-M wbfm");
- break;
- case QRadioTuner::FM:
- modulation = QStringLiteral("-M am");
- break;
- default:
- break;
+ if(!d->present)
+ return;
+
+ qDebug() << "RtlFmRadioTunerControl::start - enter\n";
+ if(d->state == QRadioTuner::ActiveState) {
+ qDebug() << "RtlFmRadioTunerControl::start - already running, exit\n";
+ return;
}
- d->rtlFm.start(QStringLiteral("rtl_fm"), QStringLiteral("-f %1 %2 -s 200000 -r 48000").arg(d->frequency).arg(modulation).split(" "));
- d->rtlFm.waitForStarted();
+ //rtl_fm_stop();
+ rtl_fm_start();
d->state = QRadioTuner::ActiveState;
+ qDebug() << "RtlFmRadioTunerControl::start - exit\n";
}
void RtlFmRadioTunerControl::stop()
{
- d->aplay.kill();
- d->aplay.waitForFinished();
- d->rtlFm.kill();
- d->rtlFm.waitForFinished();
+ if(!d->present)
+ return;
+
+ qDebug() << "RtlFmRadioTunerControl::stop - enter\n";
+ if(d->state == QRadioTuner::StoppedState) {
+ qDebug() << "RtlFmRadioTunerControl::stop - already stopped, exit\n";
+ return;
+ }
+
+ rtl_fm_stop();
d->state = QRadioTuner::StoppedState;
+ qDebug() << "RtlFmRadioTunerControl::stop - exit\n";
}
QRadioTuner::Error RtlFmRadioTunerControl::error() const