git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2770 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.90
@@ -37,7 +37,7 @@ namespace Jack | |||
JackNetDriver::JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | |||
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, | |||
const char* net_name, uint transport_sync, char network_mode ) | |||
: JackAudioDriver ( name, alias, engine, table ), fSocket ( ip, port ) | |||
: JackAudioDriver ( name, alias, engine, table ), JackNetSlaveInterface ( ip, port ) | |||
{ | |||
jack_log ( "JackNetDriver::JackNetDriver ip %s, port %d", ip, port ); | |||
@@ -59,14 +59,6 @@ namespace Jack | |||
JackNetDriver::~JackNetDriver() | |||
{ | |||
fSocket.Close(); | |||
SocketAPIEnd(); | |||
delete fNetAudioCaptureBuffer; | |||
delete fNetAudioPlaybackBuffer; | |||
delete fNetMidiCaptureBuffer; | |||
delete fNetMidiPlaybackBuffer; | |||
delete[] fTxBuffer; | |||
delete[] fRxBuffer; | |||
delete[] fMulticastIP; | |||
delete[] fMidiCapturePortList; | |||
delete[] fMidiPlaybackPortList; | |||
#ifdef JACK_MONITOR | |||
@@ -120,9 +112,6 @@ namespace Jack | |||
Restart(); | |||
//set the parameters to send | |||
strcpy ( fParams.fPacketType, "params" ); | |||
fParams.fProtocolVersion = 'a'; | |||
SetPacketType ( &fParams, SLAVE_AVAILABLE ); | |||
fParams.fSendAudioChannels = fCaptureChannels; | |||
fParams.fReturnAudioChannels = fPlaybackChannels; | |||
fParams.fSlaveSyncMode = fEngineControl->fSyncMode; | |||
@@ -131,144 +120,10 @@ namespace Jack | |||
if ( fParams.fTransportSync ) | |||
jack_info ( "NetDriver started with Master's Transport Sync." ); | |||
//init loop : get a master and start, do it until connection is ok | |||
net_status_t status; | |||
do | |||
{ | |||
//first, get a master, do it until a valid connection is running | |||
jack_info ( "Initializing Net Driver..." ); | |||
do | |||
{ | |||
status = GetNetMaster(); | |||
if ( status == NET_SOCKET_ERROR ) | |||
return false; | |||
} | |||
while ( status != NET_CONNECTED ); | |||
//then tell the master we are ready | |||
jack_info ( "Initializing connection with %s...", fParams.fMasterNetName ); | |||
status = SendMasterStartSync(); | |||
if ( status == NET_ERROR ) | |||
return false; | |||
} | |||
while ( status != NET_ROLLING ); | |||
if ( !JackNetSlaveInterface::Init() ) | |||
return false;; | |||
//driver parametering | |||
if ( SetParams() ) | |||
{ | |||
jack_error ( "Fatal error : can't alloc net driver ports." ); | |||
return false; | |||
} | |||
//init done, display parameters | |||
SessionParamsDisplay ( &fParams ); | |||
return true; | |||
} | |||
net_status_t JackNetDriver::GetNetMaster() | |||
{ | |||
jack_log ( "JackNetDriver::GetNetMaster()" ); | |||
//utility | |||
session_params_t params; | |||
int us_timeout = 2000000; | |||
int rx_bytes = 0; | |||
unsigned char loop = 0; | |||
//socket | |||
if ( fSocket.NewSocket() == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Fatal error : network unreachable - %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_SOCKET_ERROR; | |||
} | |||
//bind the socket | |||
if ( fSocket.Bind() == SOCKET_ERROR ) | |||
jack_error ( "Can't bind the socket : %s", StrError ( NET_ERROR_CODE ) ); | |||
//timeout on receive | |||
if ( fSocket.SetTimeOut ( us_timeout ) == SOCKET_ERROR ) | |||
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); | |||
//disable local loop | |||
if ( fSocket.SetOption ( IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof ( loop ) ) == SOCKET_ERROR ) | |||
jack_error ( "Can't disable multicast loop : %s", StrError ( NET_ERROR_CODE ) ); | |||
//send 'AVAILABLE' until 'SLAVE_SETUP' received | |||
jack_info ( "Waiting for a master..." ); | |||
do | |||
{ | |||
//send 'available' | |||
if ( fSocket.SendTo ( &fParams, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR ) | |||
jack_error ( "Error in data send : %s", StrError ( NET_ERROR_CODE ) ); | |||
//filter incoming packets : don't exit while no error is detected | |||
rx_bytes = fSocket.CatchHost ( ¶ms, sizeof ( session_params_t ), 0 ); | |||
if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) ) | |||
{ | |||
jack_error ( "Can't receive : %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_RECV_ERROR; | |||
} | |||
} | |||
while ( strcmp ( params.fPacketType, fParams.fPacketType ) && ( GetPacketType ( ¶ms ) != SLAVE_SETUP ) ); | |||
//connect the socket | |||
if ( fSocket.Connect() == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Error in connect : %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_CONNECT_ERROR; | |||
} | |||
//everything is OK, copy parameters and return | |||
fParams = params; | |||
return NET_CONNECTED; | |||
} | |||
net_status_t JackNetDriver::SendMasterStartSync() | |||
{ | |||
jack_log ( "JackNetDriver::GetNetMasterStartSync()" ); | |||
//tell the master to start | |||
SetPacketType ( &fParams, START_MASTER ); | |||
if ( fSocket.Send ( &fParams, sizeof ( session_params_t ), 0 ) == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Error in send : %s", StrError ( NET_ERROR_CODE ) ); | |||
return ( fSocket.GetError() == NET_CONN_ERROR ) ? NET_ERROR : NET_SEND_ERROR; | |||
} | |||
return NET_ROLLING; | |||
} | |||
void JackNetDriver::Restart() | |||
{ | |||
jack_log ( "JackNetDriver::Restart" ); | |||
jack_info ( "Restarting driver..." ); | |||
delete[] fTxBuffer; | |||
fTxBuffer = NULL; | |||
delete[] fRxBuffer; | |||
fRxBuffer = NULL; | |||
delete fNetAudioCaptureBuffer; | |||
fNetAudioCaptureBuffer = NULL; | |||
delete fNetAudioPlaybackBuffer; | |||
fNetAudioPlaybackBuffer = NULL; | |||
delete fNetMidiCaptureBuffer; | |||
fNetMidiCaptureBuffer = NULL; | |||
delete fNetMidiPlaybackBuffer; | |||
fNetMidiPlaybackBuffer = NULL; | |||
FreePorts(); | |||
delete[] fMidiCapturePortList; | |||
fMidiCapturePortList = NULL; | |||
delete[] fMidiPlaybackPortList; | |||
fMidiPlaybackPortList = NULL; | |||
#ifdef JACK_MONITOR | |||
delete fNetTimeMon; | |||
fNetTimeMon = NULL; | |||
delete fCycleTimeMon; | |||
fCycleTimeMon = NULL; | |||
#endif | |||
} | |||
int JackNetDriver::SetParams() | |||
{ | |||
fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; | |||
JackAudioDriver::SetBufferSize ( fParams.fPeriodSize ); | |||
JackAudioDriver::SetSampleRate ( fParams.fSampleRate ); | |||
@@ -279,54 +134,6 @@ namespace Jack | |||
fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; | |||
fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; | |||
//register jack ports | |||
if ( AllocPorts() != 0 ) | |||
{ | |||
jack_error ( "Can't allocate ports." ); | |||
return -1; | |||
} | |||
//TX header init | |||
strcpy ( fTxHeader.fPacketType, "header" ); | |||
fTxHeader.fDataStream = 'r'; | |||
fTxHeader.fID = fParams.fID; | |||
fTxHeader.fCycle = 0; | |||
fTxHeader.fSubCycle = 0; | |||
fTxHeader.fMidiDataSize = 0; | |||
fTxHeader.fBitdepth = fParams.fBitdepth; | |||
//RX header init | |||
strcpy ( fRxHeader.fPacketType, "header" ); | |||
fRxHeader.fDataStream = 's'; | |||
fRxHeader.fID = fParams.fID; | |||
fRxHeader.fCycle = 0; | |||
fRxHeader.fSubCycle = 0; | |||
fRxHeader.fMidiDataSize = 0; | |||
fRxHeader.fBitdepth = fParams.fBitdepth; | |||
//network buffers | |||
fTxBuffer = new char[fParams.fMtu]; | |||
fRxBuffer = new char[fParams.fMtu]; | |||
//net audio/midi buffers | |||
fTxData = fTxBuffer + sizeof ( packet_header_t ); | |||
fRxData = fRxBuffer + sizeof ( packet_header_t ); | |||
//midi net buffers | |||
fNetMidiCaptureBuffer = new NetMidiBuffer ( &fParams, fParams.fSendMidiChannels, fRxData ); | |||
fNetMidiPlaybackBuffer = new NetMidiBuffer ( &fParams, fParams.fReturnMidiChannels, fTxData ); | |||
//audio net buffers | |||
fNetAudioCaptureBuffer = new NetAudioBuffer ( &fParams, fParams.fSendAudioChannels, fRxData ); | |||
fNetAudioPlaybackBuffer = new NetAudioBuffer ( &fParams, fParams.fReturnAudioChannels, fTxData ); | |||
//audio netbuffer length | |||
fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); | |||
fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); | |||
//payload size | |||
fPayloadSize = fParams.fMtu - sizeof ( packet_header_t ); | |||
//monitor | |||
#ifdef JACK_MONITOR | |||
string plot_name; | |||
@@ -366,7 +173,45 @@ namespace Jack | |||
fLastCycleBeginDate = GetMicroSeconds(); | |||
#endif | |||
return 0; | |||
if ( SetParams() ) | |||
{ | |||
jack_error ( "Fatal error : can't alloc net driver ports." ); | |||
return false; | |||
} | |||
//init done, display parameters | |||
SessionParamsDisplay ( &fParams ); | |||
return true; | |||
} | |||
void JackNetDriver::Restart() | |||
{ | |||
jack_log ( "JackNetDriver::Restart" ); | |||
jack_info ( "Restarting driver..." ); | |||
delete[] fTxBuffer; | |||
fTxBuffer = NULL; | |||
delete[] fRxBuffer; | |||
fRxBuffer = NULL; | |||
delete fNetAudioCaptureBuffer; | |||
fNetAudioCaptureBuffer = NULL; | |||
delete fNetAudioPlaybackBuffer; | |||
fNetAudioPlaybackBuffer = NULL; | |||
delete fNetMidiCaptureBuffer; | |||
fNetMidiCaptureBuffer = NULL; | |||
delete fNetMidiPlaybackBuffer; | |||
fNetMidiPlaybackBuffer = NULL; | |||
FreePorts(); | |||
delete[] fMidiCapturePortList; | |||
fMidiCapturePortList = NULL; | |||
delete[] fMidiPlaybackPortList; | |||
fMidiPlaybackPortList = NULL; | |||
#ifdef JACK_MONITOR | |||
delete fNetTimeMon; | |||
fNetTimeMon = NULL; | |||
delete fCycleTimeMon; | |||
fCycleTimeMon = NULL; | |||
#endif | |||
} | |||
int JackNetDriver::AllocPorts() | |||
@@ -491,57 +336,10 @@ namespace Jack | |||
return 0; | |||
} | |||
//***********************************network operations************************************************************* | |||
int JackNetDriver::Recv ( size_t size, int flags ) | |||
{ | |||
int rx_bytes = fSocket.Recv ( fRxBuffer, size, flags ); | |||
//handle errors | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
{ | |||
net_error_t error = fSocket.GetError(); | |||
//no data isn't really an error in realtime processing, so just return 0 | |||
if ( error == NET_NO_DATA ) | |||
jack_error ( "No data, is the master still running ?" ); | |||
//if a network error occurs, this exception will restart the driver | |||
else if ( error == NET_CONN_ERROR ) | |||
{ | |||
jack_error ( "Connection lost." ); | |||
throw JackDriverException(); | |||
} | |||
else | |||
jack_error ( "Fatal error in receive : %s", StrError ( NET_ERROR_CODE ) ); | |||
} | |||
return rx_bytes; | |||
} | |||
int JackNetDriver::Send ( size_t size, int flags ) | |||
{ | |||
int tx_bytes = fSocket.Send ( fTxBuffer, size, flags ); | |||
//handle errors | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
{ | |||
net_error_t error = fSocket.GetError(); | |||
//if a network error occurs, this exception will restart the driver | |||
if ( error == NET_CONN_ERROR ) | |||
{ | |||
jack_error ( "Connection lost." ); | |||
throw JackDriverException(); | |||
} | |||
else | |||
jack_error ( "Fatal error in send : %s", StrError ( NET_ERROR_CODE ) ); | |||
} | |||
return tx_bytes; | |||
} | |||
//*************************************process************************************************************************ | |||
int JackNetDriver::Read() | |||
{ | |||
int rx_bytes; | |||
uint recvd_midi_pckt = 0; | |||
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
fRxHeader.fIsLastPckt = 'n'; | |||
uint midi_port_index; | |||
int audio_port_index; | |||
@@ -556,14 +354,8 @@ namespace Jack | |||
#endif | |||
//receive sync (launch the cycle) | |||
do | |||
{ | |||
rx_bytes = Recv ( fParams.fMtu, 0 ); | |||
//connection issue, send will detect it, so don't skip the cycle (return 0) | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
return 0; | |||
} | |||
while ( !rx_bytes && ( rx_head->fDataType != 's' ) ); | |||
if ( SyncRecv() == SOCKET_ERROR ) | |||
return 0; | |||
//take the time at the beginning of the cycle | |||
JackDriver::CycleTakeBeginTime(); | |||
@@ -575,45 +367,8 @@ namespace Jack | |||
#endif | |||
//audio, midi or sync if driver is late | |||
if ( fParams.fSendMidiChannels || fParams.fSendAudioChannels ) | |||
{ | |||
do | |||
{ | |||
rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); | |||
//error here, problem with recv, just skip the cycle (return -1) | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
return rx_bytes; | |||
if ( rx_bytes && ( rx_head->fDataStream == 's' ) && ( rx_head->fID == fParams.fID ) ) | |||
{ | |||
switch ( rx_head->fDataType ) | |||
{ | |||
case 'm': //midi | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
fNetMidiCaptureBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) ); | |||
if ( ++recvd_midi_pckt == rx_head->fNMidiPckt ) | |||
fNetMidiCaptureBuffer->RenderToJackPorts(); | |||
break; | |||
case 'a': //audio | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
if ( !IsNextPacket ( &fRxHeader, rx_head, fNSubProcess ) ) | |||
jack_error ( "Packet(s) missing..." ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
fRxHeader.fSubCycle = rx_head->fSubCycle; | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
fNetAudioCaptureBuffer->RenderToJackPorts ( rx_head->fSubCycle ); | |||
break; | |||
case 's': //sync | |||
jack_info ( "NetDriver : driver overloaded, skipping receive." ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
return 0; | |||
} | |||
} | |||
} | |||
while ( fRxHeader.fIsLastPckt != 'y' ); | |||
} | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
if ( DataRecv() == SOCKET_ERROR ) | |||
return SOCKET_ERROR; | |||
#ifdef JACK_MONITOR | |||
fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); | |||
@@ -625,15 +380,7 @@ namespace Jack | |||
int JackNetDriver::Write() | |||
{ | |||
uint midi_port_index; | |||
int tx_bytes, audio_port_index; | |||
//tx header | |||
if ( fEngineControl->fSyncMode ) | |||
fTxHeader.fCycle = fRxHeader.fCycle; | |||
else | |||
fTxHeader.fCycle++; | |||
fTxHeader.fSubCycle = 0; | |||
fTxHeader.fIsLastPckt = 'n'; | |||
int audio_port_index; | |||
//buffers | |||
for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) | |||
@@ -646,58 +393,18 @@ namespace Jack | |||
#endif | |||
//sync | |||
fTxHeader.fDataType = 's'; | |||
if ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels ) | |||
fTxHeader.fIsLastPckt = 'y'; | |||
fTxHeader.fPacketSize = fParams.fMtu; | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
memset ( fTxData, 0, fPayloadSize ); | |||
SetSyncPacket(); | |||
tx_bytes = Send ( fTxHeader.fPacketSize, 0 ); | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
return tx_bytes; | |||
if ( SyncSend() == SOCKET_ERROR ) | |||
return SOCKET_ERROR; | |||
#ifdef JACK_MONITOR | |||
fNetTimeMon->Add ( ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); | |||
#endif | |||
//midi | |||
if ( fParams.fReturnMidiChannels ) | |||
{ | |||
fTxHeader.fDataType = 'm'; | |||
fTxHeader.fMidiDataSize = fNetMidiPlaybackBuffer->RenderFromJackPorts(); | |||
fTxHeader.fNMidiPckt = GetNMidiPckt ( &fParams, fTxHeader.fMidiDataSize ); | |||
for ( uint subproc = 0; subproc < fTxHeader.fNMidiPckt; subproc++ ) | |||
{ | |||
fTxHeader.fSubCycle = subproc; | |||
if ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && !fParams.fReturnAudioChannels ) | |||
fTxHeader.fIsLastPckt = 'y'; | |||
fTxHeader.fPacketSize = fNetMidiPlaybackBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); | |||
fTxHeader.fPacketSize += sizeof ( packet_header_t ); | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
tx_bytes = Send ( fTxHeader.fPacketSize, 0 ); | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
return tx_bytes; | |||
} | |||
} | |||
//audio | |||
if ( fParams.fReturnAudioChannels ) | |||
{ | |||
fTxHeader.fDataType = 'a'; | |||
for ( uint subproc = 0; subproc < fNSubProcess; subproc++ ) | |||
{ | |||
fTxHeader.fSubCycle = subproc; | |||
if ( subproc == ( fNSubProcess - 1 ) ) | |||
fTxHeader.fIsLastPckt = 'y'; | |||
fTxHeader.fPacketSize = fAudioTxLen; | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
fNetAudioPlaybackBuffer->RenderFromJackPorts ( subproc ); | |||
tx_bytes = Send ( fTxHeader.fPacketSize, 0 ); | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
return tx_bytes; | |||
} | |||
} | |||
if ( DataSend() == SOCKET_ERROR ) | |||
return SOCKET_ERROR; | |||
#ifdef JACK_MONITOR | |||
fNetTimeMon->AddLast ( ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f ); | |||
@@ -792,7 +499,7 @@ namespace Jack | |||
strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); | |||
i++; | |||
strcpy ( desc->params[i].name, "network_mode" ); | |||
strcpy ( desc->params[i].name, "fast_mode" ); | |||
desc->params[i].character = 'f'; | |||
desc->params[i].type = JackDriverParamString; | |||
strcpy ( desc->params[i].value.str, "" ); | |||
@@ -22,7 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
#define __JackNetDriver__ | |||
#include "JackAudioDriver.h" | |||
#include "JackNetTool.h" | |||
#include "JackNetSlaveInterface.h" | |||
#ifdef JACK_MONITOR | |||
#include "JackFrameTimer.h" | |||
@@ -34,40 +34,14 @@ namespace Jack | |||
\Brief This class describes the Net Backend | |||
*/ | |||
class JackNetDriver : public JackAudioDriver | |||
class JackNetDriver : public JackAudioDriver, public JackNetSlaveInterface | |||
{ | |||
private: | |||
session_params_t fParams; | |||
char* fMulticastIP; | |||
JackNetSocket fSocket; | |||
uint fNSubProcess; | |||
//jack data | |||
net_transport_data_t fTransportData; | |||
//jack ports | |||
jack_port_id_t* fMidiCapturePortList; | |||
jack_port_id_t* fMidiPlaybackPortList; | |||
//headers | |||
packet_header_t fTxHeader; | |||
packet_header_t fRxHeader; | |||
//network buffers | |||
char* fTxBuffer; | |||
char* fRxBuffer; | |||
char* fTxData; | |||
char* fRxData; | |||
//jack buffers | |||
NetMidiBuffer* fNetMidiCaptureBuffer; | |||
NetMidiBuffer* fNetMidiPlaybackBuffer; | |||
NetAudioBuffer* fNetAudioCaptureBuffer; | |||
NetAudioBuffer* fNetAudioPlaybackBuffer; | |||
//sizes | |||
int fAudioRxLen; | |||
int fAudioTxLen; | |||
int fPayloadSize; | |||
//monitoring | |||
#ifdef JACK_MONITOR | |||
//time measurment | |||
@@ -78,21 +52,15 @@ namespace Jack | |||
#endif | |||
bool Init(); | |||
net_status_t GetNetMaster(); | |||
net_status_t SendMasterStartSync(); | |||
void Restart(); | |||
int SetParams(); | |||
int AllocPorts(); | |||
int FreePorts(); | |||
int SetSyncPacket(); | |||
int TransportSync(); | |||
JackMidiBuffer* GetMidiInputBuffer ( int port_index ); | |||
JackMidiBuffer* GetMidiOutputBuffer ( int port_index ); | |||
int Recv ( size_t size, int flags ); | |||
int Send ( size_t size, int flags ); | |||
int SetSyncPacket(); | |||
int TransportSync(); | |||
public: | |||
JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | |||
@@ -0,0 +1,377 @@ | |||
/* | |||
Copyright (C) 2001 Paul Davis | |||
Copyright (C) 2008 Romain Moret at Grame | |||
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., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
*/ | |||
#include "JackNetSlaveInterface.h" | |||
#include "JackEngineControl.h" | |||
#include "JackClientControl.h" | |||
#include "JackGraphManager.h" | |||
#include "JackException.h" | |||
#define DEFAULT_MULTICAST_IP "225.3.19.154" | |||
#define DEFAULT_PORT 19000 | |||
using namespace std; | |||
namespace Jack | |||
{ | |||
JackNetSlaveInterface::JackNetSlaveInterface() | |||
{ | |||
fMulticastIP = NULL; | |||
} | |||
JackNetSlaveInterface::~JackNetSlaveInterface() | |||
{ | |||
SocketAPIEnd(); | |||
delete[] fTxBuffer; | |||
delete[] fRxBuffer; | |||
delete[] fMulticastIP; | |||
delete fNetAudioCaptureBuffer; | |||
delete fNetAudioPlaybackBuffer; | |||
delete fNetMidiCaptureBuffer; | |||
delete fNetMidiPlaybackBuffer; | |||
} | |||
//*************************************initialization*********************************************************************** | |||
bool JackNetSlaveInterface::Init() | |||
{ | |||
jack_log ( "JackNetSlaveInterface::NetInit()" ); | |||
//set the parameters to send | |||
strcpy ( fParams.fPacketType, "params" ); | |||
fParams.fProtocolVersion = 'a'; | |||
SetPacketType ( &fParams, SLAVE_AVAILABLE ); | |||
//init loop : get a master and start, do it until connection is ok | |||
net_status_t status; | |||
do | |||
{ | |||
//first, get a master, do it until a valid connection is running | |||
jack_info ( "Initializing Net Driver..." ); | |||
do | |||
{ | |||
status = GetNetMaster(); | |||
if ( status == NET_SOCKET_ERROR ) | |||
return false; | |||
} | |||
while ( status != NET_CONNECTED ); | |||
//then tell the master we are ready | |||
jack_info ( "Initializing connection with %s...", fParams.fMasterNetName ); | |||
status = SendMasterStartSync(); | |||
if ( status == NET_ERROR ) | |||
return false; | |||
} | |||
while ( status != NET_ROLLING ); | |||
return true; | |||
} | |||
net_status_t JackNetSlaveInterface::GetNetMaster() | |||
{ | |||
jack_log ( "JackNetSlaveInterface::GetNetMaster()" ); | |||
//utility | |||
session_params_t params; | |||
int us_timeout = 2000000; | |||
int rx_bytes = 0; | |||
unsigned char loop = 0; | |||
//socket | |||
if ( fSocket.NewSocket() == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Fatal error : network unreachable - %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_SOCKET_ERROR; | |||
} | |||
//bind the socket | |||
if ( fSocket.Bind() == SOCKET_ERROR ) | |||
jack_error ( "Can't bind the socket : %s", StrError ( NET_ERROR_CODE ) ); | |||
//timeout on receive | |||
if ( fSocket.SetTimeOut ( us_timeout ) == SOCKET_ERROR ) | |||
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) ); | |||
//disable local loop | |||
if ( fSocket.SetOption ( IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof ( loop ) ) == SOCKET_ERROR ) | |||
jack_error ( "Can't disable multicast loop : %s", StrError ( NET_ERROR_CODE ) ); | |||
//send 'AVAILABLE' until 'SLAVE_SETUP' received | |||
jack_info ( "Waiting for a master..." ); | |||
do | |||
{ | |||
//send 'available' | |||
if ( fSocket.SendTo ( &fParams, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR ) | |||
jack_error ( "Error in data send : %s", StrError ( NET_ERROR_CODE ) ); | |||
//filter incoming packets : don't exit while no error is detected | |||
rx_bytes = fSocket.CatchHost ( ¶ms, sizeof ( session_params_t ), 0 ); | |||
if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) ) | |||
{ | |||
jack_error ( "Can't receive : %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_RECV_ERROR; | |||
} | |||
} | |||
while ( strcmp ( params.fPacketType, fParams.fPacketType ) && ( GetPacketType ( ¶ms ) != SLAVE_SETUP ) ); | |||
//connect the socket | |||
if ( fSocket.Connect() == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Error in connect : %s", StrError ( NET_ERROR_CODE ) ); | |||
return NET_CONNECT_ERROR; | |||
} | |||
//everything is OK, copy parameters and return | |||
fParams = params; | |||
return NET_CONNECTED; | |||
} | |||
net_status_t JackNetSlaveInterface::SendMasterStartSync() | |||
{ | |||
jack_log ( "JackNetSlaveInterface::GetNetMasterStartSync()" ); | |||
//tell the master to start | |||
SetPacketType ( &fParams, START_MASTER ); | |||
if ( fSocket.Send ( &fParams, sizeof ( session_params_t ), 0 ) == SOCKET_ERROR ) | |||
{ | |||
jack_error ( "Error in send : %s", StrError ( NET_ERROR_CODE ) ); | |||
return ( fSocket.GetError() == NET_CONN_ERROR ) ? NET_ERROR : NET_SEND_ERROR; | |||
} | |||
return NET_ROLLING; | |||
} | |||
int JackNetSlaveInterface::SetParams() | |||
{ | |||
fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; | |||
//TX header init | |||
strcpy ( fTxHeader.fPacketType, "header" ); | |||
fTxHeader.fDataStream = 'r'; | |||
fTxHeader.fID = fParams.fID; | |||
fTxHeader.fCycle = 0; | |||
fTxHeader.fSubCycle = 0; | |||
fTxHeader.fMidiDataSize = 0; | |||
fTxHeader.fBitdepth = fParams.fBitdepth; | |||
//RX header init | |||
strcpy ( fRxHeader.fPacketType, "header" ); | |||
fRxHeader.fDataStream = 's'; | |||
fRxHeader.fID = fParams.fID; | |||
fRxHeader.fCycle = 0; | |||
fRxHeader.fSubCycle = 0; | |||
fRxHeader.fMidiDataSize = 0; | |||
fRxHeader.fBitdepth = fParams.fBitdepth; | |||
//network buffers | |||
fTxBuffer = new char[fParams.fMtu]; | |||
fRxBuffer = new char[fParams.fMtu]; | |||
//net audio/midi buffers | |||
fTxData = fTxBuffer + sizeof ( packet_header_t ); | |||
fRxData = fRxBuffer + sizeof ( packet_header_t ); | |||
//midi net buffers | |||
fNetMidiCaptureBuffer = new NetMidiBuffer ( &fParams, fParams.fSendMidiChannels, fRxData ); | |||
fNetMidiPlaybackBuffer = new NetMidiBuffer ( &fParams, fParams.fReturnMidiChannels, fTxData ); | |||
//audio net buffers | |||
fNetAudioCaptureBuffer = new NetAudioBuffer ( &fParams, fParams.fSendAudioChannels, fRxData ); | |||
fNetAudioPlaybackBuffer = new NetAudioBuffer ( &fParams, fParams.fReturnAudioChannels, fTxData ); | |||
//audio netbuffer length | |||
fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); | |||
fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); | |||
//payload size | |||
fPayloadSize = fParams.fMtu - sizeof ( packet_header_t ); | |||
return 0; | |||
} | |||
//***********************************network operations************************************************************* | |||
int JackNetSlaveInterface::Recv ( size_t size, int flags ) | |||
{ | |||
int rx_bytes = fSocket.Recv ( fRxBuffer, size, flags ); | |||
//handle errors | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
{ | |||
net_error_t error = fSocket.GetError(); | |||
//no data isn't really an error in realtime processing, so just return 0 | |||
if ( error == NET_NO_DATA ) | |||
jack_error ( "No data, is the master still running ?" ); | |||
//if a network error occurs, this exception will restart the driver | |||
else if ( error == NET_CONN_ERROR ) | |||
{ | |||
jack_error ( "Connection lost." ); | |||
throw JackDriverException(); | |||
} | |||
else | |||
jack_error ( "Fatal error in receive : %s", StrError ( NET_ERROR_CODE ) ); | |||
} | |||
return rx_bytes; | |||
} | |||
int JackNetSlaveInterface::Send ( size_t size, int flags ) | |||
{ | |||
int tx_bytes = fSocket.Send ( fTxBuffer, size, flags ); | |||
//handle errors | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
{ | |||
net_error_t error = fSocket.GetError(); | |||
//if a network error occurs, this exception will restart the driver | |||
if ( error == NET_CONN_ERROR ) | |||
{ | |||
jack_error ( "Connection lost." ); | |||
throw JackDriverException(); | |||
} | |||
else | |||
jack_error ( "Fatal error in send : %s", StrError ( NET_ERROR_CODE ) ); | |||
} | |||
return tx_bytes; | |||
} | |||
//**************************************processes*************************************************** | |||
int JackNetSlaveInterface::SyncRecv() | |||
{ | |||
int rx_bytes; | |||
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
fRxHeader.fIsLastPckt = 'n'; | |||
//receive sync (launch the cycle) | |||
do | |||
{ | |||
rx_bytes = Recv ( fParams.fMtu, 0 ); | |||
//connection issue, send will detect it, so don't skip the cycle (return 0) | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
return rx_bytes; | |||
} | |||
while ( !rx_bytes && ( rx_head->fDataType != 's' ) ); | |||
return rx_bytes; | |||
} | |||
int JackNetSlaveInterface::DataRecv() | |||
{ | |||
uint recvd_midi_pckt = 0; | |||
int rx_bytes; | |||
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
//audio, midi or sync if driver is late | |||
if ( fParams.fSendMidiChannels || fParams.fSendAudioChannels ) | |||
{ | |||
do | |||
{ | |||
rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); | |||
//error here, problem with recv, just skip the cycle (return -1) | |||
if ( rx_bytes == SOCKET_ERROR ) | |||
return rx_bytes; | |||
if ( rx_bytes && ( rx_head->fDataStream == 's' ) && ( rx_head->fID == fParams.fID ) ) | |||
{ | |||
switch ( rx_head->fDataType ) | |||
{ | |||
case 'm': //midi | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
fNetMidiCaptureBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) ); | |||
if ( ++recvd_midi_pckt == rx_head->fNMidiPckt ) | |||
fNetMidiCaptureBuffer->RenderToJackPorts(); | |||
break; | |||
case 'a': //audio | |||
rx_bytes = Recv ( rx_head->fPacketSize, 0 ); | |||
if ( !IsNextPacket ( &fRxHeader, rx_head, fNSubProcess ) ) | |||
jack_error ( "Packet(s) missing..." ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
fRxHeader.fSubCycle = rx_head->fSubCycle; | |||
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
fNetAudioCaptureBuffer->RenderToJackPorts ( rx_head->fSubCycle ); | |||
break; | |||
case 's': //sync | |||
jack_info ( "NetSlave : overloaded, skipping receive." ); | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
return 0; | |||
} | |||
} | |||
} | |||
while ( fRxHeader.fIsLastPckt != 'y' ); | |||
} | |||
fRxHeader.fCycle = rx_head->fCycle; | |||
return 0; | |||
} | |||
int JackNetSlaveInterface::SyncSend() | |||
{ | |||
//tx header | |||
if ( fParams.fSlaveSyncMode ) | |||
fTxHeader.fCycle = fRxHeader.fCycle; | |||
else | |||
fTxHeader.fCycle++; | |||
fTxHeader.fSubCycle = 0; | |||
//sync | |||
fTxHeader.fDataType = 's'; | |||
fTxHeader.fIsLastPckt = ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels ) ? 'y' : 'n'; | |||
fTxHeader.fPacketSize = fParams.fMtu; | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
return Send ( fTxHeader.fPacketSize, 0 ); | |||
} | |||
int JackNetSlaveInterface::DataSend() | |||
{ | |||
int tx_bytes; | |||
//midi | |||
if ( fParams.fReturnMidiChannels ) | |||
{ | |||
fTxHeader.fDataType = 'm'; | |||
fTxHeader.fMidiDataSize = fNetMidiPlaybackBuffer->RenderFromJackPorts(); | |||
fTxHeader.fNMidiPckt = GetNMidiPckt ( &fParams, fTxHeader.fMidiDataSize ); | |||
for ( uint subproc = 0; subproc < fTxHeader.fNMidiPckt; subproc++ ) | |||
{ | |||
fTxHeader.fSubCycle = subproc; | |||
if ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && !fParams.fReturnAudioChannels ) | |||
fTxHeader.fIsLastPckt = 'y'; | |||
fTxHeader.fPacketSize = fNetMidiPlaybackBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); | |||
fTxHeader.fPacketSize += sizeof ( packet_header_t ); | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
tx_bytes = Send ( fTxHeader.fPacketSize, 0 ); | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
return tx_bytes; | |||
} | |||
} | |||
//audio | |||
if ( fParams.fReturnAudioChannels ) | |||
{ | |||
fTxHeader.fDataType = 'a'; | |||
for ( uint subproc = 0; subproc < fNSubProcess; subproc++ ) | |||
{ | |||
fTxHeader.fSubCycle = subproc; | |||
if ( subproc == ( fNSubProcess - 1 ) ) | |||
fTxHeader.fIsLastPckt = 'y'; | |||
fTxHeader.fPacketSize = fAudioTxLen; | |||
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
fNetAudioPlaybackBuffer->RenderFromJackPorts ( subproc ); | |||
tx_bytes = Send ( fTxHeader.fPacketSize, 0 ); | |||
if ( tx_bytes == SOCKET_ERROR ) | |||
return tx_bytes; | |||
} | |||
} | |||
return 0; | |||
} | |||
} |
@@ -0,0 +1,87 @@ | |||
/* | |||
Copyright (C) 2001 Paul Davis | |||
Copyright (C) 2008 Romain Moret at Grame | |||
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., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
*/ | |||
#ifndef __JackNetSlaveInterface__ | |||
#define __JackNetSlaveInterface__ | |||
#include "JackNetTool.h" | |||
#ifdef JACK_MONITOR | |||
#include "JackFrameTimer.h" | |||
#endif | |||
namespace Jack | |||
{ | |||
/** | |||
\Brief This class describes the Net Interface for slaves (NetDriver and NetAdapter) | |||
*/ | |||
class EXPORT JackNetSlaveInterface | |||
{ | |||
protected: | |||
session_params_t fParams; | |||
char* fMulticastIP; | |||
JackNetSocket fSocket; | |||
uint fNSubProcess; | |||
//headers | |||
packet_header_t fTxHeader; | |||
packet_header_t fRxHeader; | |||
//network buffers | |||
char* fTxBuffer; | |||
char* fRxBuffer; | |||
char* fTxData; | |||
char* fRxData; | |||
//jack buffers | |||
NetMidiBuffer* fNetMidiCaptureBuffer; | |||
NetMidiBuffer* fNetMidiPlaybackBuffer; | |||
NetAudioBuffer* fNetAudioCaptureBuffer; | |||
NetAudioBuffer* fNetAudioPlaybackBuffer; | |||
//sizes | |||
int fAudioRxLen; | |||
int fAudioTxLen; | |||
int fPayloadSize; | |||
//sub processes | |||
bool Init(); | |||
net_status_t GetNetMaster(); | |||
net_status_t SendMasterStartSync(); | |||
int SetParams(); | |||
int SyncRecv(); | |||
int SyncSend(); | |||
int DataRecv(); | |||
int DataSend(); | |||
//network operations | |||
int Recv ( size_t size, int flags ); | |||
int Send ( size_t size, int flags ); | |||
public: | |||
JackNetSlaveInterface(); | |||
JackNetSlaveInterface ( const char* ip, int port ) : fMulticastIP ( NULL ), fSocket ( ip, port ) | |||
{} | |||
~JackNetSlaveInterface(); | |||
}; | |||
} | |||
#endif |
@@ -130,6 +130,7 @@ def build(bld): | |||
'JackServerGlobals.cpp', | |||
'JackControl.cpp', | |||
'JackNetTool.cpp', | |||
'JackNetSlaveInterface.cpp', | |||
] | |||
if bld.env()['IS_LINUX']: | |||