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