Skip to content
Merged
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
70 changes: 70 additions & 0 deletions docs/JSON-RPC.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,23 @@ Results:
| result | string | Always "ok". |


### jamulusclient/setMuted

Mutes or unmutes the client.

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| params.muted | boolean | muted (true or false). |

Results:

| Name | Type | Description |
| --- | --- | --- |
| result | string | Always "ok". |


### jamulusclient/setName

Sets your name.
Expand Down Expand Up @@ -325,6 +342,23 @@ Results:
| result | string | Always "ok". |


### jamulusserver/broadcastChatMessage

Sends a message (as the server) to all connected clients. This can be used to broadcast messages from external sources (e.g. scripts or monitoring tools).

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| params.chatMessage | string | The chat message text. |

Results:

| Name | Type | Description |
| --- | --- | --- |
| result | string | Always "ok". |


### jamulusserver/getClients

Returns the list of connected clients along with details about them.
Expand Down Expand Up @@ -621,3 +655,39 @@ Parameters:
| params.servers[*].city | string | Server city. |


### jamulusserver/chatMessageReceived

Emitted when a chat message is received from either a Jamulus or RPC client and to be broadcast to all connected clients.

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| params.id | number | Channel ID of sending client or -1 for RPC sent messages. |
| params.chatMessage | string | Chat message text. |


### jamulusserver/clientConnected

Emitted when a client has connected to the server.

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| params.id | number | The channel ID assigned to the client. |
| params.address | string | The client's address. |
| params.totalChannels | number | Number of total channels connected to the server. |


### jamulusserver/clientDisconnected

Emitted when a client has disconnected from the server.

Parameters:

| Name | Type | Description |
| --- | --- | --- |
| params.id | number | The channel ID assigned to the client. |


1 change: 1 addition & 0 deletions src/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ class CClient : public QObject
// settings
CChannelCoreInfo ChannelInfo;
QString strClientName;
void OnRPCInMuteMyself ( bool bMute ) { OnControllerInMuteMyself ( bMute ); }

public:
void SetSettings ( CClientSettings* settings );
Expand Down
17 changes: 17 additions & 0 deletions src/clientrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,23 @@ CClientRpc::CClientRpc ( CClient* pClient, CClientSettings* pSettings, CRpcServe
response["result"] = jsonDevices;
Q_UNUSED ( params );
} );

/// @rpc_method jamulusclient/setMuted
/// @brief Mutes or unmutes the client.
/// @param {boolean} params.muted - muted (true or false).
/// @result {string} result - Always "ok".
pRpcServer->HandleMethod ( "jamulusclient/setMuted", [=] ( const QJsonObject& params, QJsonObject& response ) {
auto muted = params["muted"];
if ( !muted.isBool() )
{
response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: muted is not a boolean" );
return;
}

pClient->OnRPCInMuteMyself ( muted.toBool() );

response["result"] = "ok";
} );
}

QJsonValue CClientRpc::SerializeSkillLevel ( ESkillLevel eSkillLevel )
Expand Down
13 changes: 7 additions & 6 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ void CServer::OnNewConnection ( int iChID, int iTotChans, CHostAddress RecHostAd

// logging of new connected channel
Logging.AddNewConnection ( RecHostAddr.InetAddr, iTotChans );

emit ClientConnected ( iChID, RecHostAddr.InetAddr, iTotChans );
}

void CServer::OnCLReqServerFeatures ( CHostAddress RecHostAddr )
Expand Down Expand Up @@ -929,10 +931,7 @@ void CServer::DecodeReceiveData ( const int iChanCnt, const int iNumClients )
// and emit the client disconnected signal
if ( eGetStat == GS_CHAN_NOW_DISCONNECTED )
{
if ( JamController.GetRecordingEnabled() )
Comment thread
pljones marked this conversation as resolved.
{
emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock?
}
emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock?

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.

Do you want to say with the comment that you are unsure if there might be concurrency buts?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think that comment was added by @pljones?

@pljones pljones Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah - I wanted the ClientDisconnected to stop the recorder (which is on a separate Qt thread, so thread-safe - and hopefully non-interrupting therefore). My concern over the mutex was how it would affect CServer itself - it shouldn't get interrupted within the mutex by something handling the signal.


FreeChannel ( iCurChanID ); // note that the channel is now not in use

Expand Down Expand Up @@ -1355,10 +1354,10 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID, con
ChanName.toHtmlEscaped() + "</b></font> " + strChatText.toHtmlEscaped();

// Send chat text to all connected clients ---------------------------------
SendChatTextToAllConChannels ( strActualMessageText );
SendChatTextToAllConChannels ( iCurChanID, strActualMessageText );
}

