// rdcae.cpp // // Connection to the Rivendell Core Audio Engine // // (C) Copyright 2002-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 #include #include #include #include #include #include #include #include #include #include RDCae::RDCae(RDStation *station,RDConfig *config,QObject *parent) : QObject(parent) { cae_station=station; cae_config=config; cae_connected=false; argnum=0; argptr=0; // // TCP Connection // cae_socket=new Q3SocketDevice(Q3SocketDevice::Stream); cae_socket->setBlocking(false); // // Meter Connection // cae_meter_socket=new Q3SocketDevice(Q3SocketDevice::Datagram); cae_meter_socket->setBlocking(false); 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(Q_INT16 i=cae_meter_base_port;i<(cae_meter_base_port+cae_meter_port_range);i++) { if(cae_meter_socket->bind(QHostAddress(),i)) { i=(cae_meter_base_port+cae_meter_port_range)+1; } } // // Initialize Data Structures // for(int i=0;istart(RD_METER_UPDATE_INTERVAL); } RDCae::~RDCae() { delete cae_socket; } void RDCae::connectHost() { int count=10; // QHostAddress addr; QTimer *timer=new QTimer(this,"read_timer"); connect(timer,SIGNAL(timeout()),this,SLOT(readyData())); timer->start(CAE_POLL_INTERVAL); while((!cae_socket->connect(cae_station->caeAddress(cae_config), CAED_TCP_PORT))&&(--count>0)) { usleep(100000); } usleep(100000); if(count>0) { SendCommand(QString().sprintf("PW %s!", (const char *)cae_config->password())); for(int i=0;i *cards) { QString cmd=QString().sprintf("ME %u",cae_meter_socket->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+"!"); } bool RDCae::loadPlay(int card,QString name,int *stream,int *handle) { int count=0; SendCommand(QString().sprintf("LP %d %s!", card,(const char *)name)); // // This is really warty, but needed to make the method 'synchronous' // with respect to CAE. // *stream=-2; *handle=-1; while(*stream==-2) { readyData(stream,handle,name); usleep(1000); count++; } if(count>1000) { rda->syslog(LOG_ERR, "*** LoadPlay: CAE took %d mS to return stream for %s ***", count,(const char *)name); } cae_handle[card][*stream]=*handle; cae_pos[card][*stream]=0xFFFFFFFF; // CAE Daemon sends back a stream of -1 if there is an issue with allocating it // such as file missing, etc. if(*stream < 0) { return false; } return true; } void RDCae::unloadPlay(int handle) { SendCommand(QString().sprintf("UP %d!",handle)); } void RDCae::positionPlay(int handle,int pos) { if(pos<0) { return; } SendCommand(QString().sprintf("PP %d %u!",handle,pos)); } void RDCae::play(int handle,unsigned length,int speed,bool pitch) { int pitch_state=0; if(pitch) { pitch_state=1; } SendCommand(QString().sprintf("PY %d %u %d %d!", handle,length,speed,pitch_state)); } void RDCae::stopPlay(int handle) { SendCommand(QString().sprintf("SP %d!",handle)); } void RDCae::loadRecord(int card,int stream,QString name, AudioCoding coding,int chan,int samp_rate, int bit_rate) { // printf("RDCae::loadRecord(%d,%d,%s,%d,%d,%d,%d)\n", // card,stream,(const char *)name,coding,chan,samp_rate,bit_rate); SendCommand(QString().sprintf("LR %d %d %d %d %d %d %s!", card,stream,(int)coding,chan,samp_rate, bit_rate,(const char *)name)); } 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(int card,int stream,int port,int level) { SendCommand(QString().sprintf("OV %d %d %d %d!",card,stream,port,level)); } void RDCae::fadeOutputVolume(int card,int stream,int port,int level,int length) { SendCommand(QString().sprintf("FV %d %d %d %d %d!", card,stream,port,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(int card,int stream,short levels[2]) { UpdateMeters(); levels[0]=cae_stream_output_levels[card][stream][0]; levels[1]=cae_stream_output_levels[card][stream][1]; } unsigned RDCae::playPosition(int handle) { for(int i=0;ireadBlock(buf,256))>0) { buf[c]=0; for(int i=0;i=0) { if(cae_output_positions[i][j]!=cae_pos[i][j]) { emit playPositionChanged(cae_handle[i][j], cae_output_positions[i][j]); cae_pos[i][j]=cae_output_positions[i][j]; } } } } } void RDCae::SendCommand(QString cmd) { cae_socket->writeBlock((const char *)cmd,cmd.length()); } void RDCae::DispatchCommand(RDCmdCache *cmd) { int pos; int card; if(!strcmp(cmd->arg(0),"PW")) { // Password Response if(cmd->arg(1)[0]=='+') { emit isConnected(true); } else { emit isConnected(false); } } if(!strcmp(cmd->arg(0),"LP")) { // Load Play int handle=GetHandle(cmd->arg(4)); int card=CardNumber(cmd->arg(1)); int stream=StreamNumber(cmd->arg(3)); rda->syslog(LOG_ERR,"*** RDCae::DispatchCommand: received unhandled play stream from CAE, handle=%d, card=%d, stream=%d, name=\"%s\" ***", handle,card,stream,cmd->arg(2)); unloadPlay(handle); } if(!strcmp(cmd->arg(0),"UP")) { // Unload Play if(cmd->arg(2)[0]=='+') { int handle=GetHandle(cmd->arg(1)); for(int i=0;iarg(0),"PP")) { // Position Play if(cmd->arg(3)[0]=='+') { int handle=GetHandle(cmd->arg(1)); sscanf(cmd->arg(2),"%u",&pos); for(int i=0;iarg(0),"PY")) { // Play if(cmd->arg(4)[0]=='+') { emit playing(GetHandle(cmd->arg(1))); } } if(!strcmp(cmd->arg(0),"SP")) { // Stop Play if(cmd->arg(2)[0]=='+') { emit playStopped(GetHandle(cmd->arg(1))); } } if(!strcmp(cmd->arg(0),"TS")) { // Timescale Supported if(sscanf(cmd->arg(1),"%d",&card)==1) { if(cmd->arg(2)[0]=='+') { emit timescalingSupported(card,true); } else { emit timescalingSupported(card,false); } } } if(!strcmp(cmd->arg(0),"LR")) { // Load Record if(cmd->arg(8)[0]=='+') { emit recordLoaded(CardNumber(cmd->arg(1)),StreamNumber(cmd->arg(2))); } } if(!strcmp(cmd->arg(0),"UR")) { // Unload Record if(cmd->arg(4)[0]=='+') { emit recordUnloaded(CardNumber(cmd->arg(1)),StreamNumber(cmd->arg(2)), QString(cmd->arg(3)).toUInt()); } } if(!strcmp(cmd->arg(0),"RD")) { // Record } if(!strcmp(cmd->arg(0),"RS")) { // Record Start if(cmd->arg(3)[0]=='+') { emit recording(CardNumber(cmd->arg(1)),StreamNumber(cmd->arg(2))); } } if(!strcmp(cmd->arg(0),"SR")) { // Stop Record if(cmd->arg(3)[0]=='+') { emit recordStopped(CardNumber(cmd->arg(1)),StreamNumber(cmd->arg(2))); } } if(!strcmp(cmd->arg(0),"IS")) { // Input Status switch(cmd->arg(3)[0]) { case '0': emit inputStatusChanged(CardNumber(cmd->arg(1)), StreamNumber(cmd->arg(2)),true); input_status[CardNumber(cmd->arg(1))][StreamNumber(cmd->arg(2))]= true; break; case '1': emit inputStatusChanged(CardNumber(cmd->arg(1)), StreamNumber(cmd->arg(2)),false); input_status[CardNumber(cmd->arg(1))][StreamNumber(cmd->arg(2))]= false; break; } } } int RDCae::CardNumber(const char *arg) { int n=-1; sscanf(arg,"%d",&n); return n; } int RDCae::StreamNumber(const char *arg) { int n=-1; sscanf(arg,"%d",&n); return n; } int RDCae::GetHandle(const char *arg) { int n=-1; sscanf(arg,"%d",&n); return n; } void RDCae::UpdateMeters() { char msg[1501]; int n; QStringList args; while((n=cae_meter_socket->readBlock(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()==5) { cae_stream_output_levels[args[1].toInt()][args[2].toInt()][0]= args[3].toInt(); cae_stream_output_levels[args[1].toInt()][args[2].toInt()][1]= args[4].toInt(); } } if(args[0]=="MP") { if(args.size()==4) { cae_output_positions[args[1].toInt()][args[2].toInt()]=args[3].toUInt(); } } } }