mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-07 01:13:50 +02:00
* Refactored ripcd(8) to manage JACK port [dis]connections directly, rather than by delegation to caed(8).
564 lines
14 KiB
C++
564 lines
14 KiB
C++
// cae_server.cpp
|
|
//
|
|
// Network server for caed(8).
|
|
//
|
|
// (C) Copyright 2019 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 <ctype.h>
|
|
#include <stdio.h>
|
|
|
|
#include <qbytearray.h>
|
|
#include <qstringlist.h>
|
|
|
|
#include <rdapplication.h>
|
|
|
|
#include "cae_server.h"
|
|
|
|
//
|
|
// Uncomment this to send all protocol messages to syslog (DEBUG priority)
|
|
//
|
|
// #define __CAE_SERVER_LOG_PROTOCOL_MESSAGES
|
|
|
|
CaeServerConnection::CaeServerConnection(QTcpSocket *sock)
|
|
{
|
|
socket=sock;
|
|
authenticated=false;
|
|
accum="";
|
|
meter_port=0;
|
|
for(int i=0;i<RD_MAX_CARDS;i++) {
|
|
meters_enabled[i]=false;
|
|
}
|
|
}
|
|
|
|
|
|
CaeServerConnection::~CaeServerConnection()
|
|
{
|
|
socket->deleteLater();
|
|
}
|
|
|
|
|
|
|
|
|
|
CaeServer::CaeServer(RDConfig *config,QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
cae_config=config;
|
|
|
|
cae_server=new QTcpServer(this);
|
|
connect(cae_server,SIGNAL(newConnection()),this,SLOT(newConnectionData()));
|
|
|
|
cae_ready_read_mapper=new QSignalMapper(this);
|
|
connect(cae_ready_read_mapper,SIGNAL(mapped(int)),
|
|
this,SLOT(readyReadData(int)));
|
|
|
|
cae_connection_closed_mapper=new QSignalMapper(this);
|
|
connect(cae_connection_closed_mapper,SIGNAL(mapped(int)),
|
|
this,SLOT(connectionClosedData(int)));
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
uint16_t CaeServer::peerPort(int id) const
|
|
{
|
|
return cae_connections[id]->socket->peerPort();
|
|
}
|
|
|
|
|
|
uint16_t CaeServer::meterPort(int id) const
|
|
{
|
|
return cae_connections[id]->meter_port;
|
|
}
|
|
|
|
|
|
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];
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
return cae_server->listen(addr,port);
|
|
}
|
|
|
|
|
|
void CaeServer::sendCommand(const QString &cmd)
|
|
{
|
|
for(QMap<int,CaeServerConnection *>::const_iterator it=
|
|
cae_connections.begin();it!=cae_connections.end();it++) {
|
|
if(it.value()->authenticated) {
|
|
sendCommand(it.key(),cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CaeServer::sendCommand(int id,const QString &cmd)
|
|
{
|
|
#ifdef __CAE_SERVER_LOG_PROTOCOL_MESSAGES
|
|
RDApplication::syslog(cae_config,LOG_DEBUG,
|
|
"send[%d]: %s",id,(const char *)cmd.toUtf8());
|
|
#endif // __CAE_SERVER_LOG_PROTOCOL_MESSAGES
|
|
cae_connections.value(id)->socket->write(cmd.toAscii());
|
|
}
|
|
|
|
|
|
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",sock->socketDescriptor());
|
|
}
|
|
|
|
|
|
void CaeServer::readyReadData(int id)
|
|
{
|
|
QByteArray data=cae_connections.value(id)->socket->readAll();
|
|
for(int i=0;i<data.size();i++) {
|
|
char c=0xFF&data[i];
|
|
switch(c) {
|
|
case '!':
|
|
if(ProcessCommand(id,cae_connections.value(id)->accum)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case 10:
|
|
case 13:
|
|
break;
|
|
|
|
default:
|
|
cae_connections.value(id)->accum+=c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CaeServer::connectionClosedData(int id)
|
|
{
|
|
emit connectionDropped(id);
|
|
cae_connections.value(id)->socket->disconnect();
|
|
delete cae_connections.value(id);
|
|
cae_connections.remove(id);
|
|
|
|
RDApplication::syslog(cae_config,LOG_DEBUG,"removed connection %d",id);
|
|
}
|
|
|
|
|
|
bool CaeServer::ProcessCommand(int id,const QString &cmd)
|
|
{
|
|
CaeServerConnection *conn=cae_connections.value(id);
|
|
bool ok=false;
|
|
QStringList f0=cmd.split(" ",QString::SkipEmptyParts);
|
|
|
|
if(f0.size()==0) {
|
|
return false;
|
|
}
|
|
#ifdef __CAE_SERVER_LOG_PROTOCOL_MESSAGES
|
|
RDApplication::syslog(cae_config,LOG_DEBUG,
|
|
"recv[%d]: %s",id,(const char *)cmd.toUtf8());
|
|
#endif // __CAE_SERVER_LOG_PROTOCOL_MESSAGES
|
|
|
|
cae_connections.value(id)->accum="";
|
|
|
|
//
|
|
// Unpriviledged Commands
|
|
//
|
|
if(f0.at(0)=="DC") {
|
|
connectionClosedData(id);
|
|
return true;
|
|
}
|
|
|
|
if(f0.at(0)=="PW") {
|
|
if((f0.size()==2)&&(f0.at(1)==cae_config->password())) {
|
|
conn->authenticated=true;
|
|
sendCommand(id,"PW +!");
|
|
}
|
|
else {
|
|
conn->authenticated=false;
|
|
sendCommand(id,"PW -!");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Priviledged Commands
|
|
// Authentication required to execute these!
|
|
//
|
|
if(!conn->authenticated) {
|
|
return false;
|
|
}
|
|
bool was_processed=false;
|
|
|
|
if((f0.at(0)=="LP")&&(f0.size()==3)) { // Load Playback
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
emit loadPlaybackReq(id,card,f0.at(2));
|
|
was_processed=true;
|
|
}
|
|
}
|
|
if((f0.at(0)=="UP")&&(f0.size()==2)) { // Unload Playback
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok) {
|
|
emit unloadPlaybackReq(id,card);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
if((f0.at(0)=="PP")&&(f0.size()==3)) { // Play Position
|
|
unsigned handle=f0.at(1).toUInt(&ok);
|
|
if(ok) {
|
|
unsigned pos=f0.at(2).toUInt(&ok);
|
|
if(ok) {
|
|
emit playPositionReq(id,handle,pos);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="PY")&&(f0.size()==5)) { // Play
|
|
unsigned handle=f0.at(1).toUInt(&ok);
|
|
if(ok) {
|
|
unsigned len=f0.at(2).toUInt(&ok);
|
|
if(ok) {
|
|
unsigned speed=f0.at(3).toUInt(&ok);
|
|
if(ok) {
|
|
unsigned pitch=f0.at(4).toUInt(&ok);
|
|
if(ok) {
|
|
emit playReq(id,handle,len,speed,pitch);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="SP")&&(f0.size()==2)) { // Stop Playback
|
|
unsigned handle=f0.at(1).toUInt(&ok);
|
|
if(ok) {
|
|
emit stopPlaybackReq(id,handle);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
if((f0.at(0)=="TS")&&(f0.size()==2)) { // Timescaling Support
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
emit timescalingSupportReq(id,card);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
if((f0.at(0)=="LR")&&(f0.size()==8)) { // Load Recording
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
unsigned coding=f0.at(3).toUInt(&ok);
|
|
if(ok&&(coding<5)) {
|
|
unsigned chans=f0.at(4).toUInt(&ok);
|
|
if(ok&&(chans<=2)) {
|
|
unsigned samprate=f0.at(5).toUInt(&ok);
|
|
if(ok) {
|
|
unsigned bitrate=f0.at(6).toUInt(&ok);
|
|
if(ok) {
|
|
emit loadRecordingReq(id,card,port,coding,chans,samprate,
|
|
bitrate,f0.at(7));
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="UR")&&(f0.size()==3)) { // Unload Recording
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
emit unloadRecordingReq(id,card,stream);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="RD")&&(f0.size()==5)) { // Record
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
if(ok) {
|
|
unsigned len=f0.at(3).toUInt(&ok);
|
|
if(ok) {
|
|
int thres=f0.at(4).toInt(&ok);
|
|
if(ok) {
|
|
emit recordReq(id,card,stream,len,thres);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="SR")&&(f0.size()==3)) { // Stop Recording
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
if(ok) {
|
|
emit stopRecordingReq(id,card,stream);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IV")&&(f0.size()==4)) { // Set Input Volume
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
if(ok) {
|
|
int level=f0.at(3).toInt(&ok);
|
|
if(ok) {
|
|
emit setInputVolumeReq(id,card,stream,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="OV")&&(f0.size()==5)) { // Set Output Volume
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
if(ok) {
|
|
unsigned port=f0.at(3).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
int level=f0.at(4).toInt(&ok);
|
|
if(ok) {
|
|
emit setOutputVolumeReq(id,card,stream,port,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="FV")&&(f0.size()==6)) { // Fade Output Volume
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
if(ok) {
|
|
unsigned port=f0.at(3).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
int level=f0.at(4).toInt(&ok);
|
|
if(ok) {
|
|
int len=f0.at(5).toUInt(&ok);
|
|
if(ok) {
|
|
emit fadeOutputVolumeReq(id,card,stream,port,level,len);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IL")&&(f0.size()==4)) { // Set Input Level
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
int level=f0.at(3).toInt(&ok);
|
|
if(ok) {
|
|
emit setInputLevelReq(id,card,port,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="OL")&&(f0.size()==4)) { // Set Output Level
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
int level=f0.at(3).toInt(&ok);
|
|
if(ok) {
|
|
emit setOutputLevelReq(id,card,port,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IM")&&(f0.size()==4)) { // Set Input Mode
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
unsigned mode=f0.at(3).toUInt(&ok);
|
|
if(ok&&(mode<=3)) {
|
|
emit setInputModeReq(id,card,port,mode);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="OM")&&(f0.size()==4)) { // Set Output Mode
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
unsigned mode=f0.at(3).toUInt(&ok);
|
|
if(ok&&(mode<=3)) {
|
|
emit setOutputModeReq(id,card,port,mode);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IX")&&(f0.size()==4)) { // Set Input Vox Level
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned stream=f0.at(2).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
int level=f0.at(3).toInt(&ok);
|
|
if(ok) {
|
|
emit setInputVoxLevelReq(id,card,stream,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IT")&&(f0.size()==4)) { // Set Input Type
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
int type=f0.at(3).toInt(&ok);
|
|
if(ok&&(type<=1)) {
|
|
emit setInputTypeReq(id,card,port,type);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="IS")&&(f0.size()==3)) { // Get Input Status
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
emit getInputStatusReq(id,card,port);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="AL")&&(f0.size()==5)) { // Set Audio Passthrough Level
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned input=f0.at(2).toUInt(&ok);
|
|
if(ok&&(input<RD_MAX_PORTS)) {
|
|
unsigned output=f0.at(3).toUInt(&ok);
|
|
if(ok&&(output<RD_MAX_PORTS)) {
|
|
int level=f0.at(4).toInt(&ok);
|
|
if(ok) {
|
|
emit setAudioPassthroughLevelReq(id,card,input,output,level);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="CS")&&(f0.size()==3)) { // Set Clock Source
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned input=f0.at(2).toUInt(&ok);
|
|
if(ok&&(input<RD_MAX_PORTS)) {
|
|
emit setClockSourceReq(id,card,input);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
if((f0.at(0)=="OS")&&(f0.size()==5)) { // Set Output Status Flag
|
|
unsigned card=f0.at(1).toUInt(&ok);
|
|
if(ok&&(card<RD_MAX_CARDS)) {
|
|
unsigned port=f0.at(2).toUInt(&ok);
|
|
if(ok&&(port<RD_MAX_PORTS)) {
|
|
unsigned stream=f0.at(3).toUInt(&ok);
|
|
if(ok&&(stream<RD_MAX_STREAMS)) {
|
|
emit setOutputStatusFlagReq(id,card,port,stream,f0.at(4)=="1");
|
|
was_processed=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if((f0.at(0)=="ME")&&(f0.size()>=3)) { // Meter Enable
|
|
uint16_t udp_port=0xFFFF&f0.at(1).toUInt(&ok);
|
|
if(ok) {
|
|
QList<unsigned> cards;
|
|
for(int i=2;i<f0.size();i++) {
|
|
cards.push_back(f0.at(i).toUInt());
|
|
}
|
|
emit meterEnableReq(id,udp_port,cards);
|
|
was_processed=true;
|
|
}
|
|
}
|
|
|
|
if(!was_processed) { // Send generic error response
|
|
sendCommand(id,f0.join(" ")+"-!");
|
|
}
|
|
|
|
return false;
|
|
}
|