Index: configure
===================================================================
--- configure	(revision 7394)
+++ configure	(working copy)
@@ -21,6 +21,7 @@
 fftw_lib="no"
 sdl="no"
 aac="no"
+radio="no"
 transcode="no"
 vcdsupport="no"
 exif="no"
@@ -80,6 +81,10 @@
   ;;
   --disable-aac) aac="no"
   ;;
+  --enable-radio) radio="yes"
+  ;;
+  --disable-radio) radio="no"
+  ;;
   --enable-transcode) transcode="yes"
   ;;
   --disable-transcode) transcode="no"
@@ -109,6 +114,7 @@
                  opengl="yes";
                  fftw_lib="yes";
                  aac="yes";
+		 radio="yes";
                  vcdsupport="yes";
                  transcode="yes";
                  exif="yes";
@@ -126,7 +132,8 @@
                  sdl="no";  
                  opengl="no"; 
                  fftw_lib="no"; 
-                 aac="no"; 
+                 aac="no";
+		 radio="no";
                  vcdsupport="no"; 
                  transcode="no";
                  exif="no";
@@ -166,7 +173,8 @@
   --enable-fftw            enable fftw visualizers [default=no]
   --enable-sdl             use SDL for the synaesthesia output [default=no]
   --enable-aac             enable AAC/MP4 audio file decompression [default=no]
-
+  --enable-radio           enable V4L Radio decoder [default=no]
+  
 MythNews related options:
   --enable-mythnews        build the mythnews plugin [default=yes]
 
@@ -453,6 +461,16 @@
     fi
 fi
 
+if test "$radio" = "yes" ; then
+  echo "        V4L Radio     support will be included in MythMusic"
+  echo "#define RADIO_SUPPORT 1" >> ./mythmusic/mythmusic/config.h
+  echo "HEADERS += radiodecoder.h metaioradiocomment.h v4lradio.h stationsimporter.h" >> ./mythmusic/mythmusic/config.pro
+  echo "SOURCES += radiodecoder.cpp metaioradiocomment.cpp v4lradio.cpp stationsimporter.cpp" >> ./mythmusic/mythmusic/config.pro
+fi
+if test "$radio" = "no" ; then
+  echo "        V4L Radio     support will not be included in MythMusic"
+fi
+
 ###########################################################
 #                                                         #
 #   MythPhone related configuration options               #
Index: mythmusic/mythmusic/playbackbox.cpp
===================================================================
--- mythmusic/mythmusic/playbackbox.cpp	(revision 7394)
+++ mythmusic/mythmusic/playbackbox.cpp	(working copy)
@@ -50,6 +50,14 @@
     curMeta = NULL;
     curSmartPlaylistCategory = "";
     curSmartPlaylistName = "";
+
+#ifdef RADIO_SUPPORT
+    // read radio decoder settings, even radio decoder not activate???
+    radio_lock_device = gContext->GetNumSetting("RadioLockDevice", 0);
+    radio_max_freq = gContext->GetNumSetting("RadioFMaxOverride", 108000);
+    radio_min_freq = gContext->GetNumSetting("RadioFMinOverride", 87500);
+    radio_freq_step = gContext->GetNumSetting("RadioScanStep", 50);
+#endif
     
     // Set our pointers the playlists and the metadata containers
 
