mp3.c
branchtrunk
changeset 0 474a1293c3c0
child 2 4c1f7b705009
equal deleted inserted replaced
-1:000000000000 0:474a1293c3c0
       
     1 /*
       
     2  * MP3/MPlayer plugin to VDR (C++)
       
     3  *
       
     4  * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
       
     5  *
       
     6  * This code is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU General Public License
       
     8  * as published by the Free Software Foundation; either version 2
       
     9  * of the License, or (at your option) any later version.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    14  * GNU General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
       
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
       
    20  */
       
    21 
       
    22 #include <stdlib.h>
       
    23 #include <getopt.h>
       
    24 #include <strings.h>
       
    25 #include <typeinfo>
       
    26 
       
    27 #include "common.h"
       
    28 
       
    29 #include <vdr/menuitems.h>
       
    30 #include <vdr/status.h>
       
    31 #include <vdr/plugin.h>
       
    32 #if APIVERSNUM >= 10307
       
    33 #include <vdr/interface.h>
       
    34 #include <vdr/skins.h>
       
    35 #endif
       
    36 
       
    37 #include "setup.h"
       
    38 #include "setup-mp3.h"
       
    39 #include "data-mp3.h"
       
    40 #include "data-src.h"
       
    41 #include "player-mp3.h"
       
    42 #include "menu.h"
       
    43 #include "menu-async.h"
       
    44 #include "decoder.h"
       
    45 #include "i18n.h"
       
    46 #include "version.h"
       
    47 #include "service.h"
       
    48 
       
    49 #ifdef DEBUG
       
    50 #include <mad.h>
       
    51 #endif
       
    52 
       
    53 const char *sourcesSub=0;
       
    54 cFileSources MP3Sources;
       
    55 
       
    56 // --- cMenuSetupMP3 --------------------------------------------------------
       
    57 
       
    58 class cMenuSetupMP3 : public cMenuSetupPage {
       
    59 private:
       
    60   cMP3Setup data;
       
    61   //
       
    62   const char *cddb[3], *disp[2], *scan[3], *bgr[3];
       
    63   const char *aout[AUDIOOUTMODES];
       
    64   int amode, amodes[AUDIOOUTMODES];
       
    65 protected:
       
    66   virtual void Store(void);
       
    67 public:
       
    68   cMenuSetupMP3(void);
       
    69   };
       
    70 
       
    71 cMenuSetupMP3::cMenuSetupMP3(void)
       
    72 {
       
    73   static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" };
       
    74   int numModes=0;
       
    75   aout[numModes]=tr("DVB"); amodes[numModes]=AUDIOOUTMODE_DVB; numModes++;
       
    76 #ifdef WITH_OSS
       
    77   aout[numModes]=tr("OSS"); amodes[numModes]=AUDIOOUTMODE_OSS; numModes++;
       
    78 #endif
       
    79   data=MP3Setup;
       
    80   amode=0;
       
    81   for(int i=0; i<numModes; i++)
       
    82     if(amodes[i]==data.AudioOutMode) { amode=i; break; }
       
    83 
       
    84   SetSection(tr("MP3"));
       
    85   Add(new cMenuEditStraItem(tr("Setup.MP3$Audio output mode"),     &amode,numModes,aout));
       
    86   Add(new cMenuEditBoolItem(tr("Setup.MP3$Audio mode"),            &data.AudioMode, tr("Round"), tr("Dither")));
       
    87   Add(new cMenuEditBoolItem(tr("Setup.MP3$Use 48kHz mode only"),   &data.Only48kHz));
       
    88 #if APIVERSNUM >= 10307
       
    89   disp[0]=tr("classic");
       
    90   disp[1]=tr("via skin");
       
    91   Add(new cMenuEditStraItem(tr("Setup.MP3$Replay display"),        &data.ReplayDisplay, 2, disp));
       
    92 #endif
       
    93   Add(new cMenuEditIntItem( tr("Setup.MP3$Display mode"),          &data.DisplayMode, 1, 3));
       
    94   bgr[0]=tr("Black");
       
    95   bgr[1]=tr("Live");
       
    96   bgr[2]=tr("Images");
       
    97   Add(new cMenuEditStraItem(tr("Setup.MP3$Background mode"),       &data.BackgrMode, 3, bgr));
       
    98   Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial loop mode"),     &data.InitLoopMode));
       
    99   Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial shuffle mode"),  &data.InitShuffleMode));
       
   100   Add(new cMenuEditBoolItem(tr("Setup.MP3$Abort player at end of list"),&data.AbortAtEOL));
       
   101   scan[0]=tr("disabled");
       
   102   scan[1]=tr("ID3 only");
       
   103   scan[2]=tr("ID3 & Level");
       
   104   Add(new cMenuEditStraItem(tr("Setup.MP3$Background scan"),       &data.BgrScan, 3, scan));
       
   105   Add(new cMenuEditBoolItem(tr("Setup.MP3$Editor display mode"),   &data.EditorMode, tr("Filenames"), tr("ID3 names")));
       
   106   Add(new cMenuEditBoolItem(tr("Setup.MP3$Mainmenu mode"),         &data.MenuMode, tr("Playlists"), tr("Browser")));
       
   107   Add(new cMenuEditBoolItem(tr("Setup.MP3$Keep selection menu"),   &data.KeepSelect));
       
   108   Add(new cMenuEditBoolItem(tr("Setup.MP3$Title/Artist order"),    &data.TitleArtistOrder, tr("Normal"), tr("Reversed")));
       
   109   Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),             &data.HideMainMenu));
       
   110   Add(new cMenuEditIntItem( tr("Setup.MP3$Normalizer level"),      &data.TargetLevel, 0, MAX_TARGET_LEVEL));
       
   111   Add(new cMenuEditIntItem( tr("Setup.MP3$Limiter level"),         &data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
       
   112   Add(new cMenuEditBoolItem(tr("Setup.MP3$Use HTTP proxy"),        &data.UseProxy));
       
   113   Add(new cMenuEditStrItem( tr("Setup.MP3$HTTP proxy host"),       data.ProxyHost,MAX_HOSTNAME,allowed));
       
   114   Add(new cMenuEditIntItem( tr("Setup.MP3$HTTP proxy port"),       &data.ProxyPort,1,65535));
       
   115   cddb[0]=tr("disabled");
       
   116   cddb[1]=tr("local only");
       
   117   cddb[2]=tr("local&remote");
       
   118   Add(new cMenuEditStraItem(tr("Setup.MP3$CDDB for CD-Audio"),     &data.UseCddb,3,cddb));
       
   119   Add(new cMenuEditStrItem( tr("Setup.MP3$CDDB server"),           data.CddbHost,MAX_HOSTNAME,allowed));
       
   120   Add(new cMenuEditIntItem( tr("Setup.MP3$CDDB port"),             &data.CddbPort,1,65535));
       
   121 }
       
   122 
       
   123 void cMenuSetupMP3::Store(void)
       
   124 {
       
   125   data.AudioOutMode=amodes[amode];
       
   126 
       
   127   MP3Setup=data;
       
   128   SetupStore("InitLoopMode",     MP3Setup.InitLoopMode   );
       
   129   SetupStore("InitShuffleMode",  MP3Setup.InitShuffleMode);
       
   130   SetupStore("AudioMode",        MP3Setup.AudioMode      );
       
   131   SetupStore("AudioOutMode",     MP3Setup.AudioOutMode   );
       
   132   SetupStore("BgrScan",          MP3Setup.BgrScan        );
       
   133   SetupStore("EditorMode",       MP3Setup.EditorMode     );
       
   134   SetupStore("DisplayMode",      MP3Setup.DisplayMode    );
       
   135   SetupStore("BackgrMode",       MP3Setup.BackgrMode     );
       
   136   SetupStore("MenuMode",         MP3Setup.MenuMode       );
       
   137   SetupStore("TargetLevel",      MP3Setup.TargetLevel    );
       
   138   SetupStore("LimiterLevel",     MP3Setup.LimiterLevel   );
       
   139   SetupStore("Only48kHz",        MP3Setup.Only48kHz      );
       
   140   SetupStore("UseProxy",         MP3Setup.UseProxy       );
       
   141   SetupStore("ProxyHost",        MP3Setup.ProxyHost      );
       
   142   SetupStore("ProxyPort",        MP3Setup.ProxyPort      );
       
   143   SetupStore("UseCddb",          MP3Setup.UseCddb        );
       
   144   SetupStore("CddbHost",         MP3Setup.CddbHost       );
       
   145   SetupStore("CddbPort",         MP3Setup.CddbPort       );
       
   146   SetupStore("AbortAtEOL",       MP3Setup.AbortAtEOL     );
       
   147 #if APIVERSNUM >= 10307
       
   148   SetupStore("ReplayDisplay",    MP3Setup.ReplayDisplay  );
       
   149 #endif
       
   150   SetupStore("HideMainMenu",     MP3Setup.HideMainMenu   );
       
   151   SetupStore("KeepSelect",       MP3Setup.KeepSelect     );
       
   152   SetupStore("TitleArtistOrder", MP3Setup.TitleArtistOrder);
       
   153 }
       
   154 
       
   155 // --- cAsyncStatus ------------------------------------------------------------
       
   156 
       
   157 cAsyncStatus asyncStatus;
       
   158 
       
   159 cAsyncStatus::cAsyncStatus(void)
       
   160 {
       
   161   text=0;
       
   162   changed=false;
       
   163 }
       
   164 
       
   165 cAsyncStatus::~cAsyncStatus()
       
   166 {
       
   167   free((void *)text);
       
   168 }
       
   169 
       
   170 void cAsyncStatus::Set(const char *Text)
       
   171 {
       
   172   Lock();
       
   173   free((void *)text);
       
   174   text=Text ? strdup(Text) : 0;
       
   175   changed=true;
       
   176   Unlock();
       
   177 }
       
   178 
       
   179 const char *cAsyncStatus::Begin(void)
       
   180 {
       
   181   Lock();
       
   182   return text;
       
   183 }
       
   184 
       
   185 void cAsyncStatus::Finish(void)
       
   186 {
       
   187   changed=false;
       
   188   Unlock();
       
   189 }
       
   190 
       
   191 // --- --------------------------------------------------------------------
       
   192 
       
   193 static const char *TitleArtist(const char *title, const char *artist)
       
   194 {
       
   195   static char buf[256]; // clearly not multi-thread save!
       
   196   char *fmt;
       
   197   if(artist && artist[0]) {
       
   198     if(MP3Setup.TitleArtistOrder) fmt="%2$s - %1$s";
       
   199     else  fmt="%s - %s";
       
   200     }
       
   201   else fmt="%s";
       
   202   snprintf(buf,sizeof(buf),fmt,title,artist);
       
   203   return buf;
       
   204 }
       
   205 
       
   206 // --- cMP3Control --------------------------------------------------------
       
   207 
       
   208 #if APIVERSNUM >= 10307
       
   209 #define clrBackground clrGray50
       
   210 #define eDvbColor int
       
   211 #define MAXROWS 120
       
   212 #define INLINE
       
   213 #else
       
   214 #define MAXROWS MAXOSDHEIGHT
       
   215 #define INLINE inline
       
   216 #endif
       
   217 
       
   218 class cMP3Control : public cControl {
       
   219 private:
       
   220 #if APIVERSNUM >= 10307
       
   221   cOsd *osd;
       
   222   const cFont *font;
       
   223   cSkinDisplayReplay *disp;
       
   224 #else
       
   225   bool statusInterfaceOpen;
       
   226 #endif
       
   227   int bw, bh, bwc, fw, fh;
       
   228   //
       
   229   cMP3Player *player;
       
   230   bool visible, shown, bigwin, statusActive;
       
   231   time_t timeoutShow, greentime, oktime;
       
   232   int lastkeytime, number;
       
   233   bool selecting, selecthide;
       
   234   //
       
   235   cMP3PlayInfo *lastMode;
       
   236   time_t fliptime, listtime;
       
   237   int hashlist[MAXROWS];
       
   238   int flip, flipint, top, rows;
       
   239   int lastIndex, lastTotal, lastTop;
       
   240   int framesPerSecond;
       
   241   //
       
   242   bool jumpactive, jumphide, jumpsecs;
       
   243   int jumpmm;
       
   244   //
       
   245   void ShowTimed(int Seconds=0);
       
   246   void ShowProgress(bool open=false, bool bigWin=false);
       
   247   void ShowStatus(bool force);
       
   248   void HideStatus(void);
       
   249   void DisplayInfo(const char *s=0);
       
   250   void JumpDisplay(void);
       
   251   void JumpProcess(eKeys Key);
       
   252   void Jump(void);
       
   253   void Stop(void);
       
   254   INLINE void Write(int x, int y, int w, const char *text, eDvbColor fg=clrWhite, eDvbColor bg=clrBackground);
       
   255   INLINE void Fill(int x, int y, int w, int h, eDvbColor fg);
       
   256   inline void Flush(void);
       
   257 public:
       
   258   cMP3Control(void);
       
   259   virtual ~cMP3Control();
       
   260   virtual eOSState ProcessKey(eKeys Key);
       
   261   virtual void Show(void) { ShowTimed(); }
       
   262   virtual void Hide(void);
       
   263   bool Visible(void) { return visible; }
       
   264   static bool SetPlayList(cPlayList *plist);
       
   265   };
       
   266 
       
   267 cMP3Control::cMP3Control(void)
       
   268 :cControl(player=new cMP3Player)
       
   269 {
       
   270   visible=shown=bigwin=selecting=selecthide=jumpactive=jumphide=statusActive=false;
       
   271   timeoutShow=greentime=oktime=0;
       
   272   lastkeytime=number=0;
       
   273   lastMode=0;
       
   274   framesPerSecond=SecondsToFrames(1);
       
   275 #if APIVERSNUM >= 10307
       
   276   osd=0; disp=0;
       
   277   font=cFont::GetFont(fontOsd);
       
   278 #else
       
   279   statusInterfaceOpen=false;
       
   280 #endif
       
   281 #if APIVERSNUM >= 10338
       
   282   cStatus::MsgReplaying(this,"MP3",0,true);
       
   283 #else
       
   284   cStatus::MsgReplaying(this,"MP3");
       
   285 #endif
       
   286 }
       
   287 
       
   288 cMP3Control::~cMP3Control()
       
   289 {
       
   290   delete lastMode;
       
   291   Hide();
       
   292   Stop();
       
   293 }
       
   294 
       
   295 void cMP3Control::Stop(void)
       
   296 {
       
   297 #if APIVERSNUM >= 10338
       
   298   cStatus::MsgReplaying(this,0,0,false);
       
   299 #else
       
   300   cStatus::MsgReplaying(this,0);
       
   301 #endif
       
   302   delete player; player=0;
       
   303   mgr->Halt();
       
   304   mgr->Flush(); //XXX remove later
       
   305 }
       
   306 
       
   307 bool cMP3Control::SetPlayList(cPlayList *plist)
       
   308 {
       
   309   bool res;
       
   310   cControl *control=cControl::Control();
       
   311   // is there a running MP3 player?
       
   312   if(control && typeid(*control)==typeid(cMP3Control)) {
       
   313     // add songs to running playlist
       
   314     mgr->Add(plist);
       
   315     res=true;
       
   316     }
       
   317   else {
       
   318     mgr->Flush();
       
   319     mgr->Add(plist);
       
   320     cControl::Launch(new cMP3Control);
       
   321     res=false;
       
   322     }
       
   323   delete plist;
       
   324   return res;
       
   325 }
       
   326 
       
   327 void cMP3Control::ShowTimed(int Seconds)
       
   328 {
       
   329   if(!visible) {
       
   330     ShowProgress(true);
       
   331     if(Seconds>0) timeoutShow=time(0)+Seconds;
       
   332     }
       
   333 }
       
   334 
       
   335 void cMP3Control::Hide(void)
       
   336 {
       
   337   HideStatus();
       
   338   if(visible) {
       
   339 #if APIVERSNUM >= 10307
       
   340     delete osd; osd=0;
       
   341     delete disp; disp=0;
       
   342 #else
       
   343     Interface->Close();
       
   344 #endif
       
   345     visible=bigwin=false;
       
   346 #if APIVERSNUM >= 10500
       
   347     SetNeedsFastResponse(false);
       
   348 #else
       
   349     needsFastResponse=false;
       
   350 #endif
       
   351     }
       
   352 }
       
   353 
       
   354 void cMP3Control::ShowStatus(bool force)
       
   355 {
       
   356   if((asyncStatus.Changed() || (force && !statusActive)) && !jumpactive) {
       
   357     const char *text=asyncStatus.Begin();
       
   358     if(text) {
       
   359 #if APIVERSNUM >= 10307
       
   360       if(MP3Setup.ReplayDisplay || !osd) {
       
   361         if(statusActive) Skins.Message(mtStatus,0);
       
   362         Skins.Message(mtStatus,text);
       
   363         }
       
   364       else {
       
   365         if(!statusActive) osd->SaveRegion(0,bh-2*fh,bw-1,bh-fh-1);
       
   366         osd->DrawText(0,bh-2*fh,text,clrBlack,clrCyan,font,bw,fh,taCenter);
       
   367         osd->Flush();
       
   368         }
       
   369 #else
       
   370       if(!Interface->IsOpen()) {
       
   371         Interface->Open(0,-1);
       
   372         statusInterfaceOpen=true;
       
   373         }
       
   374       Interface->Status(text);
       
   375       Interface->Flush();
       
   376 #endif
       
   377       statusActive=true;
       
   378       }
       
   379     else
       
   380       HideStatus();
       
   381     asyncStatus.Finish();
       
   382     }
       
   383 }
       
   384 
       
   385 void cMP3Control::HideStatus(void)
       
   386 {
       
   387   if(statusActive) {
       
   388 #if APIVERSNUM >= 10307
       
   389     if(MP3Setup.ReplayDisplay || !osd)
       
   390       Skins.Message(mtStatus,0);
       
   391     else {
       
   392       osd->RestoreRegion();
       
   393       osd->Flush();
       
   394       }
       
   395 #else
       
   396     if(statusInterfaceOpen) {
       
   397       Interface->Close();
       
   398       statusInterfaceOpen=false;
       
   399       }
       
   400     else {
       
   401       Interface->Status(0);
       
   402       Interface->Flush();
       
   403       }
       
   404 #endif
       
   405     }
       
   406   statusActive=false;
       
   407 }
       
   408 
       
   409 #define CTAB    11 // some tabbing values for the progress display
       
   410 #define CTAB2   5
       
   411 
       
   412 void cMP3Control::Write(int x, int y, int w, const char *text, eDvbColor fg, eDvbColor bg)
       
   413 {
       
   414 #if APIVERSNUM >= 10307
       
   415   if(osd) {
       
   416     //d(printf("write x=%d y=%d w=%d ->",x,y,w))
       
   417     x*=fw; if(x<0) x+=bw;
       
   418     y*=fh; if(y<0) y+=bh;
       
   419     osd->DrawText(x,y,text,fg,bg,font,w*fw);
       
   420     //d(printf(" x=%d y=%d w=%d\n",x,y,w*fw))
       
   421     }
       
   422 #else
       
   423   if(w>0) Fill(x,y,w,1,bg);
       
   424   Interface->Write(x,y,text,fg,bg);
       
   425 #endif
       
   426 }
       
   427 
       
   428 void cMP3Control::Fill(int x, int y, int w, int h, eDvbColor fg)
       
   429 {
       
   430 #if APIVERSNUM >= 10307
       
   431   if(osd) {
       
   432     //d(printf("fill x=%d y=%d w=%d h=%d ->",x,y,w,h))
       
   433     x*=fw; if(x<0) x+=bw;
       
   434     y*=fh; if(y<0) y+=bh;
       
   435     osd->DrawRectangle(x,y,x+w*fw-1,y+h*fh-1,fg);
       
   436     //d(printf(" x=%d y=%d x2=%d y2=%d\n",x,y,x+h*fh-1,y+w*fw-1))
       
   437     }
       
   438 #else
       
   439   Interface->Fill(x,y,w,h,fg);
       
   440 #endif
       
   441 }
       
   442 
       
   443 void cMP3Control::Flush(void)
       
   444 {
       
   445 #if APIVERSNUM >= 10307
       
   446   if(MP3Setup.ReplayDisplay) Skins.Flush();
       
   447   else if(osd) osd->Flush();
       
   448 #else
       
   449   Interface->Flush();
       
   450 #endif
       
   451 }
       
   452 
       
   453 void cMP3Control::ShowProgress(bool open, bool bigWin)
       
   454 {
       
   455   int index, total;
       
   456 
       
   457   if(player->GetIndex(index,total) && total>=0) {
       
   458     if(!visible && open) {
       
   459       HideStatus();
       
   460 #if APIVERSNUM >= 10307
       
   461       if(MP3Setup.ReplayDisplay) {
       
   462         disp=Skins.Current()->DisplayReplay(false);
       
   463         if(!disp) return;
       
   464         bigWin=false;
       
   465         }
       
   466       else {
       
   467         int bt, bp;
       
   468         fw=font->Width(' ')*2;
       
   469         fh=font->Height();
       
   470         if(bigWin) {
       
   471           bw=Setup.OSDWidth;
       
   472           bh=Setup.OSDHeight;
       
   473           bt=0;
       
   474           bp=2*fh;
       
   475           rows=(bh-bp-fh/3)/fh;
       
   476           }
       
   477         else {
       
   478           bw=Setup.OSDWidth;
       
   479           bh=bp=2*fh;
       
   480           bt=Setup.OSDHeight-bh;
       
   481           rows=0;
       
   482           }
       
   483         bwc=bw/fw+1;
       
   484         //d(printf("mp3: bw=%d bh=%d bt=%d bp=%d bwc=%d rows=%d fw=%d fh=%d\n",
       
   485         //  bw,bh,bt,bp,bwc,rows,fw,fh))
       
   486         osd=cOsdProvider::NewOsd(Setup.OSDLeft,Setup.OSDTop+bt);
       
   487         if(!osd) return;
       
   488         if(bigWin) {
       
   489           tArea Areas[] = { { 0,0,bw-1,bh-bp-1,2 }, { 0,bh-bp,bw-1,bh-1,4 } };
       
   490           osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
       
   491           }
       
   492         else {
       
   493           tArea Areas[] = { { 0,0,bw-1,bh-1,4 } };
       
   494           osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
       
   495           }
       
   496         osd->DrawRectangle(0,0,bw-1,bh-1,clrGray50);
       
   497         osd->Flush();
       
   498         }
       
   499 #else
       
   500       fw=cOsd::CellWidth();
       
   501       fh=cOsd::LineHeight();
       
   502       bh=bigWin ? Setup.OSDheight : -2;
       
   503       bw=bwc=Setup.OSDwidth;
       
   504       rows=Setup.OSDheight-3;
       
   505       Interface->Open(bw,bh);
       
   506       Interface->Clear();
       
   507 #endif
       
   508       ShowStatus(true);
       
   509       bigwin=bigWin;
       
   510       visible=true;
       
   511 #if APIVERSNUM >= 10500
       
   512       SetNeedsFastResponse(true);
       
   513 #else
       
   514       needsFastResponse=true;
       
   515 #endif
       
   516       fliptime=listtime=0; flipint=0; flip=-1; top=lastTop=-1; lastIndex=lastTotal=-1;
       
   517       delete lastMode; lastMode=0;
       
   518       }
       
   519 
       
   520     cMP3PlayInfo *mode=new cMP3PlayInfo;
       
   521     bool valid=mgr->Info(-1,mode);
       
   522     bool changed=(!lastMode || mode->Hash!=lastMode->Hash);
       
   523     char buf[256];
       
   524     if(changed) { d(printf("mp3-ctrl: mode change detected\n")) }
       
   525 
       
   526     if(valid) { // send progress to status monitor
       
   527       if(changed || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle) {
       
   528         snprintf(buf,sizeof(buf),"[%c%c] (%d/%d) %s",
       
   529                   mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,TitleArtist(mode->Title,mode->Artist));
       
   530 #if APIVERSNUM >= 10338
       
   531         cStatus::MsgReplaying(this,buf,mode->Filename[0]?mode->Filename:0,true);
       
   532 #else
       
   533         cStatus::MsgReplaying(this,buf);
       
   534 #endif
       
   535         }
       
   536       }
       
   537 
       
   538     if(visible) { // refresh the OSD progress display
       
   539       bool flush=false;
       
   540 
       
   541 #if APIVERSNUM >= 10307
       
   542       if(MP3Setup.ReplayDisplay) {
       
   543         if(!statusActive) {
       
   544           if(total>0) disp->SetProgress(index,total);
       
   545           disp->SetCurrent(IndexToHMSF(index));
       
   546           disp->SetTotal(IndexToHMSF(total));
       
   547           bool Play, Forward;
       
   548           int Speed;
       
   549           if(GetReplayMode(Play,Forward,Speed)) 
       
   550             disp->SetMode(Play, Forward, Speed);
       
   551           flush=true;
       
   552           }
       
   553         }
       
   554       else {
       
   555 #endif
       
   556         if(!selecting && changed && !statusActive) {
       
   557           snprintf(buf,sizeof(buf),"(%d/%d)",mode->Num,mode->MaxNum);
       
   558           Write(0,-2,CTAB,buf);
       
   559           flush=true;
       
   560           }
       
   561 
       
   562         if(!lastMode || mode->Loop!=lastMode->Loop) {
       
   563           if(mode->Loop) Write(-4,-1,0,"L",clrBlack,clrYellow);
       
   564           else Fill(-4,-1,2,1,clrBackground);
       
   565           flush=true;
       
   566           }
       
   567         if(!lastMode || mode->Shuffle!=lastMode->Shuffle) {
       
   568           if(mode->Shuffle) Write(-2,-1,0,"S",clrWhite,clrRed);
       
   569           else Fill(-2,-1,2,1,clrBackground);
       
   570           flush=true;
       
   571           }
       
   572 
       
   573         index/=framesPerSecond; total/=framesPerSecond;
       
   574         if(index!=lastIndex || total!=lastTotal) {
       
   575           if(total>0) {
       
   576 #if APIVERSNUM >= 10307
       
   577             cProgressBar ProgressBar(bw-(CTAB+CTAB2)*fw,fh,index,total);
       
   578             osd->DrawBitmap(CTAB*fw,bh-fh,ProgressBar);
       
   579 #else
       
   580             cProgressBar ProgressBar((bw-CTAB-CTAB2)*fw,fh,index,total);
       
   581             Interface->SetBitmap(CTAB*fw,(abs(bh)-1)*fh,ProgressBar);
       
   582 #endif
       
   583             }
       
   584           snprintf(buf,sizeof(buf),total?"%02d:%02d/%02d:%02d":"%02d:%02d",index/60,index%60,total/60,total%60);
       
   585           Write(0,-1,11,buf);
       
   586           flush=true;
       
   587           }
       
   588 #if APIVERSNUM >= 10307
       
   589         }
       
   590 #endif
       
   591 
       
   592       if(!jumpactive) {
       
   593         bool doflip=false;
       
   594         if(MP3Setup.ReplayDisplay && (!lastMode || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle))
       
   595           doflip=true;
       
   596         if(!valid || changed) {
       
   597           fliptime=time(0); flip=0;
       
   598 	  doflip=true;
       
   599 	  }
       
   600         else if(time(0)>fliptime+flipint) {
       
   601 	  fliptime=time(0);
       
   602 	  flip++; if(flip>=MP3Setup.DisplayMode) flip=0;
       
   603           doflip=true;
       
   604 	  }
       
   605         if(doflip) {
       
   606           buf[0]=0;
       
   607           switch(flip) {
       
   608 	    default:
       
   609 	      flip=0;
       
   610 	      // fall through
       
   611 	    case 0:
       
   612 	      snprintf(buf,sizeof(buf),"%s",TitleArtist(mode->Title,mode->Artist));
       
   613 	      flipint=6;
       
   614 	      break;
       
   615 	    case 1:
       
   616               if(mode->Album[0]) {
       
   617       	        snprintf(buf,sizeof(buf),mode->Year>0?"from: %s (%d)":"from: %s",mode->Album,mode->Year);
       
   618 	        flipint=4;
       
   619 	        }
       
   620               else fliptime=0;
       
   621               break;
       
   622 	    case 2:
       
   623               if(mode->MaxBitrate>0)
       
   624                 snprintf(buf,sizeof(buf),"%.1f kHz, %d-%d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->MaxBitrate/1000,mode->SMode);
       
   625               else
       
   626                 snprintf(buf,sizeof(buf),"%.1f kHz, %d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->SMode);
       
   627 	      flipint=3;
       
   628 	      break;
       
   629 	    }
       
   630           if(buf[0]) {
       
   631 #if APIVERSNUM >= 10307
       
   632             if(MP3Setup.ReplayDisplay) {
       
   633               char buf2[256];
       
   634               snprintf(buf2,sizeof(buf2),"[%c%c] (%d/%d) %s",
       
   635                        mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,buf);
       
   636               disp->SetTitle(buf2);
       
   637               flush=true;
       
   638               }
       
   639             else {
       
   640 #endif
       
   641               if(!statusActive) {
       
   642                 DisplayInfo(buf);
       
   643                 flush=true;
       
   644                 }
       
   645               else { d(printf("mp3-ctrl: display info skip due to status active\n")) }
       
   646 #if APIVERSNUM >= 10307
       
   647               }
       
   648 #endif
       
   649             }
       
   650           }
       
   651         }
       
   652 
       
   653       if(bigwin) {
       
   654         bool all=(top!=lastTop || changed);
       
   655         if(all || time(0)>listtime+2) {
       
   656           int num=(top>0 && mode->Num==lastMode->Num) ? top : mode->Num - rows/2;
       
   657           if(num+rows>mode->MaxNum) num=mode->MaxNum-rows+1;
       
   658           if(num<1) num=1;
       
   659           top=num;
       
   660           for(int i=0 ; i<rows && i<MAXROWS && num<=mode->MaxNum ; i++,num++) {
       
   661             cMP3PlayInfo pi;
       
   662             mgr->Info(num,&pi); if(!pi.Title[0]) break;
       
   663             snprintf(buf,sizeof(buf),"%d.\t%s",num,TitleArtist(pi.Title,pi.Artist));
       
   664             eDvbColor fg=clrWhite, bg=clrBackground;
       
   665             int hash=MakeHash(buf);
       
   666             if(num==mode->Num) { fg=clrBlack; bg=clrCyan; hash=(hash^77) + 23; }
       
   667             if(all || hash!=hashlist[i]) {
       
   668               char *s=rindex(buf,'\t');
       
   669               if(s) {
       
   670                 *s++=0;
       
   671                 Write(0,i,5,buf,fg,bg);
       
   672                 Write(5,i,bwc-5,s,fg,bg);
       
   673                 }
       
   674               else
       
   675                 Write(0,i,bwc,buf,fg,bg);
       
   676               flush=true;
       
   677               hashlist[i]=hash;
       
   678               }
       
   679             }
       
   680           listtime=time(0); lastTop=top;
       
   681           }
       
   682         }
       
   683 
       
   684       if(flush) Flush();
       
   685       }
       
   686 
       
   687     lastIndex=index; lastTotal=total;
       
   688     delete lastMode; lastMode=mode;
       
   689     }
       
   690 }
       
   691 
       
   692 void cMP3Control::DisplayInfo(const char *s)
       
   693 {
       
   694   if(s) Write(CTAB,-2,bwc-CTAB,s);
       
   695   else Fill(CTAB,-2,bwc-CTAB,1,clrBackground);
       
   696 }
       
   697 
       
   698 void cMP3Control::JumpDisplay(void)
       
   699 {
       
   700   char buf[64];
       
   701   const char *j=tr("Jump: "), u=jumpsecs?'s':'m';
       
   702   if(!jumpmm) sprintf(buf,"%s- %c",  j,u);
       
   703   else        sprintf(buf,"%s%d- %c",j,jumpmm,u);
       
   704 #if APIVERSNUM >= 10307
       
   705   if(MP3Setup.ReplayDisplay) {
       
   706     disp->SetJump(buf);
       
   707     }
       
   708   else {
       
   709 #endif
       
   710     DisplayInfo(buf);
       
   711 #if APIVERSNUM >= 10307
       
   712     }
       
   713 #endif
       
   714 }
       
   715 
       
   716 void cMP3Control::JumpProcess(eKeys Key)
       
   717 {
       
   718  int n=Key-k0, d=jumpsecs?1:60;
       
   719   switch (Key) {
       
   720     case k0 ... k9:
       
   721       if(jumpmm*10+n <= lastTotal/d) jumpmm=jumpmm*10+n;
       
   722       JumpDisplay();
       
   723       break;
       
   724     case kBlue:
       
   725       jumpsecs=!jumpsecs;
       
   726       JumpDisplay();
       
   727       break;
       
   728     case kPlay:
       
   729     case kUp:
       
   730       jumpmm-=lastIndex/d;
       
   731       // fall through
       
   732     case kFastRew:
       
   733     case kFastFwd:
       
   734     case kLeft:
       
   735     case kRight:
       
   736       player->SkipSeconds(jumpmm*d * ((Key==kLeft || Key==kFastRew) ? -1:1));
       
   737       // fall through
       
   738     default:
       
   739       jumpactive=false;
       
   740       break;
       
   741     }
       
   742 
       
   743   if(!jumpactive) {
       
   744     if(jumphide) Hide();
       
   745 #if APIVERSNUM >= 10307
       
   746     else if(MP3Setup.ReplayDisplay) disp->SetJump(0);
       
   747 #endif
       
   748     }
       
   749 }
       
   750 
       
   751 void cMP3Control::Jump(void)
       
   752 {
       
   753   jumpmm=0; jumphide=jumpsecs=false;
       
   754   if(!visible) {
       
   755     ShowTimed(); if(!visible) return;
       
   756     jumphide=true;
       
   757     }
       
   758   JumpDisplay();
       
   759   jumpactive=true; fliptime=0; flip=-1;
       
   760 }
       
   761 
       
   762 eOSState cMP3Control::ProcessKey(eKeys Key)
       
   763 {
       
   764   if(!player->Active()) return osEnd;
       
   765 
       
   766   if(visible && timeoutShow && time(0)>timeoutShow) { Hide(); timeoutShow=0; }
       
   767   ShowProgress();
       
   768 #if APIVERSNUM >= 10307
       
   769   ShowStatus(Key==kNone && !Skins.IsOpen());
       
   770 #else
       
   771   ShowStatus(Key==kNone && !Interface->IsOpen());
       
   772 #endif
       
   773 
       
   774   if(jumpactive && Key!=kNone) { JumpProcess(Key); return osContinue; }
       
   775 
       
   776   switch(Key) {
       
   777     case kUp:
       
   778     case kUp|k_Repeat:
       
   779 #if APIVERSNUM >= 10347
       
   780     case kNext:
       
   781     case kNext|k_Repeat:    
       
   782 #endif
       
   783       mgr->Next(); player->Play();
       
   784       break;
       
   785     case kDown:
       
   786     case kDown|k_Repeat:
       
   787 #if APIVERSNUM >= 10347
       
   788     case kPrev:
       
   789     case kPrev|k_Repeat:
       
   790 #endif
       
   791       if(!player->PrevCheck()) mgr->Prev();
       
   792       player->Play();
       
   793       break;
       
   794     case kLeft:
       
   795     case kLeft|k_Repeat:
       
   796       if(bigwin) {
       
   797         if(top>0) { top-=rows; if(top<1) top=1; }
       
   798         break;
       
   799         }
       
   800       // fall through
       
   801     case kFastRew:
       
   802     case kFastRew|k_Repeat:
       
   803       if(!player->IsStream()) player->SkipSeconds(-JUMPSIZE);
       
   804       break;
       
   805     case kRight:
       
   806     case kRight|k_Repeat:
       
   807       if(bigwin) {
       
   808         if(top>0) top+=rows;
       
   809         break;
       
   810         }
       
   811       // fall through
       
   812     case kFastFwd:
       
   813     case kFastFwd|k_Repeat:
       
   814       if(!player->IsStream()) player->SkipSeconds(JUMPSIZE);
       
   815       break;
       
   816     case kRed:
       
   817       if(!player->IsStream()) Jump();
       
   818       break;
       
   819     case kGreen:
       
   820       if(lastMode) {
       
   821         if(time(0)>greentime) {
       
   822           if(lastMode->Loop || (!lastMode->Loop && !lastMode->Shuffle)) mgr->ToggleLoop();
       
   823           if(lastMode->Shuffle) mgr->ToggleShuffle();
       
   824           }
       
   825         else {
       
   826           if(!lastMode->Loop) mgr->ToggleLoop();
       
   827           else if(!lastMode->Shuffle) mgr->ToggleShuffle();
       
   828           else mgr->ToggleLoop();
       
   829           }
       
   830         greentime=time(0)+MULTI_TIMEOUT;
       
   831         }
       
   832       break;
       
   833     case kPlay:
       
   834       player->Play();
       
   835       break;
       
   836     case kPause:
       
   837     case kYellow:
       
   838       if(!player->IsStream()) player->Pause();
       
   839       break;
       
   840     case kStop:
       
   841     case kBlue:
       
   842       Hide();
       
   843       Stop();
       
   844       return osEnd;
       
   845     case kBack:
       
   846       Hide();
       
   847 #if APIVERSNUM >= 10332
       
   848       cRemote::CallPlugin(i18n_name);
       
   849       return osBack;
       
   850 #else
       
   851       return osEnd;
       
   852 #endif
       
   853 
       
   854     case k0 ... k9:
       
   855       number=number*10+Key-k0;
       
   856       if(lastMode && number>0 && number<=lastMode->MaxNum) {
       
   857         if(!visible) { ShowTimed(); selecthide=true; }
       
   858         selecting=true; lastkeytime=time_ms();
       
   859         char buf[32];
       
   860 #if APIVERSNUM >= 10307
       
   861         if(MP3Setup.ReplayDisplay) {
       
   862           snprintf(buf,sizeof(buf),"%s%d-/%d",tr("Jump: "),number,lastMode->MaxNum);
       
   863           disp->SetJump(buf);
       
   864           }
       
   865         else {
       
   866 #endif
       
   867           snprintf(buf,sizeof(buf),"(%d-/%d)",number,lastMode->MaxNum);
       
   868           Write(0,-2,CTAB,buf);
       
   869 #if APIVERSNUM >= 10307
       
   870           }
       
   871 #endif
       
   872         Flush();
       
   873         break;
       
   874         }
       
   875       number=0; lastkeytime=0;
       
   876       // fall through
       
   877     case kNone:
       
   878       if(selecting && time_ms()-lastkeytime>SELECT_TIMEOUT) {
       
   879         if(number>0) { mgr->Goto(number); player->Play();  }
       
   880         if(selecthide) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
       
   881         if(lastMode) lastMode->Hash=-1;
       
   882         number=0; selecting=selecthide=false;
       
   883 #if APIVERSNUM >= 10307
       
   884         if(MP3Setup.ReplayDisplay) disp->SetJump(0);
       
   885 #endif
       
   886         }
       
   887       break;
       
   888     case kOk:
       
   889       if(time(0)>oktime || MP3Setup.ReplayDisplay) {
       
   890         visible ? Hide() : ShowTimed();
       
   891         }
       
   892       else {
       
   893         if(visible && !bigwin) { Hide(); ShowProgress(true,true); }
       
   894         else { Hide(); ShowTimed(); }
       
   895         }
       
   896       oktime=time(0)+MULTI_TIMEOUT;
       
   897       ShowStatus(true);
       
   898       break;
       
   899     default:
       
   900       return osUnknown;
       
   901     }
       
   902   return osContinue;
       
   903 }
       
   904 
       
   905 // --- cMenuID3Info ------------------------------------------------------------
       
   906 
       
   907 class cMenuID3Info : public cOsdMenu {
       
   908 private:
       
   909   cOsdItem *Item(const char *name, const char *text);
       
   910   cOsdItem *Item(const char *name, const char *format, const float num);
       
   911   void Build(cSongInfo *info, const char *name);
       
   912 public:
       
   913   cMenuID3Info(cSong *song);
       
   914   cMenuID3Info(cSongInfo *si, const char *name);
       
   915   virtual eOSState ProcessKey(eKeys Key);
       
   916   };
       
   917 
       
   918 cMenuID3Info::cMenuID3Info(cSong *song)
       
   919 :cOsdMenu(tr("ID3 information"),12)
       
   920 {
       
   921   Build(song->Info(),song->Name());
       
   922 }
       
   923 
       
   924 cMenuID3Info::cMenuID3Info(cSongInfo *si, const char *name)
       
   925 :cOsdMenu(tr("ID3 information"),12)
       
   926 {
       
   927   Build(si,name);
       
   928 }
       
   929 
       
   930 void cMenuID3Info::Build(cSongInfo *si, const char *name)
       
   931 {
       
   932   if(si) {
       
   933     Item(tr("Filename"),name);
       
   934     if(si->HasInfo() && si->Total>0) {
       
   935       char *buf=0;
       
   936       asprintf(&buf,"%02d:%02d",si->Total/60,si->Total%60);
       
   937       Item(tr("Length"),buf);
       
   938       free(buf);
       
   939       Item(tr("Title"),si->Title);
       
   940       Item(tr("Artist"),si->Artist);
       
   941       Item(tr("Album"),si->Album);
       
   942       Item(tr("Year"),0,(float)si->Year);
       
   943       Item(tr("Samplerate"),"%.1f kHz",si->SampleFreq/1000.0);
       
   944       Item(tr("Bitrate"),"%.f kbit/s",si->Bitrate/1000.0);
       
   945       Item(tr("Channels"),0,(float)si->Channels);
       
   946       }
       
   947     Display();
       
   948     }
       
   949 }
       
   950 
       
   951 cOsdItem *cMenuID3Info::Item(const char *name, const char *format, const float num)
       
   952 {
       
   953   cOsdItem *item;
       
   954   if(num>=0.0) {
       
   955     char *buf=0;
       
   956     asprintf(&buf,format?format:"%.f",num);
       
   957     item=Item(name,buf);
       
   958     free(buf);
       
   959     }
       
   960   else item=Item(name,"");
       
   961   return item;
       
   962 }
       
   963 
       
   964 cOsdItem *cMenuID3Info::Item(const char *name, const char *text)
       
   965 {
       
   966   char *buf=0;
       
   967   asprintf(&buf,"%s:\t%s",name,text?text:"");
       
   968   cOsdItem *item = new cOsdItem(buf,osBack);
       
   969 #if APIVERSNUM >= 10307
       
   970   item->SetSelectable(false);
       
   971 #else
       
   972   item->SetColor(clrWhite, clrBackground);
       
   973 #endif
       
   974   free(buf);
       
   975   Add(item); return item;
       
   976 }
       
   977 
       
   978 eOSState cMenuID3Info::ProcessKey(eKeys Key)
       
   979 {
       
   980   eOSState state = cOsdMenu::ProcessKey(Key);
       
   981 
       
   982   if(state==osUnknown) {
       
   983      switch(Key) {
       
   984        case kRed:
       
   985        case kGreen:
       
   986        case kYellow:
       
   987        case kBlue:   return osContinue;
       
   988        case kMenu:   return osEnd;
       
   989        default: break;
       
   990        }
       
   991      }
       
   992   return state;
       
   993 }
       
   994 
       
   995 // --- cMenuInstantBrowse -------------------------------------------------------
       
   996 
       
   997 class cMenuInstantBrowse : public cMenuBrowse {
       
   998 private:
       
   999   const char *selecttext, *alltext;
       
  1000   virtual void SetButtons(void);
       
  1001   virtual eOSState ID3Info(void);
       
  1002 public:
       
  1003   cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext);
       
  1004   virtual eOSState ProcessKey(eKeys Key);
       
  1005   };
       
  1006 
       
  1007 cMenuInstantBrowse::cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext)
       
  1008 :cMenuBrowse(Source,true,true,tr("Directory browser"),excl_br)
       
  1009 {
       
  1010   selecttext=Selecttext; alltext=Alltext;
       
  1011   SetButtons();
       
  1012 }
       
  1013 
       
  1014 void cMenuInstantBrowse::SetButtons(void)
       
  1015 {
       
  1016   SetHelp(selecttext, currentdir?tr("Parent"):0, currentdir?0:alltext, tr("ID3 info"));
       
  1017   Display();
       
  1018 }
       
  1019 
       
  1020 eOSState cMenuInstantBrowse::ID3Info(void)
       
  1021 {
       
  1022   cFileObj *item=CurrentItem();
       
  1023   if(item && item->Type()==otFile) {
       
  1024     cSong *song=new cSong(item);
       
  1025     cSongInfo *si;
       
  1026     if(song && (si=song->Info())) {
       
  1027       AddSubMenu(new cMenuID3Info(si,item->Path()));
       
  1028       }
       
  1029     delete song;
       
  1030     }
       
  1031   return osContinue;
       
  1032 }
       
  1033 
       
  1034 eOSState cMenuInstantBrowse::ProcessKey(eKeys Key)
       
  1035 {
       
  1036   eOSState state=cOsdMenu::ProcessKey(Key);
       
  1037   if(state==osUnknown) {
       
  1038      switch (Key) {
       
  1039        case kYellow: lastselect=new cFileObj(source,0,0,otBase);
       
  1040                      return osBack;
       
  1041        default: break;
       
  1042        }
       
  1043      }
       
  1044   if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
       
  1045   return state;
       
  1046 }
       
  1047 
       
  1048 // --- cMenuPlayListItem -------------------------------------------------------
       
  1049 
       
  1050 class cMenuPlayListItem : public cOsdItem {
       
  1051   private:
       
  1052   bool showID3;
       
  1053   cSong *song;
       
  1054 public:
       
  1055   cMenuPlayListItem(cSong *Song, bool showid3);
       
  1056   cSong *Song(void) { return song; }
       
  1057   virtual void Set(void);
       
  1058   void Set(bool showid3);
       
  1059   };
       
  1060 
       
  1061 cMenuPlayListItem::cMenuPlayListItem(cSong *Song, bool showid3)
       
  1062 {
       
  1063   song=Song;
       
  1064   Set(showid3);
       
  1065 }
       
  1066 
       
  1067 void cMenuPlayListItem::Set(bool showid3)
       
  1068 {
       
  1069   showID3=showid3;
       
  1070   Set();
       
  1071 }
       
  1072 
       
  1073 void cMenuPlayListItem::Set(void)
       
  1074 {
       
  1075   char *buffer=0;
       
  1076   cSongInfo *si=song->Info(false);
       
  1077   if(showID3 && !si) si=song->Info();
       
  1078   if(showID3 && si && si->Title)
       
  1079     asprintf(&buffer, "%d.\t%s",song->Index()+1,TitleArtist(si->Title,si->Artist));
       
  1080   else
       
  1081     asprintf(&buffer, "%d.\t<%s>",song->Index()+1,song->Name());
       
  1082   SetText(buffer,false);
       
  1083 }
       
  1084 
       
  1085 // --- cMenuPlayList ------------------------------------------------------
       
  1086 
       
  1087 class cMenuPlayList : public cOsdMenu {
       
  1088 private:
       
  1089   cPlayList *playlist;
       
  1090   bool browsing, showid3;
       
  1091   void Buttons(void);
       
  1092   void Refresh(bool all = false);
       
  1093   void Add(void);
       
  1094   virtual void Move(int From, int To);
       
  1095   eOSState Remove(void);
       
  1096   eOSState ShowID3(void);
       
  1097   eOSState ID3Info(void);
       
  1098 public:
       
  1099   cMenuPlayList(cPlayList *Playlist);
       
  1100   virtual eOSState ProcessKey(eKeys Key);
       
  1101   };
       
  1102 
       
  1103 cMenuPlayList::cMenuPlayList(cPlayList *Playlist)
       
  1104 :cOsdMenu(tr("Playlist editor"),4)
       
  1105 {
       
  1106   browsing=showid3=false;
       
  1107   playlist=Playlist;
       
  1108   if(MP3Setup.EditorMode) showid3=true;
       
  1109 
       
  1110   cSong *mp3 = playlist->First();
       
  1111   while(mp3) {
       
  1112     cOsdMenu::Add(new cMenuPlayListItem(mp3,showid3));
       
  1113     mp3 = playlist->cList<cSong>::Next(mp3);
       
  1114     }
       
  1115   Buttons(); Display();
       
  1116 }
       
  1117 
       
  1118 void cMenuPlayList::Buttons(void)
       
  1119 {
       
  1120   SetHelp(tr("Add"), showid3?tr("Filenames"):tr("ID3 names"), tr("Remove"), tr(BUTTON"Mark"));
       
  1121 }
       
  1122 
       
  1123 void cMenuPlayList::Refresh(bool all)
       
  1124 {
       
  1125   cMenuPlayListItem *cur=(cMenuPlayListItem *)((all || Count()<2) ? First() : Get(Current()));
       
  1126   while(cur) {
       
  1127     cur->Set(showid3);
       
  1128     cur=(cMenuPlayListItem *)Next(cur);
       
  1129     }
       
  1130 }
       
  1131 
       
  1132 void cMenuPlayList::Add(void)
       
  1133 {
       
  1134   cFileObj *item=cMenuInstantBrowse::GetSelected();
       
  1135   if(item) {
       
  1136     Status(tr("Scanning directory..."));
       
  1137     cInstantPlayList *newpl=new cInstantPlayList(item);
       
  1138     if(newpl->Load()) {
       
  1139       if(newpl->Count()) {
       
  1140         if(newpl->Count()==1 || Interface->Confirm(tr("Add recursivly?"))) {
       
  1141           cSong *mp3=newpl->First();
       
  1142           while(mp3) {
       
  1143             cSong *n=new cSong(mp3);
       
  1144             if(Count()>0) {
       
  1145               cMenuPlayListItem *current=(cMenuPlayListItem *)Get(Current());
       
  1146               playlist->Add(n,current->Song());
       
  1147               cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true,current);
       
  1148               }
       
  1149             else {
       
  1150               playlist->Add(n);
       
  1151               cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true);
       
  1152               }
       
  1153             mp3=newpl->cList<cSong>::Next(mp3);
       
  1154             }
       
  1155           playlist->Save();
       
  1156           Refresh(); Display();
       
  1157           }
       
  1158         }
       
  1159       else Error(tr("Empty directory!"));
       
  1160       }
       
  1161     else Error(tr("Error scanning directory!"));
       
  1162     delete newpl;
       
  1163     Status(0);
       
  1164     }
       
  1165 }
       
  1166 
       
  1167 void cMenuPlayList::Move(int From, int To)
       
  1168 {
       
  1169   playlist->Move(From,To); playlist->Save();
       
  1170   cOsdMenu::Move(From,To);
       
  1171   Refresh(true); Display();
       
  1172 }
       
  1173 
       
  1174 eOSState cMenuPlayList::ShowID3(void)
       
  1175 {
       
  1176   showid3=!showid3;
       
  1177   Buttons(); Refresh(true); Display();
       
  1178   return osContinue;
       
  1179 }
       
  1180 
       
  1181 eOSState cMenuPlayList::ID3Info(void)
       
  1182 {
       
  1183   if(Count()>0) {
       
  1184     cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
       
  1185     AddSubMenu(new cMenuID3Info(current->Song()));
       
  1186     }
       
  1187   return osContinue;
       
  1188 }
       
  1189 
       
  1190 eOSState cMenuPlayList::Remove(void)
       
  1191 {
       
  1192   if(Count()>0) {
       
  1193     cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
       
  1194     if(Interface->Confirm(tr("Remove entry?"))) {
       
  1195       playlist->Del(current->Song()); playlist->Save();
       
  1196       cOsdMenu::Del(Current());
       
  1197       Refresh(); Display();
       
  1198       }
       
  1199     }
       
  1200   return osContinue;
       
  1201 }
       
  1202 
       
  1203 eOSState cMenuPlayList::ProcessKey(eKeys Key)
       
  1204 {
       
  1205   eOSState state = cOsdMenu::ProcessKey(Key);
       
  1206 
       
  1207   if(browsing && !HasSubMenu() && state==osContinue) { Add(); browsing=false; }
       
  1208 
       
  1209   if(state==osUnknown) {
       
  1210      switch(Key) {
       
  1211        case kOk:     return ID3Info();
       
  1212        case kRed:    browsing=true;
       
  1213                      return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr("Add"),tr("Add all")));
       
  1214        case kGreen:  return ShowID3();
       
  1215        case kYellow: return Remove();
       
  1216        case kBlue:   Mark(); return osContinue;
       
  1217        case kMenu:   return osEnd;
       
  1218        default: break;
       
  1219        }
       
  1220      }
       
  1221   return state;
       
  1222 }
       
  1223 
       
  1224 // --- cPlaylistRename --------------------------------------------------------
       
  1225 
       
  1226 class cPlaylistRename : public cOsdMenu {
       
  1227 private:
       
  1228   static char *newname;
       
  1229   const char *oldname;
       
  1230   char data[64];
       
  1231 public:
       
  1232   cPlaylistRename(const char *Oldname);
       
  1233   virtual eOSState ProcessKey(eKeys Key);
       
  1234   static const char *GetNewname(void) { return newname; }
       
  1235   };
       
  1236 
       
  1237 char *cPlaylistRename::newname = NULL;
       
  1238 
       
  1239 cPlaylistRename::cPlaylistRename(const char *Oldname)
       
  1240 :cOsdMenu(tr("Rename playlist"), 15)
       
  1241 {
       
  1242   free(newname); newname=0;
       
  1243 
       
  1244   oldname=Oldname;
       
  1245   char *buf=NULL;
       
  1246   asprintf(&buf,"%s\t%s",tr("Old name:"),oldname);
       
  1247   cOsdItem *old = new cOsdItem(buf,osContinue);
       
  1248 #if APIVERSNUM >= 10307
       
  1249   old->SetSelectable(false);
       
  1250 #else
       
  1251   old->SetColor(clrWhite, clrBackground);
       
  1252 #endif
       
  1253   Add(old);
       
  1254   free(buf);
       
  1255 
       
  1256   data[0]=0;
       
  1257   Add(new cMenuEditStrItem( tr("New name"), data, sizeof(data)-1, tr(FileNameChars)),true);
       
  1258 }
       
  1259 
       
  1260 eOSState cPlaylistRename::ProcessKey(eKeys Key)
       
  1261 {
       
  1262   eOSState state = cOsdMenu::ProcessKey(Key);
       
  1263 
       
  1264   if (state == osUnknown) {
       
  1265      switch (Key) {
       
  1266        case kOk:     if(data[0] && strcmp(data,oldname)) newname=strdup(data);
       
  1267                      return osBack;
       
  1268        case kRed:
       
  1269        case kGreen:
       
  1270        case kYellow:
       
  1271        case kBlue:   return osContinue;
       
  1272        default: break;
       
  1273        }
       
  1274      }
       
  1275   return state;
       
  1276 }
       
  1277 
       
  1278 // --- cMenuMP3Item -----------------------------------------------------
       
  1279 
       
  1280 class cMenuMP3Item : public cOsdItem {
       
  1281   private:
       
  1282   cPlayList *playlist;
       
  1283   virtual void Set(void);
       
  1284 public:
       
  1285   cMenuMP3Item(cPlayList *PlayList);
       
  1286   cPlayList *List(void) { return playlist; }
       
  1287   };
       
  1288 
       
  1289 cMenuMP3Item::cMenuMP3Item(cPlayList *PlayList)
       
  1290 {
       
  1291   playlist=PlayList;
       
  1292   Set();
       
  1293 }
       
  1294 
       
  1295 void cMenuMP3Item::Set(void)
       
  1296 {
       
  1297   char *buffer=0;
       
  1298   asprintf(&buffer," %s",playlist->BaseName());
       
  1299   SetText(buffer,false);
       
  1300 }
       
  1301 
       
  1302 // --- cMenuMP3 --------------------------------------------------------
       
  1303 
       
  1304 class cMenuMP3 : public cOsdMenu {
       
  1305 private:
       
  1306   cPlayLists *lists;
       
  1307   bool renaming, sourcing, instanting;
       
  1308   int buttonnum;
       
  1309   eOSState Play(void);
       
  1310   eOSState Edit(void);
       
  1311   eOSState New(void);
       
  1312   eOSState Delete(void);
       
  1313   eOSState Rename(bool second);
       
  1314   eOSState Source(bool second);
       
  1315   eOSState Instant(bool second);
       
  1316   void ScanLists(void);
       
  1317   eOSState SetButtons(int num);
       
  1318 public:
       
  1319   cMenuMP3(void);
       
  1320   ~cMenuMP3(void);
       
  1321   virtual eOSState ProcessKey(eKeys Key);
       
  1322   };
       
  1323 
       
  1324 cMenuMP3::cMenuMP3(void)
       
  1325 :cOsdMenu(tr("MP3"))
       
  1326 {
       
  1327   renaming=sourcing=instanting=false;
       
  1328   lists=new cPlayLists;
       
  1329   ScanLists(); SetButtons(1);
       
  1330   if(MP3Setup.MenuMode) Instant(false);
       
  1331 }
       
  1332 
       
  1333 cMenuMP3::~cMenuMP3(void)
       
  1334 {
       
  1335   delete lists;
       
  1336 }
       
  1337 
       
  1338 eOSState cMenuMP3::SetButtons(int num)
       
  1339 {
       
  1340   switch(num) {
       
  1341     case 1:
       
  1342       SetHelp(tr(BUTTON"Edit"), tr("Source"), tr("Browse"), ">>");
       
  1343       break;
       
  1344     case 2:
       
  1345       SetHelp("<<", tr(BUTTON"New"), tr(BUTTON"Delete"), tr("Rename"));
       
  1346       break;
       
  1347     }
       
  1348   buttonnum=num; Display();
       
  1349   return osContinue;
       
  1350 }
       
  1351 
       
  1352 void cMenuMP3::ScanLists(void)
       
  1353 {
       
  1354   Clear();
       
  1355   Status(tr("Scanning playlists..."));
       
  1356   bool res=lists->Load(MP3Sources.GetSource());
       
  1357   Status(0);
       
  1358   if(res) {
       
  1359     cPlayList *plist=lists->First();
       
  1360     while(plist) {
       
  1361       Add(new cMenuMP3Item(plist));
       
  1362       plist=lists->Next(plist);
       
  1363       }
       
  1364     }
       
  1365   else Error(tr("Error scanning playlists!"));
       
  1366 }
       
  1367 
       
  1368 eOSState cMenuMP3::Delete(void)
       
  1369 {
       
  1370   if(Count()>0) {
       
  1371     if(Interface->Confirm(tr("Delete playlist?")) &&
       
  1372        Interface->Confirm(tr("Are you sure?")) ) {
       
  1373       cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
       
  1374       if(plist->Delete()) {
       
  1375         lists->Del(plist);
       
  1376         cOsdMenu::Del(Current());
       
  1377         Display();
       
  1378         }
       
  1379       else Error(tr("Error deleting playlist!"));
       
  1380       }
       
  1381     }
       
  1382   return osContinue;
       
  1383 }
       
  1384 
       
  1385 eOSState cMenuMP3::New(void)
       
  1386 {
       
  1387   cPlayList *plist=new cPlayList(MP3Sources.GetSource(),0,0);
       
  1388   char name[32];
       
  1389   int i=0;
       
  1390   do {
       
  1391     if(i) sprintf(name,"%s%d",tr("unnamed"),i++);
       
  1392     else { strcpy(name,tr("unnamed")); i++; }
       
  1393     } while(plist->TestName(name));
       
  1394 
       
  1395   if(plist->Create(name)) {
       
  1396     lists->Add(plist);
       
  1397     Add(new cMenuMP3Item(plist), true);
       
  1398 
       
  1399     isyslog("MP3: playlist %s added", plist->Name());
       
  1400     return AddSubMenu(new cMenuPlayList(plist));
       
  1401     }
       
  1402   Error(tr("Error creating playlist!"));
       
  1403   delete plist;
       
  1404   return osContinue;
       
  1405 }
       
  1406 
       
  1407 eOSState cMenuMP3::Rename(bool second)
       
  1408 {
       
  1409   if(HasSubMenu() || Count() == 0) return osContinue;
       
  1410 
       
  1411   cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
       
  1412   if(!second) {
       
  1413     renaming=true;
       
  1414     return AddSubMenu(new cPlaylistRename(plist->BaseName()));
       
  1415     }
       
  1416   renaming=false;
       
  1417   const char *newname=cPlaylistRename::GetNewname();
       
  1418   if(newname) {
       
  1419     if(plist->Rename(newname)) {
       
  1420       RefreshCurrent();
       
  1421       DisplayCurrent(true);
       
  1422       }
       
  1423     else Error(tr("Error renaming playlist!"));
       
  1424     }
       
  1425   return osContinue;
       
  1426 }
       
  1427 
       
  1428 eOSState cMenuMP3::Edit(void)
       
  1429 {
       
  1430   if(HasSubMenu() || Count() == 0) return osContinue;
       
  1431 
       
  1432   cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
       
  1433   if(!plist->Load()) Error(tr("Error loading playlist!"));
       
  1434   else if(!plist->IsWinAmp()) {
       
  1435     isyslog("MP3: editing playlist %s", plist->Name());
       
  1436     return AddSubMenu(new cMenuPlayList(plist));
       
  1437     }
       
  1438   else Error(tr("Can't edit a WinAmp playlist!"));
       
  1439   return osContinue;
       
  1440 }
       
  1441 
       
  1442 eOSState cMenuMP3::Play(void)
       
  1443 {
       
  1444   if(HasSubMenu() || Count() == 0) return osContinue;
       
  1445 
       
  1446   Status(tr("Loading playlist..."));
       
  1447   cPlayList *newpl=new cPlayList(((cMenuMP3Item *)Get(Current()))->List());
       
  1448   if(newpl->Load() && newpl->Count()) {
       
  1449     isyslog("mp3: playback started with playlist %s", newpl->Name());
       
  1450     cMP3Control::SetPlayList(newpl);
       
  1451     if(MP3Setup.KeepSelect) { Status(0); return osContinue; }
       
  1452     return osEnd;
       
  1453     }
       
  1454   Status(0);
       
  1455   delete newpl;
       
  1456   Error(tr("Error loading playlist!"));
       
  1457   return osContinue;
       
  1458 }
       
  1459 
       
  1460 eOSState cMenuMP3::Source(bool second)
       
  1461 {
       
  1462   if(HasSubMenu()) return osContinue;
       
  1463 
       
  1464   if(!second) {
       
  1465     sourcing=true;
       
  1466     return AddSubMenu(new cMenuSource(&MP3Sources,tr("MP3 source")));
       
  1467     }
       
  1468   sourcing=false;
       
  1469   cFileSource *src=cMenuSource::GetSelected();
       
  1470   if(src) {
       
  1471     MP3Sources.SetSource(src);
       
  1472     ScanLists();
       
  1473     Display();
       
  1474     }
       
  1475   return osContinue;
       
  1476 }
       
  1477 
       
  1478 eOSState cMenuMP3::Instant(bool second)
       
  1479 {
       
  1480   if(HasSubMenu()) return osContinue;
       
  1481 
       
  1482   if(!second) {
       
  1483     instanting=true;
       
  1484     return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr(BUTTON"Play"),tr("Play all")));
       
  1485     }
       
  1486   instanting=false;
       
  1487   cFileObj *item=cMenuInstantBrowse::GetSelected();
       
  1488   if(item) {
       
  1489     Status(tr("Building playlist..."));
       
  1490     cInstantPlayList *newpl = new cInstantPlayList(item);
       
  1491     if(newpl->Load() && newpl->Count()) {
       
  1492       isyslog("mp3: playback started with instant playlist %s", newpl->Name());
       
  1493       cMP3Control::SetPlayList(newpl);
       
  1494       if(MP3Setup.KeepSelect) { Status(0); return Instant(false); }
       
  1495       return osEnd;
       
  1496       }
       
  1497     Status(0);
       
  1498     delete newpl;
       
  1499     Error(tr("Error building playlist!"));
       
  1500     }
       
  1501   return osContinue;
       
  1502 }
       
  1503 
       
  1504 eOSState cMenuMP3::ProcessKey(eKeys Key)
       
  1505 {
       
  1506   eOSState state = cOsdMenu::ProcessKey(Key);
       
  1507 
       
  1508   if(!HasSubMenu() && state==osContinue) { // eval the return value from submenus
       
  1509     if(renaming) return Rename(true);
       
  1510     if(sourcing) return Source(true);
       
  1511     if(instanting) return Instant(true);
       
  1512     }
       
  1513 
       
  1514   if(state == osUnknown) {
       
  1515     switch(Key) {
       
  1516       case kOk:     return Play();
       
  1517       case kRed:    return (buttonnum==1 ? Edit() : SetButtons(1)); 
       
  1518       case kGreen:  return (buttonnum==1 ? Source(false) : New());
       
  1519       case kYellow: return (buttonnum==1 ? Instant(false) : Delete());
       
  1520       case kBlue:   return (buttonnum==1 ? SetButtons(2) : Rename(false));
       
  1521       case kMenu:   return osEnd;
       
  1522       default:      break;
       
  1523       }
       
  1524     }
       
  1525   return state;
       
  1526 }
       
  1527 
       
  1528 // --- PropagateImage ----------------------------------------------------------
       
  1529 
       
  1530 void PropagateImage(const char *image)
       
  1531 {
       
  1532   cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
       
  1533   if(graphtft) graphtft->SetupParse("CoverImage",image ? image:"");
       
  1534 }
       
  1535 
       
  1536 // --- cPluginMP3 --------------------------------------------------------------
       
  1537 
       
  1538 static const char *VERSION        = PLUGIN_VERSION;
       
  1539 static const char *DESCRIPTION    = "A versatile audio player";
       
  1540 static const char *MAINMENUENTRY  = "MP3";
       
  1541 
       
  1542 class cPluginMp3 : public cPlugin {
       
  1543 private:
       
  1544 #if APIVERSNUM >= 10330
       
  1545   bool ExternalPlay(const char *path, bool test);
       
  1546 #endif
       
  1547 public:
       
  1548   cPluginMp3(void);
       
  1549   virtual ~cPluginMp3();
       
  1550   virtual const char *Version(void) { return VERSION; }
       
  1551   virtual const char *Description(void) { return tr(DESCRIPTION); }
       
  1552   virtual const char *CommandLineHelp(void);
       
  1553   virtual bool ProcessArgs(int argc, char *argv[]);
       
  1554 #if APIVERSNUM >= 10131
       
  1555   virtual bool Initialize(void);
       
  1556 #else
       
  1557   virtual bool Start(void);
       
  1558 #endif
       
  1559   virtual void Housekeeping(void);
       
  1560   virtual const char *MainMenuEntry(void);
       
  1561   virtual cOsdObject *MainMenuAction(void);
       
  1562   virtual cMenuSetupPage *SetupMenu(void);
       
  1563   virtual bool SetupParse(const char *Name, const char *Value);
       
  1564 #if APIVERSNUM >= 10330
       
  1565   virtual bool Service(const char *Id, void *Data);
       
  1566 #if APIVERSNUM >= 10331
       
  1567   virtual const char **SVDRPHelpPages(void);
       
  1568   virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
       
  1569 #endif
       
  1570 #endif
       
  1571   };
       
  1572 
       
  1573 cPluginMp3::cPluginMp3(void)
       
  1574 {
       
  1575   // Initialize any member varaiables here.
       
  1576   // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
       
  1577   // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
       
  1578 }
       
  1579 
       
  1580 cPluginMp3::~cPluginMp3()
       
  1581 {
       
  1582   InfoCache.Save();
       
  1583   delete mgr;
       
  1584 }
       
  1585 
       
  1586 const char *cPluginMp3::CommandLineHelp(void)
       
  1587 {
       
  1588   static char *help_str=0;
       
  1589   
       
  1590   free(help_str);    //                                     for easier orientation, this is column 80|
       
  1591   asprintf(&help_str,"  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
       
  1592                      "                           (default: %s)\n"
       
  1593                      "  -n CMD,   --network=CMD  execute CMD before & after network access\n"
       
  1594                      "                           (default: %s)\n"
       
  1595                      "  -C DIR,   --cache=DIR    store ID3 cache file in DIR\n"
       
  1596                      "                           (default: %s)\n"
       
  1597                      "  -B DIR,   --cddb=DIR     search CDDB files in DIR\n"
       
  1598                      "                           (default: %s)\n"
       
  1599                      "  -D DEV,   --dsp=DEV      device for OSS output\n"
       
  1600                      "                           (default: %s)\n"
       
  1601                      "  -i CMD,   --iconv=CMD    use CMD to convert background images\n"
       
  1602                      "                           (default: %s)\n"
       
  1603                      "  -c DIR,   --icache=DIR   cache converted images in DIR\n"
       
  1604                      "                           (default: %s)\n"
       
  1605                      "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
       
  1606                      "                           (default: %s)\n",
       
  1607                      
       
  1608                      mountscript,
       
  1609                      netscript ? netscript:"none",
       
  1610                      cachedir ? cachedir:"video dir",
       
  1611 #ifdef HAVE_SNDFILE
       
  1612                      cddbpath,
       
  1613 #else
       
  1614                      "none",
       
  1615 #endif
       
  1616 #ifdef WITH_OSS
       
  1617                      dspdevice,
       
  1618 #else
       
  1619                      "none",
       
  1620 #endif
       
  1621                      imageconv,
       
  1622                      imagecache,
       
  1623                      sourcesSub ? sourcesSub:"empty"
       
  1624                      );
       
  1625   return help_str;
       
  1626 }
       
  1627 
       
  1628 bool cPluginMp3::ProcessArgs(int argc, char *argv[])
       
  1629 {
       
  1630   static struct option long_options[] = {
       
  1631       { "mount",    required_argument, NULL, 'm' },
       
  1632       { "network",  required_argument, NULL, 'n' },
       
  1633       { "cddb",     required_argument, NULL, 'B' },
       
  1634       { "dsp",      required_argument, NULL, 'D' },
       
  1635       { "cache",    required_argument, NULL, 'C' },
       
  1636       { "icache",   required_argument, NULL, 'c' },
       
  1637       { "iconv",    required_argument, NULL, 'i' },
       
  1638       { "sources",  required_argument, NULL, 'S' },
       
  1639       { NULL }
       
  1640     };
       
  1641 
       
  1642   int c, option_index = 0;
       
  1643   while((c=getopt_long(argc,argv,"c:i:m:n:B:C:D:S:",long_options,&option_index))!=-1) {
       
  1644     switch (c) {
       
  1645       case 'i': imageconv=optarg; break;
       
  1646       case 'c': imagecache=optarg; break;
       
  1647       case 'm': mountscript=optarg; break;
       
  1648       case 'n': netscript=optarg; break;
       
  1649       case 'C': cachedir=optarg; break;
       
  1650       case 'S': sourcesSub=optarg; break;
       
  1651       case 'B':
       
  1652 #ifdef HAVE_SNDFILE
       
  1653                 cddbpath=optarg; break;
       
  1654 #else
       
  1655                 fprintf(stderr, "mp3: libsndfile support has not been compiled in!\n"); return false;
       
  1656 #endif
       
  1657       case 'D':
       
  1658 #ifdef WITH_OSS
       
  1659                 dspdevice=optarg; break;
       
  1660 #else
       
  1661                 fprintf(stderr, "mp3: OSS output has not been compiled in!\n"); return false;
       
  1662 #endif
       
  1663       default:  return false;
       
  1664       }
       
  1665     }
       
  1666   return true;
       
  1667 }
       
  1668 
       
  1669 #if APIVERSNUM >= 10131
       
  1670 bool cPluginMp3::Initialize(void)
       
  1671 #else
       
  1672 bool cPluginMp3::Start(void)
       
  1673 #endif
       
  1674 {
       
  1675   if(!CheckVDRVersion(1,1,29,"mp3")) return false;
       
  1676   i18n_name=Name();
       
  1677   MP3Sources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mp3sources.conf"));
       
  1678   if(MP3Sources.Count()<1) {
       
  1679      esyslog("ERROR: you should have defined at least one source in mp3sources.conf");
       
  1680      fprintf(stderr,"No source(s) defined in mp3sources.conf\n");
       
  1681      return false;
       
  1682      }
       
  1683   InfoCache.Load();
       
  1684   RegisterI18n(Phrases);
       
  1685   mgr=new cPlayManager;
       
  1686   if(!mgr) {
       
  1687     esyslog("ERROR: creating playmanager failed");
       
  1688     fprintf(stderr,"Creating playmanager failed\n");
       
  1689     return false;
       
  1690     }
       
  1691   d(printf("mp3: using %s\n",mad_version))
       
  1692   d(printf("mp3: compiled with %s\n",MAD_VERSION))
       
  1693   return true;
       
  1694 }
       
  1695 
       
  1696 void cPluginMp3::Housekeeping(void)
       
  1697 {
       
  1698   InfoCache.Save();
       
  1699 }
       
  1700 
       
  1701 const char *cPluginMp3::MainMenuEntry(void)
       
  1702 {
       
  1703   return MP3Setup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
       
  1704 }
       
  1705 
       
  1706 cOsdObject *cPluginMp3::MainMenuAction(void)
       
  1707 {
       
  1708   return new cMenuMP3;
       
  1709 }
       
  1710 
       
  1711 cMenuSetupPage *cPluginMp3::SetupMenu(void)
       
  1712 {
       
  1713   return new cMenuSetupMP3;
       
  1714 }
       
  1715 
       
  1716 bool cPluginMp3::SetupParse(const char *Name, const char *Value)
       
  1717 {
       
  1718   if      (!strcasecmp(Name, "InitLoopMode"))     MP3Setup.InitLoopMode    = atoi(Value);
       
  1719   else if (!strcasecmp(Name, "InitShuffleMode"))  MP3Setup.InitShuffleMode = atoi(Value);
       
  1720   else if (!strcasecmp(Name, "AudioMode"))        MP3Setup.AudioMode       = atoi(Value);
       
  1721   else if (!strcasecmp(Name, "BgrScan"))          MP3Setup.BgrScan         = atoi(Value);
       
  1722   else if (!strcasecmp(Name, "EditorMode"))       MP3Setup.EditorMode      = atoi(Value);
       
  1723   else if (!strcasecmp(Name, "DisplayMode"))      MP3Setup.DisplayMode     = atoi(Value);
       
  1724   else if (!strcasecmp(Name, "BackgrMode"))       MP3Setup.BackgrMode      = atoi(Value);
       
  1725   else if (!strcasecmp(Name, "MenuMode"))         MP3Setup.MenuMode        = atoi(Value);
       
  1726   else if (!strcasecmp(Name, "TargetLevel"))      MP3Setup.TargetLevel     = atoi(Value);
       
  1727   else if (!strcasecmp(Name, "LimiterLevel"))     MP3Setup.LimiterLevel    = atoi(Value);
       
  1728   else if (!strcasecmp(Name, "Only48kHz"))        MP3Setup.Only48kHz       = atoi(Value);
       
  1729   else if (!strcasecmp(Name, "UseProxy"))         MP3Setup.UseProxy        = atoi(Value);
       
  1730   else if (!strcasecmp(Name, "ProxyHost"))        strn0cpy(MP3Setup.ProxyHost,Value,MAX_HOSTNAME);
       
  1731   else if (!strcasecmp(Name, "ProxyPort"))        MP3Setup.ProxyPort       = atoi(Value);
       
  1732   else if (!strcasecmp(Name, "UseCddb"))          MP3Setup.UseCddb         = atoi(Value);
       
  1733   else if (!strcasecmp(Name, "CddbHost"))         strn0cpy(MP3Setup.CddbHost,Value,MAX_HOSTNAME);
       
  1734   else if (!strcasecmp(Name, "CddbPort"))         MP3Setup.CddbPort        = atoi(Value);
       
  1735   else if (!strcasecmp(Name, "AbortAtEOL"))       MP3Setup.AbortAtEOL      = atoi(Value);
       
  1736   else if (!strcasecmp(Name, "AudioOutMode")) {
       
  1737     MP3Setup.AudioOutMode = atoi(Value);
       
  1738 #ifndef WITH_OSS
       
  1739     if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) {
       
  1740       esyslog("WARNING: AudioOutMode OSS not supported, falling back to DVB");
       
  1741       MP3Setup.AudioOutMode=AUDIOOUTMODE_DVB;
       
  1742       }
       
  1743 #endif
       
  1744     }
       
  1745 #if APIVERSNUM >= 10307
       
  1746   else if (!strcasecmp(Name, "ReplayDisplay"))      MP3Setup.ReplayDisplay = atoi(Value);
       
  1747 #endif
       
  1748   else if (!strcasecmp(Name, "HideMainMenu"))       MP3Setup.HideMainMenu  = atoi(Value);
       
  1749   else if (!strcasecmp(Name, "KeepSelect"))         MP3Setup.KeepSelect    = atoi(Value);
       
  1750   else if (!strcasecmp(Name, "TitleArtistOrder"))   MP3Setup.TitleArtistOrder = atoi(Value);
       
  1751   else return false;
       
  1752   return true;
       
  1753 }
       
  1754 
       
  1755 #if APIVERSNUM >= 10330
       
  1756 
       
  1757 bool cPluginMp3::ExternalPlay(const char *path, bool test)
       
  1758 {
       
  1759   char real[PATH_MAX+1];
       
  1760   if(realpath(path,real)) {
       
  1761     cFileSource *src=MP3Sources.FindSource(real);
       
  1762     if(src) {
       
  1763       cFileObj *item=new cFileObj(src,0,0,otFile);
       
  1764       if(item) {
       
  1765         item->SplitAndSet(real);
       
  1766         if(item->GuessType()) {
       
  1767           if(item->Exists()) {
       
  1768             cInstantPlayList *pl=new cInstantPlayList(item);
       
  1769             if(pl && pl->Load() && pl->Count()) {
       
  1770               if(!test) cMP3Control::SetPlayList(pl);
       
  1771               else delete pl;
       
  1772               delete item;
       
  1773               return true;
       
  1774               }
       
  1775             else dsyslog("MP3 service: error building playlist");
       
  1776             delete pl;
       
  1777             }
       
  1778           else dsyslog("MP3 service: cannot play '%s'",path);
       
  1779           }
       
  1780         else dsyslog("MP3 service: GuessType() failed for '%s'",path);
       
  1781         delete item;
       
  1782         }
       
  1783       }
       
  1784     else dsyslog("MP3 service: cannot find source for '%s', real '%s'",path,real);
       
  1785     }
       
  1786   else if(errno!=ENOENT && errno!=ENOTDIR)
       
  1787     esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
       
  1788   return false;
       
  1789 }
       
  1790 
       
  1791 bool cPluginMp3::Service(const char *Id, void *Data)
       
  1792 {
       
  1793   if(!strcasecmp(Id,"MP3-Play-v1")) {
       
  1794     if(Data) {
       
  1795       struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
       
  1796       msd->result=ExternalPlay(msd->data.filename,false);
       
  1797       }
       
  1798     return true;
       
  1799     }
       
  1800   else if(!strcasecmp(Id,"MP3-Test-v1")) {
       
  1801     if(Data) {
       
  1802       struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
       
  1803       msd->result=ExternalPlay(msd->data.filename,true);
       
  1804       }
       
  1805     return true;
       
  1806     }
       
  1807   return false;
       
  1808 }
       
  1809 
       
  1810 #if APIVERSNUM >= 10331
       
  1811 
       
  1812 const char **cPluginMp3::SVDRPHelpPages(void)
       
  1813 {
       
  1814   static const char *HelpPages[] = {
       
  1815     "PLAY <filename>\n"
       
  1816     "    Triggers playback of file 'filename'.",
       
  1817     "TEST <filename>\n"
       
  1818     "    Tests is playback of file 'filename' is possible.",
       
  1819     "CURR\n"
       
  1820     "    Returns filename of song currently being replayed.",
       
  1821     NULL
       
  1822     };
       
  1823   return HelpPages;
       
  1824 }
       
  1825 
       
  1826 cString cPluginMp3::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
       
  1827 {
       
  1828   if(!strcasecmp(Command,"PLAY")) {
       
  1829     if(*Option) {
       
  1830       if(ExternalPlay(Option,false)) return "Playback triggered";
       
  1831       else { ReplyCode=550; return "Playback failed"; }
       
  1832       }
       
  1833     else { ReplyCode=501; return "Missing filename"; }
       
  1834     }
       
  1835   else if(!strcasecmp(Command,"TEST")) {
       
  1836     if(*Option) {
       
  1837       if(ExternalPlay(Option,true)) return "Playback possible";
       
  1838       else { ReplyCode=550; return "Playback not possible"; }
       
  1839       }
       
  1840     else { ReplyCode=501; return "Missing filename"; }
       
  1841     }
       
  1842   else if(!strcasecmp(Command,"CURR")) {
       
  1843     cControl *control=cControl::Control();
       
  1844     if(control && typeid(*control)==typeid(cMP3Control)) {
       
  1845       cMP3PlayInfo mode;
       
  1846       if(mgr->Info(-1,&mode)) return mode.Filename;
       
  1847       else return "<unknown>";
       
  1848       }
       
  1849     else { ReplyCode=550; return "No running playback"; }
       
  1850     }
       
  1851   return NULL;
       
  1852 }
       
  1853 
       
  1854 #endif // 1.3.31
       
  1855 #endif // 1.3.30
       
  1856 
       
  1857 VDRPLUGINCREATOR(cPluginMp3); // Don't touch this!