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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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()
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.
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();
92 void VAudioplayer::draw()
94 Log::getInstance()->log("VAudioplayer::draw", Log::DEBUG, "p=%p", this);
98 int VAudioplayer::handleCommand(int command)
100 Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "p=%p,cmd=%d", this,command);
101 Timers::getInstance()->cancelTimer(this,1);
107 play(VMediaList::MV_PREV);
108 BoxStack::getInstance()->update(this);
111 case Remote::FORWARD:
112 getPlayer()->fastForward();
115 case Remote::DF_DOWN:
117 play(VMediaList::MV_NEXT);
118 BoxStack::getInstance()->update(this);
121 case Remote::SKIPFORWARD:
122 getPlayer()->skipForward(10);
125 case Remote::SKIPBACK:
126 getPlayer()->skipBackward(10);
129 case Remote::REVERSE:
133 getPlayer()->jumpToPercent(0);
137 getPlayer()->jumpToPercent(10);
141 getPlayer()->jumpToPercent(20);
145 getPlayer()->jumpToPercent(30);
149 getPlayer()->jumpToPercent(40);
153 getPlayer()->jumpToPercent(50);
157 getPlayer()->jumpToPercent(60);
161 getPlayer()->jumpToPercent(70);
165 getPlayer()->jumpToPercent(80);
169 getPlayer()->jumpToPercent(90);
178 else showBanner(true);
179 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
180 if (playall) play(VMediaList::MV_NEXT);
187 getPlayer()->unpause();
188 if (getPlayer()->getState() != AudioPlayer::S_ERROR) justPlaying=true;
189 else if (playall) play(VMediaList::MV_NEXT);
195 getPlayer()->pause();
206 destroyBanner(); // This line inserted to prevent BoxStack deadlock in this class destructor - Chris
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);
218 void VAudioplayer::processMessage(Message* m)
220 if (m->message == Message::MOUSE_MOVE)
224 else if (m->message == Message::MOUSE_LBDOWN)
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())
232 BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
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);
242 case AudioPlayer::STATUS_CHANGE:
245 case AudioPlayer::SHORT_UPDATE:
248 BoxStack::getInstance()->update(banner,&clocksRegion);
249 BoxStack::getInstance()->update(banner,&barRegion);
250 Timers::getInstance()->setTimerD(this, 3, 1);
253 case AudioPlayer::NEW_SONG:
256 case AudioPlayer::CONNECTION_LOST:
258 if (AudioPlayer *player=getPlayer(false)) {
262 Command::getInstance()->connectionLost();
268 VAudioplayer * VAudioplayer::createPlayer(VMediaList * mparent, bool bplayall) {
269 Log::getInstance()->log("VAudioplayer::createPlayer", Log::DEBUG, "p=%p",
271 VAudioplayer *vmn=new VAudioplayer(mparent);
272 BoxStack::getInstance()->add(vmn);
273 BoxStack::getInstance()->update(vmn);
275 if (bplayall) vmn->startPlayall();
276 BoxStack::getInstance()->update(vmn);
280 void VAudioplayer::startPlayall() {
284 void VAudioplayer::play(ULONG move) {
285 currentMedia=parent->getMedia(MEDIA_TYPE_AUDIO,move);
287 const char *fname=currentMedia->getFileName();
288 ftime=currentMedia->getTime();
289 int len=strlen(fname)+2+strlen(parent->getDirname());
294 fullname=new char[len];
295 sprintf(fullname,"%s/%s",parent->getDirname(),fname);
296 if (filename) delete filename;
298 filename=new char[len];
299 strcpy(filename,fname);
300 Log::getInstance()->log("VAudioplayer::load", Log::DEBUG, "filename=%s,p=%p",
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");
310 //leave this to the update message from the player:
314 if (AudioPlayer * player=getPlayer(false)) {
317 Command::getInstance()->connectionLost();
322 Log::getInstance()->log("VAudioplayer", Log::DEBUG, "player started for %s",filename);
325 void VAudioplayer::retriggerBanner() {
326 if (! banner) return;
327 bannerTime=time(NULL);
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) {
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);
353 void VAudioplayer::destroyBanner() {
355 BoxStack::getInstance()->remove(banner);
358 Timers::getInstance()->cancelTimer(this,1);
359 Timers::getInstance()->cancelTimer(this,3);
361 void VAudioplayer::updateBanner(bool restart) {
362 time_t currTime=time(NULL);
363 time_t lastBanner=bannerTime;
364 if (currTime - lastBanner > BANNER_TIME){
367 if (banner||restart) {
371 void VAudioplayer::timercall(int clientref) {
372 Log::getInstance()->log("VAudioplayer::timercall", Log::DEBUG, "id=%d",clientref);
378 Message* m = new Message();
379 //we misuse PLAYER_EVENT here
380 m->message = Message::PLAYER_EVENT;
383 m->parameter=(clientref==1)?AudioPlayer::STATUS_CHANGE:
384 AudioPlayer::SHORT_UPDATE;
385 Command::getInstance()->postMessageFromOuterSpace(m);
390 bool stillError=false;
391 if (AudioPlayer * player=getPlayer(false)) {
392 stillError=player->getState()==AudioPlayer::S_ERROR;
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;
401 m->parameter=AudioPlayer::STREAM_END;
402 Command::getInstance()->postMessageFromOuterSpace(m);
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();
415 m->parameter=(ULONG)v;
416 Command::getInstance()->postMessageFromOuterSpace(m);
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);
428 if (Video::getInstance()->getFormat() == Video::PAL)
430 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
434 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
437 rt->setTitleBarOn(0);
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;
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);
455 void VAudioplayer::drawBanner(){
456 Log::getInstance()->log("VAudioPlayer",Log::DEBUG, "draw banner for %p",banner);
458 char *playerTitle=getPlayer()->getTitle();
459 if (playerTitle) title=playerTitle;
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;
467 const char *first=NULL;
468 char *playerInfo=NULL;
471 first=tr("Unable to play audio file");
475 playerInfo=getPlayer()->getID3Info();
476 len=strlen(filename)+strlen(pl)+30+strlen(parent->getDirname())+Media::TIMEBUFLEN+100;
478 for (UINT i=0;i<strlen(playerInfo);i++)
479 if (playerInfo[i] == '\n') numlines++;
480 len+=strlen(playerInfo);
487 if (numlines != bannerlines) {
488 if (banner) destroyBanner();
489 banner=createBannerView(numlines);
492 banner=createBannerView(numlines);
494 bannerlines=numlines;
495 char *buf=new char [len];
497 SNPRINTF(buf,len,"%s",first);
500 char tbuf[Media::TIMEBUFLEN];
502 SNPRINTF(buf,len,"%s\n"
508 tr("FileName"),currentMedia->getFileName(),
510 tr("Directory"),parent->getDirname(),
511 tr("Time"),currentMedia->getTimeString(tbuf));
514 SNPRINTF(buf,len,"%s\n"
519 tr("FileName"),currentMedia->getFileName(),
520 tr("Directory"),parent->getDirname(),
521 tr("Time"),currentMedia->getTimeString(tbuf));
524 Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
525 //now the real drawing functions
529 // w.setSurface(banner->surface);
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);
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;
543 case AudioPlayer::S_PLAY:
544 w.nextSymbol = WSymbol::PLAY;
546 case AudioPlayer::S_DONE:
548 w.nextSymbol = WSymbol::PLAY;
552 case AudioPlayer::S_BACK:
553 w.nextSymbol = WSymbol::FBWD ;
555 case AudioPlayer::S_FF:
556 w.nextSymbol = WSymbol::FFWD ;
563 w.setPosition(x, ybottom-24);
568 banner->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
571 if (playerInfo) delete playerInfo;
572 if (playerTitle) delete playerTitle;
576 void VAudioplayer::drawClocks() {
577 if (! banner) return;
578 //draw clocks and bar
579 banner->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
581 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
582 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
585 /* gmtime_r(¤tSec,&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);
595 if (currentSec >= lengthSec)
597 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
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);
606 banner->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
609 int progBarXbase = 0;
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);
615 if (currentSec > lengthSec) currentSec=lengthSec;
616 if (lengthSec == 0) return;
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);