]> git.vomp.tv Git - vompclient.git/blob - vaudioplayer.cc
Media Player From Andreas Vogel
[vompclient.git] / vaudioplayer.cc
1 /*
2     Copyright 2004-2005 Chris Tallon, Andreas Vogel
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP 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 VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "vaudioplayer.h"
22 #include "audioplayer.h"
23 #include <time.h>
24
25 VAudioplayer::VAudioplayer(VMediaList *p)
26 {
27   parent=p;
28   Video* video = Video::getInstance();
29   create(video->getScreenWidth(),video->getScreenHeight());
30   banner=NULL;
31   fullname=NULL;
32   filename=NULL;
33   ftime=0;
34   playall=false;
35   currentMedia=NULL;
36   justPlaying=false;
37   numentries=p->getNumEntries(MEDIA_TYPE_AUDIO);
38   bannerTime=0;
39  
40     barBlue.set(0, 0, 150, 150);
41
42 }
43
44 AudioPlayer * VAudioplayer::getPlayer(bool createIfNeeded)
45 {
46   AudioPlayer* rt=AudioPlayer::getInstance(this,false);
47   if (! createIfNeeded && rt == NULL) return NULL;
48   if (rt == NULL) {
49     rt=AudioPlayer::getInstance(this);
50     rt->run();
51   }
52   return rt;
53 }
54
55 VAudioplayer::~VAudioplayer()
56 {
57   if (banner) ViewMan::getInstance()->removeView(banner);
58   if (fullname) delete fullname;
59   if (filename) delete filename;
60   Timers::getInstance()->cancelTimer(this,1);
61   Timers::getInstance()->cancelTimer(this,2);
62   Timers::getInstance()->cancelTimer(this,3);
63   //TODO leave this to medialist and stop only...
64   if (getPlayer(false)) {
65     AudioPlayer::getInstance(NULL,false)->shutdown();
66   }
67 }
68   
69
70
71 void VAudioplayer::draw()
72 {
73   Log::getInstance()->log("VAudioplayer::draw", Log::DEBUG, "p=%p", this);
74 }
75
76
77 int VAudioplayer::handleCommand(int command)
78 {
79   Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "p=%p,cmd=%d", this,command);
80   Timers::getInstance()->cancelTimer(this,1);
81   int rt=1;
82   switch(command)
83   {
84     case Remote::DF_UP:
85     case Remote::UP:
86       play(VMediaList::MV_PREV);
87       ViewMan::getInstance()->updateView(this);
88       rt= 2;
89       break;
90     case Remote::FORWARD:
91                         getPlayer()->fastForward();
92       rt=2;
93       break;
94     case Remote::DF_DOWN:
95     case Remote::DOWN:
96       play(VMediaList::MV_NEXT);
97       ViewMan::getInstance()->updateView(this);
98       rt= 2;
99       break;
100     case Remote::SKIPFORWARD:
101                         getPlayer()->skipForward(10);
102                         rt=2;
103                         break;
104     case Remote::SKIPBACK:
105                         getPlayer()->skipBackward(10);
106                         rt=2;
107                         break;
108     case Remote::REVERSE:
109       rt=2;
110       break;
111                 case Remote::ZERO:
112                         getPlayer()->jumpToPercent(0);
113                         rt=2;
114                         break;
115                 case Remote::ONE:
116                         getPlayer()->jumpToPercent(10);
117                         rt=2;
118                         break;
119                 case Remote::TWO:
120                         getPlayer()->jumpToPercent(20);
121                         rt=2;
122                         break;
123                 case Remote::THREE:
124                         getPlayer()->jumpToPercent(30);
125                         rt=2;
126                         break;
127                 case Remote::FOUR:
128                         getPlayer()->jumpToPercent(40);
129                         rt=2;
130                         break;
131                 case Remote::FIVE:
132                         getPlayer()->jumpToPercent(50);
133                         rt=2;
134                         break;
135                 case Remote::SIX:
136                         getPlayer()->jumpToPercent(60);
137                         rt=2;
138                         break;
139                 case Remote::SEVEN:
140                         getPlayer()->jumpToPercent(70);
141                         rt=2;
142                         break;
143                 case Remote::EIGHT:
144                         getPlayer()->jumpToPercent(80);
145                         rt=2;
146                         break;
147                 case Remote::NINE:
148                         getPlayer()->jumpToPercent(90);
149                         rt=2;
150                         break;
151     case Remote::OK:
152     case Remote::GREEN:
153     {
154       if (banner) {
155         destroyBanner();
156         }
157       else showBanner(true);
158       if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
159         if (playall) play(VMediaList::MV_NEXT);
160       }
161       rt= 2;
162     }
163     break;
164     case Remote::PLAY:
165     {
166       getPlayer()->unpause();
167       if (getPlayer()->getState() != AudioPlayer::S_ERROR) justPlaying=true;
168       else if (playall) play(VMediaList::MV_NEXT);
169       rt= 2;
170     }
171     break;
172     case Remote::PAUSE:
173       //playall=false;
174       getPlayer()->pause();
175       justPlaying=false;
176       rt= 2;
177       break;
178     case Remote::STOP:
179       getPlayer()->stop();
180       justPlaying=false;
181       rt= 2;
182       break;
183     case Remote::BACK:
184     {
185       rt= 4;
186     }
187     break;
188   }
189   if (playall) startPlayall();
190         if (rt == 2) retriggerBanner();
191   // stop command getting to any more views
192   Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "returns p=%p,cmd=%d, rt=%d", this,command,rt);
193   return rt;
194 }
195
196 void VAudioplayer::processMessage(Message* m)
197 {
198   if (m->message == Message::MOUSE_MOVE)
199   {
200     ;
201   }
202   else if (m->message == Message::MOUSE_LBDOWN)
203   {
204     
205     //check if press is outside this view! then simulate cancel
206     int x=(m->parameter>>16)-getScreenX();
207     int y=(m->parameter&0xFFFF)-getScreenY();
208     if (x<0 || y <0 || x>getWidth() || y>getHeight())
209     {
210       ViewMan::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
211     }
212   }
213   else if (m->message == Message::PLAYER_EVENT) {
214     switch (m->parameter) {
215       case AudioPlayer::STREAM_ERR:
216       case AudioPlayer::STREAM_END:
217         if (playall) play(VMediaList::MV_NEXT);
218         else sendViewMsg(this,true);
219         break;
220       case AudioPlayer::STATUS_CHANGE:
221         updateBanner(false);
222         break;
223       case AudioPlayer::SHORT_UPDATE:
224                                 if (banner) {
225                                         drawClocks();
226                                         ViewMan::getInstance()->updateView(banner,&clocksRegion);
227                                         ViewMan::getInstance()->updateView(banner,&barRegion);
228                                         Timers::getInstance()->setTimerD(this, 3, 1);
229                                 }
230         break;
231       case AudioPlayer::NEW_SONG:
232         updateBanner(true);
233         break;
234       case AudioPlayer::CONNECTION_LOST:
235         destroyBanner();
236         if (AudioPlayer *player=getPlayer(false)) {
237           player->shutdown();
238           player=NULL;
239         }
240         Command::getInstance()->connectionLost();
241         break;
242     }
243   }
244 }
245
246 VAudioplayer * VAudioplayer::createPlayer(VMediaList * mparent, bool bplayall) {
247    Log::getInstance()->log("VAudioplayer::createPlayer", Log::DEBUG, "p=%p",
248   mparent);
249    VAudioplayer *vmn=new VAudioplayer(mparent);
250    ViewMan::getInstance()->add(vmn);
251    ViewMan::getInstance()->updateView(vmn);
252    vmn->play();
253    if (bplayall) vmn->startPlayall();
254    ViewMan::getInstance()->updateView(vmn);
255    return vmn;
256 }
257
258 void VAudioplayer::startPlayall() {
259    playall=true;
260 }
261
262 void VAudioplayer::play(ULONG move) {
263   currentMedia=parent->getMedia(MEDIA_TYPE_AUDIO,move);
264   mediaError=NULL;
265   const char *fname=currentMedia->getFileName();
266   ftime=currentMedia->getTime();
267   int len=strlen(fname)+2+strlen(parent->getDirname());
268   if (fullname) {
269      delete fullname;
270      fullname=NULL;
271      }
272   fullname=new char[len];
273   sprintf(fullname,"%s/%s",parent->getDirname(),fname);
274   if (filename) delete filename;
275   len=strlen(fname)+1;
276   filename=new char[len];
277   strcpy(filename,fname);
278   Log::getInstance()->log("VAudioplayer::load", Log::DEBUG, "filename=%s,p=%p",
279   fullname,this);
280   VDR* vdr=VDR::getInstance();
281   if (vdr->isConnected()) {
282      Log::getInstance()->log("VAudioplayer", Log::DEBUG, "request file %s",filename);
283      int wseq=getPlayer()->play(fullname);
284      if (getPlayer()->waitForSequence(wseq,2)<0) {
285        mediaError=tr("unable to open audio file");
286      }
287      justPlaying=true;
288      //leave this to the update message from the player:
289      //showBanner();
290     }
291   else {
292     if (AudioPlayer * player=getPlayer(false)) {
293       player->shutdown();
294     }
295     Command::getInstance()->connectionLost();
296     }
297   if (mediaError) {
298     showBanner(true);
299   }
300   Log::getInstance()->log("VAudioplayer", Log::DEBUG, "player started for %s",filename);
301 }
302
303 void VAudioplayer::retriggerBanner() {
304         if (! banner) return;
305         bannerTime=time(NULL);
306 }
307
308 void VAudioplayer::showBanner(bool forceNewTime) {
309   //if the loading flag is set,
310   //we are in the main thread - so we can (and must) safely hard destroy/create the banner
311   Timers::getInstance()->cancelTimer(this,1);
312   Timers::getInstance()->cancelTimer(this,3);
313   if (! filename || ! currentMedia) {
314     //hmm...
315     destroyBanner();
316     return;
317     }
318   drawBanner();
319   time_t curtime=time(NULL);
320   if (forceNewTime) retriggerBanner();
321   time_t remainingTime=BANNER_TIME+2-(curtime-bannerTime);
322   if (remainingTime < 2) remainingTime=2;
323   bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR;
324   if (remainingTime < BANNER_ERROR_TIME+2 && playerError) remainingTime=BANNER_ERROR_TIME+2;
325   Timers::getInstance()->setTimerD(this,1,remainingTime);
326   if (playerError) Timers::getInstance()->setTimerD(this,2,BANNER_ERROR_TIME);
327         Timers::getInstance()->setTimerD(this,3, 1);
328   ViewMan::getInstance()->updateView(banner);
329   }
330
331 void VAudioplayer::destroyBanner() {
332   if (banner) {
333     ViewMan::getInstance()->removeView(banner);
334     banner=NULL;
335     }
336   Timers::getInstance()->cancelTimer(this,1);
337   Timers::getInstance()->cancelTimer(this,3);
338 }
339 void VAudioplayer::updateBanner(bool restart) {
340   time_t currTime=time(NULL);
341   time_t lastBanner=bannerTime;
342   if (currTime - lastBanner > BANNER_TIME){
343     destroyBanner();
344   }
345   if (banner||restart) {
346     showBanner(restart);
347     }
348 }
349 void VAudioplayer::timercall(int clientref) {
350    Log::getInstance()->log("VAudioplayer::timercall", Log::DEBUG, "id=%d",clientref);
351    switch(clientref)
352    {
353       case 1:
354       case 3:
355         {
356           Message* m = new Message(); 
357           //we misuse PLAYER_EVENT here
358           m->message = Message::PLAYER_EVENT;
359           m->to = this;
360           m->from = this;
361           m->parameter=(clientref==1)?AudioPlayer::STATUS_CHANGE:
362                                                 AudioPlayer::SHORT_UPDATE;
363           Command::getInstance()->postMessageFromOuterSpace(m);
364           break;
365         }
366       case 2:
367         {
368           bool stillError=false;
369           if (AudioPlayer * player=getPlayer(false)) {
370             stillError=player->getState()==AudioPlayer::S_ERROR;
371           }
372           if (stillError) {
373             //send a stream end to trigger nex in playlist
374             Message* m = new Message(); 
375             //we misuse PLAYER_EVENT here
376             m->message = Message::PLAYER_EVENT;
377             m->to = this;
378             m->from = this;
379             m->parameter=AudioPlayer::STREAM_END;
380             Command::getInstance()->postMessageFromOuterSpace(m);
381           }
382           break;
383         }
384     }
385    
386   }
387
388 void VAudioplayer::sendViewMsg(View *v,bool vdestroy) {
389   Message* m = new Message(); 
390   m->message = vdestroy?Message::CLOSE_ME:Message::ADD_VIEW;
391   m->to = ViewMan::getInstance();
392   m->from = v;
393   m->parameter=(ULONG)v;
394   Command::getInstance()->postMessageFromOuterSpace(m);
395 }
396
397 View * VAudioplayer::createBannerView(int numlines) {
398   View *rt=new View();
399   UINT height=numlines*30+60;
400   UINT vheight=Video::getInstance()->getScreenHeight();
401   UINT vwidth=Video::getInstance()->getScreenWidth();
402   if (height > vheight-2*BANNER_BOTTOM_MARGIN)
403     height=vheight-2*BANNER_BOTTOM_MARGIN;
404   rt->create(vwidth -2*BANNER_X_MARGIN, height);
405   if (Video::getInstance()->getFormat() == Video::PAL)
406   {
407     rt->setScreenPos(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
408   }
409   else
410   {
411     rt->setScreenPos(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
412   }
413
414   rt->setBackgroundColour(Colour::VIEWBACKGROUND);
415   rt->setTitleBarOn(0);
416   //from vvideorec 
417         //set the regions for the closcks and bars on banner
418   barRegion.x = rt->area.w-BARLEN-20;
419   barRegion.y = rt->area.h - 30;   // FIXME, need to be - 1? and below?
420   barRegion.w = rt->area.w-BARLEN+10;
421   barRegion.h = 30;
422
423   clocksRegion.x = 130;
424   clocksRegion.y = barRegion.y + 3;
425   clocksRegion.w = 190;
426   clocksRegion.h = surface->getFontHeight();
427   Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"created banner %p",rt);
428   ViewMan::getInstance()->add(rt);
429   return rt;
430 }
431
432
433 void VAudioplayer::drawBanner(){
434   Log::getInstance()->log("VAudioPlayer",Log::DEBUG, "draw banner for %p",banner);
435   char *title=NULL;
436   char *playerTitle=getPlayer()->getTitle();
437   if (playerTitle) title=playerTitle;
438   else title=filename;
439   char *pl=tr("Playlist");
440   int num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentMedia->index);
441   bool playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
442   //1more line for long dirs
443   int numlines=playall?5:4;
444   int len=0;
445   char *first=NULL;
446   char *playerInfo=NULL;
447   if (playerError) {
448     numlines=3;
449     first=tr("Unable to play audio file");
450     len=strlen(first)+3;
451   }
452   else {
453     playerInfo=getPlayer()->getID3Info();
454     len=strlen(filename)+strlen(pl)+30+strlen(parent->getDirname())+Media::TIMEBUFLEN+100;
455     if (playerInfo) {
456       for (UINT i=0;i<strlen(playerInfo);i++) 
457         if (playerInfo[i] == '\n') numlines++;
458       len+=strlen(playerInfo);
459       first=playerInfo;
460     }
461     else {
462       first="";
463     }
464   }
465   if (numlines != bannerlines) {
466     if (banner) destroyBanner();
467     banner=createBannerView(numlines);
468   }
469   if (! banner) {
470     banner=createBannerView(numlines);
471   }
472   bannerlines=numlines;
473   char buf[len];
474   if (playerError) {
475     snprintf(buf,len,"%s",first);
476   }
477   else {
478     char tbuf[Media::TIMEBUFLEN];
479     if (playall) {
480     snprintf(buf,len,"%s\n"
481         "%s:%s\n"
482         "%s: %d/%d\n"
483         "%s:%s\n"
484         "%s:%s\n",
485         first,
486         tr("FileName"),currentMedia->getFileName(),
487         pl,num,numentries,
488         tr("Directory"),parent->getDirname(),
489         tr("Time"),currentMedia->getTimeString(tbuf));
490     }
491     else {
492     snprintf(buf,len,"%s\n"
493         "%s:%s\n"
494         "%s:%s\n"
495         "%s:%s\n",
496         first,
497         tr("FileName"),currentMedia->getFileName(),
498         tr("Directory"),parent->getDirname(),
499         tr("Time"),currentMedia->getTimeString(tbuf));
500     }
501   }
502   Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
503   //now the real drawing functions
504   banner->draw();
505   WSymbol w;
506   w.setSurface(banner->surface);
507   //title
508   banner->rectangle(0, 0, banner->area.w, 30, Colour::TITLEBARBACKGROUND);
509   banner->drawText(title, 5, 5, Colour::LIGHTTEXT);
510   banner->drawPara(buf,5,32,Colour::LIGHTTEXT);
511   int x=10;
512   int ybottom=banner->area.h;
513   banner->rectangle(0, ybottom - barRegion.h, banner->area.w, barRegion.h, Colour::TITLEBARBACKGROUND);
514   bool drawSymbol=true;
515   switch(getPlayer()->getState()) {
516     case AudioPlayer::S_PAUSE:
517       w.nextSymbol = WSymbol::PAUSE;
518       break;
519     case AudioPlayer::S_PLAY:
520       w.nextSymbol = WSymbol::PLAY;
521       break;
522     case AudioPlayer::S_DONE:
523       if (playall) 
524         w.nextSymbol = WSymbol::PLAY;
525       else
526         drawSymbol=false;
527       break;
528     case AudioPlayer::S_BACK:
529       w.nextSymbol = WSymbol::FBWD ;
530       break;
531     case AudioPlayer::S_FF:
532       w.nextSymbol = WSymbol::FFWD ;
533       break;
534     default:
535       drawSymbol=false;
536       break;
537   }
538   if (drawSymbol) {
539     w.setSurfaceOffset(x, ybottom-24);
540     w.draw();
541   }
542   else {
543     //stop symbol
544     banner->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
545   }
546   x+=30;
547   if (playerInfo) delete playerInfo;
548   if (playerTitle) delete playerTitle;
549         drawClocks();
550 }
551
552 void VAudioplayer::drawClocks() {
553         if (! banner) return;
554   //draw clocks and bar
555   banner->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
556
557   time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
558   time_t lengthSec=(time_t)(getPlayer()->getSonglen());
559         struct tm cpos;
560         struct tm slen;
561         gmtime_r(&currentSec,&cpos);
562         gmtime_r(&lengthSec,&slen);
563
564   char buffer[100];
565   if (currentSec >= lengthSec)
566   {
567     snprintf(buffer,99, "-:--:-- / -:--:--  %03dk",getPlayer()->getCurrentBitrate()/1000);
568   }
569   else
570   {
571     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i  %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec,
572                                 getPlayer()->getCurrentBitrate()/1000);
573                 //Log::getInstance()->log("VAudioplayer", Log::DEBUG, buffer);
574   }
575
576   banner->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
577   
578         // Draw progress bar
579   int progBarXbase = 0;
580         int barlen=250;
581
582   banner->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT);
583   banner->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
584
585   if (currentSec > lengthSec) currentSec=lengthSec;
586   if (lengthSec == 0) return;
587
588   // Draw yellow portion
589   int progressWidth = (barlen+2) * currentSec / lengthSec;
590   banner->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT);
591
592 }
593
594