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 void VAudioplayer::preDelete()
71 // Another note from Chris:
72 // I have moved these timer cancels here from the destructor, please see boxx.h
74 Timers::getInstance()->cancelTimer(this,1);
75 Timers::getInstance()->cancelTimer(this,2);
76 Timers::getInstance()->cancelTimer(this,3);
79 VAudioplayer::~VAudioplayer()
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.
88 if (banner) BoxStack::getInstance()->remove(banner);
89 if (fullname) delete fullname;
90 if (filename) delete filename;
93 //TODO leave this to medialist and stop only...
94 if (getPlayer(false)) {
95 AudioPlayer::getInstance(NULL,false)->shutdown();
101 void VAudioplayer::draw()
103 Log::getInstance()->log("VAudioplayer::draw", Log::DEBUG, "p=%p", this);
107 int VAudioplayer::handleCommand(int command)
109 Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "p=%p,cmd=%d", this,command);
110 Timers::getInstance()->cancelTimer(this,1);
116 play(VMediaList::MV_PREV);
117 BoxStack::getInstance()->update(this);
120 case Remote::FORWARD:
121 getPlayer()->fastForward();
124 case Remote::DF_DOWN:
126 play(VMediaList::MV_NEXT);
127 BoxStack::getInstance()->update(this);
130 case Remote::SKIPFORWARD:
131 getPlayer()->skipForward(10);
134 case Remote::SKIPBACK:
135 getPlayer()->skipBackward(10);
138 case Remote::REVERSE:
142 getPlayer()->jumpToPercent(0);
146 getPlayer()->jumpToPercent(10);
150 getPlayer()->jumpToPercent(20);
154 getPlayer()->jumpToPercent(30);
158 getPlayer()->jumpToPercent(40);
162 getPlayer()->jumpToPercent(50);
166 getPlayer()->jumpToPercent(60);
170 getPlayer()->jumpToPercent(70);
174 getPlayer()->jumpToPercent(80);
178 getPlayer()->jumpToPercent(90);
187 else showBanner(true);
188 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
189 if (playall) play(VMediaList::MV_NEXT);
196 getPlayer()->unpause();
197 if (getPlayer()->getState() != AudioPlayer::S_ERROR) justPlaying=true;
198 else if (playall) play(VMediaList::MV_NEXT);
204 getPlayer()->pause();
215 destroyBanner(); // This line inserted to prevent BoxStack deadlock in this class destructor - Chris
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);
227 void VAudioplayer::processMessage(Message* m)
229 if (m->message == Message::MOUSE_MOVE)
233 else if (m->message == Message::MOUSE_LBDOWN)
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())
241 BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
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);
251 case AudioPlayer::STATUS_CHANGE:
254 case AudioPlayer::SHORT_UPDATE:
257 BoxStack::getInstance()->update(banner,&clocksRegion);
258 BoxStack::getInstance()->update(banner,&barRegion);
259 Timers::getInstance()->setTimerD(this, 3, 1);
262 case AudioPlayer::NEW_SONG:
265 case AudioPlayer::CONNECTION_LOST:
267 if (AudioPlayer *player=getPlayer(false)) {
271 Command::getInstance()->connectionLost();
277 VAudioplayer * VAudioplayer::createPlayer(VMediaList * mparent, bool bplayall) {
278 Log::getInstance()->log("VAudioplayer::createPlayer", Log::DEBUG, "p=%p",
280 VAudioplayer *vmn=new VAudioplayer(mparent);
281 BoxStack::getInstance()->add(vmn);
282 BoxStack::getInstance()->update(vmn);
284 if (bplayall) vmn->startPlayall();
285 BoxStack::getInstance()->update(vmn);
289 void VAudioplayer::startPlayall() {
293 void VAudioplayer::play(ULONG move) {
294 currentMedia=parent->getMedia(MEDIA_TYPE_AUDIO,move);
296 const char *fname=currentMedia->getFileName();
297 ftime=currentMedia->getTime();
298 int len=strlen(fname)+2+strlen(parent->getDirname());
303 fullname=new char[len];
304 sprintf(fullname,"%s/%s",parent->getDirname(),fname);
305 if (filename) delete filename;
307 filename=new char[len];
308 strcpy(filename,fname);
309 Log::getInstance()->log("VAudioplayer::load", Log::DEBUG, "filename=%s,p=%p",
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");
319 //leave this to the update message from the player:
323 if (AudioPlayer * player=getPlayer(false)) {
326 Command::getInstance()->connectionLost();
331 Log::getInstance()->log("VAudioplayer", Log::DEBUG, "player started for %s",filename);
334 void VAudioplayer::retriggerBanner() {
335 if (! banner) return;
336 bannerTime=time(NULL);
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) {
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);
362 void VAudioplayer::destroyBanner() {
364 BoxStack::getInstance()->remove(banner);
367 Timers::getInstance()->cancelTimer(this,1);
368 Timers::getInstance()->cancelTimer(this,3);
370 void VAudioplayer::updateBanner(bool restart) {
371 time_t currTime=time(NULL);
372 time_t lastBanner=bannerTime;
373 if (currTime - lastBanner > BANNER_TIME){
376 if (banner||restart) {
380 void VAudioplayer::timercall(int clientref) {
381 Log::getInstance()->log("VAudioplayer::timercall", Log::DEBUG, "id=%d",clientref);
387 Message* m = new Message();
388 //we misuse PLAYER_EVENT here
389 m->message = Message::PLAYER_EVENT;
392 m->parameter=(clientref==1)?AudioPlayer::STATUS_CHANGE:
393 AudioPlayer::SHORT_UPDATE;
394 Command::getInstance()->postMessageFromOuterSpace(m);
399 bool stillError=false;
400 if (AudioPlayer * player=getPlayer(false)) {
401 stillError=player->getState()==AudioPlayer::S_ERROR;
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;
410 m->parameter=AudioPlayer::STREAM_END;
411 Command::getInstance()->postMessageFromOuterSpace(m);
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();
424 m->parameter=(ULONG)v;
425 Command::getInstance()->postMessageFromOuterSpace(m);
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);
437 if (Video::getInstance()->getFormat() == Video::PAL)
439 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
443 rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
446 rt->setTitleBarOn(0);
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;
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);
464 void VAudioplayer::drawBanner(){
465 Log::getInstance()->log("VAudioPlayer",Log::DEBUG, "draw banner for %p",banner);
467 char *playerTitle=getPlayer()->getTitle();
468 if (playerTitle) title=playerTitle;
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;
476 const char *first=NULL;
477 char *playerInfo=NULL;
480 first=tr("Unable to play audio file");
484 playerInfo=getPlayer()->getID3Info();
485 len=strlen(filename)+strlen(pl)+30+strlen(parent->getDirname())+Media::TIMEBUFLEN+100;
487 for (UINT i=0;i<strlen(playerInfo);i++)
488 if (playerInfo[i] == '\n') numlines++;
489 len+=strlen(playerInfo);
496 if (numlines != bannerlines) {
497 if (banner) destroyBanner();
498 banner=createBannerView(numlines);
501 banner=createBannerView(numlines);
503 bannerlines=numlines;
504 char *buf=new char [len];
506 SNPRINTF(buf,len,"%s",first);
509 char tbuf[Media::TIMEBUFLEN];
511 SNPRINTF(buf,len,"%s\n"
517 tr("FileName"),currentMedia->getFileName(),
519 tr("Directory"),parent->getDirname(),
520 tr("Time"),currentMedia->getTimeString(tbuf));
523 SNPRINTF(buf,len,"%s\n"
528 tr("FileName"),currentMedia->getFileName(),
529 tr("Directory"),parent->getDirname(),
530 tr("Time"),currentMedia->getTimeString(tbuf));
533 Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
534 //now the real drawing functions
538 // w.setSurface(banner->surface);
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);
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;
552 case AudioPlayer::S_PLAY:
553 w.nextSymbol = WSymbol::PLAY;
555 case AudioPlayer::S_DONE:
557 w.nextSymbol = WSymbol::PLAY;
561 case AudioPlayer::S_BACK:
562 w.nextSymbol = WSymbol::FBWD ;
564 case AudioPlayer::S_FF:
565 w.nextSymbol = WSymbol::FFWD ;
572 w.setPosition(x, ybottom-24);
577 banner->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
580 if (playerInfo) delete playerInfo;
581 if (playerTitle) delete playerTitle;
585 void VAudioplayer::drawClocks() {
586 if (! banner) return;
587 //draw clocks and bar
588 banner->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
590 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
591 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
594 /* gmtime_r(¤tSec,&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);
604 if (currentSec >= lengthSec)
606 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
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);
615 banner->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
618 int progBarXbase = 0;
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);
624 if (currentSec > lengthSec) currentSec=lengthSec;
625 if (lengthSec == 0) return;
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);