git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2445 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.90
| @@ -215,6 +215,7 @@ env['INCLUDEDIR'] = env.subst(env['INCLUDEDIR']) | |||
| env['SERVER'] = 'jackd' | |||
| env['CLIENTLIB'] = 'jack' | |||
| env['SERVERLIB'] = 'jackserver' | |||
| env['NETMANAGERLIB'] = 'netmanager' | |||
| env['ADDON_DIR'] = env.subst(env['LIBDIR']) + "/jack" | |||
| env['INSTALL_ADDON_DIR'] = env['DESTDIR'] + env.subst(env['LIBDIR']) + "/jack" | |||
| @@ -63,16 +63,16 @@ class EXPORT JackAudioDriver : public JackDriver | |||
| virtual int Process(); | |||
| virtual int Open(jack_nframes_t nframes, | |||
| jack_nframes_t samplerate, | |||
| int capturing, | |||
| int 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); | |||
| jack_nframes_t samplerate, | |||
| int capturing, | |||
| int 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); | |||
| virtual int Attach(); | |||
| virtual int Detach(); | |||
| @@ -1,22 +1,22 @@ | |||
| /* | |||
| Copyright (C) 2001 Paul Davis | |||
| Copyright (C) 2004-2008 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. | |||
| */ | |||
| Copyright (C) 2001 Paul Davis | |||
| Copyright (C) 2004-2008 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 __JackDriver__ | |||
| #define __JackDriver__ | |||
| @@ -29,28 +29,28 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| namespace Jack | |||
| { | |||
| class JackEngineInterface; | |||
| class JackGraphManager; | |||
| struct JackEngineControl; | |||
| struct JackClientControl; | |||
| /*! | |||
| \brief The base interface for drivers. | |||
| */ | |||
| class EXPORT JackDriverInterface | |||
| { | |||
| public: | |||
| JackDriverInterface() | |||
| {} | |||
| virtual ~JackDriverInterface() | |||
| {} | |||
| virtual int Open() = 0; | |||
| virtual int Open(jack_nframes_t nframes, | |||
| jack_nframes_t samplerate, | |||
| bool capturing, | |||
| @@ -62,71 +62,71 @@ class EXPORT JackDriverInterface | |||
| const char* playback_driver_name, | |||
| jack_nframes_t capture_latency, | |||
| jack_nframes_t playback_latency) = 0; | |||
| virtual int Attach() = 0; | |||
| virtual int Detach() = 0; | |||
| virtual int Read() = 0; | |||
| virtual int Write() = 0; | |||
| virtual int Start() = 0; | |||
| virtual int Stop() = 0; | |||
| virtual int SetBufferSize(jack_nframes_t buffer_size) = 0; | |||
| virtual int SetSampleRate(jack_nframes_t sample_rate) = 0; | |||
| virtual int Process() = 0; | |||
| virtual void SetMaster(bool onoff) = 0; | |||
| virtual bool GetMaster() = 0; | |||
| virtual void AddSlave(JackDriverInterface* slave) = 0; | |||
| virtual void RemoveSlave(JackDriverInterface* slave) = 0; | |||
| virtual int ProcessSlaves() = 0; | |||
| virtual bool IsRealTime() = 0; | |||
| }; | |||
| /* | |||
| \brief The base interface for blocking drivers. | |||
| */ | |||
| \brief The base interface for blocking drivers. | |||
| */ | |||
| class EXPORT JackBlockingInterface | |||
| { | |||
| public: | |||
| JackBlockingInterface() | |||
| {} | |||
| virtual ~JackBlockingInterface() | |||
| {} | |||
| virtual bool Init() = 0; /* To be called by the wrapping thread Init method when the driver is a "blocking" one */ | |||
| }; | |||
| /*! | |||
| \brief The base interface for drivers clients. | |||
| */ | |||
| \brief The base interface for drivers clients. | |||
| */ | |||
| class EXPORT JackDriverClientInterface : public JackDriverInterface, public JackClientInterface | |||
| {}; | |||
| /*! | |||
| \brief The base class for drivers clients. | |||
| */ | |||
| \brief The base class for drivers clients. | |||
| */ | |||
| class EXPORT JackDriverClient : public JackDriverClientInterface, public JackBlockingInterface | |||
| { | |||
| private: | |||
| std::list<JackDriverInterface*> fSlaveList; | |||
| protected: | |||
| bool fIsMaster; | |||
| public: | |||
| virtual void SetMaster(bool onoff); | |||
| virtual bool GetMaster(); | |||
| virtual void AddSlave(JackDriverInterface* slave); | |||
| @@ -135,14 +135,14 @@ class EXPORT JackDriverClient : public JackDriverClientInterface, public JackBlo | |||
| }; | |||
| /*! | |||
| \brief The base class for drivers. | |||
| */ | |||
| \brief The base class for drivers. | |||
| */ | |||
| class EXPORT JackDriver : public JackDriverClient | |||
| { | |||
| protected: | |||
| char fCaptureDriverName[JACK_CLIENT_NAME_SIZE + 1]; | |||
| char fPlaybackDriverName[JACK_CLIENT_NAME_SIZE + 1]; | |||
| char fAliasName[JACK_CLIENT_NAME_SIZE + 1]; | |||
| @@ -155,20 +155,20 @@ class EXPORT JackDriver : public JackDriverClient | |||
| JackSynchro* fSynchroTable; | |||
| JackEngineControl* fEngineControl; | |||
| JackClientControl* fClientControl; | |||
| JackClientControl* GetClientControl() const; | |||
| JackClientControl* GetClientControl() const; | |||
| void CycleIncTime(); | |||
| void CycleTakeTime(); | |||
| public: | |||
| JackDriver(const char* name, const char* alias, JackEngineInterface* engine, JackSynchro* table); | |||
| JackDriver(); | |||
| virtual ~JackDriver(); | |||
| virtual int Open(); | |||
| virtual int Open(jack_nframes_t nframes, | |||
| jack_nframes_t samplerate, | |||
| bool capturing, | |||
| @@ -180,14 +180,14 @@ class EXPORT JackDriver : public JackDriverClient | |||
| const char* playback_driver_name, | |||
| jack_nframes_t capture_latency, | |||
| jack_nframes_t playback_latency); | |||
| virtual int Close(); | |||
| virtual int Process() | |||
| { | |||
| return 0; | |||
| } | |||
| virtual int Attach() | |||
| { | |||
| return 0; | |||
| @@ -196,7 +196,7 @@ class EXPORT JackDriver : public JackDriverClient | |||
| { | |||
| return 0; | |||
| } | |||
| virtual int Read() | |||
| { | |||
| return 0; | |||
| @@ -205,7 +205,7 @@ class EXPORT JackDriver : public JackDriverClient | |||
| { | |||
| return 0; | |||
| } | |||
| virtual int Start() | |||
| { | |||
| return 0; | |||
| @@ -214,32 +214,32 @@ class EXPORT JackDriver : public JackDriverClient | |||
| { | |||
| return 0; | |||
| } | |||
| virtual int SetBufferSize(jack_nframes_t buffer_size) | |||
| { | |||
| return 0; | |||
| } | |||
| virtual int SetSampleRate(jack_nframes_t sample_rate) | |||
| { | |||
| return 0; | |||
| } | |||
| void NotifyXRun(jack_time_t callback_usecs, float delayed_usecs); // XRun notification sent by the driver | |||
| virtual bool IsRealTime(); | |||
| int ClientNotify(int refnum, const char* name, int notify, int sync, int value1, int value2); | |||
| void SetupDriverSync(int ref, bool freewheel); | |||
| virtual bool Init() | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| } // end of namespace | |||
| #endif | |||
| @@ -60,69 +60,4 @@ namespace Jack | |||
| }; | |||
| } | |||
| #endif | |||
| /* | |||
| Copyright (C) 2008 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 __JackException__ | |||
| #define __JackException__ | |||
| #include <stdexcept> | |||
| #include <iostream> | |||
| #include <string> | |||
| #include "JackError.h" | |||
| namespace Jack | |||
| { | |||
| class JackException : public std::runtime_error { | |||
| public: | |||
| JackException(const std::string& msg) : runtime_error(msg) | |||
| {} | |||
| JackException(const char* msg) : runtime_error(msg) | |||
| {} | |||
| std::string Message() | |||
| { | |||
| return what(); | |||
| } | |||
| void PrintMessage() | |||
| { | |||
| std::string str = what(); | |||
| jack_error(str.c_str()); | |||
| } | |||
| }; | |||
| class JackDriverException : public JackException { | |||
| public: | |||
| JackDriverException(const std::string& msg) : JackException(msg) | |||
| {} | |||
| JackDriverException(const char* msg) : JackException(msg) | |||
| {} | |||
| }; | |||
| } | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,690 @@ | |||
| /* | |||
| Copyright (C) 2001 Paul Davis | |||
| Copyright (C) 2004-2008 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 "driver_interface.h" | |||
| #include "JackDriverLoader.h" | |||
| #include "JackThreadedDriver.h" | |||
| #include "JackException.h" | |||
| #define DEFAULT_MULTICAST_IP "225.3.19.154" | |||
| #define DEFAULT_PORT 19000 | |||
| namespace Jack | |||
| { | |||
| JackNetDriver::JackNetDriver ( const char* name, const char* alias, JackEngineInterface* engine, JackSynchro* table, | |||
| char* ip, size_t port, int midi_input_ports, int midi_output_ports, char* net_name ) | |||
| : JackAudioDriver ( name, alias, engine, table ) | |||
| { | |||
| fMulticastIP = new char[strlen ( ip ) + 1]; | |||
| strcpy ( fMulticastIP, ip ); | |||
| fPort = port; | |||
| fParams.fSendMidiChannels = midi_input_ports; | |||
| fParams.fReturnMidiChannels = midi_output_ports; | |||
| strcpy ( fParams.fName, net_name ); | |||
| fSockfd = 0; | |||
| } | |||
| JackNetDriver::~JackNetDriver() | |||
| { | |||
| if ( fSockfd ) | |||
| close ( fSockfd ); | |||
| delete fNetAudioCaptureBuffer; | |||
| delete fNetAudioPlaybackBuffer; | |||
| delete fNetMidiCaptureBuffer; | |||
| delete fNetMidiPlaybackBuffer; | |||
| delete[] fTxBuffer; | |||
| delete[] fRxBuffer; | |||
| delete[] fMulticastIP; | |||
| delete[] fMidiCapturePortList; | |||
| delete[] fMidiPlaybackPortList; | |||
| } | |||
| //*************************************initialization*********************************************************************** | |||
| int JackNetDriver::Open ( jack_nframes_t nframes, 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 ( nframes, 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; | |||
| } | |||
| int JackNetDriver::Attach() | |||
| { | |||
| return 0; | |||
| } | |||
| int JackNetDriver::Detach() | |||
| { | |||
| return 0; | |||
| } | |||
| bool JackNetDriver::Init() | |||
| { | |||
| jack_log ( "JackNetDriver::Init()" ); | |||
| if ( fSockfd ) | |||
| Restart(); | |||
| //set the parameters to send | |||
| strcpy ( fParams.fPacketType, "params" ); | |||
| fParams.fProtocolVersion = 'a'; | |||
| SetPacketType ( &fParams, SLAVE_AVAILABLE ); | |||
| gethostname ( fParams.fSlaveNetName, 255 ); | |||
| fParams.fSendAudioChannels = fCaptureChannels; | |||
| fParams.fReturnAudioChannels = fPlaybackChannels; | |||
| //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 == SOCKET_ERROR ) | |||
| return false; | |||
| } | |||
| while ( status != CONNECTED ); | |||
| //then tell the master we are ready | |||
| jack_info ( "Initializing connection with %s...", fParams.fMasterNetName ); | |||
| status = SendMasterStartSync(); | |||
| } | |||
| while ( status != 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; | |||
| struct sockaddr_in mcast_addr, listen_addr; | |||
| struct timeval rcv_timeout; | |||
| rcv_timeout.tv_sec = 2; | |||
| rcv_timeout.tv_usec = 0; | |||
| socklen_t addr_len = sizeof ( socket_address_t ); | |||
| int rx_bytes = 0; | |||
| //set the multicast address | |||
| mcast_addr.sin_family = AF_INET; | |||
| mcast_addr.sin_port = htons ( fPort ); | |||
| inet_aton ( fMulticastIP, &mcast_addr.sin_addr ); | |||
| memset ( &mcast_addr.sin_zero, 0, 8 ); | |||
| //set the listening address | |||
| listen_addr.sin_family = AF_INET; | |||
| listen_addr.sin_port = htons ( fPort ); | |||
| listen_addr.sin_addr.s_addr = htonl ( INADDR_ANY ); | |||
| memset ( &listen_addr.sin_zero, 0, 8 ); | |||
| //set the master address family | |||
| fMasterAddr.sin_family = AF_INET; | |||
| //socket | |||
| if ( fSockfd ) | |||
| close ( fSockfd ); | |||
| if ( ( fSockfd = socket ( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) | |||
| { | |||
| jack_error ( "Fatal error : network unreachable - %s", strerror ( errno ) ); | |||
| return SOCKET_ERROR; | |||
| } | |||
| //bind the socket | |||
| if ( bind ( fSockfd, reinterpret_cast<socket_address_t*> ( &listen_addr ), addr_len ) < 0 ) | |||
| jack_error ( "Can't bind the socket : %s", strerror ( errno ) ); | |||
| //timeout on receive | |||
| setsockopt ( fSockfd, SOL_SOCKET, SO_RCVTIMEO, &rcv_timeout, sizeof ( rcv_timeout ) ); | |||
| //send 'AVAILABLE' until 'SLAVE_SETUP' received | |||
| jack_info ( "Waiting for a master..." ); | |||
| do | |||
| { | |||
| //send 'available' | |||
| if ( sendto ( fSockfd, &fParams, sizeof ( session_params_t ), MSG_DONTWAIT, | |||
| reinterpret_cast<socket_address_t*> ( &mcast_addr ), addr_len ) < 0 ) | |||
| jack_error ( "Error in data send : %s", strerror ( errno ) ); | |||
| //filter incoming packets : don't exit while receiving wrong packets | |||
| do | |||
| { | |||
| rx_bytes = recvfrom ( fSockfd, ¶ms, sizeof ( session_params_t ), 0, | |||
| reinterpret_cast<socket_address_t*> ( &fMasterAddr ), &addr_len ); | |||
| if ( ( rx_bytes < 0 ) && ( errno != EAGAIN ) ) | |||
| { | |||
| jack_error ( "Can't receive : %s", strerror ( errno ) ); | |||
| return RECV_ERROR; | |||
| } | |||
| } | |||
| while ( ( rx_bytes > 0 ) && strcmp ( params.fPacketType, fParams.fPacketType ) ); | |||
| } | |||
| while ( ( GetPacketType ( ¶ms ) != SLAVE_SETUP ) ); | |||
| //connect the socket | |||
| if ( connect ( fSockfd, reinterpret_cast<socket_address_t*> ( &fMasterAddr ), sizeof ( socket_address_t ) ) < 0 ) | |||
| { | |||
| jack_error ( "Error in connect : %s", strerror ( errno ) ); | |||
| return CONNECT_ERROR; | |||
| } | |||
| //everything is OK, copy parameters and return | |||
| fParams = params; | |||
| return CONNECTED; | |||
| } | |||
| net_status_t JackNetDriver::SendMasterStartSync() | |||
| { | |||
| jack_log ( "JackNetDriver::GetNetMasterStartSync()" ); | |||
| //tell the master to start | |||
| SetPacketType ( &fParams, START_MASTER ); | |||
| if ( send ( fSockfd, &fParams, sizeof ( session_params_t ), MSG_DONTWAIT ) < 0 ) | |||
| jack_error ( "Error in send : %s", strerror ( errno ) ); | |||
| return ROLLING; | |||
| } | |||
| void JackNetDriver::Restart() | |||
| { | |||
| jack_info ( "Restarting driver..." ); | |||
| close ( fSockfd ); | |||
| delete[] fTxBuffer; | |||
| delete[] fRxBuffer; | |||
| delete fNetAudioCaptureBuffer; | |||
| delete fNetAudioPlaybackBuffer; | |||
| delete fNetMidiCaptureBuffer; | |||
| delete fNetMidiPlaybackBuffer; | |||
| FreePorts(); | |||
| delete[] fMidiCapturePortList; | |||
| delete[] fMidiPlaybackPortList; | |||
| fTxBuffer = NULL; | |||
| fRxBuffer = NULL; | |||
| fNetAudioCaptureBuffer = NULL; | |||
| fNetAudioCaptureBuffer = NULL; | |||
| fNetMidiCaptureBuffer = NULL; | |||
| fNetMidiPlaybackBuffer = NULL; | |||
| fMidiCapturePortList = NULL; | |||
| fMidiPlaybackPortList = NULL; | |||
| } | |||
| int JackNetDriver::SetParams() | |||
| { | |||
| fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; | |||
| SetBufferSize ( fParams.fPeriodSize ); | |||
| SetSampleRate ( 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(); | |||
| 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; | |||
| //audio | |||
| port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; | |||
| for ( int port_index = 0; port_index < fCaptureChannels; port_index++ ) | |||
| { | |||
| snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, port_index + 1 ); | |||
| snprintf ( name, sizeof ( name ) - 1, "%s:capture_%d", fClientControl->fName, port_index + 1 ); | |||
| if ( ( port_id = fGraphManager->AllocatePort ( fClientControl->fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, | |||
| static_cast<JackPortFlags> ( 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 + fCaptureLatency ); | |||
| fCapturePortList[port_index] = port_id; | |||
| jack_log ( "JackNetDriver::AllocPorts() fCapturePortList[%d] port_index = %ld", port_index, port_id ); | |||
| } | |||
| port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; | |||
| for ( int port_index = 0; port_index < fPlaybackChannels; port_index++ ) | |||
| { | |||
| snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, port_index + 1 ); | |||
| snprintf ( name, sizeof ( name ) - 1, "%s:playback_%d",fClientControl->fName, port_index + 1 ); | |||
| if ( ( port_id = fGraphManager->AllocatePort ( fClientControl->fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, | |||
| static_cast<JackPortFlags> ( 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 ) + fPlaybackLatency ); | |||
| fPlaybackPortList[port_index] = port_id; | |||
| jack_log ( "JackNetDriver::AllocPorts() fPlaybackPortList[%d] port_index = %ld", port_index, port_id ); | |||
| } | |||
| //midi | |||
| port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| { | |||
| snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, port_index + 1 ); | |||
| snprintf ( name, sizeof ( name ) - 1, "%s:midi_capture_%d", fClientControl->fName, port_index + 1 ); | |||
| if ( ( port_id = fGraphManager->AllocatePort ( fClientControl->fRefNum, name, JACK_DEFAULT_MIDI_TYPE, | |||
| static_cast<JackPortFlags> ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) | |||
| { | |||
| jack_error ( "driver: cannot register port for %s", name ); | |||
| return -1; | |||
| } | |||
| fMidiCapturePortList[port_index] = port_id; | |||
| jack_log ( "JackNetDriver::AllocPorts() fMidiCapturePortList[%d] port_index = %ld", port_index, port_id ); | |||
| } | |||
| port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| { | |||
| snprintf ( alias, sizeof ( alias ) - 1, "%s:%s:in%d", fAliasName, fPlaybackDriverName, port_index + 1 ); | |||
| snprintf ( name, sizeof ( name ) - 1, "%s:midi_playback_%d", fClientControl->fName, port_index + 1 ); | |||
| if ( ( port_id = fGraphManager->AllocatePort ( fClientControl->fRefNum, name, JACK_DEFAULT_MIDI_TYPE, | |||
| static_cast<JackPortFlags> ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) | |||
| { | |||
| jack_error ( "driver: cannot register port for %s", name ); | |||
| return -1; | |||
| } | |||
| fMidiPlaybackPortList[port_index] = port_id; | |||
| jack_log ( "JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] port_index = %ld", port_index, port_id ); | |||
| } | |||
| return 0; | |||
| } | |||
| int JackNetDriver::FreePorts() | |||
| { | |||
| jack_log ( "JackNetDriver::FreePorts" ); | |||
| for ( int port_index = 0; port_index < fCaptureChannels; port_index++ ) | |||
| fGraphManager->ReleasePort ( fClientControl->fRefNum, fCapturePortList[port_index] ); | |||
| for ( int port_index = 0; port_index < fPlaybackChannels; port_index++ ) | |||
| fGraphManager->ReleasePort ( fClientControl->fRefNum, fPlaybackPortList[port_index] ); | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| fGraphManager->ReleasePort ( fClientControl->fRefNum, fMidiCapturePortList[port_index] ); | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| fGraphManager->ReleasePort ( fClientControl->fRefNum, fMidiPlaybackPortList[port_index] ); | |||
| return 0; | |||
| } | |||
| JackMidiBuffer* JackNetDriver::GetMidiInputBuffer ( int port_index ) | |||
| { | |||
| return static_cast<JackMidiBuffer*> ( fGraphManager->GetBuffer ( fMidiCapturePortList[port_index], fEngineControl->fBufferSize ) ); | |||
| } | |||
| JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer ( int port_index ) | |||
| { | |||
| return static_cast<JackMidiBuffer*> ( fGraphManager->GetBuffer ( fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize ) ); | |||
| } | |||
| int JackNetDriver::Recv ( size_t size, int flags ) | |||
| { | |||
| int rx_bytes; | |||
| if ( ( rx_bytes = recv ( fSockfd, fRxBuffer, size, flags ) ) < 0 ) | |||
| { | |||
| if ( errno == EAGAIN ) | |||
| { | |||
| jack_error ( "No incoming data, is the master still running ?" ); | |||
| return 0; | |||
| } | |||
| else if ( ( errno == ECONNABORTED ) || ( errno == ECONNREFUSED ) || ( errno == ECONNRESET ) ) | |||
| { | |||
| jack_error ( "Fatal error : %s.", strerror ( errno ) ); | |||
| throw JackDriverException ( "" ); | |||
| return -1; | |||
| } | |||
| else | |||
| { | |||
| jack_error ( "Error in receive : %s", strerror ( errno ) ); | |||
| return 0; | |||
| } | |||
| } | |||
| return rx_bytes; | |||
| } | |||
| int JackNetDriver::Send ( size_t size, int flags ) | |||
| { | |||
| int tx_bytes; | |||
| if ( ( tx_bytes = send ( fSockfd, fTxBuffer, size, flags ) ) < 0 ) | |||
| { | |||
| if ( ( errno == ECONNABORTED ) || ( errno == ECONNREFUSED ) || ( errno == ECONNRESET ) ) | |||
| { | |||
| jack_error ( "Fatal error : %s.", strerror ( errno ) ); | |||
| throw JackDriverException ( "" ); | |||
| return -1; | |||
| } | |||
| else | |||
| jack_error ( "Error in send : %s", strerror ( errno ) ); | |||
| } | |||
| return tx_bytes; | |||
| } | |||
| //*************************************process************************************************************************ | |||
| int JackNetDriver::Read() | |||
| { | |||
| int rx_bytes; | |||
| size_t recvd_midi_pckt = 0; | |||
| packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
| fRxHeader.fIsLastPckt = 'n'; | |||
| //buffers | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| fNetMidiCaptureBuffer->fPortBuffer[port_index] = GetMidiInputBuffer ( port_index ); | |||
| for ( int port_index = 0; port_index < fCaptureChannels; port_index++ ) | |||
| fNetAudioCaptureBuffer->fPortBuffer[port_index] = GetInputBuffer ( port_index ); | |||
| //receive sync (launch the cycle) | |||
| do | |||
| { | |||
| if ( ( rx_bytes = Recv ( sizeof ( packet_header_t ), 0 ) ) < 1 ) | |||
| return rx_bytes; | |||
| } | |||
| while ( !rx_bytes && ( rx_head->fDataType != 's' ) ); | |||
| JackDriver::CycleTakeTime(); | |||
| //audio, midi or sync if driver is late | |||
| if ( fParams.fSendMidiChannels || fParams.fSendAudioChannels ) | |||
| { | |||
| do | |||
| { | |||
| rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); | |||
| if ( rx_bytes < 1 ) | |||
| 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, MSG_DONTWAIT ); | |||
| 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, MSG_DONTWAIT ); | |||
| 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; | |||
| return 0; | |||
| } | |||
| int JackNetDriver::Write() | |||
| { | |||
| int tx_bytes, copy_size; | |||
| fTxHeader.fCycle = fRxHeader.fCycle; | |||
| fTxHeader.fSubCycle = 0; | |||
| fTxHeader.fIsLastPckt = 'n'; | |||
| //buffers | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| fNetMidiPlaybackBuffer->fPortBuffer[port_index] = GetMidiOutputBuffer ( port_index ); | |||
| for ( int port_index = 0; port_index < fPlaybackChannels; port_index++ ) | |||
| fNetAudioPlaybackBuffer->fPortBuffer[port_index] = GetOutputBuffer ( port_index ); | |||
| //midi | |||
| if ( fParams.fReturnMidiChannels ) | |||
| { | |||
| fTxHeader.fDataType = 'm'; | |||
| fTxHeader.fMidiDataSize = fNetMidiPlaybackBuffer->RenderFromJackPorts(); | |||
| fTxHeader.fNMidiPckt = GetNMidiPckt ( &fParams, fTxHeader.fMidiDataSize ); | |||
| for ( size_t 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, MSG_DONTWAIT ); | |||
| } | |||
| } | |||
| //audio | |||
| if ( fParams.fReturnAudioChannels ) | |||
| { | |||
| fTxHeader.fDataType = 'a'; | |||
| for ( size_t 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, MSG_DONTWAIT ); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| //*************************************loader******************************************************* | |||
| #ifdef __cplusplus | |||
| extern "C" | |||
| { | |||
| #endif | |||
| 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 = 7; | |||
| desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); | |||
| size_t 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 = JackDriverParamUInt; | |||
| desc->params[i].value.ui = 19000U; | |||
| 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, "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 = JackDriverParamUInt; | |||
| 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 ); | |||
| return desc; | |||
| } | |||
| Jack::JackDriverClientInterface* driver_initialize ( Jack::JackEngineInterface* engine, Jack::JackSynchro* table, const JSList* params ) | |||
| { | |||
| char* multicast_ip = DEFAULT_MULTICAST_IP; | |||
| char name[JACK_CLIENT_NAME_SIZE]; | |||
| gethostname ( name, JACK_CLIENT_NAME_SIZE ); | |||
| jack_nframes_t udp_port = DEFAULT_PORT; | |||
| 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; | |||
| 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 '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 ); | |||
| } | |||
| } | |||
| Jack::JackDriverClientInterface* driver = new Jack::JackRestartThreadedDriver ( | |||
| new Jack::JackNetDriver ( "system", "net_pcm", engine, table, multicast_ip, udp_port, midi_input_ports, midi_output_ports, name ) ); | |||
| 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 | |||
| } | |||
| @@ -0,0 +1,88 @@ | |||
| /* | |||
| Copyright (C) 2001 Paul Davis | |||
| Copyright (C) 2004-2008 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 __JackNetDriver__ | |||
| #define __JackNetDriver__ | |||
| #include "JackAudioDriver.h" | |||
| #include "JackNetTool.h" | |||
| namespace Jack | |||
| { | |||
| class JackNetDriver : public JackAudioDriver | |||
| { | |||
| private: | |||
| session_params_t fParams; | |||
| char* fMulticastIP; | |||
| size_t fPort; | |||
| int fSockfd; | |||
| struct sockaddr_in fMasterAddr; | |||
| size_t fNSubProcess; | |||
| jack_port_id_t* fMidiCapturePortList; | |||
| jack_port_id_t* fMidiPlaybackPortList; | |||
| packet_header_t fTxHeader; | |||
| packet_header_t fRxHeader; | |||
| char* fTxBuffer; | |||
| char* fRxBuffer; | |||
| char* fTxData; | |||
| char* fRxData; | |||
| NetMidiBuffer* fNetMidiCaptureBuffer; | |||
| NetMidiBuffer* fNetMidiPlaybackBuffer; | |||
| NetAudioBuffer* fNetAudioCaptureBuffer; | |||
| NetAudioBuffer* fNetAudioPlaybackBuffer; | |||
| int fAudioRxLen; | |||
| int fAudioTxLen; | |||
| int Attach(); | |||
| int Detach(); | |||
| bool Init(); | |||
| net_status_t GetNetMaster(); | |||
| net_status_t SendMasterStartSync(); | |||
| void Restart(); | |||
| int SetParams(); | |||
| int AllocPorts(); | |||
| int FreePorts(); | |||
| 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 Read(); | |||
| int Write(); | |||
| public: | |||
| JackNetDriver ( const char* name, const char* alias, JackEngineInterface* engine, JackSynchro* table, | |||
| char* ip, size_t port, int midi_input_ports, int midi_output_ports, char* master_name ); | |||
| ~JackNetDriver(); | |||
| int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, 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 ); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,673 @@ | |||
| /*************************************************************************** | |||
| * Copyright (C) 2008 by Romain Moret * | |||
| * moret@grame.fr * | |||
| * * | |||
| * 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., * | |||
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |||
| ***************************************************************************/ | |||
| #include "JackNetManager.h" | |||
| #include "JackError.h" | |||
| #define DEFAULT_MULTICAST_IP "225.3.19.154" | |||
| #define DEFAULT_PORT 19000 | |||
| using namespace std; | |||
| namespace Jack | |||
| { | |||
| //JackNetMaster****************************************************************************************************** | |||
| JackNetMaster::JackNetMaster ( JackNetMasterManager* manager, session_params_t& params, struct sockaddr_in& address, struct sockaddr_in& mcast_addr ) | |||
| { | |||
| jack_log ( "JackNetMaster::JackNetMaster" ); | |||
| //settings | |||
| fMasterManager = manager; | |||
| fParams = params; | |||
| fAddr = address; | |||
| fMcastAddr = mcast_addr; | |||
| fNSubProcess = fParams.fPeriodSize / fParams.fFramesPerPacket; | |||
| fClientName = const_cast<char*> ( fParams.fName ); | |||
| fNetJumpCnt = 0; | |||
| fJackClient = NULL; | |||
| fSockfd = 0; | |||
| fRunning = false; | |||
| //jack audio ports | |||
| fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels]; | |||
| for ( int port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) | |||
| fAudioCapturePorts[port_index] = NULL; | |||
| fAudioPlaybackPorts = new jack_port_t* [fParams.fReturnAudioChannels]; | |||
| for ( int port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) | |||
| fAudioPlaybackPorts[port_index] = NULL; | |||
| //jack midi ports | |||
| fMidiCapturePorts = new jack_port_t* [fParams.fSendMidiChannels]; | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| fMidiCapturePorts[port_index] = NULL; | |||
| fMidiPlaybackPorts = new jack_port_t* [fParams.fReturnMidiChannels]; | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| fMidiPlaybackPorts[port_index] = NULL; | |||
| //TX header init | |||
| strcpy ( fTxHeader.fPacketType, "header" ); | |||
| fTxHeader.fDataStream = 's'; | |||
| 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 = 'r'; | |||
| 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 buffers | |||
| fTxData = fTxBuffer + sizeof ( packet_header_t ); | |||
| fRxData = fRxBuffer + sizeof ( packet_header_t ); | |||
| //midi net buffers | |||
| fNetMidiCaptureBuffer = new NetMidiBuffer ( &fParams, fParams.fSendMidiChannels, fTxData ); | |||
| fNetMidiPlaybackBuffer = new NetMidiBuffer ( &fParams, fParams.fReturnMidiChannels, fRxData ); | |||
| //audio net buffers | |||
| fNetAudioCaptureBuffer = new NetAudioBuffer ( &fParams, fParams.fSendAudioChannels, fTxData ); | |||
| fNetAudioPlaybackBuffer = new NetAudioBuffer ( &fParams, fParams.fReturnAudioChannels, fRxData ); | |||
| //audio netbuffer length | |||
| fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); | |||
| fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); | |||
| } | |||
| JackNetMaster::~JackNetMaster() | |||
| { | |||
| jack_log ( "JackNetMaster::~JackNetMaster, ID %u.", fParams.fID ); | |||
| if ( fJackClient ) | |||
| { | |||
| jack_deactivate ( fJackClient ); | |||
| FreePorts(); | |||
| jack_client_close ( fJackClient ); | |||
| } | |||
| if ( fSockfd ) | |||
| close ( fSockfd ); | |||
| delete fNetAudioCaptureBuffer; | |||
| delete fNetAudioPlaybackBuffer; | |||
| delete fNetMidiCaptureBuffer; | |||
| delete fNetMidiPlaybackBuffer; | |||
| delete[] fAudioCapturePorts; | |||
| delete[] fAudioPlaybackPorts; | |||
| delete[] fMidiCapturePorts; | |||
| delete[] fMidiPlaybackPorts; | |||
| delete[] fTxBuffer; | |||
| delete[] fRxBuffer; | |||
| } | |||
| bool JackNetMaster::Init() | |||
| { | |||
| jack_log ( "JackNetMaster::Init, ID %u.", fParams.fID ); | |||
| session_params_t params; | |||
| struct timeval timeout; | |||
| timeout.tv_sec = 1; | |||
| timeout.tv_usec = 0; | |||
| size_t attempt = 0; | |||
| int rx_bytes = 0; | |||
| //socket | |||
| if ( ( fSockfd = socket ( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) | |||
| { | |||
| jack_error ( "Can't create socket : %s", strerror ( errno ) ); | |||
| return false; | |||
| } | |||
| //timeout on receive (for init) | |||
| if ( setsockopt ( fSockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof ( timeout ) ) < 0 ) | |||
| jack_error ( "Can't set timeout : %s", strerror ( errno ) ); | |||
| //connect | |||
| if ( connect ( fSockfd, reinterpret_cast<sockaddr*> ( &fAddr ), sizeof ( struct sockaddr ) ) < 0 ) | |||
| { | |||
| jack_error ( "Can't connect : %s", strerror ( errno ) ); | |||
| return false; | |||
| } | |||
| //send 'SLAVE_SETUP' until 'START_MASTER' received | |||
| jack_info ( "Sending parameters to %s ...", fParams.fSlaveNetName ); | |||
| do | |||
| { | |||
| SetPacketType ( &fParams, SLAVE_SETUP ); | |||
| if ( send ( fSockfd, &fParams, sizeof ( session_params_t ), 0 ) < 0 ) | |||
| jack_error ( "Error in send : ", strerror ( errno ) ); | |||
| if ( ( ( rx_bytes = recv ( fSockfd, ¶ms, sizeof ( session_params_t ), 0 ) ) < 0 ) && ( errno != EAGAIN ) ) | |||
| { | |||
| jack_error ( "Problem with network." ); | |||
| return false; | |||
| } | |||
| } | |||
| while ( ( GetPacketType ( ¶ms ) != START_MASTER ) && ( ++attempt < 5 ) ); | |||
| if ( attempt == 5 ) | |||
| { | |||
| jack_error ( "Slave doesn't respond, exiting." ); | |||
| return false; | |||
| } | |||
| //set the new timeout for the socket | |||
| if ( SetRxTimeout ( &fSockfd, &fParams ) < 0 ) | |||
| { | |||
| jack_error ( "Can't set rx timeout : %s", strerror ( errno ) ); | |||
| return false; | |||
| } | |||
| //jack client and process | |||
| jack_status_t status; | |||
| jack_options_t options = JackNullOption; | |||
| if ( ( fJackClient = jack_client_open ( fClientName, options, &status, NULL ) ) == NULL ) | |||
| { | |||
| jack_error ( "Can't open a new jack client." ); | |||
| return false; | |||
| } | |||
| jack_set_process_callback ( fJackClient, SetProcess, this ); | |||
| //port registering | |||
| int i; | |||
| char name[24]; | |||
| //audio | |||
| for ( i = 0; i < fParams.fSendAudioChannels; i++ ) | |||
| { | |||
| sprintf ( name, "to_slave_%d", i+1 ); | |||
| if ( ( fAudioCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ) ) == NULL ) | |||
| goto fail; | |||
| } | |||
| for ( i = 0; i < fParams.fReturnAudioChannels; i++ ) | |||
| { | |||
| sprintf ( name, "from_slave_%d", i+1 ); | |||
| if ( ( fAudioPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ) ) == NULL ) | |||
| goto fail; | |||
| } | |||
| //midi | |||
| for ( i = 0; i < fParams.fSendMidiChannels; i++ ) | |||
| { | |||
| sprintf ( name, "midi_to_slave_%d", i+1 ); | |||
| if ( ( fMidiCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ) ) == NULL ) | |||
| goto fail; | |||
| } | |||
| for ( i = 0; i < fParams.fReturnMidiChannels; i++ ) | |||
| { | |||
| sprintf ( name, "midi_from_slave_%d", i+1 ); | |||
| if ( ( fMidiPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ) ) == NULL ) | |||
| goto fail; | |||
| } | |||
| fRunning = true; | |||
| //finally activate jack client | |||
| if ( jack_activate ( fJackClient ) != 0 ) | |||
| { | |||
| jack_error ( "Can't activate jack client." ); | |||
| goto fail; | |||
| } | |||
| jack_info ( "NetJack new master started." ); | |||
| return true; | |||
| fail: | |||
| FreePorts(); | |||
| jack_client_close ( fJackClient ); | |||
| fJackClient = NULL; | |||
| return false; | |||
| } | |||
| void JackNetMaster::FreePorts() | |||
| { | |||
| jack_log ( "JackNetMaster::FreePorts, ID %u", fParams.fID ); | |||
| for ( int port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) | |||
| if ( fAudioCapturePorts[port_index] ) | |||
| jack_port_unregister ( fJackClient, fAudioCapturePorts[port_index] ); | |||
| for ( int port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) | |||
| if ( fAudioPlaybackPorts[port_index] ) | |||
| jack_port_unregister ( fJackClient, fAudioPlaybackPorts[port_index] ); | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| if ( fMidiCapturePorts[port_index] ) | |||
| jack_port_unregister ( fJackClient, fMidiCapturePorts[port_index] ); | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| if ( fMidiPlaybackPorts[port_index] ) | |||
| jack_port_unregister ( fJackClient, fMidiPlaybackPorts[port_index] ); | |||
| } | |||
| void JackNetMaster::Exit() | |||
| { | |||
| jack_log ( "JackNetMaster::Exit, ID %u", fParams.fID ); | |||
| //stop process | |||
| fRunning = false; | |||
| //send a 'multicast euthanasia request' - new socket is required on macosx | |||
| jack_info ( "Exiting '%s'", fParams.fName ); | |||
| SetPacketType ( &fParams, KILL_MASTER ); | |||
| int mcast_sockfd = socket ( AF_INET, SOCK_DGRAM, 0 ); | |||
| if ( mcast_sockfd < 0 ) | |||
| jack_error ( "Can't create socket : %s", strerror ( errno ) ); | |||
| if ( sendto ( mcast_sockfd, &fParams, sizeof ( session_params_t ), MSG_DONTWAIT, | |||
| reinterpret_cast<socket_address_t*> ( &fMcastAddr ), sizeof ( socket_address_t ) ) < 0 ) | |||
| jack_error ( "Can't send suicide request : %s", strerror ( errno ) ); | |||
| close ( mcast_sockfd ); | |||
| } | |||
| int JackNetMaster::Send ( char* buffer, size_t size, int flags ) | |||
| { | |||
| int tx_bytes; | |||
| if ( ( tx_bytes = send ( fSockfd, buffer, size, flags ) ) < 0 ) | |||
| { | |||
| if ( ( errno == ECONNABORTED ) || ( errno == ECONNREFUSED ) || ( errno == ECONNRESET ) ) | |||
| { | |||
| //fatal connection issue, exit | |||
| jack_error ( "'%s' : %s, please check network connection with '%s'.", | |||
| fParams.fName, strerror ( errno ), fParams.fSlaveNetName ); | |||
| Exit(); | |||
| return 0; | |||
| } | |||
| else | |||
| jack_error ( "Error in send : %s", strerror ( errno ) ); | |||
| } | |||
| return tx_bytes; | |||
| } | |||
| int JackNetMaster::Recv ( size_t size, int flags ) | |||
| { | |||
| int rx_bytes; | |||
| if ( ( rx_bytes = recv ( fSockfd, fRxBuffer, size, flags ) ) < 0 ) | |||
| { | |||
| if ( errno == EAGAIN ) | |||
| { | |||
| //too much receive failure, react... | |||
| if ( ++fNetJumpCnt == 100 ) | |||
| { | |||
| jack_error ( "Connection lost, is %s still running ?", fParams.fName ); | |||
| fNetJumpCnt = 0; | |||
| } | |||
| return 0; | |||
| } | |||
| else if ( ( errno == ECONNABORTED ) || ( errno == ECONNREFUSED ) || ( errno == ECONNRESET ) ) | |||
| { | |||
| //fatal connection issue, exit | |||
| jack_error ( "'%s' : %s, please check network connection with '%s'.", | |||
| fParams.fName, strerror ( errno ), fParams.fSlaveNetName ); | |||
| Exit(); | |||
| return 0; | |||
| } | |||
| else if ( errno != EAGAIN ) | |||
| jack_error ( "Error in receive : %s", strerror ( errno ) ); | |||
| } | |||
| return rx_bytes; | |||
| } | |||
| int JackNetMaster::SetProcess ( jack_nframes_t nframes, void* arg ) | |||
| { | |||
| JackNetMaster* master = static_cast<JackNetMaster*> ( arg ); ; | |||
| return master->Process(); | |||
| } | |||
| int JackNetMaster::Process() | |||
| { | |||
| if ( !fRunning ) | |||
| return 0; | |||
| int tx_bytes, rx_bytes, copy_size; | |||
| size_t midi_recvd_pckt = 0; | |||
| fTxHeader.fCycle++; | |||
| fTxHeader.fSubCycle = 0; | |||
| fTxHeader.fIsLastPckt = 'n'; | |||
| packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer ); | |||
| //buffers | |||
| for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) | |||
| fNetMidiCaptureBuffer->fPortBuffer[port_index] = | |||
| static_cast<JackMidiBuffer*> ( jack_port_get_buffer ( fMidiCapturePorts[port_index], fParams.fPeriodSize ) ); | |||
| for ( int port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) | |||
| fNetAudioCaptureBuffer->fPortBuffer[port_index] = | |||
| static_cast<sample_t*> ( jack_port_get_buffer ( fAudioCapturePorts[port_index], fParams.fPeriodSize ) ); | |||
| for ( int port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) | |||
| fNetMidiPlaybackBuffer->fPortBuffer[port_index] = | |||
| static_cast<JackMidiBuffer*> ( jack_port_get_buffer ( fMidiPlaybackPorts[port_index], fParams.fPeriodSize ) ); | |||
| for ( int port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) | |||
| fNetAudioPlaybackBuffer->fPortBuffer[port_index] = | |||
| static_cast<sample_t*> ( jack_port_get_buffer ( fAudioPlaybackPorts[port_index], fParams.fPeriodSize ) ); | |||
| //send ------------------------------------------------------------------------------------------------------------------ | |||
| //sync | |||
| fTxHeader.fDataType = 's'; | |||
| if ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels ) | |||
| fTxHeader.fIsLastPckt = 'y'; | |||
| tx_bytes = Send ( reinterpret_cast<char*> ( &fTxHeader ), sizeof ( packet_header_t ), MSG_DONTWAIT ); | |||
| if ( tx_bytes < 1 ) | |||
| return tx_bytes; | |||
| //midi | |||
| if ( fParams.fSendMidiChannels ) | |||
| { | |||
| fTxHeader.fDataType = 'm'; | |||
| fTxHeader.fMidiDataSize = fNetMidiCaptureBuffer->RenderFromJackPorts(); | |||
| fTxHeader.fNMidiPckt = GetNMidiPckt ( &fParams, fTxHeader.fMidiDataSize ); | |||
| for ( size_t subproc = 0; subproc < fTxHeader.fNMidiPckt; subproc++ ) | |||
| { | |||
| fTxHeader.fSubCycle = subproc; | |||
| if ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && !fParams.fSendAudioChannels ) | |||
| fTxHeader.fIsLastPckt = 'y'; | |||
| memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
| copy_size = fNetMidiCaptureBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize ); | |||
| tx_bytes = Send ( fTxBuffer, sizeof ( packet_header_t ) + copy_size, MSG_DONTWAIT ); | |||
| if ( tx_bytes < 1 ) | |||
| return tx_bytes; | |||
| } | |||
| } | |||
| //audio | |||
| if ( fParams.fSendAudioChannels ) | |||
| { | |||
| fTxHeader.fDataType = 'a'; | |||
| for ( size_t subproc = 0; subproc < fNSubProcess; subproc++ ) | |||
| { | |||
| fTxHeader.fSubCycle = subproc; | |||
| if ( subproc == ( fNSubProcess - 1 ) ) | |||
| fTxHeader.fIsLastPckt = 'y'; | |||
| memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) ); | |||
| fNetAudioCaptureBuffer->RenderFromJackPorts ( subproc ); | |||
| tx_bytes = Send ( fTxBuffer, fAudioTxLen, MSG_DONTWAIT ); | |||
| if ( tx_bytes < 1 ) | |||
| return tx_bytes; | |||
| } | |||
| } | |||
| //receive ( if there is stg to receive...)------------------------------------------------------------------------------------- | |||
| if ( fParams.fReturnMidiChannels || fParams.fReturnAudioChannels ) | |||
| { | |||
| do | |||
| { | |||
| rx_bytes = Recv ( fParams.fMtu, MSG_PEEK ); | |||
| if ( rx_bytes < 1 ) | |||
| return rx_bytes; | |||
| if ( rx_bytes && ( rx_head->fDataStream == 'r' ) && ( rx_head->fID == fParams.fID ) ) | |||
| { | |||
| switch ( rx_head->fDataType ) | |||
| { | |||
| case 'm': //midi | |||
| rx_bytes = Recv ( rx_bytes, MSG_DONTWAIT ); | |||
| fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
| fNetMidiPlaybackBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) ); | |||
| if ( ++midi_recvd_pckt == rx_head->fNMidiPckt ) | |||
| fNetMidiPlaybackBuffer->RenderToJackPorts(); | |||
| fNetJumpCnt = 0; | |||
| break; | |||
| case 'a': //audio | |||
| rx_bytes = Recv ( fAudioRxLen, MSG_DONTWAIT ); | |||
| if ( !IsNextPacket ( &fRxHeader, rx_head, fNSubProcess ) ) | |||
| jack_error ( "Packet(s) missing from '%s'...", fParams.fName ); | |||
| fRxHeader.fCycle = rx_head->fCycle; | |||
| fRxHeader.fSubCycle = rx_head->fSubCycle; | |||
| fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; | |||
| fNetAudioPlaybackBuffer->RenderToJackPorts ( rx_head->fSubCycle ); | |||
| fNetJumpCnt = 0; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| while ( fRxHeader.fIsLastPckt != 'y' ); | |||
| } | |||
| return 0; | |||
| } | |||
| //JackNetMasterManager*********************************************************************************************** | |||
| JackNetMasterManager::JackNetMasterManager ( jack_client_t* client ) | |||
| { | |||
| jack_log ( "JackNetMasterManager::JackNetMasterManager" ); | |||
| fManagerClient = client; | |||
| fManagerName = jack_get_client_name ( fManagerClient ); | |||
| fMCastIP = DEFAULT_MULTICAST_IP; | |||
| fPort = DEFAULT_PORT; | |||
| fGlobalID = 0; | |||
| fRunning = true; | |||
| //launch the manager thread | |||
| if ( jack_client_create_thread ( fManagerClient, &fManagerThread, 0, 0, NetManagerThread, this ) ) | |||
| jack_error ( "Can't create the network manager control thread." ); | |||
| } | |||
| JackNetMasterManager::~JackNetMasterManager() | |||
| { | |||
| jack_log ( "JackNetMasterManager::~JackNetMasterManager" ); | |||
| Exit(); | |||
| master_list_t::iterator it; | |||
| for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) | |||
| delete ( *it ); | |||
| } | |||
| void* JackNetMasterManager::NetManagerThread ( void* arg ) | |||
| { | |||
| jack_info ( "Starting Jack Network Manager." ); | |||
| JackNetMasterManager* master_manager = static_cast<JackNetMasterManager*> ( arg ); | |||
| master_manager->Run(); | |||
| return NULL; | |||
| } | |||
| void JackNetMasterManager::Run() | |||
| { | |||
| jack_log ( "JackNetMasterManager::Run" ); | |||
| //utility variables | |||
| socklen_t addr_len = sizeof ( socket_address_t ); | |||
| char disable = 0; | |||
| struct timeval timeout; | |||
| timeout.tv_sec = 2; | |||
| timeout.tv_usec = 0; | |||
| size_t attempt = 0; | |||
| //network | |||
| int mcast_sockfd; | |||
| struct ip_mreq multicast_req; | |||
| struct sockaddr_in mcast_addr; | |||
| struct sockaddr_in response_addr; | |||
| //data | |||
| session_params_t params; | |||
| int rx_bytes = 0; | |||
| JackNetMaster* net_master; | |||
| //socket | |||
| if ( ( mcast_sockfd = socket ( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) | |||
| { | |||
| jack_error ( "Can't create the network management input socket : %s", strerror ( errno ) ); | |||
| return; | |||
| } | |||
| //set the multicast address | |||
| mcast_addr.sin_family = AF_INET; | |||
| mcast_addr.sin_port = htons ( fPort ); | |||
| if ( inet_aton ( fMCastIP, &mcast_addr.sin_addr ) < 0 ) | |||
| { | |||
| jack_error ( "Cant set multicast address : %s", strerror ( errno ) ); | |||
| close ( mcast_sockfd ); | |||
| return; | |||
| } | |||
| memset ( &mcast_addr.sin_zero, 0, 8 ); | |||
| //bind the socket to the multicast address | |||
| if ( bind ( mcast_sockfd, reinterpret_cast<socket_address_t *> ( &mcast_addr ), addr_len ) < 0 ) | |||
| { | |||
| jack_error ( "Can't bind the network manager socket : %s", strerror ( errno ) ); | |||
| close ( mcast_sockfd ); | |||
| return; | |||
| } | |||
| //join multicast group | |||
| inet_aton ( fMCastIP, &multicast_req.imr_multiaddr ); | |||
| multicast_req.imr_interface.s_addr = htonl ( INADDR_ANY ); | |||
| if ( setsockopt ( mcast_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multicast_req, sizeof ( multicast_req ) ) < 0 ) | |||
| { | |||
| jack_error ( "Can't join multicast group : %s", strerror ( errno ) ); | |||
| close ( mcast_sockfd ); | |||
| return; | |||
| } | |||
| //disable local loop | |||
| if ( setsockopt ( mcast_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof ( disable ) ) < 0 ) | |||
| jack_error ( "Can't set multicast loop option : %s", strerror ( errno ) ); | |||
| //set a timeout on the multicast receive (the thread can now be cancelled) | |||
| if ( setsockopt ( mcast_sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof ( timeout ) ) < 0 ) | |||
| jack_error ( "Can't set timeout : %s", strerror ( errno ) ); | |||
| jack_info ( "Waiting for a slave..." ); | |||
| //main loop, wait for data, deal with it and wait again | |||
| do | |||
| { | |||
| rx_bytes = recvfrom ( mcast_sockfd, ¶ms, sizeof ( session_params_t ), 0, | |||
| reinterpret_cast<socket_address_t*> ( &response_addr ), &addr_len ); | |||
| if ( ( rx_bytes < 0 ) && ( errno != EAGAIN ) ) | |||
| { | |||
| jack_error ( "Error in receive : %s", strerror ( errno ) ); | |||
| if ( ++attempt == 10 ) | |||
| { | |||
| jack_error ( "Can't receive on the socket, exiting net manager." ); | |||
| return; | |||
| } | |||
| } | |||
| if ( rx_bytes == sizeof ( session_params_t ) ) | |||
| { | |||
| switch ( GetPacketType ( ¶ms ) ) | |||
| { | |||
| case SLAVE_AVAILABLE: | |||
| if ( ( net_master = MasterInit ( params, response_addr, mcast_addr ) ) ) | |||
| SessionParamsDisplay ( &net_master->fParams ); | |||
| else | |||
| jack_error ( "Can't init new net master..." ); | |||
| jack_info ( "Waiting for a slave..." ); | |||
| break; | |||
| case KILL_MASTER: | |||
| KillMaster ( ¶ms ); | |||
| jack_info ( "Waiting for a slave..." ); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| while ( fRunning ); | |||
| close ( mcast_sockfd ); | |||
| } | |||
| void JackNetMasterManager::Exit() | |||
| { | |||
| jack_log ( "JackNetMasterManager::Exit" ); | |||
| fRunning = false; | |||
| pthread_join ( fManagerThread, NULL ); | |||
| jack_info ( "Exiting net manager..." ); | |||
| } | |||
| JackNetMaster* JackNetMasterManager::MasterInit ( session_params_t& params, struct sockaddr_in& address, struct sockaddr_in& mcast_addr ) | |||
| { | |||
| jack_log ( "JackNetMasterManager::MasterInit, Slave : %s", params.fName ); | |||
| //settings | |||
| gethostname ( params.fMasterNetName, 255 ); | |||
| params.fMtu = 1500; | |||
| params.fID = ++fGlobalID; | |||
| params.fSampleRate = jack_get_sample_rate ( fManagerClient ); | |||
| params.fPeriodSize = jack_get_buffer_size ( fManagerClient ); | |||
| params.fBitdepth = 0; | |||
| SetFramesPerPacket ( ¶ms ); | |||
| SetSlaveName ( params ); | |||
| //create a new master and add it to the list | |||
| JackNetMaster* master = new JackNetMaster ( this, params, address, mcast_addr ); | |||
| if ( master->Init() ) | |||
| { | |||
| fMasterList.push_back ( master ); | |||
| return master; | |||
| } | |||
| delete master; | |||
| return NULL; | |||
| } | |||
| void JackNetMasterManager::SetSlaveName ( session_params_t& params ) | |||
| { | |||
| jack_log ( "JackNetMasterManager::SetSlaveName" ); | |||
| master_list_it_t it; | |||
| for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) | |||
| if ( strcmp ( ( *it )->fParams.fName, params.fName ) == 0 ) | |||
| sprintf ( params.fName, "%s-%u", params.fName, params.fID ); | |||
| } | |||
| master_list_it_t JackNetMasterManager::FindMaster ( size_t id ) | |||
| { | |||
| jack_log ( "JackNetMasterManager::FindMaster, ID %u.", id ); | |||
| master_list_it_t it; | |||
| for ( it = fMasterList.begin(); it != fMasterList.end(); it++ ) | |||
| if ( ( *it )->fParams.fID == id ) | |||
| return it; | |||
| return it; | |||
| } | |||
| void JackNetMasterManager::KillMaster ( session_params_t* params ) | |||
| { | |||
| jack_log ( "JackNetMasterManager::KillMaster, ID %u.", params->fID ); | |||
| master_list_it_t master = FindMaster ( params->fID ); | |||
| if ( master != fMasterList.end() ) | |||
| { | |||
| fMasterList.erase ( master ); | |||
| delete *master; | |||
| } | |||
| } | |||
| }//namespace | |||
| static Jack::JackNetMasterManager* master_manager = NULL; | |||
| #ifdef __cplusplus | |||
| extern "C" | |||
| { | |||
| #endif | |||
| int jack_initialize ( jack_client_t* jack_client, const char* load_init ) | |||
| { | |||
| if ( master_manager ) | |||
| { | |||
| jack_error ( "Master Manager already loaded" ); | |||
| return 1; | |||
| } | |||
| else | |||
| { | |||
| jack_log ( "Loading Master Manager" ); | |||
| master_manager = new Jack::JackNetMasterManager ( jack_client ); | |||
| return ( master_manager ) ? 0 : 1; | |||
| } | |||
| } | |||
| void jack_finish ( void* arg ) | |||
| { | |||
| if ( master_manager ) | |||
| { | |||
| jack_log ( "Unloading Master Manager" ); | |||
| delete master_manager; | |||
| master_manager = NULL; | |||
| } | |||
| } | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,118 @@ | |||
| /*************************************************************************** | |||
| * Copyright (C) 2008 by Romain Moret * | |||
| * moret@grame.fr * | |||
| * * | |||
| * 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., * | |||
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |||
| ***************************************************************************/ | |||
| #ifndef __JACKNETMASTER_H__ | |||
| #define __JACKNETMASTER_H__ | |||
| #include "JackNetTool.h" | |||
| #include <list> | |||
| #include <algorithm> | |||
| #include "thread.h" | |||
| #include <unistd.h> | |||
| #include "jack.h" | |||
| namespace Jack | |||
| { | |||
| class JackNetMasterManager; | |||
| class JackNetMaster | |||
| { | |||
| friend class JackNetMasterManager; | |||
| private: | |||
| static int SetProcess ( jack_nframes_t nframes, void* arg ); | |||
| JackNetMasterManager* fMasterManager; | |||
| session_params_t fParams; | |||
| struct sockaddr_in fAddr; | |||
| struct sockaddr_in fMcastAddr; | |||
| int fSockfd; | |||
| size_t fNSubProcess; | |||
| size_t fNetJumpCnt; | |||
| bool fRunning; | |||
| jack_client_t* fJackClient; | |||
| const char* fClientName; | |||
| jack_port_t** fAudioCapturePorts; | |||
| jack_port_t** fAudioPlaybackPorts; | |||
| jack_port_t** fMidiCapturePorts; | |||
| jack_port_t** fMidiPlaybackPorts; | |||
| packet_header_t fTxHeader; | |||
| packet_header_t fRxHeader; | |||
| char* fTxBuffer; | |||
| char* fRxBuffer; | |||
| char* fTxData; | |||
| char* fRxData; | |||
| NetAudioBuffer* fNetAudioCaptureBuffer; | |||
| NetAudioBuffer* fNetAudioPlaybackBuffer; | |||
| NetMidiBuffer* fNetMidiCaptureBuffer; | |||
| NetMidiBuffer* fNetMidiPlaybackBuffer; | |||
| int fAudioTxLen; | |||
| int fAudioRxLen; | |||
| bool Init(); | |||
| void FreePorts(); | |||
| void Exit(); | |||
| int Send ( char* buffer, size_t size, int flags ); | |||
| int Recv ( size_t size, int flags ); | |||
| int Process(); | |||
| public: | |||
| JackNetMaster ( JackNetMasterManager* manager, session_params_t& params, struct sockaddr_in& address, struct sockaddr_in& mcast_addr ); | |||
| ~JackNetMaster (); | |||
| }; | |||
| typedef std::list<JackNetMaster*> master_list_t; | |||
| typedef master_list_t::iterator master_list_it_t; | |||
| class JackNetMasterManager | |||
| { | |||
| private: | |||
| static void* NetManagerThread ( void* arg ); | |||
| static int SetProcess ( jack_nframes_t nframes, void* arg ); | |||
| jack_client_t* fManagerClient; | |||
| char* fManagerName; | |||
| char* fMCastIP; | |||
| pthread_t fManagerThread; | |||
| master_list_t fMasterList; | |||
| size_t fGlobalID; | |||
| bool fRunning; | |||
| size_t fPort; | |||
| void Run(); | |||
| JackNetMaster* MasterInit ( session_params_t& params, struct sockaddr_in& address, struct sockaddr_in& mcast_addr ); | |||
| master_list_it_t FindMaster ( size_t client_id ); | |||
| void KillMaster ( session_params_t* params ); | |||
| void SetSlaveName ( session_params_t& params ); | |||
| int Process(); | |||
| public: | |||
| JackNetMasterManager ( jack_client_t* jack_client ); | |||
| ~JackNetMasterManager(); | |||
| void Exit(); | |||
| }; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,312 @@ | |||
| #include "JackNetTool.h" | |||
| #include "JackError.h" | |||
| using namespace std; | |||
| namespace Jack | |||
| { | |||
| // NetMidiBuffer********************************************************************************** | |||
| NetMidiBuffer::NetMidiBuffer ( session_params_t* params, size_t nports, char* net_buffer ) | |||
| { | |||
| fNPorts = nports; | |||
| fMaxBufsize = fNPorts * sizeof ( sample_t ) * params->fPeriodSize ; | |||
| fMaxPcktSize = params->fMtu - sizeof ( packet_header_t ); | |||
| fBuffer = new char[fMaxBufsize]; | |||
| fPortBuffer = new JackMidiBuffer* [fNPorts]; | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| fPortBuffer[port_index] = NULL; | |||
| fNetBuffer = net_buffer; | |||
| } | |||
| NetMidiBuffer::~NetMidiBuffer() | |||
| { | |||
| delete[] fBuffer; | |||
| delete[] fPortBuffer; | |||
| } | |||
| size_t NetMidiBuffer::GetSize() | |||
| { | |||
| return fMaxBufsize; | |||
| } | |||
| void NetMidiBuffer::DisplayEvents() | |||
| { | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| { | |||
| for ( size_t event = 0; event < fPortBuffer[port_index]->event_count; event++ ) | |||
| if ( fPortBuffer[port_index]->IsValid() ) | |||
| jack_info ( "port %d : midi event %u/%u -> time : %u, size : %u", | |||
| port_index + 1, event + 1, fPortBuffer[port_index]->event_count, | |||
| fPortBuffer[port_index]->events[event].time, fPortBuffer[port_index]->events[event].size ); | |||
| } | |||
| } | |||
| int NetMidiBuffer::RenderFromJackPorts() | |||
| { | |||
| int pos = 0; | |||
| int copy_size; | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| { | |||
| copy_size = sizeof ( JackMidiBuffer ) + fPortBuffer[port_index]->event_count * sizeof ( JackMidiEvent ); | |||
| memcpy ( fBuffer + pos, fPortBuffer[port_index], copy_size ); | |||
| pos += copy_size; | |||
| memcpy ( fBuffer + pos, fPortBuffer[port_index] + ( fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos ), | |||
| fPortBuffer[port_index]->write_pos ); | |||
| pos += fPortBuffer[port_index]->write_pos; | |||
| } | |||
| return pos; | |||
| } | |||
| int NetMidiBuffer::RenderToJackPorts() | |||
| { | |||
| int pos = 0; | |||
| int copy_size; | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| { | |||
| copy_size = sizeof ( JackMidiBuffer ) + reinterpret_cast<JackMidiBuffer*>(fBuffer + pos)->event_count * sizeof ( JackMidiEvent ); | |||
| memcpy ( fPortBuffer[port_index], fBuffer + pos, copy_size ); | |||
| pos += copy_size; | |||
| memcpy ( fPortBuffer[port_index] + ( fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos ), | |||
| fBuffer + pos, fPortBuffer[port_index]->write_pos ); | |||
| pos += fPortBuffer[port_index]->write_pos; | |||
| } | |||
| return pos; | |||
| } | |||
| int NetMidiBuffer::RenderFromNetwork ( size_t subcycle, size_t copy_size ) | |||
| { | |||
| memcpy ( fBuffer + subcycle * fMaxPcktSize, fNetBuffer, copy_size ); | |||
| return copy_size; | |||
| } | |||
| int NetMidiBuffer::RenderToNetwork ( size_t subcycle, size_t total_size ) | |||
| { | |||
| int size = total_size - subcycle * fMaxPcktSize; | |||
| int copy_size = ( size <= fMaxPcktSize ) ? size : fMaxPcktSize; | |||
| memcpy ( fNetBuffer, fBuffer + subcycle * fMaxPcktSize, copy_size ); | |||
| return copy_size; | |||
| } | |||
| // net audio buffer ********************************************************************************* | |||
| NetAudioBuffer::NetAudioBuffer ( session_params_t* params, size_t nports, char* net_buffer ) | |||
| { | |||
| fNPorts = nports; | |||
| fPeriodSize = params->fPeriodSize; | |||
| fSubPeriodSize = params->fFramesPerPacket; | |||
| fSubPeriodBytesSize = fSubPeriodSize * sizeof ( sample_t ); | |||
| fPortBuffer = new sample_t* [fNPorts]; | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| fPortBuffer[port_index] = NULL; | |||
| fNetBuffer = net_buffer; | |||
| } | |||
| NetAudioBuffer::~NetAudioBuffer() | |||
| { | |||
| delete[] fPortBuffer; | |||
| } | |||
| size_t NetAudioBuffer::GetSize() | |||
| { | |||
| return fNPorts * fSubPeriodBytesSize; | |||
| } | |||
| void NetAudioBuffer::RenderFromJackPorts ( size_t subcycle ) | |||
| { | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| memcpy ( fNetBuffer + port_index * fSubPeriodBytesSize, fPortBuffer[port_index] + subcycle * fSubPeriodSize, fSubPeriodBytesSize ); | |||
| } | |||
| void NetAudioBuffer::RenderToJackPorts ( size_t subcycle ) | |||
| { | |||
| for ( int port_index = 0; port_index < fNPorts; port_index++ ) | |||
| memcpy ( fPortBuffer[port_index] + subcycle * fSubPeriodSize, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize ); | |||
| } | |||
| // SessionParams ************************************************************************************ | |||
| void SessionParamsHToN ( session_params_t* params ) | |||
| { | |||
| params->fPacketID = htonl ( params->fPacketID ); | |||
| params->fMtu = htonl ( params->fMtu ); | |||
| params->fID = htonl ( params->fID ); | |||
| params->fSendAudioChannels = htonl ( params->fSendAudioChannels ); | |||
| params->fReturnAudioChannels = htonl ( params->fReturnAudioChannels ); | |||
| params->fSendMidiChannels = htonl ( params->fSendMidiChannels ); | |||
| params->fReturnMidiChannels = htonl ( params->fReturnMidiChannels ); | |||
| params->fSampleRate = htonl ( params->fSampleRate ); | |||
| params->fPeriodSize = htonl ( params->fPeriodSize ); | |||
| params->fFramesPerPacket = htonl ( params->fFramesPerPacket ); | |||
| params->fBitdepth = htonl ( params->fBitdepth ); | |||
| } | |||
| void SessionParamsNToH ( session_params_t* params ) | |||
| { | |||
| params->fPacketID = ntohl ( params->fPacketID ); | |||
| params->fMtu = ntohl ( params->fMtu ); | |||
| params->fID = ntohl ( params->fID ); | |||
| params->fSendAudioChannels = ntohl ( params->fSendAudioChannels ); | |||
| params->fReturnAudioChannels = ntohl ( params->fReturnAudioChannels ); | |||
| params->fSendMidiChannels = ntohl ( params->fSendMidiChannels ); | |||
| params->fReturnMidiChannels = ntohl ( params->fReturnMidiChannels ); | |||
| params->fSampleRate = ntohl ( params->fSampleRate ); | |||
| params->fPeriodSize = ntohl ( params->fPeriodSize ); | |||
| params->fFramesPerPacket = ntohl ( params->fFramesPerPacket ); | |||
| params->fBitdepth = ntohl ( params->fBitdepth ); | |||
| } | |||
| void SessionParamsDisplay ( session_params_t* params ) | |||
| { | |||
| jack_info ( "********************Params********************" ); | |||
| jack_info ( "Protocol revision : %c", params->fProtocolVersion ); | |||
| jack_info ( "MTU : %u", params->fMtu ); | |||
| jack_info ( "Master name : %s", params->fMasterNetName ); | |||
| jack_info ( "Slave name : %s", params->fSlaveNetName ); | |||
| jack_info ( "ID : %u", params->fID ); | |||
| jack_info ( "Send channels (audio - midi) : %d - %d", params->fSendAudioChannels, params->fSendMidiChannels ); | |||
| jack_info ( "Return channels (audio - midi) : %d - %d", params->fReturnAudioChannels, params->fReturnMidiChannels ); | |||
| jack_info ( "Sample rate : %u frames per second", params->fSampleRate ); | |||
| jack_info ( "Period size : %u frames per period", params->fPeriodSize ); | |||
| jack_info ( "Frames per packet : %u", params->fFramesPerPacket ); | |||
| jack_info ( "Packet per period : %u", params->fPeriodSize / params->fFramesPerPacket ); | |||
| jack_info ( "Bitdepth (0 for float) : %u", params->fBitdepth ); | |||
| jack_info ( "Name : %s", params->fName ); | |||
| jack_info ( "**********************************************" ); | |||
| } | |||
| sync_packet_type_t GetPacketType ( session_params_t* params ) | |||
| { | |||
| switch ( params->fPacketID ) | |||
| { | |||
| case 0: | |||
| return SLAVE_AVAILABLE; | |||
| case 1: | |||
| return SLAVE_SETUP; | |||
| case 2: | |||
| return START_MASTER; | |||
| case 3: | |||
| return START_SLAVE; | |||
| case 4: | |||
| return KILL_MASTER; | |||
| } | |||
| return INVALID; | |||
| } | |||
| int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type ) | |||
| { | |||
| switch ( packet_type ) | |||
| { | |||
| case INVALID: | |||
| return -1; | |||
| case SLAVE_AVAILABLE: | |||
| params->fPacketID = 0; | |||
| break; | |||
| case SLAVE_SETUP: | |||
| params->fPacketID = 1; | |||
| break; | |||
| case START_MASTER: | |||
| params->fPacketID = 2; | |||
| break; | |||
| case START_SLAVE: | |||
| params->fPacketID = 3; | |||
| break; | |||
| case KILL_MASTER: | |||
| params->fPacketID = 4; | |||
| } | |||
| return 0; | |||
| } | |||
| // Packet header ********************************************************************************** | |||
| void PacketHeaderHToN ( packet_header_t* header ) | |||
| { | |||
| header->fID = htonl ( header->fID ); | |||
| header->fMidiDataSize = htonl ( header->fMidiDataSize ); | |||
| header->fBitdepth = htonl ( header->fBitdepth ); | |||
| header->fNMidiPckt = htonl ( header->fNMidiPckt ); | |||
| header->fCycle = ntohl ( header->fCycle ); | |||
| header->fSubCycle = htonl ( header->fSubCycle ); | |||
| } | |||
| void PacketHeaderNToH ( packet_header_t* header ) | |||
| { | |||
| header->fID = ntohl ( header->fID ); | |||
| header->fMidiDataSize = ntohl ( header->fMidiDataSize ); | |||
| header->fBitdepth = ntohl ( header->fBitdepth ); | |||
| header->fNMidiPckt = ntohl ( header->fNMidiPckt ); | |||
| header->fCycle = ntohl ( header->fCycle ); | |||
| header->fSubCycle = ntohl ( header->fSubCycle ); | |||
| } | |||
| void PacketHeaderDisplay ( packet_header_t* header ) | |||
| { | |||
| jack_info ( "********************Header********************" ); | |||
| jack_info ( "Data type : %c", header->fDataType ); | |||
| jack_info ( "Data stream : %c", header->fDataStream ); | |||
| jack_info ( "ID : %u", header->fID ); | |||
| jack_info ( "Cycle : %u", header->fCycle ); | |||
| jack_info ( "SubCycle : %u", header->fSubCycle ); | |||
| jack_info ( "Midi packets : %u", header->fNMidiPckt ); | |||
| jack_info ( "Midi data size : %u", header->fMidiDataSize ); | |||
| jack_info ( "Last packet : '%c'", header->fIsLastPckt ); | |||
| jack_info ( "Bitdepth : %u (0 for float)", header->fBitdepth ); | |||
| jack_info ( "**********************************************" ); | |||
| } | |||
| // Utility ******************************************************************************************************* | |||
| size_t SetFramesPerPacket ( session_params_t* params ) | |||
| { | |||
| if ( !params->fSendAudioChannels && !params->fReturnAudioChannels ) | |||
| return ( params->fFramesPerPacket = params->fPeriodSize ); | |||
| size_t period = ( int ) powf ( 2.f, ( int ) log2 ( ( params->fMtu - sizeof ( packet_header_t ) ) | |||
| / ( max ( params->fReturnAudioChannels, params->fSendAudioChannels ) * sizeof ( sample_t ) ) ) ); | |||
| ( period > params->fPeriodSize ) ? params->fFramesPerPacket = params->fPeriodSize : params->fFramesPerPacket = period; | |||
| return params->fFramesPerPacket; | |||
| } | |||
| size_t GetNMidiPckt ( session_params_t* params, size_t data_size ) | |||
| { | |||
| //even if there is no midi data, jack need an empty buffer to know there is no event to read | |||
| //99% of the cases : all data in one packet | |||
| if ( data_size <= ( params->fMtu - sizeof ( packet_header_t ) ) ) | |||
| return 1; | |||
| //else, get the number of needed packets (simply slice the biiig buffer) | |||
| size_t npckt = data_size / ( params->fMtu - sizeof ( packet_header_t ) ); | |||
| if ( data_size % ( params->fMtu - sizeof ( packet_header_t ) ) ) | |||
| return ++npckt; | |||
| return npckt; | |||
| } | |||
| int SetRxTimeout ( int* sockfd, session_params_t* params ) | |||
| { | |||
| int ret; | |||
| struct timeval timeout; | |||
| float time = static_cast<float> ( params->fFramesPerPacket ) / static_cast<float> ( params->fSampleRate ); | |||
| timeout.tv_sec = ( int ) time; | |||
| float usec = 1.25 * ( time - timeout.tv_sec ) * 1000000; | |||
| timeout.tv_usec = ( int ) usec; | |||
| if ( ( ret = setsockopt ( *sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof ( timeout ) ) ) < 0 ) | |||
| return ret; | |||
| return timeout.tv_usec; | |||
| } | |||
| // Packet ******************************************************************************************************* | |||
| bool IsNextPacket ( packet_header_t* previous, packet_header_t* next, size_t subcycles ) | |||
| { | |||
| //ignore first cycle | |||
| if ( previous->fCycle <= 1 ) | |||
| return true; | |||
| //same PcktID (cycle), next SubPcktID (subcycle) | |||
| if ( ( previous->fSubCycle < ( subcycles - 1 ) ) && ( next->fCycle == previous->fCycle ) && ( next->fSubCycle == ( previous->fSubCycle + 1 ) ) ) | |||
| return true; | |||
| //next PcktID (cycle), SubPcktID reset to 1 (first subcyle) | |||
| if ( ( next->fCycle == ( previous->fCycle + 1 ) ) && ( previous->fSubCycle == ( subcycles - 1 ) ) && ( next->fSubCycle == 0 ) ) | |||
| return true; | |||
| //else, next is'nt next, return false | |||
| return false; | |||
| } | |||
| } | |||
| @@ -0,0 +1,174 @@ | |||
| #include "types.h" | |||
| #include "JackConstants.h" | |||
| #include "JackMidiPort.h" | |||
| #include <string> | |||
| #include <algorithm> | |||
| #include <cmath> | |||
| #include <cstdlib> | |||
| #include <cstdio> | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/types.h> | |||
| #include <sys/socket.h> | |||
| #include <netdb.h> | |||
| #include <netinet/in.h> | |||
| #include <arpa/inet.h> | |||
| #include <errno.h> | |||
| namespace Jack | |||
| { | |||
| typedef struct _session_params session_params_t; | |||
| typedef struct _packet_header packet_header_t; | |||
| typedef struct _midi_portbuf_desc midi_portbuf_desc_t; | |||
| typedef struct sockaddr socket_address_t; | |||
| typedef struct in_addr address_t; | |||
| typedef jack_default_audio_sample_t sample_t; | |||
| //session params ****************************************************************************** | |||
| struct _session_params | |||
| { | |||
| char fPacketType[7]; //packet type ('param') | |||
| char fProtocolVersion; //version | |||
| int fPacketID; //indicates the packet type | |||
| char fMasterNetName[256]; //master hostname (network) | |||
| char fSlaveNetName[256]; //slave hostname (network) | |||
| size_t fMtu; //connection mtu | |||
| size_t fID; //slave's ID | |||
| int fSendAudioChannels; //number of master->slave channels | |||
| int fReturnAudioChannels; //number of slave->master channels | |||
| int fSendMidiChannels; //number of master->slave midi channels | |||
| int fReturnMidiChannels; //number of slave->master midi channels | |||
| size_t fSampleRate; //session sample rate | |||
| size_t fPeriodSize; //period size | |||
| size_t fFramesPerPacket; //complete frames per packet | |||
| size_t fBitdepth; //samples bitdepth (unused) | |||
| char fName[JACK_CLIENT_NAME_SIZE]; //slave's name | |||
| }; | |||
| //net status ********************************************************************************** | |||
| enum _net_status | |||
| { | |||
| SOCKET_ERROR, | |||
| CONNECT_ERROR, | |||
| RECV_ERROR, | |||
| CONNECTED, | |||
| ROLLING | |||
| }; | |||
| typedef enum _net_status net_status_t; | |||
| //sync packet type **************************************************************************** | |||
| enum _sync_packet_type | |||
| { | |||
| INVALID, //... | |||
| SLAVE_AVAILABLE, //a slave is available | |||
| SLAVE_SETUP, //slave configuration | |||
| START_MASTER, //slave is ready, start master | |||
| START_SLAVE, //master is ready, activate slave | |||
| KILL_MASTER //master must stop | |||
| }; | |||
| typedef enum _sync_packet_type sync_packet_type_t; | |||
| //packet header ******************************************************************************* | |||
| struct _packet_header | |||
| { | |||
| char fPacketType[7]; //packet type ( 'headr' ) | |||
| char fDataType; //a for audio, m for midi | |||
| char fDataStream; //s for send, r for return | |||
| size_t fID; //to identify the slave | |||
| size_t fBitdepth; //bitdepth of the data samples | |||
| size_t fMidiDataSize; //size of midi data (if packet is 'midi typed') in bytes | |||
| size_t fNMidiPckt; //number of midi packets of the cycle | |||
| size_t fCycle; //process cycle counter | |||
| size_t fSubCycle; //midi/audio subcycle counter | |||
| char fIsLastPckt; //is it the last packet of a given cycle ('y' or 'n') | |||
| char fFree[13]; //unused | |||
| }; | |||
| //midi data *********************************************************************************** | |||
| class NetMidiBuffer | |||
| { | |||
| private: | |||
| int fNPorts; | |||
| size_t fMaxBufsize; | |||
| int fMaxPcktSize; | |||
| //data | |||
| char* fBuffer; | |||
| char* fNetBuffer; | |||
| public: | |||
| NetMidiBuffer ( session_params_t* params, size_t nports, char* net_buffer ); | |||
| ~NetMidiBuffer(); | |||
| JackMidiBuffer** fPortBuffer; | |||
| void Reset(); | |||
| size_t GetSize(); | |||
| //utility | |||
| void DisplayEvents(); | |||
| //jack<->buffer | |||
| int RenderFromJackPorts(); | |||
| int RenderToJackPorts(); | |||
| //network<->buffer | |||
| int RenderFromNetwork ( size_t subcycle, size_t copy_size ); | |||
| int RenderToNetwork ( size_t subcycle, size_t copy_size ); | |||
| }; | |||
| // audio data ********************************************************************************* | |||
| class NetAudioBuffer | |||
| { | |||
| private: | |||
| int fNPorts; | |||
| jack_nframes_t fPeriodSize; | |||
| jack_nframes_t fSubPeriodSize; | |||
| size_t fSubPeriodBytesSize; | |||
| char* fNetBuffer; | |||
| public: | |||
| NetAudioBuffer ( session_params_t* params, size_t nports, char* net_buffer ); | |||
| ~NetAudioBuffer(); | |||
| sample_t** fPortBuffer; | |||
| size_t GetSize(); | |||
| //jack<->buffer | |||
| void RenderFromJackPorts ( size_t subcycle ); | |||
| void RenderToJackPorts ( size_t subcycle ); | |||
| }; | |||
| //utility ************************************************************************************* | |||
| //n<-->h functions | |||
| void SessionParamsHToN ( session_params_t* params ); | |||
| void SessionParamsNToH ( session_params_t* params ); | |||
| void PacketHeaderHToN ( packet_header_t* header ); | |||
| void PacketHeaderNToH ( packet_header_t* header ); | |||
| //display session parameters | |||
| void SessionParamsDisplay ( session_params_t* params ); | |||
| //display packet header | |||
| void PacketHeaderDisplay ( packet_header_t* header ); | |||
| //get the packet type from a sesion parameters | |||
| sync_packet_type_t GetPacketType ( session_params_t* params ); | |||
| //set the packet type in a session parameters | |||
| int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type ); | |||
| //step of network initialization | |||
| size_t SetFramesPerPacket ( session_params_t* params ); | |||
| //get the midi packet number for a given cycle | |||
| size_t GetNMidiPckt ( session_params_t* params, size_t data_size ); | |||
| //set the recv timeout on a socket | |||
| int SetRxTimeout ( int* sockfd, session_params_t* params ); | |||
| //check if 'next' packet is really the next after 'previous' | |||
| bool IsNextPacket ( packet_header_t* previous, packet_header_t* next, size_t subcycles ); | |||
| } | |||
| @@ -74,6 +74,7 @@ srcfiles_common_serverlib = [ | |||
| 'JackClient.cpp', | |||
| 'JackConnectionManager.cpp', | |||
| 'JackDriver.cpp', | |||
| 'JackNetTool.cpp', | |||
| 'JackEngine.cpp', | |||
| 'JackEngineControl.cpp', | |||
| 'JackError.cpp', | |||
| @@ -179,6 +180,7 @@ if env['PLATFORM'] == 'posix': | |||
| clientlib = libenv.SharedLibrary(env['CLIENTLIB'], srcfiles_common_clientlib) | |||
| serverlib = libenv.SharedLibrary(env['SERVERLIB'], srcfiles_common_serverlib) | |||
| netmanagerlib = libenv.SharedLibrary(env['NETMANAGERLIB'], 'JackNetManager.cpp') | |||
| env.Install( env['INSTALL_LIBDIR'], [clientlib, serverlib]) | |||
| env.Alias('install', env['INSTALL_LIBDIR']) | |||
| @@ -30,7 +30,7 @@ def build(bld): | |||
| 'timestamps.c', | |||
| 'JackTools.cpp', | |||
| 'JackMessageBuffer.cpp', | |||
| 'JackProcessSync.cpp' | |||
| 'JackProcessSync.cpp', | |||
| ] | |||
| serverlib = bld.create_obj('cpp', 'shlib') | |||
| @@ -58,6 +58,7 @@ def build(bld): | |||
| 'JackDriverLoader.cpp', | |||
| 'JackServerGlobals.cpp', | |||
| 'JackControl.cpp', | |||
| 'JackNetTool.cpp', | |||
| ] | |||
| serverlib.vnum = bld.env()['JACK_API_VERSION'] | |||
| @@ -79,4 +80,11 @@ def build(bld): | |||
| ] | |||
| clientlib.vnum = bld.env()['JACK_API_VERSION'] | |||
| netmanager_lib = bld.create_obj('cpp', 'shlib') | |||
| netmanager_lib.env['shlib_PATTERN'] = '%s.so' | |||
| netmanager_lib.includes = ['./jack', '.'] | |||
| netmanager_lib.name = 'netmanager' | |||
| netmanager_lib.target = 'netmanager' | |||
| netmanager_lib.source = 'JackNetManager.cpp' | |||
| install_files('PREFIX', 'jack', 'jack/*.h') | |||
| @@ -51,6 +51,7 @@ for i in range(len(srcfiles_linux_alsa)): | |||
| srcfiles_linux_freebob = ['freebob/JackFreebobDriver.cpp'] | |||
| srcfiles_linux_ffado = ['firewire/JackFFADODriver.cpp'] | |||
| srcfiles_linux_dummy = ['#/common/JackDummyDriver.cpp'] | |||
| srcfiles_linux_net = ['#/common/JackNetDriver.cpp'] | |||
| # | |||
| # Start building | |||
| @@ -69,6 +70,9 @@ driver_dir = env['INSTALL_ADDON_DIR'] + "/" | |||
| drv = serverenv.SharedLibrary( 'jack_dummy', srcfiles_linux_dummy ) | |||
| serverenv.InstallAs( driver_dir + "jack_dummy.so", drv ) | |||
| drv = serverenv.SharedLibrary( 'jack_net', srcfiles_linux_net ) | |||
| serverenv.InstallAs( driver_dir + "jack_net.so", drv ) | |||
| if env['ENABLE_ALSA']: | |||
| if not env.GetOption('clean'): | |||
| serverenv.MergeFlags( env['ALSA_FLAGS'] ) | |||
| @@ -53,3 +53,6 @@ def build(bld): | |||
| if bld.env()['BUILD_DRIVER_FFADO'] == True: | |||
| create_jack_driver_obj(bld, 'firewire', 'firewire/JackFFADODriver.cpp', "LIBFFADO") | |||
| create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') | |||
| @@ -90,6 +90,8 @@ | |||
| 4B363F530DEB0CFE001F72D9 /* PBXTargetDependency */, | |||
| 4B363F780DEB0D85001F72D9 /* PBXTargetDependency */, | |||
| 4B978E800A31D8B7009E2DD1 /* PBXTargetDependency */, | |||
| BA222AF00DC883EF001A17F4 /* PBXTargetDependency */, | |||
| BA222AF20DC883F3001A17F4 /* PBXTargetDependency */, | |||
| ); | |||
| name = "All Universal 32 bits"; | |||
| productName = All; | |||
| @@ -549,6 +551,14 @@ | |||
| 4BFA82BC0DF6A9E40087B4E1 /* showtime.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F3D0DEB0C31001F72D9 /* showtime.c */; }; | |||
| 4BFA82C80DF6A9E40087B4E1 /* impulse_grabber.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B363F750DEB0D7D001F72D9 /* impulse_grabber.c */; }; | |||
| 4BFA99AA0AAAF40C009E916C /* jdelay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFA99A90AAAF40C009E916C /* jdelay.cpp */; }; | |||
| BA222AD80DC88268001A17F4 /* JackNetTool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BA222AD60DC88268001A17F4 /* JackNetTool.cpp */; }; | |||
| BA222AD90DC88269001A17F4 /* JackNetTool.h in Headers */ = {isa = PBXBuildFile; fileRef = BA222AD70DC88268001A17F4 /* JackNetTool.h */; }; | |||
| BA222ADA0DC88269001A17F4 /* JackNetTool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BA222AD60DC88268001A17F4 /* JackNetTool.cpp */; }; | |||
| BA222ADB0DC88269001A17F4 /* JackNetTool.h in Headers */ = {isa = PBXBuildFile; fileRef = BA222AD70DC88268001A17F4 /* JackNetTool.h */; }; | |||
| BA222ADE0DC882A5001A17F4 /* JackNetDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BA222ADC0DC882A5001A17F4 /* JackNetDriver.cpp */; }; | |||
| BA222ADF0DC882A5001A17F4 /* JackNetDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = BA222ADD0DC882A5001A17F4 /* JackNetDriver.h */; }; | |||
| BA222AED0DC883B3001A17F4 /* JackNetManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BA222AEB0DC883B3001A17F4 /* JackNetManager.cpp */; }; | |||
| BA222AEE0DC883B3001A17F4 /* JackNetManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BA222AEC0DC883B3001A17F4 /* JackNetManager.h */; }; | |||
| /* End PBXBuildFile section */ | |||
| /* Begin PBXContainerItemProxy section */ | |||
| @@ -1035,6 +1045,20 @@ | |||
| remoteGlobalIDString = 4BFA99980AAAF3B0009E916C; | |||
| remoteInfo = "jdelay Universal"; | |||
| }; | |||
| BA222AEF0DC883EF001A17F4 /* PBXContainerItemProxy */ = { | |||
| isa = PBXContainerItemProxy; | |||
| containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; | |||
| proxyType = 1; | |||
| remoteGlobalIDString = BA222AE00DC882DB001A17F4; | |||
| remoteInfo = netmanager; | |||
| }; | |||
| BA222AF10DC883F3001A17F4 /* PBXContainerItemProxy */ = { | |||
| isa = PBXContainerItemProxy; | |||
| containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; | |||
| proxyType = 1; | |||
| remoteGlobalIDString = BA222AC50DC88132001A17F4; | |||
| remoteInfo = "jack_net Universal"; | |||
| }; | |||
| /* End PBXContainerItemProxy section */ | |||
| /* Begin PBXFileReference section */ | |||
| @@ -1097,6 +1121,7 @@ | |||
| 4B4F9C8B0DC20C0400706CB0 /* JackMessageBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackMessageBuffer.h; path = ../common/JackMessageBuffer.h; sourceTree = SOURCE_ROOT; }; | |||
| 4B56880F08B5C8620022B32D /* JackFifo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackFifo.cpp; path = ../common/JackFifo.cpp; sourceTree = SOURCE_ROOT; }; | |||
| 4B56881008B5C8620022B32D /* JackFifo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackFifo.h; path = ../common/JackFifo.h; sourceTree = SOURCE_ROOT; }; | |||
| 4B57F5950D72C27900B4E719 /* jack_thread_wait */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_thread_wait; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 4B5A1BBB0CD1CB9E0005BF74 /* jack_midiseq */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_midiseq; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 4B5A1BBD0CD1CC110005BF74 /* midiseq.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = midiseq.c; path = "../example-clients/midiseq.c"; sourceTree = SOURCE_ROOT; }; | |||
| 4B5A1BDA0CD1CCE10005BF74 /* jack_midisine */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_midisine; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| @@ -1288,6 +1313,14 @@ | |||
| 4BFB73F608AD291A00DB99B8 /* JackGlobals.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackGlobals.h; path = ../common/JackGlobals.h; sourceTree = SOURCE_ROOT; }; | |||
| 4BFB741E08AD2B9900DB99B8 /* JackMachThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JackMachThread.cpp; sourceTree = SOURCE_ROOT; }; | |||
| 4BFB741F08AD2B9900DB99B8 /* JackMachThread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JackMachThread.h; sourceTree = SOURCE_ROOT; }; | |||
| BA222ACF0DC88132001A17F4 /* jack_net.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = jack_net.so; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| BA222AD60DC88268001A17F4 /* JackNetTool.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackNetTool.cpp; path = ../common/JackNetTool.cpp; sourceTree = SOURCE_ROOT; }; | |||
| BA222AD70DC88268001A17F4 /* JackNetTool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackNetTool.h; path = ../common/JackNetTool.h; sourceTree = SOURCE_ROOT; }; | |||
| BA222ADC0DC882A5001A17F4 /* JackNetDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackNetDriver.cpp; path = ../common/JackNetDriver.cpp; sourceTree = SOURCE_ROOT; }; | |||
| BA222ADD0DC882A5001A17F4 /* JackNetDriver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackNetDriver.h; path = ../common/JackNetDriver.h; sourceTree = SOURCE_ROOT; }; | |||
| BA222AE90DC882DB001A17F4 /* netmanager.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = netmanager.so; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| BA222AEB0DC883B3001A17F4 /* JackNetManager.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackNetManager.cpp; path = ../common/JackNetManager.cpp; sourceTree = SOURCE_ROOT; }; | |||
| BA222AEC0DC883B3001A17F4 /* JackNetManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackNetManager.h; path = ../common/JackNetManager.h; sourceTree = SOURCE_ROOT; }; | |||
| C6859E8B029090EE04C91782 /* JackServer.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = JackServer.1; sourceTree = "<group>"; }; | |||
| /* End PBXFileReference section */ | |||
| @@ -1792,6 +1825,20 @@ | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222ACA0DC88132001A17F4 /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222AE40DC882DB001A17F4 /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| @@ -1820,6 +1867,7 @@ | |||
| 08FB7795FE84155DC02AAC07 /* Source */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BA222AEA0DC88379001A17F4 /* Net */, | |||
| 4B6BEB4A07A6CCDC00A5DBDA /* Tests */, | |||
| 4B37C20006DF1F900016E567 /* Latency */, | |||
| 4B03383E0797E19900686131 /* Simple clients */, | |||
| @@ -1895,6 +1943,10 @@ | |||
| 4B35C63E0D4731D3000DE7AE /* inprocess.so */, | |||
| 4B0A28E60D52073D002EFF74 /* jack_thread_wait */, | |||
| 4B0A292D0D52108E002EFF74 /* jack_thread_wait */, | |||
| 4B57F5950D72C27900B4E719 /* jack_thread_wait */, | |||
| 4BA7FEC30D8E76270017FF73 /* jack_server_control */, | |||
| BA222ACF0DC88132001A17F4 /* jack_net.so */, | |||
| BA222AE90DC882DB001A17F4 /* netmanager.so */, | |||
| 4BA7FEC30D8E76270017FF73 /* jack_server_control */, | |||
| 4B363DD80DEB02F6001F72D9 /* jack_alias */, | |||
| 4B363E1A0DEB03C5001F72D9 /* jack_evmon */, | |||
| @@ -2089,6 +2141,8 @@ | |||
| 4BA550F605E241B800569492 /* Driver */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BA222ADC0DC882A5001A17F4 /* JackNetDriver.cpp */, | |||
| BA222ADD0DC882A5001A17F4 /* JackNetDriver.h */, | |||
| 4B869B3D08C8D21C001CF041 /* driver_interface.h */, | |||
| 4B869B4208C8D22F001CF041 /* JackDriverLoader.h */, | |||
| 4B869D7F08C9CB00001CF041 /* JackDriverLoader.cpp */, | |||
| @@ -2270,6 +2324,17 @@ | |||
| name = Socket; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| BA222AEA0DC88379001A17F4 /* Net */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BA222AEB0DC883B3001A17F4 /* JackNetManager.cpp */, | |||
| BA222AEC0DC883B3001A17F4 /* JackNetManager.h */, | |||
| BA222AD60DC88268001A17F4 /* JackNetTool.cpp */, | |||
| BA222AD70DC88268001A17F4 /* JackNetTool.h */, | |||
| ); | |||
| name = Net; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| C6859E8C029090F304C91782 /* Documentation */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| @@ -2405,6 +2470,7 @@ | |||
| 4B35C4BF0D4731D1000DE7AE /* midiport.h in Headers */, | |||
| 4B35C4C00D4731D1000DE7AE /* JackDebugClient.h in Headers */, | |||
| 4B35C4C10D4731D1000DE7AE /* JackTools.h in Headers */, | |||
| BA222ADB0DC88269001A17F4 /* JackNetTool.h in Headers */, | |||
| 4B9A26060DBF8584006E9FBC /* jslist.h in Headers */, | |||
| 4B4F9C930DC20C0400706CB0 /* JackMessageBuffer.h in Headers */, | |||
| ); | |||
| @@ -2749,6 +2815,7 @@ | |||
| 4B6B9EF70CD095970051EE5A /* midiport.h in Headers */, | |||
| 4B5DB9840CD2429B00EBA5EE /* JackDebugClient.h in Headers */, | |||
| 4BE4CC040CDA153500CCF5BB /* JackTools.h in Headers */, | |||
| BA222AD90DC88269001A17F4 /* JackNetTool.h in Headers */, | |||
| 4B95BCAE0D913073000F7695 /* control.h in Headers */, | |||
| 4B9A26020DBF8584006E9FBC /* jslist.h in Headers */, | |||
| 4B4F9C8D0DC20C0400706CB0 /* JackMessageBuffer.h in Headers */, | |||
| @@ -2962,6 +3029,22 @@ | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222AC60DC88132001A17F4 /* Headers */ = { | |||
| isa = PBXHeadersBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| BA222ADF0DC882A5001A17F4 /* JackNetDriver.h in Headers */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222AE10DC882DB001A17F4 /* Headers */ = { | |||
| isa = PBXHeadersBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| BA222AEE0DC883B3001A17F4 /* JackNetManager.h in Headers */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXHeadersBuildPhase section */ | |||
| /* Begin PBXNativeTarget section */ | |||
| @@ -4262,6 +4345,40 @@ | |||
| productReference = 4BFA99A20AAAF3B0009E916C /* jdelay */; | |||
| productType = "com.apple.product-type.tool"; | |||
| }; | |||
| BA222AC50DC88132001A17F4 /* jack_net Universal */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = BA222ACB0DC88132001A17F4 /* Build configuration list for PBXNativeTarget "jack_net Universal" */; | |||
| buildPhases = ( | |||
| BA222AC60DC88132001A17F4 /* Headers */, | |||
| BA222AC80DC88132001A17F4 /* Sources */, | |||
| BA222ACA0DC88132001A17F4 /* Frameworks */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| ); | |||
| name = "jack_net Universal"; | |||
| productName = jack_coreaudio; | |||
| productReference = BA222ACF0DC88132001A17F4 /* jack_net.so */; | |||
| productType = "com.apple.product-type.library.dynamic"; | |||
| }; | |||
| BA222AE00DC882DB001A17F4 /* netmanager */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = BA222AE50DC882DB001A17F4 /* Build configuration list for PBXNativeTarget "netmanager" */; | |||
| buildPhases = ( | |||
| BA222AE10DC882DB001A17F4 /* Headers */, | |||
| BA222AE20DC882DB001A17F4 /* Sources */, | |||
| BA222AE40DC882DB001A17F4 /* Frameworks */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| ); | |||
| name = netmanager; | |||
| productName = jack_coreaudio; | |||
| productReference = BA222AE90DC882DB001A17F4 /* netmanager.so */; | |||
| productType = "com.apple.product-type.library.dynamic"; | |||
| }; | |||
| /* End PBXNativeTarget section */ | |||
| /* Begin PBXProject section */ | |||
| @@ -4311,7 +4428,9 @@ | |||
| 4B699D97097D421700A18468 /* jack_coreaudio Universal */, | |||
| 4B978DB10A31CF4A009E2DD1 /* jack_portaudio Universal */, | |||
| 4B699DA6097D421700A18468 /* jack_dummy Universal */, | |||
| BA222AC50DC88132001A17F4 /* jack_net Universal */, | |||
| 4BD623ED0CBCF0F000DE782F /* inprocess */, | |||
| BA222AE00DC882DB001A17F4 /* netmanager */, | |||
| 4B35C41B0D4731D1000DE7AE /* jackdmp framework 64bits */, | |||
| 4B35C4270D4731D1000DE7AE /* Jackmp.framework 64 bits */, | |||
| 4B35C4850D4731D1000DE7AE /* Jackservermp.framework 64 bits */, | |||
| @@ -4922,6 +5041,7 @@ | |||
| 4B35C4F30D4731D1000DE7AE /* JackEngineControl.cpp in Sources */, | |||
| 4B35C4F40D4731D1000DE7AE /* JackDebugClient.cpp in Sources */, | |||
| 4B35C4F50D4731D1000DE7AE /* JackTools.cpp in Sources */, | |||
| BA222ADA0DC88269001A17F4 /* JackNetTool.cpp in Sources */, | |||
| 4B9A26640DBF8B14006E9FBC /* JackError.cpp in Sources */, | |||
| 4B9A26680DBF8B23006E9FBC /* JackControl.cpp in Sources */, | |||
| 4B0206A10DC0BAB400319AF1 /* JackProcessSync.cpp in Sources */, | |||
| @@ -5290,6 +5410,7 @@ | |||
| 4B6F7AEE0CD0CDBD00F48A9D /* JackEngineControl.cpp in Sources */, | |||
| 4B5DB9830CD2429A00EBA5EE /* JackDebugClient.cpp in Sources */, | |||
| 4BE4CC030CDA153500CCF5BB /* JackTools.cpp in Sources */, | |||
| BA222AD80DC88268001A17F4 /* JackNetTool.cpp in Sources */, | |||
| 4B95BCAC0D913064000F7695 /* JackControl.cpp in Sources */, | |||
| 4B9A25B60DBF8330006E9FBC /* JackError.cpp in Sources */, | |||
| 4B02069E0DC0BAB400319AF1 /* JackProcessSync.cpp in Sources */, | |||
| @@ -5531,6 +5652,22 @@ | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222AC80DC88132001A17F4 /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| BA222ADE0DC882A5001A17F4 /* JackNetDriver.cpp in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| BA222AE20DC882DB001A17F4 /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| BA222AED0DC883B3001A17F4 /* JackNetManager.cpp in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXSourcesBuildPhase section */ | |||
| /* Begin PBXTargetDependency section */ | |||
| @@ -5879,6 +6016,16 @@ | |||
| target = 4BFA99980AAAF3B0009E916C /* jdelay Universal */; | |||
| targetProxy = 4BFA99AB0AAAF41D009E916C /* PBXContainerItemProxy */; | |||
| }; | |||
| BA222AF00DC883EF001A17F4 /* PBXTargetDependency */ = { | |||
| isa = PBXTargetDependency; | |||
| target = BA222AE00DC882DB001A17F4 /* netmanager */; | |||
| targetProxy = BA222AEF0DC883EF001A17F4 /* PBXContainerItemProxy */; | |||
| }; | |||
| BA222AF20DC883F3001A17F4 /* PBXTargetDependency */ = { | |||
| isa = PBXTargetDependency; | |||
| target = BA222AC50DC88132001A17F4 /* jack_net Universal */; | |||
| targetProxy = BA222AF10DC883F3001A17F4 /* PBXContainerItemProxy */; | |||
| }; | |||
| /* End PBXTargetDependency section */ | |||
| /* Begin XCBuildConfiguration section */ | |||
| @@ -13673,6 +13820,278 @@ | |||
| }; | |||
| name = Default; | |||
| }; | |||
| BA222ACC0DC88132001A17F4 /* Development */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUGGING_SYMBOLS = YES; | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_DYNAMIC_NO_PIC = NO; | |||
| GCC_ENABLE_FIX_AND_CONTINUE = YES; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = YES; | |||
| GCC_MODEL_TUNING = G4; | |||
| GCC_OPTIMIZATION_LEVEL = 0; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| OPTIMIZATION_CFLAGS = "-O0"; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackservermp, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = jack_net; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| ZERO_LINK = YES; | |||
| }; | |||
| name = Development; | |||
| }; | |||
| BA222ACD0DC88132001A17F4 /* Deployment */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| COPY_PHASE_STRIP = YES; | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_ENABLE_FIX_AND_CONTINUE = NO; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = NO; | |||
| GCC_MODEL_TUNING = G4; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| MACOSX_DEPLOYMENT_TARGET = 10.4; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackservermp, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = jack_net; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| ZERO_LINK = NO; | |||
| }; | |||
| name = Deployment; | |||
| }; | |||
| BA222ACE0DC88132001A17F4 /* Default */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = NO; | |||
| GCC_MODEL_TUNING = G4; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "-DMACH_RPC_MACH_SEMA"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackdmp, | |||
| "-framework", | |||
| AudioToolBox, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = jack_dummy; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| }; | |||
| name = Default; | |||
| }; | |||
| BA222AE60DC882DB001A17F4 /* Development */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUGGING_SYMBOLS = YES; | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_DYNAMIC_NO_PIC = NO; | |||
| GCC_ENABLE_FIX_AND_CONTINUE = YES; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = YES; | |||
| GCC_MODEL_TUNING = G4; | |||
| GCC_OPTIMIZATION_LEVEL = 0; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ""; | |||
| HEADER_SEARCH_PATHS = ../common; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| OPTIMIZATION_CFLAGS = "-O0"; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackservermp, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = netmanager; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| ZERO_LINK = YES; | |||
| }; | |||
| name = Development; | |||
| }; | |||
| BA222AE70DC882DB001A17F4 /* Deployment */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| COPY_PHASE_STRIP = YES; | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_ENABLE_FIX_AND_CONTINUE = NO; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = NO; | |||
| GCC_MODEL_TUNING = G4; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ""; | |||
| HEADER_SEARCH_PATHS = ../common; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| MACOSX_DEPLOYMENT_TARGET = 10.4; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackservermp, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = netmanager; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| ZERO_LINK = NO; | |||
| }; | |||
| name = Deployment; | |||
| }; | |||
| BA222AE80DC882DB001A17F4 /* Default */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ( | |||
| i386, | |||
| ppc, | |||
| ); | |||
| DYLIB_COMPATIBILITY_VERSION = 1; | |||
| DYLIB_CURRENT_VERSION = 1; | |||
| EXECUTABLE_EXTENSION = so; | |||
| GCC_GENERATE_DEBUGGING_SYMBOLS = NO; | |||
| GCC_MODEL_TUNING = G4; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ""; | |||
| HEADER_SEARCH_PATHS = ../common; | |||
| INSTALL_PATH = /usr/local/lib; | |||
| LIBRARY_STYLE = DYNAMIC; | |||
| MACH_O_TYPE = mh_dylib; | |||
| OTHER_CFLAGS = ""; | |||
| OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; | |||
| OTHER_LDFLAGS = ( | |||
| "-framework", | |||
| Jackdmp, | |||
| "-framework", | |||
| AudioToolBox, | |||
| "-framework", | |||
| CoreAudio, | |||
| "-framework", | |||
| CoreServices, | |||
| "-framework", | |||
| AudioUnit, | |||
| ); | |||
| OTHER_REZFLAGS = ""; | |||
| PREBINDING = NO; | |||
| PRODUCT_NAME = inprocess; | |||
| SDKROOT = ""; | |||
| SECTORDER_FLAGS = ""; | |||
| WARNING_CFLAGS = ( | |||
| "-Wmost", | |||
| "-Wno-four-char-constants", | |||
| "-Wno-unknown-pragmas", | |||
| ); | |||
| }; | |||
| name = Default; | |||
| }; | |||
| /* End XCBuildConfiguration section */ | |||
| /* Begin XCConfigurationList section */ | |||
| @@ -14396,6 +14815,26 @@ | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Default; | |||
| }; | |||
| BA222ACB0DC88132001A17F4 /* Build configuration list for PBXNativeTarget "jack_net Universal" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| BA222ACC0DC88132001A17F4 /* Development */, | |||
| BA222ACD0DC88132001A17F4 /* Deployment */, | |||
| BA222ACE0DC88132001A17F4 /* Default */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Default; | |||
| }; | |||
| BA222AE50DC882DB001A17F4 /* Build configuration list for PBXNativeTarget "netmanager" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| BA222AE60DC882DB001A17F4 /* Development */, | |||
| BA222AE70DC882DB001A17F4 /* Deployment */, | |||
| BA222AE80DC882DB001A17F4 /* Default */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Default; | |||
| }; | |||
| /* End XCConfigurationList section */ | |||
| }; | |||
| rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; | |||
| @@ -14,6 +14,10 @@ sudo cp jackdmp /usr/local/bin | |||
| sudo install -d /usr/local/lib/jackmp | |||
| sudo cp jack_coreaudio.so /usr/local/lib/jackmp | |||
| sudo cp jack_dummy.so /usr/local/lib/jackmp | |||
| sudo cp jack_net.so /usr/local/lib/jackmp | |||
| # Copy tools | |||
| sudo cp netmanager.so /usr/local/lib/jackmp | |||
| # Create links to jackmp ressources | |||
| cd /usr/local/lib && [ -f libjack.0.dylib ] && sudo mv -f libjack.0.dylib tmp_libjack.0.dylib | |||