2023-09-15 Fred Gleason <fredg@paravelsystems.com>

* Added a 'Set Timeout' ['TO'] CAE command.
	* Added a 'Touch' ['TH'] CAE command.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2023-09-15 11:52:18 -04:00
parent c8e6dd62e8
commit c6b18468f1
11 changed files with 565 additions and 586 deletions

View File

@ -24421,3 +24421,6 @@
* Implemented the modified 'Set Audio Passthrough Level' ['AL'] CAE
command.
* Implemented the new 'Update Audio Ports' ['AP'] CAE command.
2023-09-15 Fred Gleason <fredg@paravelsystems.com>
* Added a 'Set Timeout' ['TO'] CAE command.
* Added a 'Touch' ['TH'] CAE command.

View File

@ -1,4 +1,4 @@
## automake.am
## Makefile.am
##
## Core Audio Engine Makefile.am for Rivendell
##
@ -31,6 +31,7 @@ sbin_PROGRAMS = caed
dist_caed_SOURCES = cae.cpp cae.h\
cae_server.cpp cae_server.h\
connection.cpp connection.h\
driver.cpp driver.h\
driver_alsa.cpp driver_alsa.h\
driver_hpi.cpp driver_hpi.h\
@ -39,6 +40,7 @@ dist_caed_SOURCES = cae.cpp cae.h\
nodist_caed_SOURCES = moc_cae.cpp\
moc_cae_server.cpp\
moc_connection.cpp\
moc_driver.cpp\
moc_driver_alsa.cpp\
moc_driver_hpi.cpp\

View File

