/* 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 "JackNetDriver.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackGraphManager.h" #include "JackDriverLoader.h" #include "JackThreadedDriver.h" #include "JackWaitThreadedDriver.h" #include "JackException.h" #define DEFAULT_MULTICAST_IP "225.3.19.154" #define DEFAULT_PORT 19000 using namespace std; namespace Jack { #ifdef JACK_MONITOR uint JackNetDriver::fMeasureCnt = 128; uint JackNetDriver::fMeasurePoints = 4; string JackNetDriver::fMonitorFieldNames[] = { string ( "end of read" ), string ( "start of write" ), string ( "sync send" ), string ( "end of write" ) }; uint JackNetDriver::fMonitorPlotOptionsCnt = 2; string JackNetDriver::fMonitorPlotOptions[] = { string ( "set xlabel \"audio cycles\"" ), string ( "set ylabel \"% of audio cycle\"" ) }; #endif 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_master_mode, char network_slave_mode ) : JackAudioDriver ( name, alias, engine, table ), fSocket ( ip, port ) { jack_log ( "JackNetDriver::JackNetDriver ip %s, port %d", ip, port ); fMulticastIP = new char[strlen ( ip ) + 1]; strcpy ( fMulticastIP, ip ); fParams.fMtu = mtu; fParams.fSendMidiChannels = midi_input_ports; fParams.fReturnMidiChannels = midi_output_ports; strcpy ( fParams.fName, net_name ); fSocket.GetName ( fParams.fSlaveNetName ); fParams.fTransportSync = transport_sync; fParams.fNetworkMasterMode = network_master_mode; fParams.fNetworkSlaveMode = network_slave_mode; #ifdef JACK_MONITOR fMonitor = NULL; fMeasure = NULL; #endif } 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 delete[] fMeasure; delete fMonitor; #endif } //*************************************initialization*********************************************************************** int JackNetDriver::Open ( jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency ) { int res = JackAudioDriver::Open ( buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency ); fEngineControl->fPeriod = 0; fEngineControl->fComputation = 500 * 1000; fEngineControl->fConstraint = 500 * 1000; return res; } #ifdef JACK_MONITOR int JackNetDriver::Close() { if ( fMonitor ) fMonitor->Save(); return JackDriver::Close(); } #endif int JackNetDriver::Attach() { return 0; } int JackNetDriver::Detach() { return 0; } bool JackNetDriver::Init() { jack_log ( "JackNetDriver::Init()" ); //new loading, but existing socket, restart the driver if ( fSocket.IsSocket() ) 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; //is transport sync ? 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 ); //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; //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 ) ); //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 receiving wrong packets do { 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 ( ( rx_bytes > 0 ) && strcmp ( params.fPacketType, fParams.fPacketType ) ); } while ( ( 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[] fMeasure; fMeasure = NULL; delete fMonitor; fMonitor = NULL; #endif } int JackNetDriver::SetParams() { fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; JackAudioDriver::SetBufferSize ( fParams.fPeriodSize ); JackAudioDriver::SetSampleRate ( fParams.fSampleRate ); JackDriver::NotifyBufferSize ( fParams.fPeriodSize ); JackDriver::NotifySampleRate ( fParams.fSampleRate ); //allocate midi ports lists 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 = string ( fParams.fName ); plot_name += string ( "_slave" ); plot_name += ( fEngineControl->fSyncMode ) ? string ( "_sync" ) : string ( "_async" ); fMonitor = new JackGnuPlotMonitor ( JackNetDriver::fMeasureCnt, JackNetDriver::fMeasurePoints, plot_name ); fMeasure = new float[JackNetDriver::fMeasurePoints]; fMonitor->SetPlotFile ( JackNetDriver::fMonitorPlotOptions, JackNetDriver::fMonitorPlotOptionsCnt, JackNetDriver::fMonitorFieldNames, JackNetDriver::fMeasurePoints ); #endif return 0; } int JackNetDriver::AllocPorts() { jack_log ( "JackNetDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate ); JackPort* port; jack_port_id_t port_id; char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; unsigned long port_flags; int audio_port_index; uint midi_port_index; //audio port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) { snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, audio_port_index + 1 ); snprintf ( name, sizeof ( name ) - 1, "%s:capture_%d", fClientControl.fName, audio_port_index + 1 ); if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) { jack_error ( "driver: cannot register port for %s", name ); return -1; } port = fGraphManager->GetPort ( port_id ); port->SetAlias ( alias ); port->SetLatency ( fEngineControl->fBufferSize ); fCapturePortList[audio_port_index] = port_id; jack_log ( "JackNetDriver::AllocPorts() fCapturePortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_id, port->GetLatency() ); } port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) { snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, audio_port_index + 1 ); snprintf ( name, sizeof ( name ) - 1, "%s:playback_%d",fClientControl.fName, audio_port_index + 1 ); if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) { jack_error ( "driver: cannot register port for %s", name ); return -1; } port = fGraphManager->GetPort ( port_id ); port->SetAlias ( alias ); port->SetLatency ( fEngineControl->fBufferSize + ( ( fEngineControl->fSyncMode ) ? 0 : fEngineControl->fBufferSize ) ); fPlaybackPortList[audio_port_index] = port_id; jack_log ( "JackNetDriver::AllocPorts() fPlaybackPortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_id, port->GetLatency() ); } //midi port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) { snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, midi_port_index + 1 ); snprintf ( name, sizeof ( name ) - 1, "%s:midi_capture_%d", fClientControl.fName, midi_port_index + 1 ); if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) { jack_error ( "driver: cannot register port for %s", name ); return -1; } port = fGraphManager->GetPort ( port_id ); port->SetLatency ( fEngineControl->fBufferSize ); fMidiCapturePortList[midi_port_index] = port_id; jack_log ( "JackNetDriver::AllocPorts() fMidiCapturePortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_id, port->GetLatency() ); } port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) { snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, midi_port_index + 1 ); snprintf ( name, sizeof ( name ) - 1, "%s:midi_playback_%d", fClientControl.fName, midi_port_index + 1 ); if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) { jack_error ( "driver: cannot register port for %s", name ); return -1; } port = fGraphManager->GetPort ( port_id ); port->SetLatency ( fEngineControl->fBufferSize + ( ( fEngineControl->fSyncMode ) ? 0 : fEngineControl->fBufferSize ) ); fMidiPlaybackPortList[midi_port_index] = port_id; jack_log ( "JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_id, port->GetLatency() ); } return 0; } int JackNetDriver::FreePorts() { jack_log ( "JackNetDriver::FreePorts" ); int audio_port_index; uint midi_port_index; for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index] ); for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index] ); for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index] ); for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index] ); return 0; } JackMidiBuffer* JackNetDriver::GetMidiInputBuffer ( int port_index ) { return static_cast ( fGraphManager->GetBuffer ( fMidiCapturePortList[port_index], fEngineControl->fBufferSize ) ); } JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer ( int port_index ) { return static_cast ( fGraphManager->GetBuffer ( fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize ) ); } int JackNetDriver::SetSyncPacket() { if ( fParams.fTransportSync ) { //set the TransportData //copy to TxBuffer memcpy ( fTxData, &fTransportData, sizeof ( net_transport_data_t ) ); } 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() { //slow mode and first cycle : skip read if ( ( fParams.fNetworkSlaveMode == 's' ) && ( fRxHeader.fCycle == 0 ) ) return 0; int rx_bytes; uint recvd_midi_pckt = 0; packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); fRxHeader.fIsLastPckt = 'n'; uint midi_port_index; int audio_port_index; //buffers for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) fNetMidiCaptureBuffer->SetBuffer ( midi_port_index, GetMidiInputBuffer ( midi_port_index ) ); for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) fNetAudioCaptureBuffer->SetBuffer ( audio_port_index, GetInputBuffer ( audio_port_index ) ); //receive sync (launch the cycle) do { rx_bytes = Recv ( sizeof ( packet_header_t ), 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' ) ); //take the time at the beginning of the cycle JackDriver::CycleTakeBeginTime(); //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_bytes, 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 ( fAudioRxLen, 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; #ifdef JACK_MONITOR fMeasureId = 0; fMeasure[fMeasureId++] = ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f; #endif return 0; } int JackNetDriver::Write() { uint midi_port_index; int tx_bytes, copy_size, audio_port_index; //tx header if ( fEngineControl->fSyncMode ) fTxHeader.fCycle = fRxHeader.fCycle; else fTxHeader.fCycle++; fTxHeader.fSubCycle = 0; fTxHeader.fIsLastPckt = 'n'; //buffers for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) fNetMidiPlaybackBuffer->SetBuffer ( midi_port_index, GetMidiOutputBuffer ( midi_port_index ) ); for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) fNetAudioPlaybackBuffer->SetBuffer ( audio_port_index, GetOutputBuffer ( audio_port_index ) ); #ifdef JACK_MONITOR fMeasure[fMeasureId++] = ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f; #endif //sync fTxHeader.fDataType = 's'; if ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels ) fTxHeader.fIsLastPckt = 'y'; memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); //memset ( fTxData, 0, fPayloadSize ); //SetSyncPacket(); tx_bytes = Send ( sizeof ( packet_header_t ), 0 ); if ( tx_bytes == SOCKET_ERROR ) return tx_bytes; #ifdef JACK_MONITOR fMeasure[fMeasureId++] = ( ( 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'; memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); copy_size = fNetMidiPlaybackBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); tx_bytes = Send ( sizeof ( packet_header_t ) + copy_size, 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'; fNetAudioPlaybackBuffer->RenderFromJackPorts ( subproc ); memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); tx_bytes = Send ( fAudioTxLen, 0 ); if ( tx_bytes == SOCKET_ERROR ) return tx_bytes; } } #ifdef JACK_MONITOR fMeasure[fMeasureId++] = ( ( float ) ( GetMicroSeconds() - JackDriver::fBeginDateUst ) / ( float ) fEngineControl->fPeriodUsecs ) * 100.f; fMonitor->Write ( fMeasure ); #endif return 0; } //*************************************loader******************************************************* #ifdef __cplusplus extern "C" { #endif EXPORT jack_driver_desc_t* driver_get_descriptor () { jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); strcpy ( desc->name, "net" ); desc->nparams = 11; desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); int i = 0; strcpy ( desc->params[i].name, "multicast_ip" ); desc->params[i].character = 'a'; desc->params[i].type = JackDriverParamString; strcpy ( desc->params[i].value.str, DEFAULT_MULTICAST_IP ); strcpy ( desc->params[i].short_desc, "Multicast Address" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "udp_net_port" ); desc->params[i].character = 'p'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 19000; strcpy ( desc->params[i].short_desc, "UDP port" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "mtu" ); desc->params[i].character = 'M'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 1500; strcpy ( desc->params[i].short_desc, "MTU to the master" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "input_ports" ); desc->params[i].character = 'C'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 2; strcpy ( desc->params[i].short_desc, "Number of audio input ports" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "output_ports" ); desc->params[i].character = 'P'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 2; strcpy ( desc->params[i].short_desc, "Number of audio output ports" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "midi_in_ports" ); desc->params[i].character = 'i'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 0; strcpy ( desc->params[i].short_desc, "Number of midi input ports" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "midi_out_ports" ); desc->params[i].character = 'o'; desc->params[i].type = JackDriverParamUInt; desc->params[i].value.i = 0; strcpy ( desc->params[i].short_desc, "Number of midi output ports" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "client_name" ); desc->params[i].character = 'n'; desc->params[i].type = JackDriverParamString; strcpy ( desc->params[i].value.str, "'hostname'" ); strcpy ( desc->params[i].short_desc, "Name of the jack client" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "transport_sync" ); desc->params[i].character = 't'; desc->params[i].type = JackDriverParamUInt; desc->params[i].value.ui = 1U; strcpy ( desc->params[i].short_desc, "Sync transport with master's" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "network_master_mode" ); desc->params[i].character = 'm'; desc->params[i].type = JackDriverParamChar; desc->params[i].value.ui = 's'; strcpy ( desc->params[i].short_desc, "Slow network add 1 cycle latency" ); strcpy ( desc->params[i].long_desc, "'s' for slow, 'f' for fast, default is slow.\ Fast network will make the master waiting for the current cycle return data." ); i++; strcpy ( desc->params[i].name, "network_slave_mode" ); desc->params[i].character = 's'; desc->params[i].type = JackDriverParamChar; desc->params[i].value.ui = 's'; strcpy ( desc->params[i].short_desc, "Slow network add 1 cycle latency" ); strcpy ( desc->params[i].long_desc, "'s' for slow, 'f' for fast, default is slow.\ Fast network will make the slave waiting for the first cycle data." ); return desc; } EXPORT Jack::JackDriverClientInterface* driver_initialize ( Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params ) { if ( SocketAPIInit() < 0 ) { jack_error ( "Can't init Socket API, exiting..." ); return NULL; } const char* multicast_ip = DEFAULT_MULTICAST_IP; char name[JACK_CLIENT_NAME_SIZE]; GetHostName ( name, JACK_CLIENT_NAME_SIZE ); int udp_port = DEFAULT_PORT; int mtu = 1500; uint transport_sync = 1; jack_nframes_t period_size = 128; jack_nframes_t sample_rate = 48000; int audio_capture_ports = 2; int audio_playback_ports = 2; int midi_input_ports = 0; int midi_output_ports = 0; bool monitor = false; char network_master_mode = 's'; char network_slave_mode = 's'; const JSList* node; const jack_driver_param_t* param; for ( node = params; node; node = jack_slist_next ( node ) ) { param = ( const jack_driver_param_t* ) node->data; switch ( param->character ) { case 'a' : multicast_ip = strdup ( param->value.str ); break; case 'p': udp_port = param->value.ui; break; case 'M': mtu = param->value.i; break; case 'C': audio_capture_ports = param->value.i; break; case 'P': audio_playback_ports = param->value.i; break; case 'i': midi_input_ports = param->value.i; break; case 'o': midi_output_ports = param->value.i; break; case 'n' : strncpy ( name, param->value.str, JACK_CLIENT_NAME_SIZE ); break; case 't' : transport_sync = param->value.ui; break; case 'm' : switch ( param->value.ui ) { case 's' : network_master_mode = 's'; break; case 'f' : network_master_mode = 'f'; break; default : network_master_mode = 's'; break; } break; case 's' : switch ( param->value.ui ) { case 's' : network_slave_mode = 's'; break; case 'f' : network_slave_mode = 'f'; break; default : network_slave_mode = 's'; break; } break; } } Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver ( new Jack::JackNetDriver ( "system", "net_pcm", engine, table, multicast_ip, udp_port, mtu, midi_input_ports, midi_output_ports, name, transport_sync, network_master_mode, network_slave_mode ) ); if ( driver->Open ( period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports, monitor, "from_master_", "to_master_", 0, 0 ) == 0 ) return driver; delete driver; return NULL; } #ifdef __cplusplus } #endif }