@@ -936,6 +944,17 @@
 
 void PlaybackBoxMusic::pause(void)
 {
+#ifdef RADIO_SUPPORT
+    // sometimes radio decoder can't stop from pause state
+    // sets pause key act as mute key :(
+    if (decoder && decoder->factory()->extension() == ".fmd")
+    {
+        if (output)
+            output->ToggleMute();
+        return;
+    }
+#endif
+
     if (output) 
     {
         isplaying = !isplaying;
@@ -1018,6 +1037,20 @@
         info_text->SetText("");
 
     isplaying = false;
+
+#ifdef RADIO_SUPPORT
+    // restore rating image
+    if (showrating && curMeta)
+    {
+        if (ratings_image)
+            ratings_image->setRepeat(curMeta->Rating());
+    }
+    else
+    {
+        if (ratings_image)
+            ratings_image->setRepeat(0);
+    }
+#endif
 }
 
 void PlaybackBoxMusic::stopAll()
@@ -1087,6 +1120,23 @@
 
 void PlaybackBoxMusic::seekforward()
 {
+#ifdef RADIO_SUPPORT
+    if (decoder && decoder->factory()->extension() == ".fmd")
+    {
+        // seek only available if radio device in lock mode
+        if (artist_text && radio_lock_device)
+        {
+            QString sfreq = (artist_text->GetText().left(6)).stripWhiteSpace();
+            int ifreq = (int) (sfreq.toFloat() * 1000.0);
+
+            ifreq = ifreq + radio_freq_step;
+            if (ifreq > radio_max_freq) ifreq = radio_max_freq;
+
+            seek(ifreq);
+        }
+        return;
+    }
+#endif
     int nextTime = currentTime + 5;
     if (nextTime > maxTime)
         nextTime = maxTime;
@@ -1095,6 +1145,23 @@
 
 void PlaybackBoxMusic::seekback()
 {
+#ifdef RADIO_SUPPORT
+    if (decoder && decoder->factory()->extension() == ".fmd")
+    {
+        // seek only available if radio device in lock mode
+        if (artist_text && radio_lock_device)
+        {
+            QString sfreq = (artist_text->GetText().left(6)).stripWhiteSpace();
+            int ifreq = (int) (sfreq.toFloat() * 1000.0);
+
+            ifreq = ifreq - radio_freq_step;
+            if (ifreq < radio_min_freq) ifreq = radio_min_freq;
+
+            seek(ifreq);
+        }
+        return;
+    }
+#endif
     int nextTime = currentTime - 5;
     if (nextTime < 0)
         nextTime = 0;
@@ -1106,6 +1173,14 @@
     if (output)
     {
         output->Reset();
+#ifdef RADIO_SUPPORT
+        if (decoder && decoder->factory()->extension() == ".fmd" && decoder->running())
+        {
+            if (artist_text)
+                artist_text->SetText(QString().setNum((float) pos / 1000.0, 'f', 2) + " " + tr("MHz"));
+        }
+        else
+#endif
         output->SetTimecode(pos*1000);
 
         if (decoder && decoder->running()) 
@@ -1419,6 +1494,19 @@
                 }
             }
 
+#ifdef RADIO_SUPPORT 
+            // signal viewer only available if radio device in lock mode 
+            if (radio_lock_device && decoder && decoder->factory()->extension() == ".fmd")
+            {
+                if (ratings_image)
+                {
+                    decoder->mutex()->lock();
+                    ratings_image->setRepeat((int) (decoder->radioSignalQuality() * 10.0));
+                    decoder->mutex()->unlock();
+                }
+            }
+#endif
+
             break;
         }
         case OutputEvent::Error:
Index: mythmusic/mythmusic/decoder.h
===================================================================
--- mythmusic/mythmusic/decoder.h	(revision 7394)
+++ mythmusic/mythmusic/decoder.h	(working copy)
@@ -81,6 +81,10 @@
 
     virtual Metadata *getMetadata() = 0;
     virtual void commitMetadata(Metadata *mdata) = 0;
+    
+#ifdef RADIO_SUPPORT    
+    virtual float radioSignalQuality() const { return 0; }
+#endif
 
     static void SetLocationFormatUseTags(void);
 
@@ -177,4 +181,15 @@
 };
 #endif
 
+#ifdef RADIO_SUPPORT
+class RadioDecoderFactory : public DecoderFactory
+{
+public:
+    bool supports(const QString &) const;
+    const QString &extension() const;
+    const QString &description() const;
+    Decoder *create(const QString &, QIODevice *, AudioOutput *, bool);
+};
 #endif
+
+#endif
Index: mythmusic/mythmusic/globalsettings.cpp
===================================================================
--- mythmusic/mythmusic/globalsettings.cpp	(revision 7394)
+++ mythmusic/mythmusic/globalsettings.cpp	(working copy)
@@ -1,5 +1,5 @@
 #include "mythtv/mythcontext.h"
-
+#include "config.h"
 #include <unistd.h>
 
 #include "globalsettings.h"
@@ -12,6 +12,10 @@
 #include <qprocess.h>
 #include <qapplication.h>
 
+#ifdef RADIO_SUPPORT
+#include "stationsimporter.h"
+#endif
+
 // General Settings
 
 static HostLineEdit *SetMusicDirectory()
@@ -297,6 +301,195 @@
     return gc;
 };
 