@ -145,12 +145,14 @@ MainObject::MainObject(QObject *parent)
// Server Front End
//
cae_server=new CaeServer(this);
if(!cae_server->listen(QHostAddress::Any,CAED_TCP_PORT)) {
if(!cae_server->bind(QHostAddress::Any,CAED_TCP_PORT)) {
rda->syslog(LOG_ERR,"caed: failed to bind port %d",CAED_TCP_PORT);
exit(1);
}
connect(cae_server,SIGNAL(connectionDropped(int)),
this,SLOT(connectionDroppedData(int)));
// connect(cae_server,SIGNAL(connectionDropped(int)),
// this,SLOT(connectionDroppedData(int)));
connect(cae_server,SIGNAL(connectionClosed(const SessionId &)),
this,SLOT(connectionClosedData(const SessionId &)));
connect(cae_server,SIGNAL(playPositionReq(const SessionId &,int)),
this,SLOT(playPositionData(const SessionId &,int)));
connect(cae_server,SIGNAL(startPlaybackReq(const SessionId &,const QString &,
@ -159,7 +161,6 @@ MainObject::MainObject(QObject *parent)
unsigned,unsigned,int,int,int)));
connect(cae_server,SIGNAL(playStopReq(const SessionId &)),
this,SLOT(stopPlaybackData(const SessionId &)));
/*
connect(cae_server,SIGNAL(loadPlaybackReq(int,unsigned,const QString &)),
this,SLOT(loadPlaybackData(int,unsigned,const QString &)));
connect(cae_server,SIGNAL(unloadPlaybackReq(int,unsigned)),
@ -198,6 +199,7 @@ MainObject::MainObject(QObject *parent)
unsigned,int,unsigned)),
this,SLOT(fadeOutputVolumeData(int,unsigned,unsigned,
unsigned,int,unsigned)));
/*
connect(cae_server,SIGNAL(setInputLevelReq(int,unsigned,unsigned,int)),
this,SLOT(setInputLevelData(int,unsigned,unsigned,int)));
connect(cae_server,SIGNAL(setOutputLevelReq(int,unsigned,unsigned,int)),
@ -236,11 +238,11 @@ MainObject::MainObject(QObject *parent)
this,
SLOT(openRtpCaptureChannelData(int,unsigned,unsigned,uint16_t,
unsigned,unsigned)));
*/
connect(cae_server,SIGNAL(meterEnableReq(const QHostAddress &,uint16_t,
const QList<unsigned> &)),
this,SLOT(meterEnableData(const QHostAddress &,uint16_t,
const QList<unsigned> &)));
*/
signal(SIGHUP,SigHandler);
signal(SIGINT,SigHandler);
signal(SIGTERM,SigHandler);
@ -412,7 +414,7 @@ void MainObject::startPlaybackData(const SessionId &sid,const QString &cutname,
sess->setStartPosition(start_pos);
sess->setEndPosition(end_pos);
sess->setSpeed(speed);
cae_sessions[sid]=sess;
cae_play_sessions[sid]=sess;
rda->syslog(LOG_DEBUG,"playback started - session: %s card: %d port: %d stream: %d filename: %s",
sid.dump().toUtf8().constData(),cardnum,portnum,streamnum,
@ -428,7 +430,7 @@ void MainObject::playPositionData(const SessionId &sid,int position)
//
// Find the session
//
if((sess=cae_sessions.value(sid))==NULL) {
if((sess=cae_play_sessions.value(sid))==NULL) {
rda->syslog(LOG_WARNING,
"attempting to position non-existent session - session: %s",
sid.dump().toUtf8().constData());
@ -449,8 +451,7 @@ void MainObject::playPositionData(const SessionId &sid,int position)
sid.dump().toUtf8().constData(),position);
return;
}
cae_server->sendCommand(sid,QString::asprintf("PP %d %d %d",
sid.processId(),
cae_server->sendCommand(sid,QString::asprintf("PP %d %d",
sid.serialNumber(),
position));
rda->syslog(LOG_DEBUG,"play position succeeded - session: %s position: %d",
@ -460,45 +461,23 @@ void MainObject::playPositionData(const SessionId &sid,int position)
void MainObject::stopPlaybackData(const SessionId &sid)
{
//
// Maintainer's Note:
// *Do not* call this from an iterator! Use StopPlayout() instead.
//
Session *sess=NULL;
Driver *dvr=NULL;
//
// Find the session
//
if((sess=cae_sessions.value(sid))==NULL) {
if((sess=cae_play_sessions.value(sid))==NULL) {
rda->syslog(LOG_WARNING,
"attempting to stop playing non-existent session - session: %s",
sid.dump().toUtf8().constData());
return;
}
//
// Find the card
//
if((dvr=GetDriver(sess->cardNumber()))==NULL) {
rda->syslog(LOG_WARNING,"no such card - session: %s card: %d",
sid.dump().toUtf8().constData(),sess->cardNumber());
return;
}
//
// Stop Transport
//
if(!dvr->stopPlayback(sess->cardNumber(),sess->streamNumber())) {
rda->syslog(LOG_WARNING,
"stop playback failed - session: %s",
sid.dump().toUtf8().constData());
}
//
// Unload
//
if(!dvr->unloadPlayback(sess->cardNumber(),sess->streamNumber())) {
rda->syslog(LOG_WARNING,
"unload playback failed - session: %s",
sid.dump().toUtf8().constData());
}
StopPlayout(sess);
//
// Delete session
@ -506,7 +485,7 @@ void MainObject::stopPlaybackData(const SessionId &sid)
QString msg=QString::asprintf("stopped playback - session: %s",
sid.dump().toUtf8().constData());
delete sess;
cae_sessions.remove(sid);
cae_play_sessions.remove(sid);
rda->syslog(LOG_DEBUG,"%s",msg.toUtf8().constData());
}
@ -1263,19 +1242,14 @@ void MainObject::openRtpCaptureChannelData(int id,unsigned card,unsigned port,
{
}
/*
void MainObject::meterEnableData(const QHostAddress &addr,uint16_t udp_port,
const QList<unsigned> &cards)
{
/*
QString cmd=QString::asprintf("ME %u",0xFFFF&udp_port);
for(int i=0;i<cards.size();i++) {
cmd+=QString::asprintf(" %u",cards.at(i));
}
if((udp_port<0)||(udp_port>0xFFFF)) {
cae_server->sendCommand(id,cmd+" -!");
return;
}
cae_server->setMeterPort(id,udp_port);
for(int i=0;i<cards.size();i++) {
if((cards.at(i)<0)||(cards.at(i)>=RD_MAX_CARDS)) {
@ -1285,31 +1259,55 @@ void MainObject::meterEnableData(const QHostAddress &addr,uint16_t udp_port,
cae_server->setMetersEnabled(id,cards.at(i),true);
}
cae_server->sendCommand(id,cmd+" +!");
// cae_server->sendCommand(id,cmd+" +!");
SendMeterOutputStatusUpdate();
*/
}
*/
/*
void MainObject::connectionDroppedData(int id)
{
KillSocket(id);
}
*/
void MainObject::connectionClosedData(const SessionId &sid)
{
rda->syslog(LOG_DEBUG,"cleaning up sessions from %s",
sid.dump().toUtf8().constData());
//
// Clean up active play sessions
//
QMutableMapIterator<SessionId,Session *> it(cae_play_sessions);
while(it.hasNext()) {
it.next();
if(it.key().belongsTo(sid)) {
StopPlayout(it.value());
delete it.value();
it.remove();
}
}
//
// Clean up active record sessions
//
// FIXME: Implement this!
}
void MainObject::statePlayUpdate(int card,int stream,int state)
{
if(state==0) { // Stopped
for(QMap<SessionId,Session *>::iterator it=cae_sessions.begin();
it!=cae_sessions.end();it++) {
for(QMap<SessionId,Session *>::iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->cardNumber()==card)&&
(it.value()->streamNumber()==stream)) {
cae_server->
sendCommand(it.key(),QString::asprintf("SP %d %d",
it.key().processId(),
sendCommand(it.key(),QString::asprintf("SP %d",
it.key().serialNumber()));
delete it.value();
cae_sessions.remove(it.key());
cae_play_sessions.remove(it.key());
return;
}
}
@ -1431,6 +1429,40 @@ void MainObject::updateMeters()
}
void MainObject::StopPlayout(Session *sess)
{
Driver *dvr=NULL;
//
// Find the card
//
if((dvr=GetDriver(sess->cardNumber()))==NULL) {
rda->syslog(LOG_WARNING,"no such card - session: %s card: %d",
sess->sessionId().dump().toUtf8().constData(),
sess->cardNumber());
return;
}
//
// Stop Transport
//
if(!dvr->stopPlayback(sess->cardNumber(),sess->streamNumber())) {
rda->syslog(LOG_WARNING,
"stop playback failed - session: %s",
sess->sessionId().dump().toUtf8().constData());
}
//
// Unload
//
if(!dvr->unloadPlayback(sess->cardNumber(),sess->streamNumber())) {
rda->syslog(LOG_WARNING,
"unload playback failed - session: %s",
sess->sessionId().dump().toUtf8().constData());
}
}
void MainObject::InitProvisioning() const
{
QString sql;
@ -1513,7 +1545,7 @@ void MainObject::InitMixers()
}
}
/*
void MainObject::KillSocket(int ch)
{
for(int i=0;i<RD_MAX_CARDS;i++) {
@ -1556,7 +1588,7 @@ void MainObject::KillSocket(int ch)
}
}
}
*/
pid_t MainObject::GetPid(QString pidfile)
{
@ -1832,15 +1864,15 @@ void MainObject::FreeMadDecoder(int card,int stream)
void MainObject::SendMeterLevelUpdate(const QString &type,int cardnum,
int portnum,short levels[])
{
QList<int> ids=cae_server->connectionIds();
for(int l=0;l<ids.size();l++) {
if((cae_server->meterPort(ids.at(l))>0)&&
cae_server->metersEnabled(ids.at(l),cardnum)) {
for(QMap<SessionId,Session *>::const_iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->cardNumber()==cardnum)&&(it.value()->meterPort()>0)&&
(it.value()->metersEnabled())) {
SendMeterUpdate(QString::asprintf("ML %s %d %d %d %d",
type.toUtf8().constData(),
cardnum,portnum,levels[0],levels[1]),
ids.at(l));
it.value());
}
}
}
@ -1849,13 +1881,13 @@ void MainObject::SendMeterLevelUpdate(const QString &type,int cardnum,
void MainObject::SendStreamMeterLevelUpdate(int cardnum,int streamnum,
short levels[])
{
QList<int> ids=cae_server->connectionIds();
for(int l=0;l<ids.size();l++) {
if((cae_server->meterPort(ids.at(l))>0)&&
cae_server->metersEnabled(ids.at(l),cardnum)) {
for(QMap<SessionId,Session *>::const_iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->cardNumber()==cardnum)&&(it.value()->meterPort()>0)&&
(it.value()->metersEnabled())) {
SendMeterUpdate(QString::asprintf("MO %d %d %d %d",
cardnum,streamnum,levels[0],levels[1]),ids.at(l));
cardnum,streamnum,levels[0],levels[1]),
it.value());
}
}
}
@ -1863,14 +1895,13 @@ void MainObject::SendStreamMeterLevelUpdate(int cardnum,int streamnum,
void MainObject::SendMeterPositionUpdate(int cardnum,unsigned pos[])
{
QList<int> ids=cae_server->connectionIds();
for(unsigned k=0;k<RD_MAX_STREAMS;k++) {
for(int l=0;l<ids.size();l++) {
if((cae_server->meterPort(ids.at(l))>0)&&
cae_server->metersEnabled(ids.at(l),cardnum)) {
for(QMap<SessionId,Session *>::const_iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->cardNumber()==cardnum)&&(it.value()->meterPort()>0)&&
(it.value()->metersEnabled())) {
SendMeterUpdate(QString::asprintf("MP %d %d %d",cardnum,k,pos[k]),
ids.at(l));
it.value());
}
}
}
@ -1879,17 +1910,17 @@ void MainObject::SendMeterPositionUpdate(int cardnum,unsigned pos[])
void MainObject::SendMeterOutputStatusUpdate()
{
QList<int> ids=cae_server->connectionIds();
for(unsigned i=0;i<RD_MAX_CARDS;i++) {
for(int i=0;i<RD_MAX_CARDS;i++) {
if(GetDriver(i)!=NULL) {
for(unsigned j=0;j<RD_MAX_PORTS;j++) {
for(unsigned k=0;k<RD_MAX_STREAMS;k++) {
for(int l=0;l<ids.size();l++) {
if((cae_server->meterPort(ids.at(l))>0)&&
cae_server->metersEnabled(ids.at(l),i)) {
for(QMap<SessionId,Session *>::const_iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->cardNumber()==i)&&
(it.value()->meterPort()>0)&&(it.value()->metersEnabled())) {
SendMeterUpdate(QString::asprintf("MS %d %d %d %d",i,j,k,
output_status_flag[i][j][k]),ids.at(l));
output_status_flag[i][j][k]),
it.value());
}
}
}
@ -1901,21 +1932,22 @@ void MainObject::SendMeterOutputStatusUpdate()
void MainObject::SendMeterOutputStatusUpdate(int card,int port,int stream)
{
QList<int> ids=cae_server->connectionIds();
for(int l=0;l<ids.size();l++) {
if((cae_server->meterPort(ids.at(l))>0)&&
cae_server->metersEnabled(ids.at(l),card)) {
for(QMap<SessionId,Session *>::const_iterator it=cae_play_sessions.begin();
it!=cae_play_sessions.end();it++) {
if((it.value()->streamNumber()==stream)&&(it.value()->meterPort()>0)&&
(it.value()->metersEnabled())) {
SendMeterUpdate(QString::asprintf("MS %d %d %d %d",card,port,stream,
output_status_flag[card][port][stream]),ids.at(l));
output_status_flag[card][port][stream]),
it.value());
}
}
}
void MainObject::SendMeterUpdate(const QString &msg,int conn_id)
void MainObject::SendMeterUpdate(const QString &msg,Session *sess)
{
meter_socket->writeDatagram(msg.toUtf8(),cae_server->peerAddress(conn_id),
cae_server->meterPort(conn_id));
meter_socket->writeDatagram(msg.toUtf8(),sess->sessionId().address(),
sess->meterPort());
}

View File

@ -47,8 +47,8 @@
#include <rdconfig.h>
#include <rdstation.h>
#include "driver.h"
#include "cae_server.h"
#include "driver.h"
#ifndef HAVE_SRC_CONV
void src_int_to_float_array (const int *in, float *out, int len);
@ -123,17 +123,20 @@ class MainObject : public QObject
void openRtpCaptureChannelData(int id,unsigned card,unsigned port,
uint16_t udp_port,unsigned samprate,
unsigned chans);
void meterEnableData(const QHostAddress &addr,uint16_t udp_port,
const QList<unsigned> &cards);
// void meterEnableData(const QHostAddress &addr,uint16_t udp_port,
// const QList<unsigned> &cards);
void statePlayUpdate(int card,int stream,int state);
void stateRecordUpdate(int card,int stream,int state);
void updateMeters();
void connectionDroppedData(int id);
// void connectionDroppedData(int id);
void connectionClosedData(const SessionId &sid);
private:
void StopPlayout(Session *sess);
void InitProvisioning() const;
void InitMixers();
void KillSocket(int);
// void KillSocket(int);
bool CheckDaemon(QString);
pid_t GetPid(QString pidfile);
int GetNextHandle();
@ -146,7 +149,8 @@ class MainObject : public QObject
void SendMeterPositionUpdate(int cardnum,unsigned pos[]);
void SendMeterOutputStatusUpdate();
void SendMeterOutputStatusUpdate(int card,int port,int stream);
void SendMeterUpdate(const QString &msg,int conn_id);
// void SendMeterUpdate(const QString &msg,int conn_id);
void SendMeterUpdate(const QString &msg,Session *sess);
Driver *GetDriver(unsigned card) const;
void MakeDriver(unsigned *next_card,RDStation::AudioDriver type);
QList<Driver *> d_drivers;
@ -171,7 +175,7 @@ class MainObject : public QObject
} play_handle[256];
int next_play_handle;
QMap<SessionId,Session *> cae_sessions;
QMap<SessionId,Session *> cae_play_sessions;
private:
bool CheckLame();

View File

@ -42,60 +42,7 @@ CaeServer::CaeServer(QObject *parent)
}
QList<int> CaeServer::connectionIds() const
{
QList<int> ret;
/*
for(QMap<int,CaeServerConnection *>::const_iterator it=
cae_connections.begin();it!=cae_connections.end();it++) {
ret.push_back(it.key());
}
*/
return ret;
}
QHostAddress CaeServer::peerAddress(int id) const
{
// return cae_connections[id]->socket->peerAddress();
return QHostAddress();
}
uint16_t CaeServer::peerPort(int id) const
{
// return cae_connections[id]->socket->peerPort();
return 0;
}
uint16_t CaeServer::meterPort(int id) const
{
// return cae_connections[id]->meter_port;
return 0;
}
void CaeServer::setMeterPort(int id,uint16_t port)
{
// cae_connections[id]->meter_port=port;
}
bool CaeServer::metersEnabled(int id,unsigned card) const
{
// return cae_connections[id]->meters_enabled[card];
return false;
}
void CaeServer::setMetersEnabled(int id,unsigned card,bool state)
{
// cae_connections[id]->meters_enabled[card]=state;
}
bool CaeServer::listen(const QHostAddress &addr,uint16_t port)
bool CaeServer::bind(const QHostAddress &addr,uint16_t port)
{
return d_server_socket->bind(port);
}
@ -124,26 +71,6 @@ void CaeServer::sendCommand(const SessionId &dest,const QString &cmd)
}
void CaeServer::newConnectionData()
{/*
QTcpSocket *sock=cae_server->nextPendingConnection();
cae_connection_closed_mapper->setMapping(sock,sock->socketDescriptor());
connect(sock,SIGNAL(disconnected()),cae_connection_closed_mapper,SLOT(map()));
cae_ready_read_mapper->setMapping(sock,sock->socketDescriptor());
connect(sock,SIGNAL(readyRead()),cae_ready_read_mapper,SLOT(map()));
cae_connections[sock->socketDescriptor()]=new CaeServerConnection(sock);
RDApplication::syslog(cae_config,LOG_DEBUG,
"added connection %d [%s:%u]",sock->socketDescriptor(),
sock->peerAddress().toString().toUtf8().constData(),
0xFFFF&sock->peerPort());
*/
}
void CaeServer::readyReadData()
{
QNetworkDatagram dgram=d_server_socket->receiveDatagram(1500);
@ -152,9 +79,20 @@ void CaeServer::readyReadData()
}
void CaeServer::connectionExpiredData(const SessionId &sid)
{
Connection *conn=cae_connections.value(sid);
if(conn!=NULL) {
conn->deleteLater();
cae_connections.remove(sid);
emit connectionClosed(sid);
}
}
/*
void CaeServer::connectionClosedData(int id)
{
/*
QString logmsg=
QString::asprintf("removed connection %d [%s:%u]",
id,
@ -175,9 +113,8 @@ void CaeServer::connectionClosedData(int id)
cae_connections.remove(id);
RDApplication::syslog(cae_config,priority,"%s",logmsg.toUtf8().constData());
*/
}
*/
bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
const QString &cmd)
@ -185,7 +122,6 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
bool was_processed=false;
bool ok=false;
QStringList f0=cmd.split(" ",QString::SkipEmptyParts);
pid_t pid;
unsigned serial;
QString cutname;
unsigned cardnum;
@ -200,34 +136,61 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
int coding;
int channels;
int bitrate;
int interval;
SessionId origin(src_addr,src_port);
//
// Connection Management
//
if((f0.at(0)=="TO")&&(f0.size()==2)) { // Set Timeout
interval=f0.at(1).toInt(&ok);
if(ok&&(interval>=0)) {
Connection *conn=cae_connections.value(origin);
if(conn==NULL) {
conn=new Connection(origin,this);
connect(conn,SIGNAL(connectionExpired(const SessionId &)),
this,SLOT(connectionExpiredData(const SessionId &)));
cae_connections[origin]=conn;
}
conn->setTimeout(interval);
was_processed=true;
}
}
if((f0.at(0)=="TH")&&(f0.size()==1)) { // Touch
Connection *conn=cae_connections.value(origin);
if(conn==NULL) {
rda->syslog(LOG_WARNING,"%s attempted to touch non-existent connection",
origin.dump().toUtf8().constData());
}
else {
conn->touch();
}
was_processed=true;
}
//
// Playback Operations
//
if((f0.at(0)=="PY")&&(f0.size()==9)) { // Start Playback
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(3);
if(cutname.length()==10) {
cardnum=f0.at(4).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(5).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
start_pos=f0.at(6).toInt(&ok);
if(ok&&(start_pos>=0)) {
end_pos=f0.at(7).toInt(&ok);
if(ok&&(end_pos>=0)&&(end_pos>=start_pos)) {
speed=f0.at(8).toInt(&ok);
if(ok&&(speed>0)) {
emit startPlaybackReq(origin,cutname,cardnum,portnum,
start_pos,end_pos,speed);
was_processed=true;
}
if((f0.at(0)=="PY")&&(f0.size()==8)) { // Start Playback
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(2);
if(cutname.length()==10) {
cardnum=f0.at(3).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(4).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
start_pos=f0.at(5).toInt(&ok);
if(ok&&(start_pos>=0)) {
end_pos=f0.at(6).toInt(&ok);
if(ok&&(end_pos>=0)&&(end_pos>=start_pos)) {
speed=f0.at(7).toInt(&ok);
if(ok&&(speed>0)) {
emit startPlaybackReq(origin,cutname,cardnum,portnum,
start_pos,end_pos,speed);
was_processed=true;
}
}
}
@ -237,122 +200,94 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
}
}
if((f0.at(0)=="PP")&&(f0.size()==4)) { // Play Position
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if((f0.at(0)=="PP")&&(f0.size()==3)) { // Play Position
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
position=f0.at(2).toInt(&ok);
if(ok&&(position>=0)) {
emit playPositionReq(origin,position);
was_processed=true;
}
}
}
if((f0.at(0)=="PE")&&(f0.size()==2)) { // Pause Playback
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playPauseReq(origin);
was_processed=true;
}
}
if((f0.at(0)=="PR")&&(f0.size()==2)) { // Resume Playback
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playResumeReq(origin);
was_processed=true;
}
}
if((f0.at(0)=="SP")&&(f0.size()==2)) { // Stop Playback
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playStopReq(origin);
was_processed=true;
}
}
if((f0.at(0)=="OV")&&(f0.size()==3)) { // Set Output Volume
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
level=f0.at(2).toInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
position=f0.at(3).toInt(&ok);
if(ok&&(position>=0)) {
emit playPositionReq(origin,position);
emit playSetOutputVolumeReq(origin,level);
was_processed=true;
}
}
}
if((f0.at(0)=="FV")&&(f0.size()==4)) { // Fade Output Volume
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
level=f0.at(2).toInt(&ok);
if(ok) {
length=f0.at(3).toInt(&ok);
if(ok&&length>=0) {
emit playFadeOutputVolumeReq(origin,level,length);
was_processed=true;
}
}
}
}
if((f0.at(0)=="PE")&&(f0.size()==3)) { // Pause Playback
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playPauseReq(origin);
was_processed=true;
}
}
}
if((f0.at(0)=="PR")&&(f0.size()==3)) { // Resume Playback
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playResumeReq(origin);
was_processed=true;
}
}
}
if((f0.at(0)=="SP")&&(f0.size()==3)) { // Stop Playback
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit playStopReq(origin);
was_processed=true;
}
}
}
if((f0.at(0)=="OV")&&(f0.size()==4)) { // Set Output Volume
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
level=f0.at(3).toInt(&ok);
if(ok) {
emit playSetOutputVolumeReq(origin,level);
was_processed=true;
}
}
}
}
if((f0.at(0)=="FV")&&(f0.size()==5)) { // Fade Output Volume
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
level=f0.at(3).toInt(&ok);
if(ok) {
length=f0.at(4).toInt(&ok);
if(ok&&length>=0) {
emit playFadeOutputVolumeReq(origin,level,length);
was_processed=true;
}
}
}
}
}
//
// Record Operations
//
if((f0.at(0)=="LR")&&(f0.size()==10)) { // Cue Recording
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(3);
if(cutname.length()==10) {
cardnum=f0.at(4).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(5).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
coding=f0.at(6).toInt(&ok);
if(ok&&(coding>=0)&&(coding<=4)) {
channels=f0.at(7).toInt(&ok);
if(ok&&(channels>0)) {
bitrate=f0.at(8).toInt(&ok);
if(ok&&(bitrate>=0)) {
emit recordCueReq(origin,cutname,cardnum,portnum,
coding,channels,bitrate);
was_processed=true;
}
if((f0.at(0)=="LR")&&(f0.size()==8)) { // Cue Recording
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(2);
if(cutname.length()==10) {
cardnum=f0.at(3).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(4).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
coding=f0.at(5).toInt(&ok);
if(ok&&(coding>=0)&&(coding<=4)) {
channels=f0.at(6).toInt(&ok);
if(ok&&(channels>0)) {
bitrate=f0.at(7).toInt(&ok);
if(ok&&(bitrate>=0)) {
emit recordCueReq(origin,cutname,cardnum,portnum,
coding,channels,bitrate);
was_processed=true;
}
}
}
@ -362,54 +297,46 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
}
}
if((f0.at(0)=="RD")&&(f0.size()==5)) { // Start Recording
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
length=f0.at(3).toInt(&ok);
if(ok&&(length>=0)) {
threshold=f0.at(4).toInt(&ok);
if(ok&&(threshold<=0)) {
emit recordStartReq(origin,length,threshold);
was_processed=true;
}
if((f0.at(0)=="RD")&&(f0.size()==4)) { // Start Recording
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
length=f0.at(2).toInt(&ok);
if(ok&&(length>=0)) {
threshold=f0.at(3).toInt(&ok);
if(ok&&(threshold<=0)) {
emit recordStartReq(origin,length,threshold);
was_processed=true;
}
}
}
}
if((f0.at(0)=="RC")&&(f0.size()==11)) { // Cue and Start Recording
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(3);
if(cutname.length()==10) {
cardnum=f0.at(4).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(5).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
coding=f0.at(6).toInt(&ok);
if(ok&&(coding>=0)&&(coding<=4)) {
channels=f0.at(7).toInt(&ok);
if(ok&&(channels>0)) {
bitrate=f0.at(8).toInt(&ok);
if(ok&&(bitrate>=0)) {
length=f0.at(9).toInt(&ok);
if(ok&&(length>=0)) {
threshold=f0.at(10).toInt(&ok);
if(ok&&(threshold<=0)) {
emit recordCueAndStartReq(origin,cutname,
cardnum,portnum,
coding,channels,bitrate,
length,threshold);
was_processed=true;
}
if((f0.at(0)=="RC")&&(f0.size()==10)) { // Cue and Start Recording
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
cutname=f0.at(2);
if(cutname.length()==10) {
cardnum=f0.at(3).toUInt(&ok);
if(ok&&(cardnum<RD_MAX_CARDS)) {
portnum=f0.at(4).toInt(&ok);
if(ok&&(portnum<RD_MAX_PORTS)) {
coding=f0.at(5).toInt(&ok);
if(ok&&(coding>=0)&&(coding<=4)) {
channels=f0.at(6).toInt(&ok);
if(ok&&(channels>0)) {
bitrate=f0.at(7).toInt(&ok);
if(ok&&(bitrate>=0)) {
length=f0.at(8).toInt(&ok);
if(ok&&(length>=0)) {
threshold=f0.at(9).toInt(&ok);
if(ok&&(threshold<=0)) {
emit recordCueAndStartReq(origin,cutname,
cardnum,portnum,
coding,channels,bitrate,
length,threshold);
was_processed=true;
}
}
}
@ -421,16 +348,12 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
}
}
if((f0.at(0)=="SR")&&(f0.size()==3)) { // Stop Recording
pid=f0.at(1).toUInt(&ok);
if(ok&&(pid>0)) {
origin.setProcessId(pid);
serial=f0.at(2).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit recordStopReq(origin);
was_processed=true;
}
if((f0.at(0)=="SR")&&(f0.size()==2)) { // Stop Recording
serial=f0.at(1).toUInt(&ok);
if(ok) {
origin.setSerialNumber(serial);
emit recordStopReq(origin);
was_processed=true;
}
}
@ -483,10 +406,6 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
//
// Meter Commands
//
/*
* This needs to die!
* Replace with multicast meter system.
*
if(f0.at(0)=="ME") { // Meter Enable
if(f0.size()>2) { // So we don't warn if no cards are specified
uint16_t udp_port=0xFFFF&f0.at(1).toUInt(&ok);
@ -500,7 +419,6 @@ bool CaeServer::ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
}
was_processed=true;
}
*/
if(!was_processed) {
rda->syslog(LOG_WARNING,

View File

@ -31,6 +31,7 @@
#include <rdconfig.h>
#include "connection.h"
#include "session.h"
class CaeServer : public QObject
@ -38,23 +39,19 @@ class CaeServer : public QObject
Q_OBJECT;
public:
CaeServer(QObject *parent=0);
QList<int> connectionIds() const;
QHostAddress peerAddress(int id) const;
uint16_t peerPort(int id) const;
uint16_t meterPort(int id) const;
void setMeterPort(int id,uint16_t port);
bool metersEnabled(int id,unsigned card) const;
void setMetersEnabled(int id,unsigned card,bool state);
bool listen(const QHostAddress &addr,uint16_t port);
bool bind(const QHostAddress &addr,uint16_t port);
void sendCommand(const QString &cmd);
void sendCommand(const SessionId &dest,const QString &cmd);
signals:
void connectionDropped(int id);
// void connectionDropped(int id);
//
// New Signals
//
void connectionClosed(const SessionId &sid);
void startPlaybackReq(const SessionId &sid,const QString &cutname,
unsigned cardnum,unsigned portnum,
int start_pos,int end_pos,int speed);
@ -111,13 +108,14 @@ class CaeServer : public QObject
const QList<unsigned> &cards);
private slots:
void newConnectionData();
void readyReadData();
void connectionClosedData(int id);
void connectionExpiredData(const SessionId &sid);
// void connectionClosedData(int id);
private:
bool ProcessCommand(const QHostAddress &src_addr,uint16_t src_port,
const QString &cmd);
QMap<SessionId,Connection *> cae_connections;
QSignalMapper *cae_ready_read_mapper;
QUdpSocket *d_server_socket;
};

83
cae/connection.cpp Normal file
View File

@ -0,0 +1,83 @@
// connection.cpp
//
// UDP connection context for CAE protocol commands
//
// (C) Copyright 2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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 "connection.h"
Connection::Connection(const SessionId &sid,QObject *parent)
: QObject(parent)
{
d_session_id=sid;
d_interval=0;
d_timer=new QTimer(this);
d_timer->setSingleShot(true);
connect(d_timer,SIGNAL(timeout()),this,SLOT(timerData()));
}
Connection::~Connection()
{
delete d_timer;
}
SessionId Connection::sessionId() const
{
return d_session_id;
}
QString Connection::dump() const
{
return d_session_id.dump();
}
bool Connection::operator!=(const Connection &other) const
{
return (other.d_session_id.address()!=d_session_id.address())||
(other.d_session_id.port()!=d_session_id.port());
}
bool Connection::operator<(const Connection &other) const
{
return other.d_session_id<d_session_id;
}
void Connection::setTimeout(int msecs)
{
d_interval=msecs;
touch();
}
void Connection::touch()
{
d_timer->stop();
d_timer->start(d_interval);
}
void Connection::timerData()
{
emit connectionExpired(d_session_id);
}

60
cae/connection.h Normal file
View File

@ -0,0 +1,60 @@
// connection.h
//
// UDP connection context for CAE protocol commands
//
// (C) Copyright 2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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.
//
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdint.h>
#include <QHostAddress>
#include <QObject>
#include <QTimer>
#include "session.h"
class Connection : public QObject
{
Q_OBJECT;
public:
Connection(const SessionId &sid,QObject *parent);
~Connection();
SessionId sessionId() const;
QString dump() const;
bool operator!=(const Connection &other) const;
bool operator<(const Connection &other) const;
signals:
void connectionExpired(const SessionId &sid);
public slots:
void setTimeout(int msecs);
void touch();
private slots:
void timerData();
private:
SessionId d_session_id;
QTimer *d_timer;
int d_interval;
};
#endif // SESSION_H

View File

@ -20,19 +20,16 @@
#include "session.h"
SessionId::SessionId(const QHostAddress &addr,uint16_t port,int pid,int serial)
SessionId::SessionId(const QHostAddress &src_addr,uint16_t src_port,int serial)
{
d_address=addr;
d_port=port;
d_process_id=pid;
d_address=src_addr;
d_port=src_port;
d_serial_number=serial;
}
SessionId::SessionId()
{
d_port=0;
d_process_id=0;
d_serial_number=0;
}
@ -49,18 +46,6 @@ uint16_t SessionId::port() const
}
int SessionId::processId() const
{
return d_process_id;
}
void SessionId::setProcessId(int pid)
{
d_process_id=pid;
}
int SessionId::serialNumber() const
{
return d_serial_number;
@ -75,31 +60,41 @@ void SessionId::setSerialNumber(int serial)
QString SessionId::dump() const
{
return address().toString()+
QString::asprintf(":%d:%d:%d",0xFFFF&port(),processId(),serialNumber());
return QString::asprintf("%s:%d:%d",d_address.toString().toUtf8().constData(),
0xFFFF&d_port,d_serial_number);
}
bool SessionId::belongsTo(const SessionId &other) const
{
return ((other.d_address==d_address)&&(other.d_port==d_port));
}
bool SessionId::operator!=(const SessionId &other) const
{
return (other.d_address!=d_address)||(other.d_port!=d_port)||
(other.d_serial_number!=d_serial_number);
}
bool SessionId::operator<(const SessionId &other) const
{
if(other.d_address!=d_address) {
if(other.d_address.toIPv4Address()!=d_address.toIPv4Address()) {
return other.d_address.toIPv4Address()<d_address.toIPv4Address();
}
if(other.d_port!=d_port) {
return other.d_port<d_port;
}
if(other.d_process_id!=d_process_id) {
return other.d_process_id<d_process_id;
}
return other.d_serial_number<d_serial_number;
}
Session::Session(const QHostAddress &addr,uint16_t port,int pid,int serial)
Session::Session(const QHostAddress &addr,uint16_t port,int serial)
{
d_session_id=SessionId(addr,port,pid,serial);
d_session_id=SessionId(addr,port,serial);
d_card_number=-1;
d_port_number=-1;
@ -128,6 +123,24 @@ Session::Session(const SessionId &sid)
d_meters_enabled=false;
}
/*
Session::Session(const Connection &conn)
{
d_session_id=SessionId(conn);
d_session_id=sid;
d_card_number=-1;
d_port_number=-1;
d_stream_number=-1;
d_start_position=-1;
d_end_position=-1;
d_speed=100000;
d_meter_port=0;
d_meters_enabled=false;
}
*/
SessionId Session::sessionId() const
{

View File

@ -28,21 +28,20 @@
class SessionId
{
public:
SessionId(const QHostAddress &addr,uint16_t port,int pid=0,int serial=0);
SessionId(const QHostAddress &src_addr,uint16_t src_port,int serial=0);
SessionId();
QHostAddress address() const;
uint16_t port() const;
int processId() const;
void setProcessId(int pid);
int serialNumber() const;
void setSerialNumber(int serial);
QString dump() const;
bool belongsTo(const SessionId &other) const;
bool operator!=(const SessionId &other) const;
bool operator<(const SessionId &other) const;
private:
QHostAddress d_address;
uint16_t d_port;
int d_process_id;
int d_serial_number;
};
@ -52,7 +51,7 @@ class SessionId
class Session
{
public:
Session(const QHostAddress &addr,uint16_t port,int pid,int serial);
Session(const QHostAddress &addr,uint16_t port,int serial=0);
Session(const SessionId &sid);
SessionId sessionId() const;
int cardNumber() const;

View File

@ -142,6 +142,54 @@
</sect2>
</sect1>
<sect1 xml:id="sect.connection_management">
<title>Connection Management</title>
<para>
<sect2 xml:id="sect.connection_management.commands">
<title>Commands</title>
<sect3 xml:id="sect.connection_management.commands.set_timeout">
<title>Set Timeout</title>
<para>
Set the timeout interval for the connection.
</para>
<para>
<userinput>
TO
<replaceable>interval</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>interval</replaceable></term>
<listitem>
<para>
When set to a non-zero value, the client must send a
<link linkend="sect.connection_management.commands.touch">
Touch message
</link>
at an interval of least that many milliseconds, otherwise
the engine will clear the connection, releasing all associated
resources.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
<sect3 xml:id="sect.connection_management.commands.touch">
<title>Touch</title>
<para>
Keep connection alive.
</para>
<para>
<userinput>
TH
</userinput>
</para>
</sect3>
</sect2>
</para>
</sect1>
<sect1 xml:id="sect.playback_operations">
<title>Playback Operations</title>
<sect2 xml:id="sect.playback_operations.commands">
@ -156,7 +204,6 @@
<para>
<userinput>
PY
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>cut-name</replaceable>
<replaceable>card-num</replaceable>
@ -167,15 +214,6 @@
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
An integer value, giving the process ID of the process
originating the command.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -183,7 +221,7 @@
An integer value, giving the serial number of the command.
This can be any 32 bit positive integer value, but
<emphasis>must</emphasis> be unique within the scope of the
process originating the command!
connection sending the command!
</para>
</listitem>
</varlistentry>
@ -249,24 +287,11 @@
<para>
<userinput>
PP
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>position</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -302,23 +327,10 @@
<para>
<userinput>
PE
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -343,23 +355,10 @@
<para>
<userinput>
PR
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -384,23 +383,10 @@
<para>
<userinput>
SP
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -424,24 +410,11 @@
<para>
<userinput>
OV
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>level</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -475,25 +448,12 @@
<para>
<userinput>
FV
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>level</replaceable>
<replaceable>length</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. Should be the same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -540,23 +500,10 @@
<para>
<userinput>
SP
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. The same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -580,24 +527,11 @@
<para>
<userinput>
PP
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>position</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. The same value as that used in the prior
<link linkend="sect.playback_operations.commands.start_playback">
<command>Start Playback</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -640,7 +574,6 @@
<para>
<userinput>
LR
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>cut-name</replaceable>
<replaceable>card-num</replaceable>
@ -651,15 +584,6 @@
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
An integer value, giving the process ID of the process
originating the command.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -667,7 +591,7 @@
An integer value, giving the serial number of the command.
This can be any 32 bit positive integer value, but
<emphasis>must</emphasis> be unique within the scope of the
process originating the command!
connection sending the command!
</para>
</listitem>
</varlistentry>
@ -749,25 +673,12 @@
<para>
<userinput>
RD
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>length</replaceable>
<replaceable>threshold</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. The same value as that used in the prior
<link linkend="sect.record_operations.commands.cue_recording">
<command>Cue Recording</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -813,7 +724,6 @@
<para>
<userinput>
RC
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>cut-name</replaceable>
<replaceable>card-num</replaceable>
@ -826,15 +736,6 @@
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
An integer value, giving the process ID of the process
originating the command.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -842,7 +743,7 @@
An integer value, giving the serial number of the command.
This can be any 32 bit positive integer value, but
<emphasis>must</emphasis> be unique within the scope of the
process originating the command!
connection sending the command!
</para>
</listitem>
</varlistentry>
@ -941,27 +842,10 @@
<para>
<userinput>
SR
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
</userinput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. The same value as that used in the prior
<link linkend="sect.record_operations.commands.cue_recording">
<command>Cue Recording</command>
</link>
or
<link linkend="sect.record_operations.commands.cue_and_start_recording">
<command>Cue and Start Recording</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>
@ -994,28 +878,11 @@
<para>
<computeroutput>
RS
<replaceable>pid</replaceable>
<replaceable>serial</replaceable>
<replaceable>state</replaceable>
</computeroutput>
</para>
<variablelist>
<varlistentry>
<term><replaceable>pid</replaceable></term>
<listitem>
<para>
Integer value. The same value as that used in the prior
<link linkend="sect.record_operations.commands.cue_recording">
<command>Cue Recording</command>
</link>
or
<link linkend="sect.record_operations.commands.cue_and_start_recording">
<command>Cue and Start Recording</command>
</link>
operation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>serial</replaceable></term>
<listitem>