void CServer::SendChatTextToAllConChannels ( const QString& strChatText )
void CServer::SendChatTextToAllConChannels ( const int iSendingChanID, const QString& strChatText )
{
// Send chat text to all connected clients ---------------------------------
for ( int i = 0; i < iMaxNumChannels; i++ )
Expand All @@ -1368,6 +1367,8 @@ void CServer::SendChatTextToAllConChannels ( const QString& strChatText )
vecChannels[i].CreateChatTextMes ( strChatText );
}
}
// forward the message to the RPC server
emit sentChatMessage ( iSendingChanID, strChatText );
}

void CServer::SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText )
Expand Down
4 changes: 3 additions & 1 deletion src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>
void SetEnableDelayPanning ( bool bDelayPanningOn ) { bDelayPan = bDelayPanningOn; }
bool IsDelayPanningEnabled() { return bDelayPan; }

void SendChatTextToAllConChannels ( const QString& strChatText );
void SendChatTextToAllConChannels ( const int iSendingChanID, const QString& strChatText );
void SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText );

protected:
Expand Down Expand Up @@ -331,6 +331,8 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>
void Started();
void Stopped();
void ClientDisconnected ( const int iChID );
void ClientConnected ( const int iChID, const QHostAddress RecHostAddr, const int iTotChans );
void sentChatMessage ( const int iSendingChanID, const QString& strChatText );
void SvrRegStatusChanged();
void AudioFrame ( const int iChID,
const QString stChName,
Expand Down
57 changes: 57 additions & 0 deletions src/serverrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@

#include "serverrpc.h"

/* Definitions ****************************************************************/
#define INVALID_CLIENT_ID -1

CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* parent ) : QObject ( parent )
{
// API doc already part of CClientRpc
Expand All @@ -56,6 +59,60 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare
Q_UNUSED ( params );
} );

/// @rpc_notification jamulusserver/clientConnected
/// @brief Emitted when a client has connected to the server.
/// @param {number} params.id - The channel ID assigned to the client.
/// @param {string} params.address - The client's address.
/// @param {number} params.totalChannels - Number of total channels connected to the server.
connect ( pServer, &CServer::ClientConnected, [=] ( const int iChanID, const QHostAddress RecHostAddr, const int iTotChans ) {
pRpcServer->BroadcastNotification ( "jamulusserver/clientConnected",
QJsonObject{
{ "id", iChanID },
{ "address", RecHostAddr.toString() },
{ "totalChannels", iTotChans },
} );
} );

/// @rpc_notification jamulusserver/clientDisconnected
/// @brief Emitted when a client has disconnected from the server.
/// @param {number} params.id - The channel ID assigned to the client.
connect ( pServer, &CServer::ClientDisconnected, [=] ( const int iChanID ) {
pRpcServer->BroadcastNotification ( "jamulusserver/clientDisconnected",
QJsonObject{
{ "id", iChanID },
} );
} );

/// @rpc_notification jamulusserver/chatMessageReceived
/// @brief Emitted when a chat message is received from either a Jamulus or RPC client and to be broadcast to all connected clients.
/// @param {number} params.id - Channel ID of sending client or -1 for RPC sent messages.
/// @param {string} params.chatMessage - Chat message text.
connect ( pServer, &CServer::sentChatMessage, [=] ( const int iSendingChanID, const QString& strChatText ) {
pRpcServer->BroadcastNotification ( "jamulusserver/chatMessageReceived",
Comment thread
ann0see marked this conversation as resolved.
QJsonObject{
{ "id", iSendingChanID },
{ "chatMessage", strChatText },
} );
} );

/// @rpc_method jamulusserver/broadcastChatMessage
/// @brief Sends a message (as the server) to all connected clients. This can be used to broadcast messages from external sources (e.g. scripts or
/// monitoring tools).
/// @param {string} params.chatMessage - The chat message text.
/// @result {string} result - Always "ok".
pRpcServer->HandleMethod ( "jamulusserver/broadcastChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) {
auto jsonChatMessage = params["chatMessage"];
if ( !jsonChatMessage.isString() )
{
response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string" );
return;
}

Comment thread
pljones marked this conversation as resolved.
// set invalid channel ID to make clear this message was not sent by a Jamulus client
pServer->SendChatTextToAllConChannels ( INVALID_CLIENT_ID, jsonChatMessage.toString() );
response["result"] = "ok";
} );

/// @rpc_method jamulusserver/getRecorderStatus
/// @brief Returns the recorder state.
/// @param {object} params - No parameters (empty object).
Expand Down