+
+#ifdef RADIO_SUPPORT
+static HostComboBox *RadioDevice()
+{
+    HostComboBox *gc = new HostComboBox("RadioDevice", true);
+    gc->setLabel(QObject::tr("Radio device"));
+    QDir dev("/dev", "radio*", QDir::Name, QDir::System);
+    gc->fillSelectionsFromDir(dev);
+
+    dev.setNameFilter("radio*");
+    dev.setPath("/dev/v4l");
+    gc->fillSelectionsFromDir(dev);
+    //gc->setHelpText(QObject::tr("Radio device used for playback."));
+    return gc;
+};
+
+static HostSpinBox *RadioFMinOverride()
+{
+    HostSpinBox *gc = new HostSpinBox("RadioFMinOverride", 65000, 108000, 500);
+    gc->setLabel(QObject::tr("Minimum frequency [kHz]"));
+    gc->setValue(87500);
+    gc->setHelpText(QObject::tr("Override radio device minimum frequency to this value."));
+    return gc;
+};
+
+static HostSpinBox *RadioFMaxOverride()
+{
+    HostSpinBox *gc = new HostSpinBox("RadioFMaxOverride", 65000, 108000, 500);
+    gc->setLabel(QObject::tr("Maximum frequency [kHz]"));
+    gc->setValue(108000);
+    gc->setHelpText(QObject::tr("Override radio device maximum frequency to this value."));
+    return gc;
+};
+
+static HostSpinBox *RadioScanStep()
+{
+    HostSpinBox *gc = new HostSpinBox("RadioScanStep", 5, 500, 5);
+    gc->setLabel(QObject::tr("Frequency step (kHz)"));
+    gc->setValue(50);
+    gc->setHelpText(QObject::tr("Sets radio frequency to this value multiplication."));
+    return gc;
+};
+
+static HostSpinBox *RadioSignalMinQuality()
+{
+    HostSpinBox *gc = new HostSpinBox("RadioSignalMinQuality", 10, 100, 1);
+    gc->setLabel(QObject::tr("Minimum signal quality [%]"));
+    gc->setValue(60);
+    gc->setHelpText(QObject::tr("Minimum signal for frequency good quality."));
+    return gc;
+};
+
+static HostLineEdit *RadioDirectory()
+{
+    HostLineEdit *gc = new HostLineEdit("RadioLocation");
+    gc->setLabel(QObject::tr("Directory to hold radio file"));
+    gc->setValue("/mnt/store/music/radio");
+    gc->setHelpText(QObject::tr("This directory must exist, and the user "
+                    "running MythMusic needs to have write permission "
+                    "to the directory. Sets under MythMusic directory."));
+    return gc;
+};
+
+static TransButtonSetting *ButtonImport()
+{
+    TransButtonSetting *gc = new TransButtonSetting();
+    gc->setLabel(QObject::tr("Import MythFM Station Database"));
+    gc->setHelpText(QObject::tr("Import all MythFM station database and save it to .fmd file."));
+    return gc;
+}
+
+static TransButtonSetting *ButtonScan()
+{
+    TransButtonSetting *gc = new TransButtonSetting();
+    gc->setLabel(QObject::tr("Scan Radio Stations"));
+    gc->setHelpText(QObject::tr("Scan all radio stations and save it to .fmd file."));
+    return gc;
+}
+
+static HostCheckBox *RadioUseOldV4L2Calls()
+{
+    HostCheckBox *gc = new HostCheckBox("RadioUseOldV4L2Calls");
+    gc->setLabel(QObject::tr("Use old V4L2 calls"));
+    gc->setValue(true);
+    gc->setHelpText(QObject::tr("Sets this value to figure out old version V4L2 calls. "
+                    "MythMusic will try the other version of the call in case this set "
+                    "did not work."));
+    return gc;
+};
+
+static HostCheckBox *RadioIVTVPassthrough()
+{
+    HostCheckBox *gc = new HostCheckBox("RadioIVTVPassthrough");
+    gc->setLabel(QObject::tr("Activate ivtv passthrough mode"));
+    gc->setValue(false);
+    gc->setHelpText(QObject::tr("Sets this value to enable ivtv passthrough mode in "
+                    "case ivtv driver activate. (experimental)"));
+    return gc;
+};
+
+static HostCheckBox *RadioLockDevice()
+{
+    HostCheckBox *gc = new HostCheckBox("RadioLockDevice");
+    gc->setLabel(QObject::tr("Lock radio device while playing"));
+    gc->setValue(false);
+    gc->setHelpText(QObject::tr("Note that activate this setting can be  "
+                    "disturb your tv recording schedule. Be carefull. "
+                    "Frequency seeker and signal viewer only available "
+                    "in lock mode."));
+    return gc;
+};
+
+static HostComboBox *RadioAudioDevice()
+{
+    HostComboBox *gc = new HostComboBox("RadioAudioDevice", true);
+    gc->setLabel(QObject::tr("Radio audio device"));
+    QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
+    gc->fillSelectionsFromDir(dev);
+    dev.setNameFilter("video*");
+    gc->fillSelectionsFromDir(dev);
+
+    dev.setNameFilter("dsp*");
+    dev.setPath("/dev/sound");
+    gc->fillSelectionsFromDir(dev);
+    gc->setHelpText(QObject::tr("Audio device used as audio input for audio playback."));
+    return gc;
+};
+
+static HostComboBox *RadioAudioRate()
+{
+    HostComboBox *gc = new HostComboBox("RadioAudioRate", true);
+    gc->setLabel(QObject::tr("Audio sampling rate"));
+    gc->addSelection("32000"); // prepare for bttv audio
+    gc->addSelection("44100"); // prepare for audio pass-through
+    gc->addSelection("48000"); // prepare for ivtv audio
+    gc->setHelpText(QObject::tr("Sets the audio sampling rate for your DSP. "
+                    "Ensure that you choose a sampling rate appropriate "
+                    "for your device.  btaudio may only allow 32000, "
+                    "ivtv may only allow 48000."));
+    return gc;
+};
+
+static HostSlider *RadioVolume()
+{
+    HostSlider *gc = new HostSlider("RadioVolume", 0, 100, 5);
+    gc->setLabel(QObject::tr("Radio device volume"));
+    gc->setValue(100);
+    gc->setHelpText(QObject::tr("Volume position for radio device."));
+    return gc;
+};
+
+static HostSlider *RadioTreble()
+{
+    HostSlider *gc = new HostSlider("RadioTreble", 0, 100, 5);
+    gc->setLabel(QObject::tr("Radio device treble"));
+    gc->setValue(50);
+    gc->setHelpText(QObject::tr("Treble position for radio device."));
+    return gc;
+};
+
+static HostSlider *RadioBass()
+{
+    HostSlider *gc = new HostSlider("RadioBass", 0, 100, 5);
+    gc->setLabel(QObject::tr("Radio device bass"));
+    gc->setValue(50);
+    gc->setHelpText(QObject::tr("Bass position for radio device."));
+    return gc;
+};
+
+static HostSlider *RadioBalance()
+{
+    HostSlider *gc = new HostSlider("RadioBalance", -100, 100, 5);
+    gc->setLabel(QObject::tr("Radio device balance"));
+    gc->setValue(0);
+    gc->setHelpText(QObject::tr("Balance position for radio device."));
+    return gc;
+};
+
+static HostCheckBox *RadioAudioAsFile()
+{
+    HostCheckBox *gc = new HostCheckBox("RadioAudioAsFile");
+    gc->setLabel(QObject::tr("Open audio device as file"));
+    gc->setValue(false);
+    gc->setHelpText(QObject::tr("Open audio device as raw PCM stream file. "
+                    "Activate this setting to enable ivtv radio audio support."));
+    return gc;
+};
+#endif
+
 //Player Settings
 
 static HostComboBox *PlayMode()
