// rdcae.cpp // // Connection to the Rivendell Core Audio Engine // // (C) Copyright 2002-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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include __RDCae_PlayChannel::__RDCae_PlayChannel(unsigned card,unsigned port) { d_card=card; d_port=port; d_position=0; for(int i=0;i<2;i++) { d_stream_levels[i]=RD_MUTE_DEPTH; } } unsigned __RDCae_PlayChannel::card() const { return d_card; } unsigned __RDCae_PlayChannel::port() const { return d_port; } unsigned __RDCae_PlayChannel::position() const { return d_position; } void __RDCae_PlayChannel::setPosition(unsigned pos) { d_position=pos; } void __RDCae_PlayChannel::getStreamLevels(short lvls[2]) { for(int i=0;i<2;i++) { lvls[i]=d_stream_levels[i]; } } void __RDCae_PlayChannel::setStreamLevels(short left_lvl,short right_lvl) { d_stream_levels[0]=left_lvl; d_stream_levels[1]=right_lvl; } bool __RDCae_PlayChannel::operator==(const __RDCae_PlayChannel &other) const { return (d_card==other.d_card)&&(d_port==other.d_port); } RDCae::RDCae(RDStation *station,RDConfig *config,QObject *parent) : QObject(parent) { int flags=0; cae_station=station; cae_config=config; cae_connected=false; next_serial_number=1; // // Control Connection // if((cae_socket=socket(AF_INET,SOCK_STREAM,0))<0) { rda->syslog(LOG_ERR,"failed to create socket [%s]",strerror(errno)); exit(RDCoreApplication::ExitInternalError); } if((flags=fcntl(cae_socket,F_GETFL,NULL))<0) { rda->syslog(LOG_ERR,"failed to get control socket options [%s]", strerror(errno)); exit(RDCoreApplication::ExitInternalError); } flags=flags|O_NONBLOCK; if(fcntl(cae_socket,F_SETFL,flags)<0) { rda->syslog(LOG_ERR,"failed to set control socket options [%s]", strerror(errno)); exit(RDCoreApplication::ExitInternalError); } // // Meter Connection // if((cae_meter_socket=socket(AF_INET,SOCK_DGRAM,0))<0) { rda->syslog(LOG_ERR,"failed to meter create socket [%s]",strerror(errno)); exit(RDCoreApplication::ExitInternalError); } if((flags=fcntl(cae_meter_socket,F_GETFL,NULL))<0) { rda->syslog(LOG_ERR,"failed to get meter socket options [%s]", strerror(errno)); exit(RDCoreApplication::ExitInternalError); } flags=flags|O_NONBLOCK; if(fcntl(cae_meter_socket,F_SETFL,flags)<0) { rda->syslog(LOG_ERR,"failed to set meter socket options [%s]", strerror(errno)); exit(RDCoreApplication::ExitInternalError); } cae_meter_base_port=cae_config->meterBasePort(); cae_meter_port_range=cae_config->meterPortRange(); if(cae_meter_port_range>999) { cae_meter_port_range=999; } for(int16_t i=cae_meter_base_port;i<(cae_meter_base_port+cae_meter_port_range);i++) { struct sockaddr_in sa; memset(&sa,0,sizeof(sa)); sa.sin_family=AF_INET; sa.sin_port=htons(i); sa.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(cae_meter_socket,(struct sockaddr *)(&sa),sizeof(sa))==0) { cae_meter_port=i; i=(cae_meter_base_port+cae_meter_port_range)+1; } } // // Initialize Data Structures // for(int i=0;i=0) { close(cae_socket); } if(cae_meter_socket>=0) { close(cae_meter_socket); } } bool RDCae::connectHost(QString *err_msg) { int count=10; struct sockaddr_in sa; QTimer *timer=new QTimer(this); connect(timer,SIGNAL(timeout()),this,SLOT(readyData())); timer->start(CAE_POLL_INTERVAL); memset(&sa,0,sizeof(sa)); sa.sin_family=AF_INET; sa.sin_port=htons(CAED_TCP_PORT); sa.sin_addr.s_addr= htonl(rda->station()->caeAddress(rda->config()).toIPv4Address()); while((::connect(cae_socket,(struct sockaddr *)(&sa),sizeof(sa))<0)&& (--count>0)) { usleep(100000); } usleep(100000); if(count>0) { SendCommand(QString().sprintf("PW %s!", cae_config->password().toUtf8().constData())); for(int i=0;isyslog(LOG_ERR,"%s",err_msg->toUtf8().constData()); return false; } *err_msg="ok"; return true; } void RDCae::enableMetering(QList *cards) { QString cmd=QString().sprintf("ME %u",0xFFFF&cae_meter_port); for(int i=0;isize();i++) { if(cards->at(i)>=0) { bool found=false; for(int j=0;jat(i)==cards->at(j)) { found=true; } } if(!found) { cmd+=QString().sprintf(" %d",cards->at(i)); } } } SendCommand(cmd+"!"); } unsigned RDCae::loadPlay(unsigned card,unsigned port,const QString &name) { unsigned serial=next_serial_number++; SendCommand(QString().sprintf("LP %u %u %u %s!", serial,card,port,name.toUtf8().constData())); bool found=false; for(QMap::const_iterator it=cae_play_channels.begin();it!=cae_play_channels.end();it++) { if((it.value()->card()==card)&&(it.value()->port()==port)) { found=true; break; } } cae_play_channels[serial]=new __RDCae_PlayChannel(card,port); if(!found) { emit playPortStatusChanged(card,port,true); } return serial; } void RDCae::unloadPlay(unsigned serial) { SendCommand(QString().sprintf("UP %u!",serial)); } void RDCae::positionPlay(unsigned serial,int pos) { if(pos<0) { return; } SendCommand(QString().sprintf("PP %u %u!",serial,pos)); } void RDCae::play(unsigned serial,unsigned length,int speed,bool pitch) { int pitch_state=0; if(pitch) { pitch_state=1; } //printf(" play: %s\n",QTime::currentTime().toString("hh:mm:ss.zzz").toUtf8().constData()); SendCommand(QString().sprintf("PY %u %u %d %d!", serial,length,speed,pitch_state)); } void RDCae::stopPlay(unsigned serial) { SendCommand(QString().sprintf("SP %u!",serial)); } void RDCae::loadRecord(int card,int stream,QString name, AudioCoding coding,int chan,int samp_rate, int bit_rate) { SendCommand(QString().sprintf("LR %d %d %d %d %d %d %s!", card,stream,(int)coding,chan,samp_rate, bit_rate,name.toUtf8().constData())); } void RDCae::unloadRecord(int card,int stream) { SendCommand(QString().sprintf("UR %d %d!",card,stream)); } void RDCae::record(int card,int stream,unsigned length,int threshold) { SendCommand(QString().sprintf("RD %d %d %u %d!", card,stream,length,threshold)); } void RDCae::stopRecord(int card,int stream) { SendCommand(QString().sprintf("SR %d %d!",card,stream)); } void RDCae::setClockSource(int card,RDCae::ClockSource src) { SendCommand(QString().sprintf("CS %d %d!",card,src)); } void RDCae::setInputVolume(int card,int stream,int level) { SendCommand(QString().sprintf("IV %d %d %d!",card,stream,level)); } void RDCae::setOutputVolume(unsigned serial,int level) { SendCommand(QString().sprintf("OV %u %d!",serial,level)); } void RDCae::fadeOutputVolume(unsigned serial,int level,int length) { SendCommand(QString().sprintf("FV %u %d %d!",serial,level,length)); } void RDCae::setInputLevel(int card,int port,int level) { SendCommand(QString().sprintf("IL %d %d %d!",card,port,level)); } void RDCae::setOutputLevel(int card,int port,int level) { SendCommand(QString().sprintf("OL %d %d %d!",card,port,level)); } void RDCae::setInputMode(int card,int stream,RDCae::ChannelMode mode) { SendCommand(QString().sprintf("IM %d %d %d!",card,stream,mode)); } void RDCae::setOutputMode(int card,int stream,RDCae::ChannelMode mode) { SendCommand(QString().sprintf("OM %d %d %d!",card,stream,mode)); } void RDCae::setInputVOXLevel(int card,int stream,int level) { SendCommand(QString().sprintf("IX %d %d %d!",card,stream,level)); } void RDCae::setInputType(int card,int port,RDCae::SourceType type) { SendCommand(QString().sprintf("IT %d %d %d!",card,port,type)); } void RDCae::setPassthroughVolume(int card,int in_port,int out_port,int level) { SendCommand(QString(). sprintf("AL %d %d %d %d!",card,in_port,out_port,level)); } bool RDCae::inputStatus(int card,int port) const { return input_status[card][port]; } void RDCae::inputMeterUpdate(int card,int port,short levels[2]) { UpdateMeters(); levels[0]=cae_input_levels[card][port][0]; levels[1]=cae_input_levels[card][port][1]; } void RDCae::outputMeterUpdate(int card,int port,short levels[2]) { UpdateMeters(); levels[0]=cae_output_levels[card][port][0]; levels[1]=cae_output_levels[card][port][1]; } void RDCae::outputStreamMeterUpdate(unsigned serial,short levels[2]) { __RDCae_PlayChannel *chan=NULL; if((chan=cae_play_channels.value(serial))!=NULL) { UpdateMeters(); chan->getStreamLevels(levels); } } unsigned RDCae::playPosition(unsigned serial) { __RDCae_PlayChannel *chan=NULL; if((chan=cae_play_channels.value(serial))!=NULL) { return chan->position(); } return 0; } void RDCae::requestTimescale(int card) { SendCommand(QString().sprintf("TS %d!",card)); } bool RDCae::playPortStatus(int card,int port,unsigned except_serial) const { for(QMap::const_iterator it= cae_play_channels.begin();it!=cae_play_channels.end();it++) { if((((int)it.value()->card())==card)&& (((int)it.value()->port())==port)&& (it.key()!=except_serial)) { return true; } } return false; } void RDCae::readyData() { char data[1501]; int n; if((n=read(cae_socket,data,1500))>0) { for(int i=0;i=0)&&(card=0)&&(card=0)&&(port=0)&&(card=0)&&(port=0)&&(card=0)&&(port=0)&&(card=0)&&(port=0)&&(card=0)&&(portsyslog(LOG_WARNING,"CAE response command \"%s\" was not processed", cmd.toUtf8().constData()); } } void RDCae::UpdateMeters() { char msg[1501]; int n; QStringList args; __RDCae_PlayChannel *chan=NULL; bool ok=false; while((n=read(cae_meter_socket,msg,1500))>0) { msg[n]=0; args=QString(msg).split(" "); if(args[0]=="ML") { if(args.size()==6) { if(args[1]=="I") { cae_input_levels[args[2].toInt()][args[3].toInt()][0]=args[4].toInt(); cae_input_levels[args[2].toInt()][args[3].toInt()][1]=args[5].toInt(); } if(args[1]=="O") { cae_output_levels[args[2].toInt()][args[3].toInt()][0]= args[4].toInt(); cae_output_levels[args[2].toInt()][args[3].toInt()][1]= args[5].toInt(); } } } if(args[0]=="MO") { if(args.size()==4) { unsigned serial=args.at(1).toUInt(&ok); if(ok) { if((chan=cae_play_channels.value(serial))!=NULL) { chan->setStreamLevels(args.at(2).toShort(),args.at(3).toShort()); } } } } if(args[0]=="MP") { if(args.size()==3) { unsigned serial=args.at(1).toUInt(&ok); if(ok) { emit playPositionChanged(serial,args.at(2).toUInt()); } } } } } bool RDCae::SerialCheck(unsigned serial,int linenum) const { if(serial==0) { rda->syslog(LOG_WARNING, "attempting to use null serial value at rdcae.cpp:%d",linenum); return false; } return true; }