diff --git a/Jamulus.pro b/Jamulus.pro index 14f4c53375..403d861d1b 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -429,6 +429,7 @@ HEADERS += src/buffer.h \ src/serverlogging.h \ src/serverrpc.h \ src/rpcserver.h \ + src/stereomixserver.h \ src/settings.h \ src/socket.h \ src/soundbase.h \ @@ -533,6 +534,7 @@ SOURCES += src/buffer.cpp \ src/serverlogging.cpp \ src/serverrpc.cpp \ src/rpcserver.cpp \ + src/stereomixserver.cpp \ src/settings.cpp \ src/signalhandler.cpp \ src/socket.cpp \ diff --git a/src/main.cpp b/src/main.cpp index 0d944376bb..e5c76decf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ extern void qt_set_sequence_auto_mnemonic ( bool bEnable ); #include "rpcserver.h" #include "serverrpc.h" #include "clientrpc.h" +#include "stereomixserver.h" // Implementation ************************************************************** @@ -89,6 +90,7 @@ int main ( int argc, char** argv ) int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS; quint16 iPortNumber = DEFAULT_PORT_NUMBER; int iJsonRpcPortNumber = INVALID_PORT; + int iStereoMixPortNumber = INVALID_PORT; quint16 iQosNumber = DEFAULT_QOS_NUMBER; ELicenceType eLicenceType = LT_NO_LICENCE; QString strMIDISetup = ""; @@ -176,6 +178,15 @@ int main ( int argc, char** argv ) continue; } + // Stereo mix port number ---------------------------------------------- + if ( GetNumericArgument ( argc, argv, i, "--stereomixport", "--stereomixport", 0, 65535, rDbleArgument ) ) + { + iStereoMixPortNumber = static_cast ( rDbleArgument ); + qInfo() << qUtf8Printable ( QString ( "- stereo mix port number: %1" ).arg ( iStereoMixPortNumber ) ); + CommandLineOptions << "--stereomixport"; + continue; + } + // Quality of Service -------------------------------------------------- if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) { @@ -899,6 +910,12 @@ int main ( int argc, char** argv ) new CServerRpc ( pRpcServer, &Server, pRpcServer ); } + if ( iStereoMixPortNumber != INVALID_PORT ) + { + auto pStereoMixServer = new CStereoMixServer ( &Server, iStereoMixPortNumber ); + pStereoMixServer->Start(); + } + #ifndef HEADLESS if ( bUseGUI ) { @@ -1013,6 +1030,9 @@ QString UsageArguments ( char** argv ) " --norecord disables recording (when enabled by default by -R)\n" " -s, --server start server\n" " --serverbindip IP address the server will bind to (rather than all)\n" + " --stereomixport enable Stereo Mix server which streams PCM samples\n" + " at 48000 Hz in s16le stereo format, set TCP port number\n" + " (only accessible from localhost)\n" " -T, --multithreading use multithreading to make better use of\n" " multi-core CPUs and support more clients\n" " -u, --numchannels maximum number of channels\n" diff --git a/src/server.h b/src/server.h index 1f10747b00..128152d810 100644 --- a/src/server.h +++ b/src/server.h @@ -402,6 +402,8 @@ class CServer : public QObject, public CServerSlots const int iNumAudChan, const CVector vecsData ); + void StreamFrame ( const int iServerFrameSizeSamples, const CVector& data ); + void CLVersionAndOSReceived ( CHostAddress InetAddr, COSUtil::EOpSystemType eOSType, QString strVersion ); // pass through from jam controller diff --git a/src/stereomixserver.cpp b/src/stereomixserver.cpp new file mode 100644 index 0000000000..e563fcf097 --- /dev/null +++ b/src/stereomixserver.cpp @@ -0,0 +1,86 @@ +/******************************************************************************\ + * Copyright (c) 2021 + * + * Author(s): + * dtinth + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + \******************************************************************************/ + +#include "stereomixserver.h" + +CStereoMixServer::CStereoMixServer ( CServer* pServer, int iPort ) : + QObject ( pServer ), + pServer ( pServer ), + iPort ( iPort ), + pTransportServer ( new QTcpServer ( this ) ) +{ + connect ( pTransportServer, &QTcpServer::newConnection, this, &CStereoMixServer::OnNewConnection ); + connect ( pServer, &CServer::StreamFrame, this, &CStereoMixServer::OnStreamFrame ); +} + +CStereoMixServer::~CStereoMixServer() +{ + if ( pTransportServer->isListening() ) + { + qInfo() << "- stopping stereo mix server"; + pTransportServer->close(); + } +} + +void CStereoMixServer::Start() +{ + if ( iPort < 0 ) + { + return; + } + if ( pTransportServer->listen ( QHostAddress ( "127.0.0.1" ), iPort ) ) + { + qInfo() << "- stereo mix server started on port" << pTransportServer->serverPort(); + } + else + { + qInfo() << "- unable to start stereo mix server:" << pTransportServer->errorString(); + } +} + +void CStereoMixServer::OnNewConnection() +{ + QTcpSocket* pSocket = pTransportServer->nextPendingConnection(); + if ( !pSocket ) + { + return; + } + + qInfo() << "- sending stereo mix to:" << pSocket->peerAddress().toString(); + vecClients.append ( pSocket ); + + connect ( pSocket, &QTcpSocket::disconnected, [this, pSocket]() { + qInfo() << "- finish sending stereo mix to:" << pSocket->peerAddress().toString(); + vecClients.removeAll ( pSocket ); + pSocket->deleteLater(); + } ); +} + +void CStereoMixServer::OnStreamFrame ( const int iServerFrameSizeSamples, const CVector& data ) +{ + for ( auto socket : vecClients ) + { + socket->write ( reinterpret_cast ( &data[0] ), sizeof ( int16_t ) * ( 2 * iServerFrameSizeSamples ) ); + } +} \ No newline at end of file diff --git a/src/stereomixserver.h b/src/stereomixserver.h new file mode 100644 index 0000000000..5023f20c9e --- /dev/null +++ b/src/stereomixserver.h @@ -0,0 +1,56 @@ +/******************************************************************************\ + * Copyright (c) 2021 + * + * Author(s): + * dtinth + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include "util.h" +#include "server.h" + +typedef std::function CRpcHandler; + +/* Classes ********************************************************************/ +class CStereoMixServer : public QObject +{ + Q_OBJECT + +public: + CStereoMixServer ( CServer* pServer, int iPort ); + virtual ~CStereoMixServer(); + + void Start(); + +private: + int iPort; + QTcpServer* pTransportServer; + CServer* pServer; + QVector vecClients; + +protected slots: + void OnNewConnection(); + void OnStreamFrame ( const int iServerFrameSizeSamples, const CVector& data ); +};