@@ -510,6 +703,50 @@
     general2->addChild(CDWriteSpeed());
     general2->addChild(CDBlankType());
     addChild(general2);
+    
+#ifdef RADIO_SUPPORT    
+    VerticalConfigurationGroup* general3 = new VerticalConfigurationGroup(false);
+    general3->setLabel(QObject::tr("Radio Settings"));
+    general3->addChild(RadioDevice());
+    general3->addChild(RadioLockDevice());
+    general3->addChild(RadioFMinOverride());
+    general3->addChild(RadioFMaxOverride());
+    general3->addChild(RadioScanStep());
+    general3->addChild(RadioSignalMinQuality());
+    general3->addChild(RadioDirectory());
+    
+    HorizontalConfigurationGroup* buttonImporter =
+            new HorizontalConfigurationGroup(false, false);
+    
+    StationsImporter* importer = new StationsImporter();
+    
+    TransButtonSetting* buttonImport = ButtonImport();
+    buttonImporter->addChild(buttonImport);
+    QObject::connect(buttonImport, SIGNAL(pressed()), importer, SLOT(importStations()));
+    
+    TransButtonSetting* buttonScan = ButtonScan();
+    buttonImporter->addChild(buttonScan);
+    QObject::connect(buttonScan, SIGNAL(pressed()), importer, SLOT(scanStations()));
+    
+    general3->addChild(buttonImporter);
+    
+    addChild(general3);
+    
+    VerticalConfigurationGroup* general4 = new VerticalConfigurationGroup(false);
+    general4->setLabel(QObject::tr("Radio Settings (part 2)"));
+    general4->addChild(RadioAudioDevice());
+    general4->addChild(RadioAudioAsFile());
+    general4->addChild(RadioAudioRate());
+    general4->addChild(RadioVolume());
+    general4->addChild(RadioTreble());
+    general4->addChild(RadioBass());
+    general4->addChild(RadioBalance());
+    
+    general4->addChild(RadioUseOldV4L2Calls());
+    general4->addChild(RadioIVTVPassthrough());
+    
+    addChild(general4);
+#endif
 }
 
 MusicPlayerSettings::MusicPlayerSettings()
