// rdcartslot.cpp // // The cart slot widget. // // (C) Copyright 2012-2023 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 "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), palette().color(QPalette::Background)); slot_playing_color= QPalette(QColor(BUTTON_PLAY_BACKGROUND_COLOR), palette().color(QPalette::Background)); // // 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 RDSlotButton(slotnum,this); slot_start_button->setPortLabel(slot_options->outputPortLabel()); slot_start_button->setGeometry(0,0,sizeHint().height(),sizeHint().height()); slot_start_button->setFont(hugeButtonFont()); slot_start_button->setDisabled(true); connect(slot_deck,SIGNAL(stateChanged(int,RDPlayDeck::State)), slot_start_button,SLOT(setState(int,RDPlayDeck::State))); 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_deck->state()==RDPlayDeck::Stopped)&& (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->serial(),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)) { 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,QString(), &slot_temp_cart)) { load(cartnum); } } else { unload(); } break; case RDSlotOptions::BreakawayMode: if(slot_svcs_dialog->exec(&slot_svcname)) { 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)) { if(old_mode!=slot_options->mode()) { slot_box->clear(); } updateOptions(); } } void RDCartSlot::stateChangedData(int id,RDPlayDeck::State 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_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_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...")); } 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`,"+ // 00 "`CART`.`FORCED_LENGTH` "+ // 01 "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; 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::asprintf("`LENGTH`=%d,",length)+ QString::asprintf("`LOG_ID`=%d,",slot_number+1)+ QString::asprintf("`CART_NUMBER`=%u,",slot_logline->cartNumber())+ QString::asprintf("`EVENT_TYPE`=%d,",action)+ QString::asprintf("`EVENT_SOURCE`=%d,",slot_logline->source())+ QString::asprintf("`EXT_LENGTH`=%d,",slot_logline->extLength())+ QString::asprintf("`PLAY_SOURCE`=%d,",RDLogLine::CartSlot)+ QString::asprintf("`CUT_NUMBER`=%d,",slot_logline->cutNumber())+ QString::asprintf("`USAGE_CODE`=%d,",slot_logline->usageCode())+ QString::asprintf("`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())+"'"; RDSqlQuery::apply(sql); } } 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; } }