Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,20 @@ void CServer::OnTimer()
// calculate levels for all connected clients
const bool bSendChannelLevels = CreateLevelsForAllConChannels ( iNumClients, vecNumAudioChannels, vecvecsData, vecChannelLevels );

{
bool bAnyActivity = false;
for ( int j = 0; j < iNumClients && !bAnyActivity; j++ )
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is appropriate. Worst case is N clients and only channel n=N-1 is sending audio.
The code would iterate over (N - 1) * vecvecsData[n].Size() + 1 single samples before leaving the loop on every server tick.
Even if this would be accepted this wouldn't be the way to implement it. A server should rather be queried for activity and only then a calculation should be taking place for a given amount of time and not on every tick and not for every single sample.

Copy link
Copy Markdown
Contributor Author

@mcfnord mcfnord Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feedback.

My new design will use a custom UDP message and get back the # of seconds since last audio, and a bit mask of all audible channels at the most recent sample.

Using UDP solves the firewall problem, and using a custom message rather than an existing message solves the binary protocol problem.

Not sure, but I think sampling only on second-tick changes should be adequate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I can use the existing levels! So i'll close this now

{
const int iDataSize = vecvecsData[j].Size();
for ( int k = 0; k < iDataSize && !bAnyActivity; k++ )
{
if ( vecvecsData[j][k] < -100 || vecvecsData[j][k] > 100 ) bAnyActivity = true;
}
}
if ( bAnyActivity ) m_iLastAudioActivityMs = QDateTime::currentMSecsSinceEpoch();
m_bIsSilent = ( QDateTime::currentMSecsSinceEpoch() - m_iLastAudioActivityMs ) > 2000;
}

for ( int iChanCnt = 0; iChanCnt < iNumClients; iChanCnt++ )
{
// get actual ID of current channel
Expand Down Expand Up @@ -731,6 +745,8 @@ void CServer::OnTimer()
{
// Disable server if no clients are connected. In this case the server
// does not consume any significant CPU when no client is connected.
m_iLastAudioActivityMs = QDateTime::currentMSecsSinceEpoch();
m_bIsSilent = false;
Stop();
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>
}
QString GetRecordingDir() { return JamController.GetRecordingDir(); }

bool GetIsSilent() const { return m_bIsSilent; }

void SetWelcomeMessage ( const QString& strNWelcMess );
QString GetWelcomeMessage() { return strWelcomeMessage; }

Expand Down Expand Up @@ -312,6 +314,9 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>

std::unique_ptr<CThreadPool> pThreadPool;

bool m_bIsSilent = false;
qint64 m_iLastAudioActivityMs = 0;

signals:
void Started();
void Stopped();
Expand Down
9 changes: 9 additions & 0 deletions src/serverrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,15 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare
response["result"] = "acknowledged";
Q_UNUSED ( params );
} );

/// @rpc_method jamulusserver/getSilenceStatus
/// @brief Returns whether the server mix is currently silent.
/// @param {object} params - No parameters (empty object).
/// @result {boolean} result.silent - True if all connected clients have been below the audio threshold for ≥2 seconds. False if no clients are connected or audio is active.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no clients are connected, I guess that sounds like true (silent) to me!

pRpcServer->HandleMethod ( "jamulusserver/getSilenceStatus", [=] ( const QJsonObject& params, QJsonObject& response ) {
response["result"] = QJsonObject{ { "silent", pServer->GetIsSilent() } };
Q_UNUSED ( params );
} );
}

#if defined( Q_OS_MACOS ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
Expand Down
Loading