mp3.c
branchtrunk
changeset 0 474a1293c3c0
child 2 4c1f7b705009
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mp3.c	Sat Dec 29 14:47:40 2007 +0100
     1.3 @@ -0,0 +1,1857 @@
     1.4 +/*
     1.5 + * MP3/MPlayer plugin to VDR (C++)
     1.6 + *
     1.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
     1.8 + *
     1.9 + * This code is free software; you can redistribute it and/or
    1.10 + * modify it under the terms of the GNU General Public License
    1.11 + * as published by the Free Software Foundation; either version 2
    1.12 + * of the License, or (at your option) any later version.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful,
    1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.17 + * GNU General Public License for more details.
    1.18 + *
    1.19 + * You should have received a copy of the GNU General Public License
    1.20 + * along with this program; if not, write to the Free Software
    1.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    1.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    1.23 + */
    1.24 +
    1.25 +#include <stdlib.h>
    1.26 +#include <getopt.h>
    1.27 +#include <strings.h>
    1.28 +#include <typeinfo>
    1.29 +
    1.30 +#include "common.h"
    1.31 +
    1.32 +#include <vdr/menuitems.h>
    1.33 +#include <vdr/status.h>
    1.34 +#include <vdr/plugin.h>
    1.35 +#if APIVERSNUM >= 10307
    1.36 +#include <vdr/interface.h>
    1.37 +#include <vdr/skins.h>
    1.38 +#endif
    1.39 +
    1.40 +#include "setup.h"
    1.41 +#include "setup-mp3.h"
    1.42 +#include "data-mp3.h"
    1.43 +#include "data-src.h"
    1.44 +#include "player-mp3.h"
    1.45 +#include "menu.h"
    1.46 +#include "menu-async.h"
    1.47 +#include "decoder.h"
    1.48 +#include "i18n.h"
    1.49 +#include "version.h"
    1.50 +#include "service.h"
    1.51 +
    1.52 +#ifdef DEBUG
    1.53 +#include <mad.h>
    1.54 +#endif
    1.55 +
    1.56 +const char *sourcesSub=0;
    1.57 +cFileSources MP3Sources;
    1.58 +
    1.59 +// --- cMenuSetupMP3 --------------------------------------------------------
    1.60 +
    1.61 +class cMenuSetupMP3 : public cMenuSetupPage {
    1.62 +private:
    1.63 +  cMP3Setup data;
    1.64 +  //
    1.65 +  const char *cddb[3], *disp[2], *scan[3], *bgr[3];
    1.66 +  const char *aout[AUDIOOUTMODES];
    1.67 +  int amode, amodes[AUDIOOUTMODES];
    1.68 +protected:
    1.69 +  virtual void Store(void);
    1.70 +public:
    1.71 +  cMenuSetupMP3(void);
    1.72 +  };
    1.73 +
    1.74 +cMenuSetupMP3::cMenuSetupMP3(void)
    1.75 +{
    1.76 +  static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" };
    1.77 +  int numModes=0;
    1.78 +  aout[numModes]=tr("DVB"); amodes[numModes]=AUDIOOUTMODE_DVB; numModes++;
    1.79 +#ifdef WITH_OSS
    1.80 +  aout[numModes]=tr("OSS"); amodes[numModes]=AUDIOOUTMODE_OSS; numModes++;
    1.81 +#endif
    1.82 +  data=MP3Setup;
    1.83 +  amode=0;
    1.84 +  for(int i=0; i<numModes; i++)
    1.85 +    if(amodes[i]==data.AudioOutMode) { amode=i; break; }
    1.86 +
    1.87 +  SetSection(tr("MP3"));
    1.88 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Audio output mode"),     &amode,numModes,aout));
    1.89 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Audio mode"),            &data.AudioMode, tr("Round"), tr("Dither")));
    1.90 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Use 48kHz mode only"),   &data.Only48kHz));
    1.91 +#if APIVERSNUM >= 10307
    1.92 +  disp[0]=tr("classic");
    1.93 +  disp[1]=tr("via skin");
    1.94 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Replay display"),        &data.ReplayDisplay, 2, disp));
    1.95 +#endif
    1.96 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Display mode"),          &data.DisplayMode, 1, 3));
    1.97 +  bgr[0]=tr("Black");
    1.98 +  bgr[1]=tr("Live");
    1.99 +  bgr[2]=tr("Images");
   1.100 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Background mode"),       &data.BackgrMode, 3, bgr));
   1.101 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial loop mode"),     &data.InitLoopMode));
   1.102 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial shuffle mode"),  &data.InitShuffleMode));
   1.103 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Abort player at end of list"),&data.AbortAtEOL));
   1.104 +  scan[0]=tr("disabled");
   1.105 +  scan[1]=tr("ID3 only");
   1.106 +  scan[2]=tr("ID3 & Level");
   1.107 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Background scan"),       &data.BgrScan, 3, scan));
   1.108 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Editor display mode"),   &data.EditorMode, tr("Filenames"), tr("ID3 names")));
   1.109 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Mainmenu mode"),         &data.MenuMode, tr("Playlists"), tr("Browser")));
   1.110 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Keep selection menu"),   &data.KeepSelect));
   1.111 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Title/Artist order"),    &data.TitleArtistOrder, tr("Normal"), tr("Reversed")));
   1.112 +  Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),             &data.HideMainMenu));
   1.113 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Normalizer level"),      &data.TargetLevel, 0, MAX_TARGET_LEVEL));
   1.114 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Limiter level"),         &data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
   1.115 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Use HTTP proxy"),        &data.UseProxy));
   1.116 +  Add(new cMenuEditStrItem( tr("Setup.MP3$HTTP proxy host"),       data.ProxyHost,MAX_HOSTNAME,allowed));
   1.117 +  Add(new cMenuEditIntItem( tr("Setup.MP3$HTTP proxy port"),       &data.ProxyPort,1,65535));
   1.118 +  cddb[0]=tr("disabled");
   1.119 +  cddb[1]=tr("local only");
   1.120 +  cddb[2]=tr("local&remote");
   1.121 +  Add(new cMenuEditStraItem(tr("Setup.MP3$CDDB for CD-Audio"),     &data.UseCddb,3,cddb));
   1.122 +  Add(new cMenuEditStrItem( tr("Setup.MP3$CDDB server"),           data.CddbHost,MAX_HOSTNAME,allowed));
   1.123 +  Add(new cMenuEditIntItem( tr("Setup.MP3$CDDB port"),             &data.CddbPort,1,65535));
   1.124 +}
   1.125 +
   1.126 +void cMenuSetupMP3::Store(void)
   1.127 +{
   1.128 +  data.AudioOutMode=amodes[amode];
   1.129 +
   1.130 +  MP3Setup=data;
   1.131 +  SetupStore("InitLoopMode",     MP3Setup.InitLoopMode   );
   1.132 +  SetupStore("InitShuffleMode",  MP3Setup.InitShuffleMode);
   1.133 +  SetupStore("AudioMode",        MP3Setup.AudioMode      );
   1.134 +  SetupStore("AudioOutMode",     MP3Setup.AudioOutMode   );
   1.135 +  SetupStore("BgrScan",          MP3Setup.BgrScan        );
   1.136 +  SetupStore("EditorMode",       MP3Setup.EditorMode     );
   1.137 +  SetupStore("DisplayMode",      MP3Setup.DisplayMode    );
   1.138 +  SetupStore("BackgrMode",       MP3Setup.BackgrMode     );
   1.139 +  SetupStore("MenuMode",         MP3Setup.MenuMode       );
   1.140 +  SetupStore("TargetLevel",      MP3Setup.TargetLevel    );
   1.141 +  SetupStore("LimiterLevel",     MP3Setup.LimiterLevel   );
   1.142 +  SetupStore("Only48kHz",        MP3Setup.Only48kHz      );
   1.143 +  SetupStore("UseProxy",         MP3Setup.UseProxy       );
   1.144 +  SetupStore("ProxyHost",        MP3Setup.ProxyHost      );
   1.145 +  SetupStore("ProxyPort",        MP3Setup.ProxyPort      );
   1.146 +  SetupStore("UseCddb",          MP3Setup.UseCddb        );
   1.147 +  SetupStore("CddbHost",         MP3Setup.CddbHost       );
   1.148 +  SetupStore("CddbPort",         MP3Setup.CddbPort       );
   1.149 +  SetupStore("AbortAtEOL",       MP3Setup.AbortAtEOL     );
   1.150 +#if APIVERSNUM >= 10307
   1.151 +  SetupStore("ReplayDisplay",    MP3Setup.ReplayDisplay  );
   1.152 +#endif
   1.153 +  SetupStore("HideMainMenu",     MP3Setup.HideMainMenu   );
   1.154 +  SetupStore("KeepSelect",       MP3Setup.KeepSelect     );
   1.155 +  SetupStore("TitleArtistOrder", MP3Setup.TitleArtistOrder);
   1.156 +}
   1.157 +
   1.158 +// --- cAsyncStatus ------------------------------------------------------------
   1.159 +
   1.160 +cAsyncStatus asyncStatus;
   1.161 +
   1.162 +cAsyncStatus::cAsyncStatus(void)
   1.163 +{
   1.164 +  text=0;
   1.165 +  changed=false;
   1.166 +}
   1.167 +
   1.168 +cAsyncStatus::~cAsyncStatus()
   1.169 +{
   1.170 +  free((void *)text);
   1.171 +}
   1.172 +
   1.173 +void cAsyncStatus::Set(const char *Text)
   1.174 +{
   1.175 +  Lock();
   1.176 +  free((void *)text);
   1.177 +  text=Text ? strdup(Text) : 0;
   1.178 +  changed=true;
   1.179 +  Unlock();
   1.180 +}
   1.181 +
   1.182 +const char *cAsyncStatus::Begin(void)
   1.183 +{
   1.184 +  Lock();
   1.185 +  return text;
   1.186 +}
   1.187 +
   1.188 +void cAsyncStatus::Finish(void)
   1.189 +{
   1.190 +  changed=false;
   1.191 +  Unlock();
   1.192 +}
   1.193 +
   1.194 +// --- --------------------------------------------------------------------
   1.195 +
   1.196 +static const char *TitleArtist(const char *title, const char *artist)
   1.197 +{
   1.198 +  static char buf[256]; // clearly not multi-thread save!
   1.199 +  char *fmt;
   1.200 +  if(artist && artist[0]) {
   1.201 +    if(MP3Setup.TitleArtistOrder) fmt="%2$s - %1$s";
   1.202 +    else  fmt="%s - %s";
   1.203 +    }
   1.204 +  else fmt="%s";
   1.205 +  snprintf(buf,sizeof(buf),fmt,title,artist);
   1.206 +  return buf;
   1.207 +}
   1.208 +
   1.209 +// --- cMP3Control --------------------------------------------------------
   1.210 +
   1.211 +#if APIVERSNUM >= 10307
   1.212 +#define clrBackground clrGray50
   1.213 +#define eDvbColor int
   1.214 +#define MAXROWS 120
   1.215 +#define INLINE
   1.216 +#else
   1.217 +#define MAXROWS MAXOSDHEIGHT
   1.218 +#define INLINE inline
   1.219 +#endif
   1.220 +
   1.221 +class cMP3Control : public cControl {
   1.222 +private:
   1.223 +#if APIVERSNUM >= 10307
   1.224 +  cOsd *osd;
   1.225 +  const cFont *font;
   1.226 +  cSkinDisplayReplay *disp;
   1.227 +#else
   1.228 +  bool statusInterfaceOpen;
   1.229 +#endif
   1.230 +  int bw, bh, bwc, fw, fh;
   1.231 +  //
   1.232 +  cMP3Player *player;
   1.233 +  bool visible, shown, bigwin, statusActive;
   1.234 +  time_t timeoutShow, greentime, oktime;
   1.235 +  int lastkeytime, number;
   1.236 +  bool selecting, selecthide;
   1.237 +  //
   1.238 +  cMP3PlayInfo *lastMode;
   1.239 +  time_t fliptime, listtime;
   1.240 +  int hashlist[MAXROWS];
   1.241 +  int flip, flipint, top, rows;
   1.242 +  int lastIndex, lastTotal, lastTop;
   1.243 +  int framesPerSecond;
   1.244 +  //
   1.245 +  bool jumpactive, jumphide, jumpsecs;
   1.246 +  int jumpmm;
   1.247 +  //
   1.248 +  void ShowTimed(int Seconds=0);
   1.249 +  void ShowProgress(bool open=false, bool bigWin=false);
   1.250 +  void ShowStatus(bool force);
   1.251 +  void HideStatus(void);
   1.252 +  void DisplayInfo(const char *s=0);
   1.253 +  void JumpDisplay(void);
   1.254 +  void JumpProcess(eKeys Key);
   1.255 +  void Jump(void);
   1.256 +  void Stop(void);
   1.257 +  INLINE void Write(int x, int y, int w, const char *text, eDvbColor fg=clrWhite, eDvbColor bg=clrBackground);
   1.258 +  INLINE void Fill(int x, int y, int w, int h, eDvbColor fg);
   1.259 +  inline void Flush(void);
   1.260 +public:
   1.261 +  cMP3Control(void);
   1.262 +  virtual ~cMP3Control();
   1.263 +  virtual eOSState ProcessKey(eKeys Key);
   1.264 +  virtual void Show(void) { ShowTimed(); }
   1.265 +  virtual void Hide(void);
   1.266 +  bool Visible(void) { return visible; }
   1.267 +  static bool SetPlayList(cPlayList *plist);
   1.268 +  };
   1.269 +
   1.270 +cMP3Control::cMP3Control(void)
   1.271 +:cControl(player=new cMP3Player)
   1.272 +{
   1.273 +  visible=shown=bigwin=selecting=selecthide=jumpactive=jumphide=statusActive=false;
   1.274 +  timeoutShow=greentime=oktime=0;
   1.275 +  lastkeytime=number=0;
   1.276 +  lastMode=0;
   1.277 +  framesPerSecond=SecondsToFrames(1);
   1.278 +#if APIVERSNUM >= 10307
   1.279 +  osd=0; disp=0;
   1.280 +  font=cFont::GetFont(fontOsd);
   1.281 +#else
   1.282 +  statusInterfaceOpen=false;
   1.283 +#endif
   1.284 +#if APIVERSNUM >= 10338
   1.285 +  cStatus::MsgReplaying(this,"MP3",0,true);
   1.286 +#else
   1.287 +  cStatus::MsgReplaying(this,"MP3");
   1.288 +#endif
   1.289 +}
   1.290 +
   1.291 +cMP3Control::~cMP3Control()
   1.292 +{
   1.293 +  delete lastMode;
   1.294 +  Hide();
   1.295 +  Stop();
   1.296 +}
   1.297 +
   1.298 +void cMP3Control::Stop(void)
   1.299 +{
   1.300 +#if APIVERSNUM >= 10338
   1.301 +  cStatus::MsgReplaying(this,0,0,false);
   1.302 +#else
   1.303 +  cStatus::MsgReplaying(this,0);
   1.304 +#endif
   1.305 +  delete player; player=0;
   1.306 +  mgr->Halt();
   1.307 +  mgr->Flush(); //XXX remove later
   1.308 +}
   1.309 +
   1.310 +bool cMP3Control::SetPlayList(cPlayList *plist)
   1.311 +{
   1.312 +  bool res;
   1.313 +  cControl *control=cControl::Control();
   1.314 +  // is there a running MP3 player?
   1.315 +  if(control && typeid(*control)==typeid(cMP3Control)) {
   1.316 +    // add songs to running playlist
   1.317 +    mgr->Add(plist);
   1.318 +    res=true;
   1.319 +    }
   1.320 +  else {
   1.321 +    mgr->Flush();
   1.322 +    mgr->Add(plist);
   1.323 +    cControl::Launch(new cMP3Control);
   1.324 +    res=false;
   1.325 +    }
   1.326 +  delete plist;
   1.327 +  return res;
   1.328 +}
   1.329 +
   1.330 +void cMP3Control::ShowTimed(int Seconds)
   1.331 +{
   1.332 +  if(!visible) {
   1.333 +    ShowProgress(true);
   1.334 +    if(Seconds>0) timeoutShow=time(0)+Seconds;
   1.335 +    }
   1.336 +}
   1.337 +
   1.338 +void cMP3Control::Hide(void)
   1.339 +{
   1.340 +  HideStatus();
   1.341 +  if(visible) {
   1.342 +#if APIVERSNUM >= 10307
   1.343 +    delete osd; osd=0;
   1.344 +    delete disp; disp=0;
   1.345 +#else
   1.346 +    Interface->Close();
   1.347 +#endif
   1.348 +    visible=bigwin=false;
   1.349 +#if APIVERSNUM >= 10500
   1.350 +    SetNeedsFastResponse(false);
   1.351 +#else
   1.352 +    needsFastResponse=false;
   1.353 +#endif
   1.354 +    }
   1.355 +}
   1.356 +
   1.357 +void cMP3Control::ShowStatus(bool force)
   1.358 +{
   1.359 +  if((asyncStatus.Changed() || (force && !statusActive)) && !jumpactive) {
   1.360 +    const char *text=asyncStatus.Begin();
   1.361 +    if(text) {
   1.362 +#if APIVERSNUM >= 10307
   1.363 +      if(MP3Setup.ReplayDisplay || !osd) {
   1.364 +        if(statusActive) Skins.Message(mtStatus,0);
   1.365 +        Skins.Message(mtStatus,text);
   1.366 +        }
   1.367 +      else {
   1.368 +        if(!statusActive) osd->SaveRegion(0,bh-2*fh,bw-1,bh-fh-1);
   1.369 +        osd->DrawText(0,bh-2*fh,text,clrBlack,clrCyan,font,bw,fh,taCenter);
   1.370 +        osd->Flush();
   1.371 +        }
   1.372 +#else
   1.373 +      if(!Interface->IsOpen()) {
   1.374 +        Interface->Open(0,-1);
   1.375 +        statusInterfaceOpen=true;
   1.376 +        }
   1.377 +      Interface->Status(text);
   1.378 +      Interface->Flush();
   1.379 +#endif
   1.380 +      statusActive=true;
   1.381 +      }
   1.382 +    else
   1.383 +      HideStatus();
   1.384 +    asyncStatus.Finish();
   1.385 +    }
   1.386 +}
   1.387 +
   1.388 +void cMP3Control::HideStatus(void)
   1.389 +{
   1.390 +  if(statusActive) {
   1.391 +#if APIVERSNUM >= 10307
   1.392 +    if(MP3Setup.ReplayDisplay || !osd)
   1.393 +      Skins.Message(mtStatus,0);
   1.394 +    else {
   1.395 +      osd->RestoreRegion();
   1.396 +      osd->Flush();
   1.397 +      }
   1.398 +#else
   1.399 +    if(statusInterfaceOpen) {
   1.400 +      Interface->Close();
   1.401 +      statusInterfaceOpen=false;
   1.402 +      }
   1.403 +    else {
   1.404 +      Interface->Status(0);
   1.405 +      Interface->Flush();
   1.406 +      }
   1.407 +#endif
   1.408 +    }
   1.409 +  statusActive=false;
   1.410 +}
   1.411 +
   1.412 +#define CTAB    11 // some tabbing values for the progress display
   1.413 +#define CTAB2   5
   1.414 +
   1.415 +void cMP3Control::Write(int x, int y, int w, const char *text, eDvbColor fg, eDvbColor bg)
   1.416 +{
   1.417 +#if APIVERSNUM >= 10307
   1.418 +  if(osd) {
   1.419 +    //d(printf("write x=%d y=%d w=%d ->",x,y,w))
   1.420 +    x*=fw; if(x<0) x+=bw;
   1.421 +    y*=fh; if(y<0) y+=bh;
   1.422 +    osd->DrawText(x,y,text,fg,bg,font,w*fw);
   1.423 +    //d(printf(" x=%d y=%d w=%d\n",x,y,w*fw))
   1.424 +    }
   1.425 +#else
   1.426 +  if(w>0) Fill(x,y,w,1,bg);
   1.427 +  Interface->Write(x,y,text,fg,bg);
   1.428 +#endif
   1.429 +}
   1.430 +
   1.431 +void cMP3Control::Fill(int x, int y, int w, int h, eDvbColor fg)
   1.432 +{
   1.433 +#if APIVERSNUM >= 10307
   1.434 +  if(osd) {
   1.435 +    //d(printf("fill x=%d y=%d w=%d h=%d ->",x,y,w,h))
   1.436 +    x*=fw; if(x<0) x+=bw;
   1.437 +    y*=fh; if(y<0) y+=bh;
   1.438 +    osd->DrawRectangle(x,y,x+w*fw-1,y+h*fh-1,fg);
   1.439 +    //d(printf(" x=%d y=%d x2=%d y2=%d\n",x,y,x+h*fh-1,y+w*fw-1))
   1.440 +    }
   1.441 +#else
   1.442 +  Interface->Fill(x,y,w,h,fg);
   1.443 +#endif
   1.444 +}
   1.445 +
   1.446 +void cMP3Control::Flush(void)
   1.447 +{
   1.448 +#if APIVERSNUM >= 10307
   1.449 +  if(MP3Setup.ReplayDisplay) Skins.Flush();
   1.450 +  else if(osd) osd->Flush();
   1.451 +#else
   1.452 +  Interface->Flush();
   1.453 +#endif
   1.454 +}
   1.455 +
   1.456 +void cMP3Control::ShowProgress(bool open, bool bigWin)
   1.457 +{
   1.458 +  int index, total;
   1.459 +
   1.460 +  if(player->GetIndex(index,total) && total>=0) {
   1.461 +    if(!visible && open) {
   1.462 +      HideStatus();
   1.463 +#if APIVERSNUM >= 10307
   1.464 +      if(MP3Setup.ReplayDisplay) {
   1.465 +        disp=Skins.Current()->DisplayReplay(false);
   1.466 +        if(!disp) return;
   1.467 +        bigWin=false;
   1.468 +        }
   1.469 +      else {
   1.470 +        int bt, bp;
   1.471 +        fw=font->Width(' ')*2;
   1.472 +        fh=font->Height();
   1.473 +        if(bigWin) {
   1.474 +          bw=Setup.OSDWidth;
   1.475 +          bh=Setup.OSDHeight;
   1.476 +          bt=0;
   1.477 +          bp=2*fh;
   1.478 +          rows=(bh-bp-fh/3)/fh;
   1.479 +          }
   1.480 +        else {
   1.481 +          bw=Setup.OSDWidth;
   1.482 +          bh=bp=2*fh;
   1.483 +          bt=Setup.OSDHeight-bh;
   1.484 +          rows=0;
   1.485 +          }
   1.486 +        bwc=bw/fw+1;
   1.487 +        //d(printf("mp3: bw=%d bh=%d bt=%d bp=%d bwc=%d rows=%d fw=%d fh=%d\n",
   1.488 +        //  bw,bh,bt,bp,bwc,rows,fw,fh))
   1.489 +        osd=cOsdProvider::NewOsd(Setup.OSDLeft,Setup.OSDTop+bt);
   1.490 +        if(!osd) return;
   1.491 +        if(bigWin) {
   1.492 +          tArea Areas[] = { { 0,0,bw-1,bh-bp-1,2 }, { 0,bh-bp,bw-1,bh-1,4 } };
   1.493 +          osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
   1.494 +          }
   1.495 +        else {
   1.496 +          tArea Areas[] = { { 0,0,bw-1,bh-1,4 } };
   1.497 +          osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
   1.498 +          }
   1.499 +        osd->DrawRectangle(0,0,bw-1,bh-1,clrGray50);
   1.500 +        osd->Flush();
   1.501 +        }
   1.502 +#else
   1.503 +      fw=cOsd::CellWidth();
   1.504 +      fh=cOsd::LineHeight();
   1.505 +      bh=bigWin ? Setup.OSDheight : -2;
   1.506 +      bw=bwc=Setup.OSDwidth;
   1.507 +      rows=Setup.OSDheight-3;
   1.508 +      Interface->Open(bw,bh);
   1.509 +      Interface->Clear();
   1.510 +#endif
   1.511 +      ShowStatus(true);
   1.512 +      bigwin=bigWin;
   1.513 +      visible=true;
   1.514 +#if APIVERSNUM >= 10500
   1.515 +      SetNeedsFastResponse(true);
   1.516 +#else
   1.517 +      needsFastResponse=true;
   1.518 +#endif
   1.519 +      fliptime=listtime=0; flipint=0; flip=-1; top=lastTop=-1; lastIndex=lastTotal=-1;
   1.520 +      delete lastMode; lastMode=0;
   1.521 +      }
   1.522 +
   1.523 +    cMP3PlayInfo *mode=new cMP3PlayInfo;
   1.524 +    bool valid=mgr->Info(-1,mode);
   1.525 +    bool changed=(!lastMode || mode->Hash!=lastMode->Hash);
   1.526 +    char buf[256];
   1.527 +    if(changed) { d(printf("mp3-ctrl: mode change detected\n")) }
   1.528 +
   1.529 +    if(valid) { // send progress to status monitor
   1.530 +      if(changed || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle) {
   1.531 +        snprintf(buf,sizeof(buf),"[%c%c] (%d/%d) %s",
   1.532 +                  mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,TitleArtist(mode->Title,mode->Artist));
   1.533 +#if APIVERSNUM >= 10338
   1.534 +        cStatus::MsgReplaying(this,buf,mode->Filename[0]?mode->Filename:0,true);
   1.535 +#else
   1.536 +        cStatus::MsgReplaying(this,buf);
   1.537 +#endif
   1.538 +        }
   1.539 +      }
   1.540 +
   1.541 +    if(visible) { // refresh the OSD progress display
   1.542 +      bool flush=false;
   1.543 +
   1.544 +#if APIVERSNUM >= 10307
   1.545 +      if(MP3Setup.ReplayDisplay) {
   1.546 +        if(!statusActive) {
   1.547 +          if(total>0) disp->SetProgress(index,total);
   1.548 +          disp->SetCurrent(IndexToHMSF(index));
   1.549 +          disp->SetTotal(IndexToHMSF(total));
   1.550 +          bool Play, Forward;
   1.551 +          int Speed;
   1.552 +          if(GetReplayMode(Play,Forward,Speed)) 
   1.553 +            disp->SetMode(Play, Forward, Speed);
   1.554 +          flush=true;
   1.555 +          }
   1.556 +        }
   1.557 +      else {
   1.558 +#endif
   1.559 +        if(!selecting && changed && !statusActive) {
   1.560 +          snprintf(buf,sizeof(buf),"(%d/%d)",mode->Num,mode->MaxNum);
   1.561 +          Write(0,-2,CTAB,buf);
   1.562 +          flush=true;
   1.563 +          }
   1.564 +
   1.565 +        if(!lastMode || mode->Loop!=lastMode->Loop) {
   1.566 +          if(mode->Loop) Write(-4,-1,0,"L",clrBlack,clrYellow);
   1.567 +          else Fill(-4,-1,2,1,clrBackground);
   1.568 +          flush=true;
   1.569 +          }
   1.570 +        if(!lastMode || mode->Shuffle!=lastMode->Shuffle) {
   1.571 +          if(mode->Shuffle) Write(-2,-1,0,"S",clrWhite,clrRed);
   1.572 +          else Fill(-2,-1,2,1,clrBackground);
   1.573 +          flush=true;
   1.574 +          }
   1.575 +
   1.576 +        index/=framesPerSecond; total/=framesPerSecond;
   1.577 +        if(index!=lastIndex || total!=lastTotal) {
   1.578 +          if(total>0) {
   1.579 +#if APIVERSNUM >= 10307
   1.580 +            cProgressBar ProgressBar(bw-(CTAB+CTAB2)*fw,fh,index,total);
   1.581 +            osd->DrawBitmap(CTAB*fw,bh-fh,ProgressBar);
   1.582 +#else
   1.583 +            cProgressBar ProgressBar((bw-CTAB-CTAB2)*fw,fh,index,total);
   1.584 +            Interface->SetBitmap(CTAB*fw,(abs(bh)-1)*fh,ProgressBar);
   1.585 +#endif
   1.586 +            }
   1.587 +          snprintf(buf,sizeof(buf),total?"%02d:%02d/%02d:%02d":"%02d:%02d",index/60,index%60,total/60,total%60);
   1.588 +          Write(0,-1,11,buf);
   1.589 +          flush=true;
   1.590 +          }
   1.591 +#if APIVERSNUM >= 10307
   1.592 +        }
   1.593 +#endif
   1.594 +
   1.595 +      if(!jumpactive) {
   1.596 +        bool doflip=false;
   1.597 +        if(MP3Setup.ReplayDisplay && (!lastMode || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle))
   1.598 +          doflip=true;
   1.599 +        if(!valid || changed) {
   1.600 +          fliptime=time(0); flip=0;
   1.601 +	  doflip=true;
   1.602 +	  }
   1.603 +        else if(time(0)>fliptime+flipint) {
   1.604 +	  fliptime=time(0);
   1.605 +	  flip++; if(flip>=MP3Setup.DisplayMode) flip=0;
   1.606 +          doflip=true;
   1.607 +	  }
   1.608 +        if(doflip) {
   1.609 +          buf[0]=0;
   1.610 +          switch(flip) {
   1.611 +	    default:
   1.612 +	      flip=0;
   1.613 +	      // fall through
   1.614 +	    case 0:
   1.615 +	      snprintf(buf,sizeof(buf),"%s",TitleArtist(mode->Title,mode->Artist));
   1.616 +	      flipint=6;
   1.617 +	      break;
   1.618 +	    case 1:
   1.619 +              if(mode->Album[0]) {
   1.620 +      	        snprintf(buf,sizeof(buf),mode->Year>0?"from: %s (%d)":"from: %s",mode->Album,mode->Year);
   1.621 +	        flipint=4;
   1.622 +	        }
   1.623 +              else fliptime=0;
   1.624 +              break;
   1.625 +	    case 2:
   1.626 +              if(mode->MaxBitrate>0)
   1.627 +                snprintf(buf,sizeof(buf),"%.1f kHz, %d-%d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->MaxBitrate/1000,mode->SMode);
   1.628 +              else
   1.629 +                snprintf(buf,sizeof(buf),"%.1f kHz, %d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->SMode);
   1.630 +	      flipint=3;
   1.631 +	      break;
   1.632 +	    }
   1.633 +          if(buf[0]) {
   1.634 +#if APIVERSNUM >= 10307
   1.635 +            if(MP3Setup.ReplayDisplay) {
   1.636 +              char buf2[256];
   1.637 +              snprintf(buf2,sizeof(buf2),"[%c%c] (%d/%d) %s",
   1.638 +                       mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,buf);
   1.639 +              disp->SetTitle(buf2);
   1.640 +              flush=true;
   1.641 +              }
   1.642 +            else {
   1.643 +#endif
   1.644 +              if(!statusActive) {
   1.645 +                DisplayInfo(buf);
   1.646 +                flush=true;
   1.647 +                }
   1.648 +              else { d(printf("mp3-ctrl: display info skip due to status active\n")) }
   1.649 +#if APIVERSNUM >= 10307
   1.650 +              }
   1.651 +#endif
   1.652 +            }
   1.653 +          }
   1.654 +        }
   1.655 +
   1.656 +      if(bigwin) {
   1.657 +        bool all=(top!=lastTop || changed);
   1.658 +        if(all || time(0)>listtime+2) {
   1.659 +          int num=(top>0 && mode->Num==lastMode->Num) ? top : mode->Num - rows/2;
   1.660 +          if(num+rows>mode->MaxNum) num=mode->MaxNum-rows+1;
   1.661 +          if(num<1) num=1;
   1.662 +          top=num;
   1.663 +          for(int i=0 ; i<rows && i<MAXROWS && num<=mode->MaxNum ; i++,num++) {
   1.664 +            cMP3PlayInfo pi;
   1.665 +            mgr->Info(num,&pi); if(!pi.Title[0]) break;
   1.666 +            snprintf(buf,sizeof(buf),"%d.\t%s",num,TitleArtist(pi.Title,pi.Artist));
   1.667 +            eDvbColor fg=clrWhite, bg=clrBackground;
   1.668 +            int hash=MakeHash(buf);
   1.669 +            if(num==mode->Num) { fg=clrBlack; bg=clrCyan; hash=(hash^77) + 23; }
   1.670 +            if(all || hash!=hashlist[i]) {
   1.671 +              char *s=rindex(buf,'\t');
   1.672 +              if(s) {
   1.673 +                *s++=0;
   1.674 +                Write(0,i,5,buf,fg,bg);
   1.675 +                Write(5,i,bwc-5,s,fg,bg);
   1.676 +                }
   1.677 +              else
   1.678 +                Write(0,i,bwc,buf,fg,bg);
   1.679 +              flush=true;
   1.680 +              hashlist[i]=hash;
   1.681 +              }
   1.682 +            }
   1.683 +          listtime=time(0); lastTop=top;
   1.684 +          }
   1.685 +        }
   1.686 +
   1.687 +      if(flush) Flush();
   1.688 +      }
   1.689 +
   1.690 +    lastIndex=index; lastTotal=total;
   1.691 +    delete lastMode; lastMode=mode;
   1.692 +    }
   1.693 +}
   1.694 +
   1.695 +void cMP3Control::DisplayInfo(const char *s)
   1.696 +{
   1.697 +  if(s) Write(CTAB,-2,bwc-CTAB,s);
   1.698 +  else Fill(CTAB,-2,bwc-CTAB,1,clrBackground);
   1.699 +}
   1.700 +
   1.701 +void cMP3Control::JumpDisplay(void)
   1.702 +{
   1.703 +  char buf[64];
   1.704 +  const char *j=tr("Jump: "), u=jumpsecs?'s':'m';
   1.705 +  if(!jumpmm) sprintf(buf,"%s- %c",  j,u);
   1.706 +  else        sprintf(buf,"%s%d- %c",j,jumpmm,u);
   1.707 +#if APIVERSNUM >= 10307
   1.708 +  if(MP3Setup.ReplayDisplay) {
   1.709 +    disp->SetJump(buf);
   1.710 +    }
   1.711 +  else {
   1.712 +#endif
   1.713 +    DisplayInfo(buf);
   1.714 +#if APIVERSNUM >= 10307
   1.715 +    }
   1.716 +#endif
   1.717 +}
   1.718 +
   1.719 +void cMP3Control::JumpProcess(eKeys Key)
   1.720 +{
   1.721 + int n=Key-k0, d=jumpsecs?1:60;
   1.722 +  switch (Key) {
   1.723 +    case k0 ... k9:
   1.724 +      if(jumpmm*10+n <= lastTotal/d) jumpmm=jumpmm*10+n;
   1.725 +      JumpDisplay();
   1.726 +      break;
   1.727 +    case kBlue:
   1.728 +      jumpsecs=!jumpsecs;
   1.729 +      JumpDisplay();
   1.730 +      break;
   1.731 +    case kPlay:
   1.732 +    case kUp:
   1.733 +      jumpmm-=lastIndex/d;
   1.734 +      // fall through
   1.735 +    case kFastRew:
   1.736 +    case kFastFwd:
   1.737 +    case kLeft:
   1.738 +    case kRight:
   1.739 +      player->SkipSeconds(jumpmm*d * ((Key==kLeft || Key==kFastRew) ? -1:1));
   1.740 +      // fall through
   1.741 +    default:
   1.742 +      jumpactive=false;
   1.743 +      break;
   1.744 +    }
   1.745 +
   1.746 +  if(!jumpactive) {
   1.747 +    if(jumphide) Hide();
   1.748 +#if APIVERSNUM >= 10307
   1.749 +    else if(MP3Setup.ReplayDisplay) disp->SetJump(0);
   1.750 +#endif
   1.751 +    }
   1.752 +}
   1.753 +
   1.754 +void cMP3Control::Jump(void)
   1.755 +{
   1.756 +  jumpmm=0; jumphide=jumpsecs=false;
   1.757 +  if(!visible) {
   1.758 +    ShowTimed(); if(!visible) return;
   1.759 +    jumphide=true;
   1.760 +    }
   1.761 +  JumpDisplay();
   1.762 +  jumpactive=true; fliptime=0; flip=-1;
   1.763 +}
   1.764 +
   1.765 +eOSState cMP3Control::ProcessKey(eKeys Key)
   1.766 +{
   1.767 +  if(!player->Active()) return osEnd;
   1.768 +
   1.769 +  if(visible && timeoutShow && time(0)>timeoutShow) { Hide(); timeoutShow=0; }
   1.770 +  ShowProgress();
   1.771 +#if APIVERSNUM >= 10307
   1.772 +  ShowStatus(Key==kNone && !Skins.IsOpen());
   1.773 +#else
   1.774 +  ShowStatus(Key==kNone && !Interface->IsOpen());
   1.775 +#endif
   1.776 +
   1.777 +  if(jumpactive && Key!=kNone) { JumpProcess(Key); return osContinue; }
   1.778 +
   1.779 +  switch(Key) {
   1.780 +    case kUp:
   1.781 +    case kUp|k_Repeat:
   1.782 +#if APIVERSNUM >= 10347
   1.783 +    case kNext:
   1.784 +    case kNext|k_Repeat:    
   1.785 +#endif
   1.786 +      mgr->Next(); player->Play();
   1.787 +      break;
   1.788 +    case kDown:
   1.789 +    case kDown|k_Repeat:
   1.790 +#if APIVERSNUM >= 10347
   1.791 +    case kPrev:
   1.792 +    case kPrev|k_Repeat:
   1.793 +#endif
   1.794 +      if(!player->PrevCheck()) mgr->Prev();
   1.795 +      player->Play();
   1.796 +      break;
   1.797 +    case kLeft:
   1.798 +    case kLeft|k_Repeat:
   1.799 +      if(bigwin) {
   1.800 +        if(top>0) { top-=rows; if(top<1) top=1; }
   1.801 +        break;
   1.802 +        }
   1.803 +      // fall through
   1.804 +    case kFastRew:
   1.805 +    case kFastRew|k_Repeat:
   1.806 +      if(!player->IsStream()) player->SkipSeconds(-JUMPSIZE);
   1.807 +      break;
   1.808 +    case kRight:
   1.809 +    case kRight|k_Repeat:
   1.810 +      if(bigwin) {
   1.811 +        if(top>0) top+=rows;
   1.812 +        break;
   1.813 +        }
   1.814 +      // fall through
   1.815 +    case kFastFwd:
   1.816 +    case kFastFwd|k_Repeat:
   1.817 +      if(!player->IsStream()) player->SkipSeconds(JUMPSIZE);
   1.818 +      break;
   1.819 +    case kRed:
   1.820 +      if(!player->IsStream()) Jump();
   1.821 +      break;
   1.822 +    case kGreen:
   1.823 +      if(lastMode) {
   1.824 +        if(time(0)>greentime) {
   1.825 +          if(lastMode->Loop || (!lastMode->Loop && !lastMode->Shuffle)) mgr->ToggleLoop();
   1.826 +          if(lastMode->Shuffle) mgr->ToggleShuffle();
   1.827 +          }
   1.828 +        else {
   1.829 +          if(!lastMode->Loop) mgr->ToggleLoop();
   1.830 +          else if(!lastMode->Shuffle) mgr->ToggleShuffle();
   1.831 +          else mgr->ToggleLoop();
   1.832 +          }
   1.833 +        greentime=time(0)+MULTI_TIMEOUT;
   1.834 +        }
   1.835 +      break;
   1.836 +    case kPlay:
   1.837 +      player->Play();
   1.838 +      break;
   1.839 +    case kPause:
   1.840 +    case kYellow:
   1.841 +      if(!player->IsStream()) player->Pause();
   1.842 +      break;
   1.843 +    case kStop:
   1.844 +    case kBlue:
   1.845 +      Hide();
   1.846 +      Stop();
   1.847 +      return osEnd;
   1.848 +    case kBack:
   1.849 +      Hide();
   1.850 +#if APIVERSNUM >= 10332
   1.851 +      cRemote::CallPlugin(i18n_name);
   1.852 +      return osBack;
   1.853 +#else
   1.854 +      return osEnd;
   1.855 +#endif
   1.856 +
   1.857 +    case k0 ... k9:
   1.858 +      number=number*10+Key-k0;
   1.859 +      if(lastMode && number>0 && number<=lastMode->MaxNum) {
   1.860 +        if(!visible) { ShowTimed(); selecthide=true; }
   1.861 +        selecting=true; lastkeytime=time_ms();
   1.862 +        char buf[32];
   1.863 +#if APIVERSNUM >= 10307
   1.864 +        if(MP3Setup.ReplayDisplay) {
   1.865 +          snprintf(buf,sizeof(buf),"%s%d-/%d",tr("Jump: "),number,lastMode->MaxNum);
   1.866 +          disp->SetJump(buf);
   1.867 +          }
   1.868 +        else {
   1.869 +#endif
   1.870 +          snprintf(buf,sizeof(buf),"(%d-/%d)",number,lastMode->MaxNum);
   1.871 +          Write(0,-2,CTAB,buf);
   1.872 +#if APIVERSNUM >= 10307
   1.873 +          }
   1.874 +#endif
   1.875 +        Flush();
   1.876 +        break;
   1.877 +        }
   1.878 +      number=0; lastkeytime=0;
   1.879 +      // fall through
   1.880 +    case kNone:
   1.881 +      if(selecting && time_ms()-lastkeytime>SELECT_TIMEOUT) {
   1.882 +        if(number>0) { mgr->Goto(number); player->Play();  }
   1.883 +        if(selecthide) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
   1.884 +        if(lastMode) lastMode->Hash=-1;
   1.885 +        number=0; selecting=selecthide=false;
   1.886 +#if APIVERSNUM >= 10307
   1.887 +        if(MP3Setup.ReplayDisplay) disp->SetJump(0);
   1.888 +#endif
   1.889 +        }
   1.890 +      break;
   1.891 +    case kOk:
   1.892 +      if(time(0)>oktime || MP3Setup.ReplayDisplay) {
   1.893 +        visible ? Hide() : ShowTimed();
   1.894 +        }
   1.895 +      else {
   1.896 +        if(visible && !bigwin) { Hide(); ShowProgress(true,true); }
   1.897 +        else { Hide(); ShowTimed(); }
   1.898 +        }
   1.899 +      oktime=time(0)+MULTI_TIMEOUT;
   1.900 +      ShowStatus(true);
   1.901 +      break;
   1.902 +    default:
   1.903 +      return osUnknown;
   1.904 +    }
   1.905 +  return osContinue;
   1.906 +}
   1.907 +
   1.908 +// --- cMenuID3Info ------------------------------------------------------------
   1.909 +
   1.910 +class cMenuID3Info : public cOsdMenu {
   1.911 +private:
   1.912 +  cOsdItem *Item(const char *name, const char *text);
   1.913 +  cOsdItem *Item(const char *name, const char *format, const float num);
   1.914 +  void Build(cSongInfo *info, const char *name);
   1.915 +public:
   1.916 +  cMenuID3Info(cSong *song);
   1.917 +  cMenuID3Info(cSongInfo *si, const char *name);
   1.918 +  virtual eOSState ProcessKey(eKeys Key);
   1.919 +  };
   1.920 +
   1.921 +cMenuID3Info::cMenuID3Info(cSong *song)
   1.922 +:cOsdMenu(tr("ID3 information"),12)
   1.923 +{
   1.924 +  Build(song->Info(),song->Name());
   1.925 +}
   1.926 +
   1.927 +cMenuID3Info::cMenuID3Info(cSongInfo *si, const char *name)
   1.928 +:cOsdMenu(tr("ID3 information"),12)
   1.929 +{
   1.930 +  Build(si,name);
   1.931 +}
   1.932 +
   1.933 +void cMenuID3Info::Build(cSongInfo *si, const char *name)
   1.934 +{
   1.935 +  if(si) {
   1.936 +    Item(tr("Filename"),name);
   1.937 +    if(si->HasInfo() && si->Total>0) {
   1.938 +      char *buf=0;
   1.939 +      asprintf(&buf,"%02d:%02d",si->Total/60,si->Total%60);
   1.940 +      Item(tr("Length"),buf);
   1.941 +      free(buf);
   1.942 +      Item(tr("Title"),si->Title);
   1.943 +      Item(tr("Artist"),si->Artist);
   1.944 +      Item(tr("Album"),si->Album);
   1.945 +      Item(tr("Year"),0,(float)si->Year);
   1.946 +      Item(tr("Samplerate"),"%.1f kHz",si->SampleFreq/1000.0);
   1.947 +      Item(tr("Bitrate"),"%.f kbit/s",si->Bitrate/1000.0);
   1.948 +      Item(tr("Channels"),0,(float)si->Channels);
   1.949 +      }
   1.950 +    Display();
   1.951 +    }
   1.952 +}
   1.953 +
   1.954 +cOsdItem *cMenuID3Info::Item(const char *name, const char *format, const float num)
   1.955 +{
   1.956 +  cOsdItem *item;
   1.957 +  if(num>=0.0) {
   1.958 +    char *buf=0;
   1.959 +    asprintf(&buf,format?format:"%.f",num);
   1.960 +    item=Item(name,buf);
   1.961 +    free(buf);
   1.962 +    }
   1.963 +  else item=Item(name,"");
   1.964 +  return item;
   1.965 +}
   1.966 +
   1.967 +cOsdItem *cMenuID3Info::Item(const char *name, const char *text)
   1.968 +{
   1.969 +  char *buf=0;
   1.970 +  asprintf(&buf,"%s:\t%s",name,text?text:"");
   1.971 +  cOsdItem *item = new cOsdItem(buf,osBack);
   1.972 +#if APIVERSNUM >= 10307
   1.973 +  item->SetSelectable(false);
   1.974 +#else
   1.975 +  item->SetColor(clrWhite, clrBackground);
   1.976 +#endif
   1.977 +  free(buf);
   1.978 +  Add(item); return item;
   1.979 +}
   1.980 +
   1.981 +eOSState cMenuID3Info::ProcessKey(eKeys Key)
   1.982 +{
   1.983 +  eOSState state = cOsdMenu::ProcessKey(Key);
   1.984 +
   1.985 +  if(state==osUnknown) {
   1.986 +     switch(Key) {
   1.987 +       case kRed:
   1.988 +       case kGreen:
   1.989 +       case kYellow:
   1.990 +       case kBlue:   return osContinue;
   1.991 +       case kMenu:   return osEnd;
   1.992 +       default: break;
   1.993 +       }
   1.994 +     }
   1.995 +  return state;
   1.996 +}
   1.997 +
   1.998 +// --- cMenuInstantBrowse -------------------------------------------------------
   1.999 +
  1.1000 +class cMenuInstantBrowse : public cMenuBrowse {
  1.1001 +private:
  1.1002 +  const char *selecttext, *alltext;
  1.1003 +  virtual void SetButtons(void);
  1.1004 +  virtual eOSState ID3Info(void);
  1.1005 +public:
  1.1006 +  cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext);
  1.1007 +  virtual eOSState ProcessKey(eKeys Key);
  1.1008 +  };
  1.1009 +
  1.1010 +cMenuInstantBrowse::cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext)
  1.1011 +:cMenuBrowse(Source,true,true,tr("Directory browser"),excl_br)
  1.1012 +{
  1.1013 +  selecttext=Selecttext; alltext=Alltext;
  1.1014 +  SetButtons();
  1.1015 +}
  1.1016 +
  1.1017 +void cMenuInstantBrowse::SetButtons(void)
  1.1018 +{
  1.1019 +  SetHelp(selecttext, currentdir?tr("Parent"):0, currentdir?0:alltext, tr("ID3 info"));
  1.1020 +  Display();
  1.1021 +}
  1.1022 +
  1.1023 +eOSState cMenuInstantBrowse::ID3Info(void)
  1.1024 +{
  1.1025 +  cFileObj *item=CurrentItem();
  1.1026 +  if(item && item->Type()==otFile) {
  1.1027 +    cSong *song=new cSong(item);
  1.1028 +    cSongInfo *si;
  1.1029 +    if(song && (si=song->Info())) {
  1.1030 +      AddSubMenu(new cMenuID3Info(si,item->Path()));
  1.1031 +      }
  1.1032 +    delete song;
  1.1033 +    }
  1.1034 +  return osContinue;
  1.1035 +}
  1.1036 +
  1.1037 +eOSState cMenuInstantBrowse::ProcessKey(eKeys Key)
  1.1038 +{
  1.1039 +  eOSState state=cOsdMenu::ProcessKey(Key);
  1.1040 +  if(state==osUnknown) {
  1.1041 +     switch (Key) {
  1.1042 +       case kYellow: lastselect=new cFileObj(source,0,0,otBase);
  1.1043 +                     return osBack;
  1.1044 +       default: break;
  1.1045 +       }
  1.1046 +     }
  1.1047 +  if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
  1.1048 +  return state;
  1.1049 +}
  1.1050 +
  1.1051 +// --- cMenuPlayListItem -------------------------------------------------------
  1.1052 +
  1.1053 +class cMenuPlayListItem : public cOsdItem {
  1.1054 +  private:
  1.1055 +  bool showID3;
  1.1056 +  cSong *song;
  1.1057 +public:
  1.1058 +  cMenuPlayListItem(cSong *Song, bool showid3);
  1.1059 +  cSong *Song(void) { return song; }
  1.1060 +  virtual void Set(void);
  1.1061 +  void Set(bool showid3);
  1.1062 +  };
  1.1063 +
  1.1064 +cMenuPlayListItem::cMenuPlayListItem(cSong *Song, bool showid3)
  1.1065 +{
  1.1066 +  song=Song;
  1.1067 +  Set(showid3);
  1.1068 +}
  1.1069 +
  1.1070 +void cMenuPlayListItem::Set(bool showid3)
  1.1071 +{
  1.1072 +  showID3=showid3;
  1.1073 +  Set();
  1.1074 +}
  1.1075 +
  1.1076 +void cMenuPlayListItem::Set(void)
  1.1077 +{
  1.1078 +  char *buffer=0;
  1.1079 +  cSongInfo *si=song->Info(false);
  1.1080 +  if(showID3 && !si) si=song->Info();
  1.1081 +  if(showID3 && si && si->Title)
  1.1082 +    asprintf(&buffer, "%d.\t%s",song->Index()+1,TitleArtist(si->Title,si->Artist));
  1.1083 +  else
  1.1084 +    asprintf(&buffer, "%d.\t<%s>",song->Index()+1,song->Name());
  1.1085 +  SetText(buffer,false);
  1.1086 +}
  1.1087 +
  1.1088 +// --- cMenuPlayList ------------------------------------------------------
  1.1089 +
  1.1090 +class cMenuPlayList : public cOsdMenu {
  1.1091 +private:
  1.1092 +  cPlayList *playlist;
  1.1093 +  bool browsing, showid3;
  1.1094 +  void Buttons(void);
  1.1095 +  void Refresh(bool all = false);
  1.1096 +  void Add(void);
  1.1097 +  virtual void Move(int From, int To);
  1.1098 +  eOSState Remove(void);
  1.1099 +  eOSState ShowID3(void);
  1.1100 +  eOSState ID3Info(void);
  1.1101 +public:
  1.1102 +  cMenuPlayList(cPlayList *Playlist);
  1.1103 +  virtual eOSState ProcessKey(eKeys Key);
  1.1104 +  };
  1.1105 +
  1.1106 +cMenuPlayList::cMenuPlayList(cPlayList *Playlist)
  1.1107 +:cOsdMenu(tr("Playlist editor"),4)
  1.1108 +{
  1.1109 +  browsing=showid3=false;
  1.1110 +  playlist=Playlist;
  1.1111 +  if(MP3Setup.EditorMode) showid3=true;
  1.1112 +
  1.1113 +  cSong *mp3 = playlist->First();
  1.1114 +  while(mp3) {
  1.1115 +    cOsdMenu::Add(new cMenuPlayListItem(mp3,showid3));
  1.1116 +    mp3 = playlist->cList<cSong>::Next(mp3);
  1.1117 +    }
  1.1118 +  Buttons(); Display();
  1.1119 +}
  1.1120 +
  1.1121 +void cMenuPlayList::Buttons(void)
  1.1122 +{
  1.1123 +  SetHelp(tr("Add"), showid3?tr("Filenames"):tr("ID3 names"), tr("Remove"), tr(BUTTON"Mark"));
  1.1124 +}
  1.1125 +
  1.1126 +void cMenuPlayList::Refresh(bool all)
  1.1127 +{
  1.1128 +  cMenuPlayListItem *cur=(cMenuPlayListItem *)((all || Count()<2) ? First() : Get(Current()));
  1.1129 +  while(cur) {
  1.1130 +    cur->Set(showid3);
  1.1131 +    cur=(cMenuPlayListItem *)Next(cur);
  1.1132 +    }
  1.1133 +}
  1.1134 +
  1.1135 +void cMenuPlayList::Add(void)
  1.1136 +{
  1.1137 +  cFileObj *item=cMenuInstantBrowse::GetSelected();
  1.1138 +  if(item) {
  1.1139 +    Status(tr("Scanning directory..."));
  1.1140 +    cInstantPlayList *newpl=new cInstantPlayList(item);
  1.1141 +    if(newpl->Load()) {
  1.1142 +      if(newpl->Count()) {
  1.1143 +        if(newpl->Count()==1 || Interface->Confirm(tr("Add recursivly?"))) {
  1.1144 +          cSong *mp3=newpl->First();
  1.1145 +          while(mp3) {
  1.1146 +            cSong *n=new cSong(mp3);
  1.1147 +            if(Count()>0) {
  1.1148 +              cMenuPlayListItem *current=(cMenuPlayListItem *)Get(Current());
  1.1149 +              playlist->Add(n,current->Song());
  1.1150 +              cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true,current);
  1.1151 +              }
  1.1152 +            else {
  1.1153 +              playlist->Add(n);
  1.1154 +              cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true);
  1.1155 +              }
  1.1156 +            mp3=newpl->cList<cSong>::Next(mp3);
  1.1157 +            }
  1.1158 +          playlist->Save();
  1.1159 +          Refresh(); Display();
  1.1160 +          }
  1.1161 +        }
  1.1162 +      else Error(tr("Empty directory!"));
  1.1163 +      }
  1.1164 +    else Error(tr("Error scanning directory!"));
  1.1165 +    delete newpl;
  1.1166 +    Status(0);
  1.1167 +    }
  1.1168 +}
  1.1169 +
  1.1170 +void cMenuPlayList::Move(int From, int To)
  1.1171 +{
  1.1172 +  playlist->Move(From,To); playlist->Save();
  1.1173 +  cOsdMenu::Move(From,To);
  1.1174 +  Refresh(true); Display();
  1.1175 +}
  1.1176 +
  1.1177 +eOSState cMenuPlayList::ShowID3(void)
  1.1178 +{
  1.1179 +  showid3=!showid3;
  1.1180 +  Buttons(); Refresh(true); Display();
  1.1181 +  return osContinue;
  1.1182 +}
  1.1183 +
  1.1184 +eOSState cMenuPlayList::ID3Info(void)
  1.1185 +{
  1.1186 +  if(Count()>0) {
  1.1187 +    cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
  1.1188 +    AddSubMenu(new cMenuID3Info(current->Song()));
  1.1189 +    }
  1.1190 +  return osContinue;
  1.1191 +}
  1.1192 +
  1.1193 +eOSState cMenuPlayList::Remove(void)
  1.1194 +{
  1.1195 +  if(Count()>0) {
  1.1196 +    cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
  1.1197 +    if(Interface->Confirm(tr("Remove entry?"))) {
  1.1198 +      playlist->Del(current->Song()); playlist->Save();
  1.1199 +      cOsdMenu::Del(Current());
  1.1200 +      Refresh(); Display();
  1.1201 +      }
  1.1202 +    }
  1.1203 +  return osContinue;
  1.1204 +}
  1.1205 +
  1.1206 +eOSState cMenuPlayList::ProcessKey(eKeys Key)
  1.1207 +{
  1.1208 +  eOSState state = cOsdMenu::ProcessKey(Key);
  1.1209 +
  1.1210 +  if(browsing && !HasSubMenu() && state==osContinue) { Add(); browsing=false; }
  1.1211 +
  1.1212 +  if(state==osUnknown) {
  1.1213 +     switch(Key) {
  1.1214 +       case kOk:     return ID3Info();
  1.1215 +       case kRed:    browsing=true;
  1.1216 +                     return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr("Add"),tr("Add all")));
  1.1217 +       case kGreen:  return ShowID3();
  1.1218 +       case kYellow: return Remove();
  1.1219 +       case kBlue:   Mark(); return osContinue;
  1.1220 +       case kMenu:   return osEnd;
  1.1221 +       default: break;
  1.1222 +       }
  1.1223 +     }
  1.1224 +  return state;
  1.1225 +}
  1.1226 +
  1.1227 +// --- cPlaylistRename --------------------------------------------------------
  1.1228 +
  1.1229 +class cPlaylistRename : public cOsdMenu {
  1.1230 +private:
  1.1231 +  static char *newname;
  1.1232 +  const char *oldname;
  1.1233 +  char data[64];
  1.1234 +public:
  1.1235 +  cPlaylistRename(const char *Oldname);
  1.1236 +  virtual eOSState ProcessKey(eKeys Key);
  1.1237 +  static const char *GetNewname(void) { return newname; }
  1.1238 +  };
  1.1239 +
  1.1240 +char *cPlaylistRename::newname = NULL;
  1.1241 +
  1.1242 +cPlaylistRename::cPlaylistRename(const char *Oldname)
  1.1243 +:cOsdMenu(tr("Rename playlist"), 15)
  1.1244 +{
  1.1245 +  free(newname); newname=0;
  1.1246 +
  1.1247 +  oldname=Oldname;
  1.1248 +  char *buf=NULL;
  1.1249 +  asprintf(&buf,"%s\t%s",tr("Old name:"),oldname);
  1.1250 +  cOsdItem *old = new cOsdItem(buf,osContinue);
  1.1251 +#if APIVERSNUM >= 10307
  1.1252 +  old->SetSelectable(false);
  1.1253 +#else
  1.1254 +  old->SetColor(clrWhite, clrBackground);
  1.1255 +#endif
  1.1256 +  Add(old);
  1.1257 +  free(buf);
  1.1258 +
  1.1259 +  data[0]=0;
  1.1260 +  Add(new cMenuEditStrItem( tr("New name"), data, sizeof(data)-1, tr(FileNameChars)),true);
  1.1261 +}
  1.1262 +
  1.1263 +eOSState cPlaylistRename::ProcessKey(eKeys Key)
  1.1264 +{
  1.1265 +  eOSState state = cOsdMenu::ProcessKey(Key);
  1.1266 +
  1.1267 +  if (state == osUnknown) {
  1.1268 +     switch (Key) {
  1.1269 +       case kOk:     if(data[0] && strcmp(data,oldname)) newname=strdup(data);
  1.1270 +                     return osBack;
  1.1271 +       case kRed:
  1.1272 +       case kGreen:
  1.1273 +       case kYellow:
  1.1274 +       case kBlue:   return osContinue;
  1.1275 +       default: break;
  1.1276 +       }
  1.1277 +     }
  1.1278 +  return state;
  1.1279 +}
  1.1280 +
  1.1281 +// --- cMenuMP3Item -----------------------------------------------------
  1.1282 +
  1.1283 +class cMenuMP3Item : public cOsdItem {
  1.1284 +  private:
  1.1285 +  cPlayList *playlist;
  1.1286 +  virtual void Set(void);
  1.1287 +public:
  1.1288 +  cMenuMP3Item(cPlayList *PlayList);
  1.1289 +  cPlayList *List(void) { return playlist; }
  1.1290 +  };
  1.1291 +
  1.1292 +cMenuMP3Item::cMenuMP3Item(cPlayList *PlayList)
  1.1293 +{
  1.1294 +  playlist=PlayList;
  1.1295 +  Set();
  1.1296 +}
  1.1297 +
  1.1298 +void cMenuMP3Item::Set(void)
  1.1299 +{
  1.1300 +  char *buffer=0;
  1.1301 +  asprintf(&buffer," %s",playlist->BaseName());
  1.1302 +  SetText(buffer,false);
  1.1303 +}
  1.1304 +
  1.1305 +// --- cMenuMP3 --------------------------------------------------------
  1.1306 +
  1.1307 +class cMenuMP3 : public cOsdMenu {
  1.1308 +private:
  1.1309 +  cPlayLists *lists;
  1.1310 +  bool renaming, sourcing, instanting;
  1.1311 +  int buttonnum;
  1.1312 +  eOSState Play(void);
  1.1313 +  eOSState Edit(void);
  1.1314 +  eOSState New(void);
  1.1315 +  eOSState Delete(void);
  1.1316 +  eOSState Rename(bool second);
  1.1317 +  eOSState Source(bool second);
  1.1318 +  eOSState Instant(bool second);
  1.1319 +  void ScanLists(void);
  1.1320 +  eOSState SetButtons(int num);
  1.1321 +public:
  1.1322 +  cMenuMP3(void);
  1.1323 +  ~cMenuMP3(void);
  1.1324 +  virtual eOSState ProcessKey(eKeys Key);
  1.1325 +  };
  1.1326 +
  1.1327 +cMenuMP3::cMenuMP3(void)
  1.1328 +:cOsdMenu(tr("MP3"))
  1.1329 +{
  1.1330 +  renaming=sourcing=instanting=false;
  1.1331 +  lists=new cPlayLists;
  1.1332 +  ScanLists(); SetButtons(1);
  1.1333 +  if(MP3Setup.MenuMode) Instant(false);
  1.1334 +}
  1.1335 +
  1.1336 +cMenuMP3::~cMenuMP3(void)
  1.1337 +{
  1.1338 +  delete lists;
  1.1339 +}
  1.1340 +
  1.1341 +eOSState cMenuMP3::SetButtons(int num)
  1.1342 +{
  1.1343 +  switch(num) {
  1.1344 +    case 1:
  1.1345 +      SetHelp(tr(BUTTON"Edit"), tr("Source"), tr("Browse"), ">>");
  1.1346 +      break;
  1.1347 +    case 2:
  1.1348 +      SetHelp("<<", tr(BUTTON"New"), tr(BUTTON"Delete"), tr("Rename"));
  1.1349 +      break;
  1.1350 +    }
  1.1351 +  buttonnum=num; Display();
  1.1352 +  return osContinue;
  1.1353 +}
  1.1354 +
  1.1355 +void cMenuMP3::ScanLists(void)
  1.1356 +{
  1.1357 +  Clear();
  1.1358 +  Status(tr("Scanning playlists..."));
  1.1359 +  bool res=lists->Load(MP3Sources.GetSource());
  1.1360 +  Status(0);
  1.1361 +  if(res) {
  1.1362 +    cPlayList *plist=lists->First();
  1.1363 +    while(plist) {
  1.1364 +      Add(new cMenuMP3Item(plist));
  1.1365 +      plist=lists->Next(plist);
  1.1366 +      }
  1.1367 +    }
  1.1368 +  else Error(tr("Error scanning playlists!"));
  1.1369 +}
  1.1370 +
  1.1371 +eOSState cMenuMP3::Delete(void)
  1.1372 +{
  1.1373 +  if(Count()>0) {
  1.1374 +    if(Interface->Confirm(tr("Delete playlist?")) &&
  1.1375 +       Interface->Confirm(tr("Are you sure?")) ) {
  1.1376 +      cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
  1.1377 +      if(plist->Delete()) {
  1.1378 +        lists->Del(plist);
  1.1379 +        cOsdMenu::Del(Current());
  1.1380 +        Display();
  1.1381 +        }
  1.1382 +      else Error(tr("Error deleting playlist!"));
  1.1383 +      }
  1.1384 +    }
  1.1385 +  return osContinue;
  1.1386 +}
  1.1387 +
  1.1388 +eOSState cMenuMP3::New(void)
  1.1389 +{
  1.1390 +  cPlayList *plist=new cPlayList(MP3Sources.GetSource(),0,0);
  1.1391 +  char name[32];
  1.1392 +  int i=0;
  1.1393 +  do {
  1.1394 +    if(i) sprintf(name,"%s%d",tr("unnamed"),i++);
  1.1395 +    else { strcpy(name,tr("unnamed")); i++; }
  1.1396 +    } while(plist->TestName(name));
  1.1397 +
  1.1398 +  if(plist->Create(name)) {
  1.1399 +    lists->Add(plist);
  1.1400 +    Add(new cMenuMP3Item(plist), true);
  1.1401 +
  1.1402 +    isyslog("MP3: playlist %s added", plist->Name());
  1.1403 +    return AddSubMenu(new cMenuPlayList(plist));
  1.1404 +    }
  1.1405 +  Error(tr("Error creating playlist!"));
  1.1406 +  delete plist;
  1.1407 +  return osContinue;
  1.1408 +}
  1.1409 +
  1.1410 +eOSState cMenuMP3::Rename(bool second)
  1.1411 +{
  1.1412 +  if(HasSubMenu() || Count() == 0) return osContinue;
  1.1413 +
  1.1414 +  cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
  1.1415 +  if(!second) {
  1.1416 +    renaming=true;
  1.1417 +    return AddSubMenu(new cPlaylistRename(plist->BaseName()));
  1.1418 +    }
  1.1419 +  renaming=false;
  1.1420 +  const char *newname=cPlaylistRename::GetNewname();
  1.1421 +  if(newname) {
  1.1422 +    if(plist->Rename(newname)) {
  1.1423 +      RefreshCurrent();
  1.1424 +      DisplayCurrent(true);
  1.1425 +      }
  1.1426 +    else Error(tr("Error renaming playlist!"));
  1.1427 +    }
  1.1428 +  return osContinue;
  1.1429 +}
  1.1430 +
  1.1431 +eOSState cMenuMP3::Edit(void)
  1.1432 +{
  1.1433 +  if(HasSubMenu() || Count() == 0) return osContinue;
  1.1434 +
  1.1435 +  cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
  1.1436 +  if(!plist->Load()) Error(tr("Error loading playlist!"));
  1.1437 +  else if(!plist->IsWinAmp()) {
  1.1438 +    isyslog("MP3: editing playlist %s", plist->Name());
  1.1439 +    return AddSubMenu(new cMenuPlayList(plist));
  1.1440 +    }
  1.1441 +  else Error(tr("Can't edit a WinAmp playlist!"));
  1.1442 +  return osContinue;
  1.1443 +}
  1.1444 +
  1.1445 +eOSState cMenuMP3::Play(void)
  1.1446 +{
  1.1447 +  if(HasSubMenu() || Count() == 0) return osContinue;
  1.1448 +
  1.1449 +  Status(tr("Loading playlist..."));
  1.1450 +  cPlayList *newpl=new cPlayList(((cMenuMP3Item *)Get(Current()))->List());
  1.1451 +  if(newpl->Load() && newpl->Count()) {
  1.1452 +    isyslog("mp3: playback started with playlist %s", newpl->Name());
  1.1453 +    cMP3Control::SetPlayList(newpl);
  1.1454 +    if(MP3Setup.KeepSelect) { Status(0); return osContinue; }
  1.1455 +    return osEnd;
  1.1456 +    }
  1.1457 +  Status(0);
  1.1458 +  delete newpl;
  1.1459 +  Error(tr("Error loading playlist!"));
  1.1460 +  return osContinue;
  1.1461 +}
  1.1462 +
  1.1463 +eOSState cMenuMP3::Source(bool second)
  1.1464 +{
  1.1465 +  if(HasSubMenu()) return osContinue;
  1.1466 +
  1.1467 +  if(!second) {
  1.1468 +    sourcing=true;
  1.1469 +    return AddSubMenu(new cMenuSource(&MP3Sources,tr("MP3 source")));
  1.1470 +    }
  1.1471 +  sourcing=false;
  1.1472 +  cFileSource *src=cMenuSource::GetSelected();
  1.1473 +  if(src) {
  1.1474 +    MP3Sources.SetSource(src);
  1.1475 +    ScanLists();
  1.1476 +    Display();
  1.1477 +    }
  1.1478 +  return osContinue;
  1.1479 +}
  1.1480 +
  1.1481 +eOSState cMenuMP3::Instant(bool second)
  1.1482 +{
  1.1483 +  if(HasSubMenu()) return osContinue;
  1.1484 +
  1.1485 +  if(!second) {
  1.1486 +    instanting=true;
  1.1487 +    return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr(BUTTON"Play"),tr("Play all")));
  1.1488 +    }
  1.1489 +  instanting=false;
  1.1490 +  cFileObj *item=cMenuInstantBrowse::GetSelected();
  1.1491 +  if(item) {
  1.1492 +    Status(tr("Building playlist..."));
  1.1493 +    cInstantPlayList *newpl = new cInstantPlayList(item);
  1.1494 +    if(newpl->Load() && newpl->Count()) {
  1.1495 +      isyslog("mp3: playback started with instant playlist %s", newpl->Name());
  1.1496 +      cMP3Control::SetPlayList(newpl);
  1.1497 +      if(MP3Setup.KeepSelect) { Status(0); return Instant(false); }
  1.1498 +      return osEnd;
  1.1499 +      }
  1.1500 +    Status(0);
  1.1501 +    delete newpl;
  1.1502 +    Error(tr("Error building playlist!"));
  1.1503 +    }
  1.1504 +  return osContinue;
  1.1505 +}
  1.1506 +
  1.1507 +eOSState cMenuMP3::ProcessKey(eKeys Key)
  1.1508 +{
  1.1509 +  eOSState state = cOsdMenu::ProcessKey(Key);
  1.1510 +
  1.1511 +  if(!HasSubMenu() && state==osContinue) { // eval the return value from submenus
  1.1512 +    if(renaming) return Rename(true);
  1.1513 +    if(sourcing) return Source(true);
  1.1514 +    if(instanting) return Instant(true);
  1.1515 +    }
  1.1516 +
  1.1517 +  if(state == osUnknown) {
  1.1518 +    switch(Key) {
  1.1519 +      case kOk:     return Play();
  1.1520 +      case kRed:    return (buttonnum==1 ? Edit() : SetButtons(1)); 
  1.1521 +      case kGreen:  return (buttonnum==1 ? Source(false) : New());
  1.1522 +      case kYellow: return (buttonnum==1 ? Instant(false) : Delete());
  1.1523 +      case kBlue:   return (buttonnum==1 ? SetButtons(2) : Rename(false));
  1.1524 +      case kMenu:   return osEnd;
  1.1525 +      default:      break;
  1.1526 +      }
  1.1527 +    }
  1.1528 +  return state;
  1.1529 +}
  1.1530 +
  1.1531 +// --- PropagateImage ----------------------------------------------------------
  1.1532 +
  1.1533 +void PropagateImage(const char *image)
  1.1534 +{
  1.1535 +  cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
  1.1536 +  if(graphtft) graphtft->SetupParse("CoverImage",image ? image:"");
  1.1537 +}
  1.1538 +
  1.1539 +// --- cPluginMP3 --------------------------------------------------------------
  1.1540 +
  1.1541 +static const char *VERSION        = PLUGIN_VERSION;
  1.1542 +static const char *DESCRIPTION    = "A versatile audio player";
  1.1543 +static const char *MAINMENUENTRY  = "MP3";
  1.1544 +
  1.1545 +class cPluginMp3 : public cPlugin {
  1.1546 +private:
  1.1547 +#if APIVERSNUM >= 10330
  1.1548 +  bool ExternalPlay(const char *path, bool test);
  1.1549 +#endif
  1.1550 +public:
  1.1551 +  cPluginMp3(void);
  1.1552 +  virtual ~cPluginMp3();
  1.1553 +  virtual const char *Version(void) { return VERSION; }
  1.1554 +  virtual const char *Description(void) { return tr(DESCRIPTION); }
  1.1555 +  virtual const char *CommandLineHelp(void);
  1.1556 +  virtual bool ProcessArgs(int argc, char *argv[]);
  1.1557 +#if APIVERSNUM >= 10131
  1.1558 +  virtual bool Initialize(void);
  1.1559 +#else
  1.1560 +  virtual bool Start(void);
  1.1561 +#endif
  1.1562 +  virtual void Housekeeping(void);
  1.1563 +  virtual const char *MainMenuEntry(void);
  1.1564 +  virtual cOsdObject *MainMenuAction(void);
  1.1565 +  virtual cMenuSetupPage *SetupMenu(void);
  1.1566 +  virtual bool SetupParse(const char *Name, const char *Value);
  1.1567 +#if APIVERSNUM >= 10330
  1.1568 +  virtual bool Service(const char *Id, void *Data);
  1.1569 +#if APIVERSNUM >= 10331
  1.1570 +  virtual const char **SVDRPHelpPages(void);
  1.1571 +  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
  1.1572 +#endif
  1.1573 +#endif
  1.1574 +  };
  1.1575 +
  1.1576 +cPluginMp3::cPluginMp3(void)
  1.1577 +{
  1.1578 +  // Initialize any member varaiables here.
  1.1579 +  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  1.1580 +  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  1.1581 +}
  1.1582 +
  1.1583 +cPluginMp3::~cPluginMp3()
  1.1584 +{
  1.1585 +  InfoCache.Save();
  1.1586 +  delete mgr;
  1.1587 +}
  1.1588 +
  1.1589 +const char *cPluginMp3::CommandLineHelp(void)
  1.1590 +{
  1.1591 +  static char *help_str=0;
  1.1592 +  
  1.1593 +  free(help_str);    //                                     for easier orientation, this is column 80|
  1.1594 +  asprintf(&help_str,"  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
  1.1595 +                     "                           (default: %s)\n"
  1.1596 +                     "  -n CMD,   --network=CMD  execute CMD before & after network access\n"
  1.1597 +                     "                           (default: %s)\n"
  1.1598 +                     "  -C DIR,   --cache=DIR    store ID3 cache file in DIR\n"
  1.1599 +                     "                           (default: %s)\n"
  1.1600 +                     "  -B DIR,   --cddb=DIR     search CDDB files in DIR\n"
  1.1601 +                     "                           (default: %s)\n"
  1.1602 +                     "  -D DEV,   --dsp=DEV      device for OSS output\n"
  1.1603 +                     "                           (default: %s)\n"
  1.1604 +                     "  -i CMD,   --iconv=CMD    use CMD to convert background images\n"
  1.1605 +                     "                           (default: %s)\n"
  1.1606 +                     "  -c DIR,   --icache=DIR   cache converted images in DIR\n"
  1.1607 +                     "                           (default: %s)\n"
  1.1608 +                     "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
  1.1609 +                     "                           (default: %s)\n",
  1.1610 +                     
  1.1611 +                     mountscript,
  1.1612 +                     netscript ? netscript:"none",
  1.1613 +                     cachedir ? cachedir:"video dir",
  1.1614 +#ifdef HAVE_SNDFILE
  1.1615 +                     cddbpath,
  1.1616 +#else
  1.1617 +                     "none",
  1.1618 +#endif
  1.1619 +#ifdef WITH_OSS
  1.1620 +                     dspdevice,
  1.1621 +#else
  1.1622 +                     "none",
  1.1623 +#endif
  1.1624 +                     imageconv,
  1.1625 +                     imagecache,
  1.1626 +                     sourcesSub ? sourcesSub:"empty"
  1.1627 +                     );
  1.1628 +  return help_str;
  1.1629 +}
  1.1630 +
  1.1631 +bool cPluginMp3::ProcessArgs(int argc, char *argv[])
  1.1632 +{
  1.1633 +  static struct option long_options[] = {
  1.1634 +      { "mount",    required_argument, NULL, 'm' },
  1.1635 +      { "network",  required_argument, NULL, 'n' },
  1.1636 +      { "cddb",     required_argument, NULL, 'B' },
  1.1637 +      { "dsp",      required_argument, NULL, 'D' },
  1.1638 +      { "cache",    required_argument, NULL, 'C' },
  1.1639 +      { "icache",   required_argument, NULL, 'c' },
  1.1640 +      { "iconv",    required_argument, NULL, 'i' },
  1.1641 +      { "sources",  required_argument, NULL, 'S' },
  1.1642 +      { NULL }
  1.1643 +    };
  1.1644 +
  1.1645 +  int c, option_index = 0;
  1.1646 +  while((c=getopt_long(argc,argv,"c:i:m:n:B:C:D:S:",long_options,&option_index))!=-1) {
  1.1647 +    switch (c) {
  1.1648 +      case 'i': imageconv=optarg; break;
  1.1649 +      case 'c': imagecache=optarg; break;
  1.1650 +      case 'm': mountscript=optarg; break;
  1.1651 +      case 'n': netscript=optarg; break;
  1.1652 +      case 'C': cachedir=optarg; break;
  1.1653 +      case 'S': sourcesSub=optarg; break;
  1.1654 +      case 'B':
  1.1655 +#ifdef HAVE_SNDFILE
  1.1656 +                cddbpath=optarg; break;
  1.1657 +#else
  1.1658 +                fprintf(stderr, "mp3: libsndfile support has not been compiled in!\n"); return false;
  1.1659 +#endif
  1.1660 +      case 'D':
  1.1661 +#ifdef WITH_OSS
  1.1662 +                dspdevice=optarg; break;
  1.1663 +#else
  1.1664 +                fprintf(stderr, "mp3: OSS output has not been compiled in!\n"); return false;
  1.1665 +#endif
  1.1666 +      default:  return false;
  1.1667 +      }
  1.1668 +    }
  1.1669 +  return true;
  1.1670 +}
  1.1671 +
  1.1672 +#if APIVERSNUM >= 10131
  1.1673 +bool cPluginMp3::Initialize(void)
  1.1674 +#else
  1.1675 +bool cPluginMp3::Start(void)
  1.1676 +#endif
  1.1677 +{
  1.1678 +  if(!CheckVDRVersion(1,1,29,"mp3")) return false;
  1.1679 +  i18n_name=Name();
  1.1680 +  MP3Sources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mp3sources.conf"));
  1.1681 +  if(MP3Sources.Count()<1) {
  1.1682 +     esyslog("ERROR: you should have defined at least one source in mp3sources.conf");
  1.1683 +     fprintf(stderr,"No source(s) defined in mp3sources.conf\n");
  1.1684 +     return false;
  1.1685 +     }
  1.1686 +  InfoCache.Load();
  1.1687 +  RegisterI18n(Phrases);
  1.1688 +  mgr=new cPlayManager;
  1.1689 +  if(!mgr) {
  1.1690 +    esyslog("ERROR: creating playmanager failed");
  1.1691 +    fprintf(stderr,"Creating playmanager failed\n");
  1.1692 +    return false;
  1.1693 +    }
  1.1694 +  d(printf("mp3: using %s\n",mad_version))
  1.1695 +  d(printf("mp3: compiled with %s\n",MAD_VERSION))
  1.1696 +  return true;
  1.1697 +}
  1.1698 +
  1.1699 +void cPluginMp3::Housekeeping(void)
  1.1700 +{
  1.1701 +  InfoCache.Save();
  1.1702 +}
  1.1703 +
  1.1704 +const char *cPluginMp3::MainMenuEntry(void)
  1.1705 +{
  1.1706 +  return MP3Setup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
  1.1707 +}
  1.1708 +
  1.1709 +cOsdObject *cPluginMp3::MainMenuAction(void)
  1.1710 +{
  1.1711 +  return new cMenuMP3;
  1.1712 +}
  1.1713 +
  1.1714 +cMenuSetupPage *cPluginMp3::SetupMenu(void)
  1.1715 +{
  1.1716 +  return new cMenuSetupMP3;
  1.1717 +}
  1.1718 +
  1.1719 +bool cPluginMp3::SetupParse(const char *Name, const char *Value)
  1.1720 +{
  1.1721 +  if      (!strcasecmp(Name, "InitLoopMode"))     MP3Setup.InitLoopMode    = atoi(Value);
  1.1722 +  else if (!strcasecmp(Name, "InitShuffleMode"))  MP3Setup.InitShuffleMode = atoi(Value);
  1.1723 +  else if (!strcasecmp(Name, "AudioMode"))        MP3Setup.AudioMode       = atoi(Value);
  1.1724 +  else if (!strcasecmp(Name, "BgrScan"))          MP3Setup.BgrScan         = atoi(Value);
  1.1725 +  else if (!strcasecmp(Name, "EditorMode"))       MP3Setup.EditorMode      = atoi(Value);
  1.1726 +  else if (!strcasecmp(Name, "DisplayMode"))      MP3Setup.DisplayMode     = atoi(Value);
  1.1727 +  else if (!strcasecmp(Name, "BackgrMode"))       MP3Setup.BackgrMode      = atoi(Value);
  1.1728 +  else if (!strcasecmp(Name, "MenuMode"))         MP3Setup.MenuMode        = atoi(Value);
  1.1729 +  else if (!strcasecmp(Name, "TargetLevel"))      MP3Setup.TargetLevel     = atoi(Value);
  1.1730 +  else if (!strcasecmp(Name, "LimiterLevel"))     MP3Setup.LimiterLevel    = atoi(Value);
  1.1731 +  else if (!strcasecmp(Name, "Only48kHz"))        MP3Setup.Only48kHz       = atoi(Value);
  1.1732 +  else if (!strcasecmp(Name, "UseProxy"))         MP3Setup.UseProxy        = atoi(Value);
  1.1733 +  else if (!strcasecmp(Name, "ProxyHost"))        strn0cpy(MP3Setup.ProxyHost,Value,MAX_HOSTNAME);
  1.1734 +  else if (!strcasecmp(Name, "ProxyPort"))        MP3Setup.ProxyPort       = atoi(Value);
  1.1735 +  else if (!strcasecmp(Name, "UseCddb"))          MP3Setup.UseCddb         = atoi(Value);
  1.1736 +  else if (!strcasecmp(Name, "CddbHost"))         strn0cpy(MP3Setup.CddbHost,Value,MAX_HOSTNAME);
  1.1737 +  else if (!strcasecmp(Name, "CddbPort"))         MP3Setup.CddbPort        = atoi(Value);
  1.1738 +  else if (!strcasecmp(Name, "AbortAtEOL"))       MP3Setup.AbortAtEOL      = atoi(Value);
  1.1739 +  else if (!strcasecmp(Name, "AudioOutMode")) {
  1.1740 +    MP3Setup.AudioOutMode = atoi(Value);
  1.1741 +#ifndef WITH_OSS
  1.1742 +    if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) {
  1.1743 +      esyslog("WARNING: AudioOutMode OSS not supported, falling back to DVB");
  1.1744 +      MP3Setup.AudioOutMode=AUDIOOUTMODE_DVB;
  1.1745 +      }
  1.1746 +#endif
  1.1747 +    }
  1.1748 +#if APIVERSNUM >= 10307
  1.1749 +  else if (!strcasecmp(Name, "ReplayDisplay"))      MP3Setup.ReplayDisplay = atoi(Value);
  1.1750 +#endif
  1.1751 +  else if (!strcasecmp(Name, "HideMainMenu"))       MP3Setup.HideMainMenu  = atoi(Value);
  1.1752 +  else if (!strcasecmp(Name, "KeepSelect"))         MP3Setup.KeepSelect    = atoi(Value);
  1.1753 +  else if (!strcasecmp(Name, "TitleArtistOrder"))   MP3Setup.TitleArtistOrder = atoi(Value);
  1.1754 +  else return false;
  1.1755 +  return true;
  1.1756 +}
  1.1757 +
  1.1758 +#if APIVERSNUM >= 10330
  1.1759 +
  1.1760 +bool cPluginMp3::ExternalPlay(const char *path, bool test)
  1.1761 +{
  1.1762 +  char real[PATH_MAX+1];
  1.1763 +  if(realpath(path,real)) {
  1.1764 +    cFileSource *src=MP3Sources.FindSource(real);
  1.1765 +    if(src) {
  1.1766 +      cFileObj *item=new cFileObj(src,0,0,otFile);
  1.1767 +      if(item) {
  1.1768 +        item->SplitAndSet(real);
  1.1769 +        if(item->GuessType()) {
  1.1770 +          if(item->Exists()) {
  1.1771 +            cInstantPlayList *pl=new cInstantPlayList(item);
  1.1772 +            if(pl && pl->Load() && pl->Count()) {
  1.1773 +              if(!test) cMP3Control::SetPlayList(pl);
  1.1774 +              else delete pl;
  1.1775 +              delete item;
  1.1776 +              return true;
  1.1777 +              }
  1.1778 +            else dsyslog("MP3 service: error building playlist");
  1.1779 +            delete pl;
  1.1780 +            }
  1.1781 +          else dsyslog("MP3 service: cannot play '%s'",path);
  1.1782 +          }
  1.1783 +        else dsyslog("MP3 service: GuessType() failed for '%s'",path);
  1.1784 +        delete item;
  1.1785 +        }
  1.1786 +      }
  1.1787 +    else dsyslog("MP3 service: cannot find source for '%s', real '%s'",path,real);
  1.1788 +    }
  1.1789 +  else if(errno!=ENOENT && errno!=ENOTDIR)
  1.1790 +    esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
  1.1791 +  return false;
  1.1792 +}
  1.1793 +
  1.1794 +bool cPluginMp3::Service(const char *Id, void *Data)
  1.1795 +{
  1.1796 +  if(!strcasecmp(Id,"MP3-Play-v1")) {
  1.1797 +    if(Data) {
  1.1798 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
  1.1799 +      msd->result=ExternalPlay(msd->data.filename,false);
  1.1800 +      }
  1.1801 +    return true;
  1.1802 +    }
  1.1803 +  else if(!strcasecmp(Id,"MP3-Test-v1")) {
  1.1804 +    if(Data) {
  1.1805 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
  1.1806 +      msd->result=ExternalPlay(msd->data.filename,true);
  1.1807 +      }
  1.1808 +    return true;
  1.1809 +    }
  1.1810 +  return false;
  1.1811 +}
  1.1812 +
  1.1813 +#if APIVERSNUM >= 10331
  1.1814 +
  1.1815 +const char **cPluginMp3::SVDRPHelpPages(void)
  1.1816 +{
  1.1817 +  static const char *HelpPages[] = {
  1.1818 +    "PLAY <filename>\n"
  1.1819 +    "    Triggers playback of file 'filename'.",
  1.1820 +    "TEST <filename>\n"
  1.1821 +    "    Tests is playback of file 'filename' is possible.",
  1.1822 +    "CURR\n"
  1.1823 +    "    Returns filename of song currently being replayed.",
  1.1824 +    NULL
  1.1825 +    };
  1.1826 +  return HelpPages;
  1.1827 +}
  1.1828 +
  1.1829 +cString cPluginMp3::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
  1.1830 +{
  1.1831 +  if(!strcasecmp(Command,"PLAY")) {
  1.1832 +    if(*Option) {
  1.1833 +      if(ExternalPlay(Option,false)) return "Playback triggered";
  1.1834 +      else { ReplyCode=550; return "Playback failed"; }
  1.1835 +      }
  1.1836 +    else { ReplyCode=501; return "Missing filename"; }
  1.1837 +    }
  1.1838 +  else if(!strcasecmp(Command,"TEST")) {
  1.1839 +    if(*Option) {
  1.1840 +      if(ExternalPlay(Option,true)) return "Playback possible";
  1.1841 +      else { ReplyCode=550; return "Playback not possible"; }
  1.1842 +      }
  1.1843 +    else { ReplyCode=501; return "Missing filename"; }
  1.1844 +    }
  1.1845 +  else if(!strcasecmp(Command,"CURR")) {
  1.1846 +    cControl *control=cControl::Control();
  1.1847 +    if(control && typeid(*control)==typeid(cMP3Control)) {
  1.1848 +      cMP3PlayInfo mode;
  1.1849 +      if(mgr->Info(-1,&mode)) return mode.Filename;
  1.1850 +      else return "<unknown>";
  1.1851 +      }
  1.1852 +    else { ReplyCode=550; return "No running playback"; }
  1.1853 +    }
  1.1854 +  return NULL;
  1.1855 +}
  1.1856 +
  1.1857 +#endif // 1.3.31
  1.1858 +#endif // 1.3.30
  1.1859 +
  1.1860 +VDRPLUGINCREATOR(cPluginMp3); // Don't touch this!