Index: mythmusic/mythmusic/decoder.cpp
===================================================================
--- mythmusic/mythmusic/decoder.cpp	(revision 7394)
+++ mythmusic/mythmusic/decoder.cpp	(working copy)
@@ -121,6 +121,9 @@
 #ifdef AAC_SUPPORT
         Decoder::registerFactory(new aacDecoderFactory);
 #endif
+#ifdef RADIO_SUPPORT
+        Decoder::registerFactory(new RadioDecoderFactory);
+#endif
     }
 }
 

Index: mythmusic/mythmusic/editmetadata.cpp
===================================================================
--- mythmusic/mythmusic/editmetadata.cpp	(revision 7394)
+++ mythmusic/mythmusic/editmetadata.cpp	(working copy)
@@ -5,6 +5,35 @@
 #include "decoder.h"
 #include "genres.c"
 
+#include "config.h"
+
+#include <math.h>
+
+#ifdef RADIO_SUPPORT
+int intFrequency(int freq)
+{
+    int step = gContext->GetNumSetting("RadioScanStep",         50);
+    int minf = gContext->GetNumSetting("RadioFMinOverride",  87500);
+    int maxf = gContext->GetNumSetting("RadioFMaxOverride", 108000);
+    
+    if (freq < minf || freq > maxf) freq = 87500;
+     
+    // rounding frequency to step
+    freq = (int)(rint(freq / step) * step);
+
+    return freq;
+}
+
+QString strFrequency(int freq)
+{
+    float ffreq = (float) intFrequency(freq) / 1000.0;
+
+    QString s = QString().setNum(ffreq, 'f', 2) + " " + QObject::tr("MHz");
+
+    return s;
+}
+#endif
+
 EditMetadataDialog::EditMetadataDialog(Metadata *source_metadata,
                                  MythMainWindow *parent,
                                  QString window_name,
@@ -487,7 +516,23 @@
 void EditMetadataDialog::saveToDatabase()
 {
     cancelPopup();
+    
+#ifdef RADIO_SUPPORT
+    Decoder *decoder = Decoder::create(m_metadata->Filename(), NULL, NULL, true);
+    if (decoder)
+    {
+        if (decoder->factory()->extension() == ".fmd")
+        {
+            int i     = m_metadata->Track();
 
+            m_metadata->setArtist(strFrequency(i));
+            m_metadata->setAlbum(tr("Radio Stations"));
+            m_metadata->setTrack(intFrequency(i));
+        }
+        delete decoder;
+    }
+#endif
+
     m_metadata->updateDatabase(QString::null);
     *m_sourceMetadata = m_metadata;
     done(1);
@@ -509,6 +554,16 @@
     Decoder *decoder = Decoder::create(m_metadata->Filename(), NULL, NULL, true);
     if (decoder)
     {
+#ifdef RADIO_SUPPORT
+        if (decoder->factory()->extension() == ".fmd")
+        {
+            int i     = m_metadata->Track();
+
+            m_metadata->setArtist(strFrequency(i));
+            m_metadata->setAlbum(tr("Radio Stations"));
+            m_metadata->setTrack(intFrequency(i));
+        }
+#endif
         decoder->commitMetadata(m_metadata);
         delete decoder;
     }
Index: mythmusic/mythmusic/playbackbox.h
===================================================================
--- mythmusic/mythmusic/playbackbox.h	(revision 7394)
+++ mythmusic/mythmusic/playbackbox.h	(working copy)
@@ -9,6 +9,8 @@
 #include <mythtv/dialogbox.h>
 #include <mythtv/audiooutput.h>
 
+#include "config.h"
+
 #include "mainvisual.h"
 #include "metadata.h"
 #include "playlist.h"
@@ -153,6 +155,13 @@
     bool show_whole_tree;
     bool keyboard_accelerators;
     bool volume_control;
+    
+#ifdef RADIO_SUPPORT    
+    bool radio_lock_device;    
+    int radio_max_freq;
+    int radio_min_freq;
+    int radio_freq_step;
+#endif
 
     MythPopupBox *playlist_popup;
 

