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