diff options
Diffstat (limited to 'rtlfmradiotunercontrol.cpp')
-rw-r--r-- | rtlfmradiotunercontrol.cpp | 225 |
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 |