From 2a34e88c1e1ced28e75c487cd13402e1c9cf9fa3 Mon Sep 17 00:00:00 2001 From: Michael Goddard Date: Wed, 29 Jun 2011 13:38:46 +1000 Subject: Initial copy of QtMultimediaKit. Comes from original repo, with SHA1: 2c82d5611655e5967f5c5095af50c0991c4378b2 --- src/plugins/v4l/radio/v4lradiocontrol.cpp | 538 ++++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 src/plugins/v4l/radio/v4lradiocontrol.cpp (limited to 'src/plugins/v4l/radio/v4lradiocontrol.cpp') diff --git a/src/plugins/v4l/radio/v4lradiocontrol.cpp b/src/plugins/v4l/radio/v4lradiocontrol.cpp new file mode 100644 index 000000000..1b698279a --- /dev/null +++ b/src/plugins/v4l/radio/v4lradiocontrol.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "v4lradiocontrol.h" +#include "v4lradioservice.h" + +#include + +#include + +#include +#include "linux/videodev2.h" + +#include +#include +#include +#include +#include +#include + +V4LRadioControl::V4LRadioControl(QObject *parent) + :QRadioTunerControl(parent) +{ + fd = -1; + initRadio(); + muted = false; + stereo = false; + m_error = false; + sig = 0; + currentBand = QRadioTuner::FM; + step = 100000; + scanning = false; + playTime.restart(); + timer = new QTimer(this); + timer->setInterval(200); + connect(timer,SIGNAL(timeout()),this,SLOT(search())); + timer->start(); +} + +V4LRadioControl::~V4LRadioControl() +{ + timer->stop(); + + if(fd > 0) + ::close(fd); +} + +bool V4LRadioControl::isAvailable() const +{ + return available; +} + +QtMultimediaKit::AvailabilityError V4LRadioControl::availabilityError() const +{ + if (fd > 0) + return QtMultimediaKit::NoError; + else + return QtMultimediaKit::ResourceError; +} + +QRadioTuner::State V4LRadioControl::state() const +{ + return fd > 0 ? QRadioTuner::ActiveState : QRadioTuner::StoppedState; +} + +QRadioTuner::Band V4LRadioControl::band() const +{ + return currentBand; +} + +bool V4LRadioControl::isBandSupported(QRadioTuner::Band b) const +{ + QRadioTuner::Band bnd = (QRadioTuner::Band)b; + switch(bnd) { + case QRadioTuner::FM: + if(freqMin <= 87500000 && freqMax >= 108000000) + return true; + break; + case QRadioTuner::LW: + if(freqMin <= 148500 && freqMax >= 283500) + return true; + case QRadioTuner::AM: + if(freqMin <= 520000 && freqMax >= 1610000) + return true; + default: + if(freqMin <= 1711000 && freqMax >= 30000000) + return true; + } + + return false; +} + +void V4LRadioControl::setBand(QRadioTuner::Band b) +{ + if(freqMin <= 87500000 && freqMax >= 108000000 && b == QRadioTuner::FM) { + // FM 87.5 to 108.0 MHz, except Japan 76-90 MHz + currentBand = (QRadioTuner::Band)b; + step = 100000; // 100kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 148500 && freqMax >= 283500 && b == QRadioTuner::LW) { + // LW 148.5 to 283.5 kHz, 9kHz channel spacing (Europe, Africa, Asia) + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 520000 && freqMax >= 1610000 && b == QRadioTuner::AM) { + // AM 520 to 1610 kHz, 9 or 10kHz channel spacing, extended 1610 to 1710 kHz + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 1711000 && freqMax >= 30000000 && b == QRadioTuner::SW) { + // SW 1.711 to 30.0 MHz, divided into 15 bands. 5kHz channel spacing + currentBand = (QRadioTuner::Band)b; + step = 500; // 500Hz steps + emit bandChanged(currentBand); + } + playTime.restart(); +} + +int V4LRadioControl::frequency() const +{ + return currentFreq; +} + +int V4LRadioControl::frequencyStep(QRadioTuner::Band b) const +{ + int step = 0; + + if(b == QRadioTuner::FM) + step = 100000; // 100kHz steps + else if(b == QRadioTuner::LW) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::AM) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::SW) + step = 500; // 500Hz steps + + return step; +} + +QPair V4LRadioControl::frequencyRange(QRadioTuner::Band b) const +{ + if(b == QRadioTuner::AM) + return qMakePair(520000,1710000); + else if(b == QRadioTuner::FM) + return qMakePair(87500000,108000000); + else if(b == QRadioTuner::SW) + return qMakePair(1711111,30000000); + else if(b == QRadioTuner::LW) + return qMakePair(148500,283500); + + return qMakePair(0,0); +} + +void V4LRadioControl::setFrequency(int frequency) +{ + qint64 f = frequency; + + v4l2_frequency freq; + + if(frequency < freqMin) + f = freqMax; + if(frequency > freqMax) + f = freqMin; + + if(fd > 0) { + memset( &freq, 0, sizeof( freq ) ); + // Use the first tuner + freq.tuner = 0; + if ( ioctl( fd, VIDIOC_G_FREQUENCY, &freq ) >= 0 ) { + if(low) { + // For low, freq in units of 62.5Hz, so convert from Hz to units. + freq.frequency = (int)(f/62.5); + } else { + // For high, freq in units of 62.5kHz, so convert from Hz to units. + freq.frequency = (int)(f/62500); + } + ioctl( fd, VIDIOC_S_FREQUENCY, &freq ); + currentFreq = f; + playTime.restart(); + emit frequencyChanged(currentFreq); + } + } + playTime.restart(); +} + +bool V4LRadioControl::isStereo() const +{ + return stereo; +} + +QRadioTuner::StereoMode V4LRadioControl::stereoMode() const +{ + return QRadioTuner::Auto; +} + +void V4LRadioControl::setStereoMode(QRadioTuner::StereoMode mode) +{ + bool stereo = true; + + if(mode == QRadioTuner::ForceMono) + stereo = false; + + v4l2_tuner tuner; + + memset( &tuner, 0, sizeof( tuner ) ); + + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) >= 0 ) { + if(stereo) + tuner.audmode = V4L2_TUNER_MODE_STEREO; + else + tuner.audmode = V4L2_TUNER_MODE_MONO; + + if ( ioctl( fd, VIDIOC_S_TUNER, &tuner ) >= 0 ) { + emit stereoStatusChanged(stereo); + } + } +} + +int V4LRadioControl::signalStrength() const +{ + v4l2_tuner tuner; + + // Return the first tuner founds signal strength. + for ( int index = 0; index < tuners; ++index ) { + memset( &tuner, 0, sizeof( tuner ) ); + tuner.index = index; + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) < 0 ) + continue; + if ( tuner.type != V4L2_TUNER_RADIO ) + continue; + // percentage signal strength + return tuner.signal*100/65535; + } + + return 0; +} + +int V4LRadioControl::volume() const +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_VOLUME; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + if(queryctrl.maximum == 0) { + return vol; + } else { + // percentage volume returned + return queryctrl.default_value*100/queryctrl.maximum; + } + } + } + return 0; +} + +void V4LRadioControl::setVolume(int volume) +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_VOLUME; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + v4l2_control control; + + if(queryctrl.maximum > 0) { + memset( &control, 0, sizeof( control ) ); + control.id = V4L2_CID_AUDIO_VOLUME; + control.value = volume*queryctrl.maximum/100; + ioctl( fd, VIDIOC_S_CTRL, &control ); + } else { + setVol(volume); + } + emit volumeChanged(volume); + } + } +} + +bool V4LRadioControl::isMuted() const +{ + return muted; +} + +void V4LRadioControl::setMuted(bool muted) +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_MUTE; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + v4l2_control control; + memset( &control, 0, sizeof( control ) ); + control.id = V4L2_CID_AUDIO_MUTE; + control.value = (muted ? queryctrl.maximum : queryctrl.minimum ); + ioctl( fd, VIDIOC_S_CTRL, &control ); + this->muted = muted; + emit mutedChanged(muted); + } + } +} + +bool V4LRadioControl::isSearching() const +{ + return scanning; +} + +void V4LRadioControl::cancelSearch() +{ + scanning = false; + timer->stop(); +} + +void V4LRadioControl::searchForward() +{ + // Scan up + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = true; + timer->start(); +} + +void V4LRadioControl::searchBackward() +{ + // Scan down + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = false; + timer->start(); +} + +void V4LRadioControl::start() +{ +} + +void V4LRadioControl::stop() +{ +} + +QRadioTuner::Error V4LRadioControl::error() const +{ + if(m_error) + return QRadioTuner::OpenError; + + return QRadioTuner::NoError; +} + +QString V4LRadioControl::errorString() const +{ + return QString(); +} + +void V4LRadioControl::search() +{ + int signal = signalStrength(); + if(sig != signal) { + sig = signal; + emit signalStrengthChanged(sig); + } + + if(!scanning) return; + + if (signal > 25) { + cancelSearch(); + return; + } + + if(forward) { + setFrequency(currentFreq+step); + } else { + setFrequency(currentFreq-step); + } +} + +bool V4LRadioControl::initRadio() +{ + v4l2_tuner tuner; + v4l2_input input; + v4l2_frequency freq; + v4l2_capability cap; + + low = false; + available = false; + freqMin = freqMax = currentFreq = 0; + + fd = ::open("/dev/radio0", O_RDWR); + + if(fd != -1) { + // Capabilities + memset( &cap, 0, sizeof( cap ) ); + if(::ioctl(fd, VIDIOC_QUERYCAP, &cap ) >= 0) { + if(((cap.capabilities & V4L2_CAP_RADIO) == 0) && ((cap.capabilities & V4L2_CAP_AUDIO) == 0)) + available = true; + } + + // Tuners + memset( &input, 0, sizeof( input ) ); + tuners = 0; + for ( ;; ) { + memset( &input, 0, sizeof( input ) ); + input.index = tuners; + if ( ioctl( fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + break; + ++tuners; + } + + // Freq bands + for ( int index = 0; index < tuners; ++index ) { + memset( &tuner, 0, sizeof( tuner ) ); + tuner.index = index; + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) < 0 ) + continue; + if ( tuner.type != V4L2_TUNER_RADIO ) + continue; + if ( ( tuner.capability & V4L2_TUNER_CAP_LOW ) != 0 ) { + // Units are 1/16th of a kHz. + low = true; + } + if(low) { + freqMin = tuner.rangelow * 62.5; + freqMax = tuner.rangehigh * 62.5; + } else { + freqMin = tuner.rangelow * 62500; + freqMax = tuner.rangehigh * 62500; + } + } + + // frequency + memset( &freq, 0, sizeof( freq ) ); + if(::ioctl(fd, VIDIOC_G_FREQUENCY, &freq ) >= 0) { + if ( ((int)freq.frequency) != -1 ) { // -1 means not set. + if(low) + currentFreq = freq.frequency * 62.5; + else + currentFreq = freq.frequency * 62500; + } + } + + // stereo + bool stereo = false; + memset( &tuner, 0, sizeof( tuner ) ); + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) >= 0 ) { + if((tuner.rxsubchans & V4L2_TUNER_SUB_STEREO) != 0) + stereo = true; + } + + vol = getVol(); + + return true; + } + m_error = true; + emit error(); + + return false; +} + +void V4LRadioControl::setVol(int v) +{ + int fd = ::open( "/dev/mixer", O_RDWR, 0 ); + if ( fd < 0 ) + return; + int volume = v; + if ( volume < 0 ) + volume = 0; + else if ( volume > 100 ) + volume = 100; + vol = volume; + volume += volume << 8; + ::ioctl( fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &volume ); + ::close( fd ); +} + +int V4LRadioControl::getVol() +{ + int fd = ::open( "/dev/mixer", O_RDWR, 0 ); + if ( fd >= 0 ) { + int volume = 0; + ::ioctl( fd, MIXER_READ(SOUND_MIXER_VOLUME), &volume ); + int left = ( volume & 0xFF ); + int right = ( ( volume >> 8 ) & 0xFF ); + if ( left > right ) + vol = left; + else + vol = right; + ::close( fd ); + return vol; + } + return 0; +} + -- cgit v1.2.3