2 Copyright 2004-2005 Chris Tallon, Andreas Vogel
4 This file is part of VOMP.
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.
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.
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
23 #include "vaudioplayer.h"
25 #include "audioplayer.h"
38 VAudioplayer::VAudioplayer(VMediaList *p)
41 Video* video = Video::getInstance();
42 setSize(video->getScreenWidth(),video->getScreenHeight());
51 numentries=p->getNumEntries(MEDIA_TYPE_AUDIO);
54 barBlue.set(0, 0, 150, 150);
58 AudioPlayer * VAudioplayer::getPlayer(bool createIfNeeded)
60 AudioPlayer* rt=AudioPlayer::getInstance(this,false);
61 if (! createIfNeeded && rt == NULL) return NULL;
63 rt=AudioPlayer::getInstance(this);
69 VAudioplayer::~VAudioplayer()
71 if (banner) BoxStack::getInstance()->remove(banner);
72 if (fullname) delete fullname;
73 if (filename) delete filename;
74 Timers::getInstance()->cancelTimer(this,1);
75 Timers::getInstance()->cancelTimer(this,2);
76 Timers::getInstance()->cancelTimer(this,3);
77 //TODO leave this to medialist and stop only...
78 if (getPlayer(false)) {
79 AudioPlayer::getInstance(NULL,false)->shutdown();
85 void VAudioplayer::draw()
87 Log::getInstance()->log("VAudioplayer::draw", Log::DEBUG, "p=%p", this);
91 int VAudioplayer::handleCommand(int command)
93 Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "p=%p,cmd=%d", this,command);
94 Timers::getInstance()->cancelTimer(this,1);
100 play(VMediaList::MV_PREV);
101 BoxStack::getInstance()->update(this);
104 case Remote::FORWARD:
105 getPlayer()->fastForward();
108 case Remote::DF_DOWN:
110 play(VMediaList::MV_NEXT);
111 BoxStack::getInstance()->update(this);
114 case Remote::SKIPFORWARD:
115 getPlayer()->skipForward(10);
118 case Remote::SKIPBACK:
119 getPlayer()->skipBackward(10);
122 case Remote::REVERSE:
126 getPlayer()->jumpToPercent(0);
130 getPlayer()->jumpToPercent(10);
134 getPlayer()->jumpToPercent(20);
138 getPlayer()->jumpToPercent(30);
142 getPlayer()->jumpToPercent(40);
146 getPlayer()->jumpToPercent(50);
150 getPlayer()->jumpToPercent(60);
154 getPlayer()->jumpToPercent(70);
158 getPlayer()->jumpToPercent(80);
162 getPlayer()->jumpToPercent(90);
171 else showBanner(true);
172 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
173 if (playall) play(VMediaList::MV_NEXT);
180 getPlayer()->unpause();
181 if (getPlayer()->getState() != AudioPlayer::S_ERROR) justPlaying=true;
182 else if (playall) play(VMediaList::MV_NEXT);
188 getPlayer()->pause();
203 if (playall) startPlayall();
204 if (rt == 2) retriggerBanner();
205 // stop command getting to any more views
206 Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "returns p=%p,cmd=%d, rt=%d", this,command,rt);
210 void VAudioplayer::processMessage(Message* m)
212 if (m->message == Message::MOUSE_MOVE)
216 else if (m->message == Message::MOUSE_LBDOWN)
219 //check if press is outside this view! then simulate cancel
220 int x=(m->parameter>>16)-getScreenX();
221 int y=(m->parameter&0xFFFF)-getScreenY();
222 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
224 BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
227 else if (m->message == Message::PLAYER_EVENT) {
228 switch (m->parameter) {
229 case AudioPlayer::STREAM_ERR:
230 case AudioPlayer::STREAM_END:
231 if (playall) play(VMediaList::MV_NEXT);
232 else sendViewMsg(this,true);
234 case AudioPlayer::STATUS_CHANGE:
237 case AudioPlayer::SHORT_UPDATE:
240 BoxStack::getInstance()->update(banner,&clocksRegion);
241 BoxStack::getInstance()->update(banner,&barRegion);
242 Timers::getInstance()->setTimerD(this, 3, 1);
245 case AudioPlayer::NEW_SONG:
248 case AudioPlayer::CONNECTION_LOST:
250 if (AudioPlayer *player=getPlayer(false)) {
254 Command::getInstance()->connectionLost();
260 VAudioplayer * VAudioplayer::createPlayer(VMediaList * mparent, bool bplayall) {
261 Log::getInstance()->log("VAudioplayer::createPlayer", Log::DEBUG, "p=%p",
263 VAudioplayer *vmn=new VAudioplayer(mparent);
264 BoxStack::getInstance()->add(vmn);
265 BoxStack::getInstance()->update(vmn);
267 if (bplayall) vmn->startPlayall();
268 BoxStack::getInstance()->update(vmn);
272 void VAudioplayer::startPlayall() {
276 void VAudioplayer::play(ULONG move) {
277 currentMedia=parent->getMedia(MEDIA_TYPE_AUDIO,move);
279 const char *fname=currentMedia->getFileName();
280 ftime=currentMedia->getTime();
281 int len=strlen(fname)+2+strlen(parent->getDirname());
286 fullname=new char[len];
287 sprintf(fullname,"%s/%s",parent->getDirname(),fname);
288 if (filename) delete filename;
290 filename=new char[len];
291 strcpy(filename,fname);
292 Log::getInstance()->log("VAudioplayer::load", Log::DEBUG, "filename=%s,p=%p",
294 VDR* vdr=VDR::getInstance();
295 if (vdr->isConnected()) {
296 Log::getInstance()->log("VAudioplayer", Log::DEBUG, "request file %s",filename);
297 int wseq=getPlayer()->play(fullname);
298 if (getPlayer()->waitForSequence(wseq,2)<0) {
299 mediaError=tr("unable to open audio file");
302 //leave this to the update message from the player:
306 if (AudioPlayer * player=getPlayer(false)) {
309 Command::getInstance()->connectionLost();
314 Log::getInstance()->log("VAudioplayer", Log::DEBUG, "player started for %s",filename);
317 void VAudioplayer::retriggerBanner() {
318 if (! banner) return;
319 bannerTime=time(NULL);
322 void VAudioplayer::showBanner(bool forceNewTime) {
323 //if the loading flag is set,
324 //we are in the main thread - so we can (and must) safely hard destroy/create the banner
325 Timers::getInstance()->cancelTimer(this,1);
326 Timers::getInstance()->cancelTimer(this,3);
327 if (! filename || ! currentMedia) {
333 time_t curtime=time(NULL);
334 if (forceNewTime) retriggerBanner();
335 time_t remainingTime=BANNER_TIME+2-(curtime-bannerTime);
336 if (remainingTime < 2) remainingTime=2;
337 bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR;
338 if (remainingTime < BANNER_ERROR_TIME+2 && playerError) remainingTime=BANNER_ERROR_TIME+2;
339 Timers::getInstance()->setTimerD(this,1,remainingTime);
340 if (playerError) Timers::getInstance()->setTimerD(this,2,BANNER_ERROR_TIME);
341 Timers::getInstance()->setTimerD(this,3, 1);
342 BoxStack::getInstance()->update(banner);
345 void VAudioplayer::destroyBanner() {
347 BoxStack::getInstance()->remove(banner);
350 Timers::getInstance()->cancelTimer(this,1);
351 Timers::getInstance()->cancelTimer(this,3);
353 void VAudioplayer::updateBanner(bool restart) {
354 time_t currTime=time(NULL);
355 time_t lastBanner=bannerTime;
356 if (currTime - lastBanner > BANNER_TIME){
359 if (banner||restart) {
363 void VAudioplayer::timercall(int clientref) {
364 Log::getInstance()->log("VAudioplayer::timercall", Log::DEBUG, "id=%d",clientref);
370 Message* m = new Message();
371 //we misuse PLAYER_EVENT here
372 m->message = Message::PLAYER_EVENT;
375 m->parameter=(clientref==1)?AudioPlayer::STATUS_CHANGE:
376 AudioPlayer::SHORT_UPDATE;
377 Command::getInstance()->postMessageFromOuterSpace(m);
382 bool stillError=false;
383 if (AudioPlayer * player=getPlayer(false)) {
384 stillError=player->getState()==AudioPlayer::S_ERROR;
387 //send a stream end to trigger nex in playlist
388 Message* m = new Message();
389 //we misuse PLAYER_EVENT here
390 m->message = Message::PLAYER_EVENT;
393 m->parameter=AudioPlayer::STREAM_END;
394 Command::getInstance()->postMessageFromOuterSpace(m);
402 void VAudioplayer::sendViewMsg(Boxx *v,bool vdestroy) {
403 Message* m = new Message();
404 m->message = vdestroy?Message::CLOSE_ME:Message::ADD_VIEW;
405 m->to = BoxStack::getInstance();
407 m->parameter=(ULONG)v;
408 Command::getInstance()->postMessageFromOuterSpace(m);
411 TBBoxx * VAudioplayer::createBannerView(int numlines) {
412 TBBoxx *rt=new TBBoxx();
413 UINT height=numlines*30+60;
414 UINT vheight=Video::getInstance()->getScreenHeight();
415 UINT vwidth=Video::getInstance()->getScreenWidth();
416 if (height > vheight-2*BANNER_BOTTOM_MARGIN)
417 height=vheight-2*BANNER_BOTTOM_MARGIN;
418 rt->setSize(vwidth -2*BANNER_X_MARGIN, height);
420 if (Video::getInstance()->getFormat() == Video::PAL)
422 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
426 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
429 rt->setTitleBarOn(0);
431 //set the regions for the closcks and bars on banner
432 barRegion.x = rt->getWidth()-BARLEN-20;
433 barRegion.y = rt->getHeight() - 30; // FIXME, need to be - 1? and below?
434 barRegion.w = rt->getWidth()-BARLEN+10;
437 clocksRegion.x = 130;
438 clocksRegion.y = barRegion.y + 3;
439 clocksRegion.w = 190;
440 clocksRegion.h = surface->getFontHeight();
441 Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"created banner %p",rt);
442 BoxStack::getInstance()->add(rt);
447 void VAudioplayer::drawBanner(){
448 Log::getInstance()->log("VAudioPlayer",Log::DEBUG, "draw banner for %p",banner);
450 char *playerTitle=getPlayer()->getTitle();
451 if (playerTitle) title=playerTitle;
453 const char *pl=tr("Playlist");
454 int num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentMedia->index);
455 bool playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
456 //1more line for long dirs
457 int numlines=playall?5:4;
459 const char *first=NULL;
460 char *playerInfo=NULL;
463 first=tr("Unable to play audio file");
467 playerInfo=getPlayer()->getID3Info();
468 len=strlen(filename)+strlen(pl)+30+strlen(parent->getDirname())+Media::TIMEBUFLEN+100;
470 for (UINT i=0;i<strlen(playerInfo);i++)
471 if (playerInfo[i] == '\n') numlines++;
472 len+=strlen(playerInfo);
479 if (numlines != bannerlines) {
480 if (banner) destroyBanner();
481 banner=createBannerView(numlines);
484 banner=createBannerView(numlines);
486 bannerlines=numlines;
487 char *buf=new char [len];
489 SNPRINTF(buf,len,"%s",first);
492 char tbuf[Media::TIMEBUFLEN];
494 SNPRINTF(buf,len,"%s\n"
500 tr("FileName"),currentMedia->getFileName(),
502 tr("Directory"),parent->getDirname(),
503 tr("Time"),currentMedia->getTimeString(tbuf));
506 SNPRINTF(buf,len,"%s\n"
511 tr("FileName"),currentMedia->getFileName(),
512 tr("Directory"),parent->getDirname(),
513 tr("Time"),currentMedia->getTimeString(tbuf));
516 Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
517 //now the real drawing functions
521 // w.setSurface(banner->surface);
523 banner->rectangle(0, 0, banner->getWidth(), 30, Colour::TITLEBARBACKGROUND);
524 banner->drawText(title, 5, 5, Colour::LIGHTTEXT);
525 banner->drawPara(buf,5,32,Colour::LIGHTTEXT);
528 int ybottom=banner->getHeight();
529 banner->rectangle(0, ybottom - barRegion.h, banner->getWidth(), barRegion.h, Colour::TITLEBARBACKGROUND);
530 bool drawSymbol=true;
531 switch(getPlayer()->getState()) {
532 case AudioPlayer::S_PAUSE:
533 w.nextSymbol = WSymbol::PAUSE;
535 case AudioPlayer::S_PLAY:
536 w.nextSymbol = WSymbol::PLAY;
538 case AudioPlayer::S_DONE:
540 w.nextSymbol = WSymbol::PLAY;
544 case AudioPlayer::S_BACK:
545 w.nextSymbol = WSymbol::FBWD ;
547 case AudioPlayer::S_FF:
548 w.nextSymbol = WSymbol::FFWD ;
555 w.setPosition(x, ybottom-24);
560 banner->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
563 if (playerInfo) delete playerInfo;
564 if (playerTitle) delete playerTitle;
568 void VAudioplayer::drawClocks() {
569 if (! banner) return;
570 //draw clocks and bar
571 banner->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
573 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
574 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
577 /* gmtime_r(¤tSec,&cpos);
578 gmtime_r(&lengthSec,&slen);*/
579 cpos.tm_hour=currentSec/3600;
580 cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
581 cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
587 if (currentSec >= lengthSec)
589 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
593 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,
594 getPlayer()->getCurrentBitrate()/1000);
595 //Log::getInstance()->log("VAudioplayer", Log::DEBUG, buffer);
598 banner->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
601 int progBarXbase = 0;
604 banner->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT);
605 banner->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
607 if (currentSec > lengthSec) currentSec=lengthSec;
608 if (lengthSec == 0) return;
610 // Draw yellow portion
611 int progressWidth = (barlen+2) * currentSec / lengthSec;
612 banner->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT);