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']: | |||