// rdcartslot.cpp // // The cart slot widget. // // (C) Copyright 2012-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "rdconfig.h" #include "rdconf.h" #include "rdescape_string.h" #include "rdcart.h" #include "rdcartslot.h" RDCartSlot::RDCartSlot(int slotnum,RDRipc *ripc,RDCae *cae,RDStation *station, RDConfig *config,RDListSvcs *svcs_dialog, RDSlotDialog *slot_dialog,RDCartDialog *cart_dialog, RDCueEditDialog *cue_dialog, const QString &caption,RDAirPlayConf *conf, QWidget *parent) : RDWidget(parent) { slot_number=slotnum; slot_ripc=ripc; slot_cae=cae; slot_station=station; slot_config=config; slot_svcs_dialog=svcs_dialog; slot_slot_dialog=slot_dialog; slot_cart_dialog=cart_dialog; slot_cue_dialog=cue_dialog; slot_caption=caption; slot_airplay_conf=conf; slot_svc_names=NULL; slot_stop_requested=false; slot_logline=new RDLogLine(); slot_pause_enabled=false; slot_user=NULL; slot_svcname=""; slot_breakaway_cart=0; slot_breakaway_length=0; slot_timescaling_active=false; slot_temp_cart=false; // // Palettes // slot_ready_color= QPalette(QColor(BUTTON_STOPPED_BACKGROUND_COLOR),backgroundColor()); slot_playing_color= QPalette(QColor(BUTTON_PLAY_BACKGROUND_COLOR),backgroundColor()); // // Slot Options // slot_options=new RDSlotOptions(station->name(),slotnum); slot_options->load(); // // Play Deck // slot_deck=new RDPlayDeck(slot_cae,0,this); connect(slot_deck,SIGNAL(stateChanged(int,RDPlayDeck::State)), this,SLOT(stateChangedData(int,RDPlayDeck::State))); connect(slot_deck,SIGNAL(position(int,int)), this,SLOT(positionData(int,int))); connect(slot_deck,SIGNAL(hookEnd(int)),this,SLOT(hookEndData(int))); connect(slot_cae,SIGNAL(timescalingSupported(int,bool)), this,SLOT(timescalingSupportedData(int,bool))); // // Start Button // slot_start_button=new QPushButton(QString().sprintf("%d",slotnum+1),this); slot_start_button->setGeometry(0,0,sizeHint().height(),sizeHint().height()); slot_start_button->setFont(hugeButtonFont()); slot_start_button->setDisabled(true); connect(slot_start_button,SIGNAL(clicked()),this,SLOT(startData())); // // Slot Box // slot_box=new RDSlotBox(slot_deck,conf,this); slot_box->setBarMode(false); slot_box->setAllowDrags(station->enableDragdrop()); slot_box->setAcceptDrops(station->enableDragdrop()); slot_box->setGeometry(5+sizeHint().height(),0, slot_box->sizeHint().width(), slot_box->sizeHint().height()); connect(slot_box,SIGNAL(doubleClicked()),this,SLOT(doubleClickedData())); connect(slot_box,SIGNAL(cartDropped(unsigned)), this,SLOT(cartDroppedData(unsigned))); // // Load Button // slot_load_button=new QPushButton(tr("Load"),this); slot_load_button-> setGeometry(sizeHint().height()+5+slot_box->sizeHint().width()+5,0, sizeHint().height(),sizeHint().height()); slot_load_button->setFont(buttonFont()); connect(slot_load_button,SIGNAL(clicked()),this,SLOT(loadData())); // // Options Button // slot_options_button=new QPushButton(this); slot_options_button-> setGeometry(2*sizeHint().height()+10+slot_box->sizeHint().width()+5,0, sizeHint().height(),sizeHint().height()); slot_options_button->setFont(buttonFont()); connect(slot_options_button,SIGNAL(clicked()),this,SLOT(optionsData())); updateOptions(); InitializeOptions(); } RDCartSlot::~RDCartSlot() { stop(); ClearTempCart(); delete slot_logline; delete slot_options; } QSize RDCartSlot::sizeHint() const { return QSize(670,86); } QSizePolicy RDCartSlot::sizePolicy() const { return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); } void RDCartSlot::setUser(RDUser *user) { slot_user=user; } RDSlotOptions *RDCartSlot::slotOptions() const { return slot_options; } void RDCartSlot::updateOptions() { slot_deck->setCard(slot_options->card()); slot_deck->setPort(slot_options->outputPort()); switch(slot_options->mode()) { case RDSlotOptions::CartDeckMode: SetInput(false); slot_logline->setHookMode(slot_options->hookMode()); if(slot_options->hookMode()) { slot_options_button->setText(tr("Options")+"\n"+tr("[Hook]")); } else { slot_options_button->setText(tr("Options")+"\n"+tr("[Full]")); } break; case RDSlotOptions::BreakawayMode: SetInput(true); slot_start_button->setDisabled(true); slot_box->setService(slot_svcname); slot_box->setStatusLine(tr("Waiting for break...")); slot_load_button->setText(tr("Load")); slot_logline->setHookMode(false); slot_options_button->setText(tr("Options")+"\n"+tr("[Breakaway]")); break; case RDSlotOptions::LastMode: break; } slot_box->setMode(slot_options->mode()); slot_options->save(); if(slot_logline->cartNumber()!=0) { load(slot_logline->cartNumber()); } } void RDCartSlot::setSvcNames(std::vector *svcnames) { slot_svc_names=svcnames; } void RDCartSlot::setCart(RDCart *cart,int break_len) { if(cart==NULL) { slot_logline->clear(); if(slot_options->mode()!=RDSlotOptions::BreakawayMode) { slot_logline->setHookMode(slot_options->hookMode()); } slot_box->clear(); } else { slot_logline->loadCart(cart->number(),RDLogLine::Play,0,true, RDLogLine::NoTrans,break_len); slot_logline-> setEvent(0,RDLogLine::Play,slot_logline->timescalingActive(),break_len); slot_box->setCart(slot_logline); slot_box->setBarMode(false); } } bool RDCartSlot::load(int cartnum,int break_len) { bool ret=false; RDCart *cart=new RDCart(cartnum); if(cart->exists()) { if(slot_deck->state()!=RDPlayDeck::Stopped) { stop(); } setCart(cart,break_len); slot_start_button-> setEnabled(slot_options->mode()==RDSlotOptions::CartDeckMode); slot_start_button->setPalette(slot_ready_color); slot_load_button->setText(tr("Unload")); slot_options->setCartNumber(cartnum); slot_options->save(); ret=true; } delete cart; return ret; } void RDCartSlot::unload() { if(slot_deck->state()==RDPlayDeck::Stopped) { ClearTempCart(); setCart(NULL); slot_start_button->setDisabled(true); slot_start_button->setPalette(palette()); slot_load_button->setText(tr("Load")); slot_options->setCartNumber(0); slot_options->save(); } } bool RDCartSlot::play() { bool ret=false; if(slot_logline->cartNumber()!=0) { if(slot_deck->setCart(slot_logline,true)) { if(slot_options->hookMode()&&(slot_logline->hookStartPoint()>=0)) { slot_deck->playHook(); } else { slot_deck->play(slot_logline->playPosition()); } slot_logline->setStartTime(RDLogLine::Actual,QTime::currentTime()); ret=true; } } return ret; } bool RDCartSlot::pause() { return false; } bool RDCartSlot::stop() { bool ret=false; if(slot_logline->cartNumber()!=0) { slot_stop_requested=true; slot_deck->stop(); RDCart *cart=new RDCart(slot_logline->cartNumber()); setCart(cart); delete cart; ret=true; } return ret; } bool RDCartSlot::breakAway(unsigned msecs) { bool ret=false; unsigned cartnum=0; if(slot_options->mode()==RDSlotOptions::BreakawayMode) { if(msecs==0) { stop(); SetInput(true); unload(); slot_box->setService(slot_svcname); slot_box->setStatusLine(tr("Waiting for break...")); } else { cartnum=SelectCart(slot_svcname,msecs); if(cartnum!=0) { switch(slot_deck->state()) { case RDPlayDeck::Playing: case RDPlayDeck::Paused: case RDPlayDeck::Stopping: slot_breakaway_cart=cartnum; slot_breakaway_length=msecs; stop(); break; case RDPlayDeck::Stopped: case RDPlayDeck::Finished: SetInput(false); if(slot_timescaling_active) { load(cartnum,msecs); } else { load(cartnum); } play(); syslog(LOG_INFO,"started breakaway, len: %u cart: %u cut: %d", msecs,cartnum,slot_logline->cutNumber()); break; } } else { slot_box->setStatusLine(tr("No cart found for length")+" "+ RDGetTimeLength(msecs,false,false)); } } } return ret; } bool RDCartSlot::pauseEnabled() const { return slot_pause_enabled; } void RDCartSlot::setPauseEnabled(bool state) { slot_pause_enabled=state; } void RDCartSlot::updateMeters() { short lvls[2]; switch(slot_deck->state()) { case RDPlayDeck::Playing: case RDPlayDeck::Stopping: slot_cae-> outputStreamMeterUpdate(slot_deck->card(),slot_deck->stream(),lvls); slot_box->updateMeters(lvls); break; case RDPlayDeck::Paused: case RDPlayDeck::Stopped: case RDPlayDeck::Finished: break; } } void RDCartSlot::startData() { switch(slot_deck->state()) { case RDPlayDeck::Playing: case RDPlayDeck::Stopping: case RDPlayDeck::Paused: stop(); break; case RDPlayDeck::Stopped: case RDPlayDeck::Finished: play(); break; } } void RDCartSlot::doubleClickedData() { if(slot_logline->cartNumber()==0) { loadData(); } else { if(slot_cue_dialog->exec(slot_logline)==0) { slot_box->setBarMode(true); slot_box->setCart(slot_logline); } } } void RDCartSlot::loadData() { int cartnum; QString svcname; switch(slot_options->mode()) { case RDSlotOptions::CartDeckMode: cartnum=slot_logline->cartNumber(); if(cartnum==0) { if(slot_cart_dialog->exec(&cartnum,RDCart::All,&svcname,0, slot_user->name(),slot_user->password(), &slot_temp_cart)==0) { load(cartnum); } } else { unload(); } break; case RDSlotOptions::BreakawayMode: if(slot_svcs_dialog->exec(&slot_svcname)==0) { slot_box->setService(slot_svcname); slot_box->setStatusLine(tr("Waiting for break...")); } break; case RDSlotOptions::LastMode: break; } } void RDCartSlot::optionsData() { RDSlotOptions::Mode old_mode=slot_options->mode(); if(slot_slot_dialog->exec(slot_options)==0) { if(old_mode!=slot_options->mode()) { slot_box->clear(); } updateOptions(); } } void RDCartSlot::stateChangedData(int id,RDPlayDeck::State state) { //printf("stateChangedData(%d,%d)\n",id,state); short lvls[2]={-10000,-10000}; RDCart *cart=NULL; switch(state) { case RDPlayDeck::Playing: LogPlayout(state); slot_start_button-> setEnabled(slot_options->mode()==RDSlotOptions::CartDeckMode); slot_start_button->setPalette(slot_playing_color); slot_load_button->setDisabled(true); slot_options_button->setDisabled(true); break; case RDPlayDeck::Stopped: case RDPlayDeck::Finished: LogPlayout(state); slot_start_button-> setEnabled(slot_options->mode()==RDSlotOptions::CartDeckMode); slot_start_button->setPalette(slot_ready_color); slot_load_button->setEnabled(true); slot_options_button->setEnabled(true); slot_box->setTimer(0); slot_box->updateMeters(lvls); slot_box->setCart(slot_logline); switch(slot_options->mode()) { case RDSlotOptions::CartDeckMode: if(!slot_stop_requested) { switch(slot_options->stopAction()) { case RDSlotOptions::RecueOnStop: cart=new RDCart(slot_logline->cartNumber()); setCart(cart); delete cart; break; case RDSlotOptions::UnloadOnStop: unload(); break; case RDSlotOptions::LoopOnStop: play(); break; case RDSlotOptions::LastStop: break; } } break; case RDSlotOptions::BreakawayMode: if(slot_breakaway_cart>0) { SetInput(false); load(slot_breakaway_cart); play(); syslog(LOG_INFO,"started breakaway, len: %u cart: %u cut: %d", slot_breakaway_length,slot_breakaway_cart, slot_logline->cutNumber()); slot_breakaway_cart=0; slot_breakaway_length=0; } else { SetInput(true); unload(); slot_box->setService(slot_svcname); slot_box->setStatusLine(tr("Waiting for break...")); // LogPlayout(RDAirPlayConf::TrafficFinish); } break; case RDSlotOptions::LastMode: break; } slot_stop_requested=false; break; case RDPlayDeck::Stopping: case RDPlayDeck::Paused: break; } } void RDCartSlot::positionData(int id,int msecs) { slot_box->setTimer(msecs); } void RDCartSlot::hookEndData(int id) { if(slot_options->hookMode()) { stop(); } } void RDCartSlot::timescalingSupportedData(int card,bool state) { if(card==slot_options->card()) { slot_timescaling_active=state; } } void RDCartSlot::cartDroppedData(unsigned cartnum) { if(cartnum==0) { unload(); } else { load(cartnum); } } void RDCartSlot::InitializeOptions() { slot_svcname=slot_options->service(); switch(slot_options->mode()) { case RDSlotOptions::CartDeckMode: if(slot_options->cartNumber()>0) { load(slot_options->cartNumber()); } break; case RDSlotOptions::BreakawayMode: slot_box->setService(slot_svcname); slot_box->setStatusLine(tr("Waiting for break...")); break; case RDSlotOptions::LastMode: break; } slot_cae->requestTimescale(slot_options->card()); } unsigned RDCartSlot::SelectCart(const QString &svcname,unsigned msecs) { QString sql; RDSqlQuery *q; unsigned cartnum=0; int diff=1000000; sql=QString("select AUTOFILLS.CART_NUMBER,CART.FORCED_LENGTH from ")+ "AUTOFILLS left join CART on AUTOFILLS.CART_NUMBER=CART.NUMBER"+ QString(). sprintf(" where (CART.FORCED_LENGTH>%u)&&(CART.FORCED_LENGTH<%u)&&", (unsigned)((double)msecs*RD_TIMESCALE_MIN), (unsigned)((double)msecs*RD_TIMESCALE_MAX))+ "(SERVICE=\""+RDEscapeString(svcname)+"\")"; q=new RDSqlQuery(sql); while(q->next()) { int cur_diff = msecs-q->value(1).toInt(); if(::abs(cur_diff)value(0).toUInt(); diff=::abs(cur_diff); } } delete q; return cartnum; } void RDCartSlot::SetInput(bool state) { int level=-10000; if(state) { level=0; } slot_cae-> setPassthroughVolume(slot_options->card(),slot_options->inputPort(), slot_options->outputPort(),level); } void RDCartSlot::LogPlayout(RDPlayDeck::State state) { if(state==RDPlayDeck::Playing) { RDCut *cut=new RDCut(slot_logline->cutName()); cut->logPlayout(); delete cut; } if((state!=RDPlayDeck::Stopped)&&(state!=RDPlayDeck::Finished)) { return; } RDAirPlayConf::TrafficAction action=RDAirPlayConf::TrafficFinish; if(state==RDPlayDeck::Stopped) { action=RDAirPlayConf::TrafficStop; } QString sql; RDSqlQuery *q; QDateTime datetime=QDateTime(QDate::currentDate(),QTime::currentTime()); int length= slot_logline->startTime(RDLogLine::Actual).msecsTo(datetime.time()); if(length<0) { // Event crossed midnight! length+=86400000; datetime.setDate(datetime.date().addDays(-1)); } if(!slot_svcname.isEmpty()) { QDateTime eventDateTime(datetime.date(), slot_logline->startTime(RDLogLine::Actual)); QString svctablename=slot_svcname; svctablename.replace(" ","_"); sql=QString("insert into ELR_LINES set ")+ "SERVICE_NAME=\""+RDEscapeString(slot_svcname)+"\","+ QString().sprintf("LENGTH=%d,",length)+ QString().sprintf("LOG_ID=%d,",slot_number+1)+ QString().sprintf("CART_NUMBER=%u,",slot_logline->cartNumber())+ QString().sprintf("EVENT_TYPE=%d,",action)+ QString().sprintf("EVENT_SOURCE=%d,",slot_logline->source())+ QString().sprintf("EXT_LENGTH=%d,",slot_logline->extLength())+ QString().sprintf("PLAY_SOURCE=%d,",RDLogLine::CartSlot)+ QString().sprintf("CUT_NUMBER=%d,",slot_logline->cutNumber())+ QString().sprintf("USAGE_CODE=%d,",slot_logline->usageCode())+ QString().sprintf("START_SOURCE=%d,",slot_logline->startSource())+ "STATION_NAME=\""+RDEscapeString(slot_station->name())+"\","+ "EVENT_DATETIME="+RDCheckDateTime(eventDateTime,"yyyy-MM-dd hh:mm:ss")+ ","+ "EXT_START_TIME="+RDCheckDateTime( slot_logline->extStartTime(),"hh:mm:ss")+","+ "EXT_DATA=\""+RDEscapeString(slot_logline->extData())+"\","+ "EXT_EVENT_ID=\""+RDEscapeString(slot_logline->extEventId())+"\","+ "EXT_ANNC_TYPE=\""+RDEscapeString(slot_logline->extAnncType())+"\","+ "EXT_CART_NAME=\""+RDEscapeString(slot_logline->extCartName())+"\","+ "TITLE=\""+RDEscapeString(slot_logline->title())+"\","+ "ARTIST=\""+RDEscapeString(slot_logline->artist())+"\","+ "SCHEDULED_TIME="+ RDCheckDateTime(slot_logline->startTime(RDLogLine::Logged),"hh:mm:ss")+","+ "ISRC=\""+RDEscapeString(slot_logline->isrc())+"\","+ "PUBLISHER=\""+RDEscapeString(slot_logline->publisher())+"\","+ "COMPOSER=\""+RDEscapeString(slot_logline->composer())+"\","+ "ONAIR_FLAG=\""+RDYesNo(slot_ripc->onairFlag())+"\","+ "ALBUM=\""+RDEscapeString(slot_logline->album())+"\","+ "LABEL=\""+RDEscapeString(slot_logline->label())+"\","+ "CONDUCTOR=\""+RDEscapeString(slot_logline->conductor())+"\","+ "USER_DEFINED=\""+RDEscapeString(slot_logline->userDefined())+"\","+ "SONG_ID=\""+RDEscapeString(slot_logline->songId())+"\","+ "DESCRIPTION=\""+RDEscapeString(slot_logline->description())+"\","+ "OUTCUE=\""+RDEscapeString(slot_logline->outcue())+"\","+ "ISCI=\""+RDEscapeString(slot_logline->isci())+"\""; q=new RDSqlQuery(sql); delete q; } } void RDCartSlot::ClearTempCart() { RDCart *cart=NULL; if(slot_temp_cart) { cart=new RDCart(slot_logline->cartNumber()); if(cart->exists()) { cart->remove(slot_station,slot_user,slot_config); } slot_temp_cart=false; delete cart; } }