From 4c488ee522154e46cc55e40c3f4b810e02750cce Mon Sep 17 00:00:00 2001 From: sletz Date: Wed, 4 Jun 2008 13:34:25 +0000 Subject: [PATCH] Merge network branch git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2445 0c269be4-1314-0410-8aa9-9f06e86f4224 --- SConstruct | 1 + common/JackAudioDriver.h | 20 +- common/JackDriver.h | 134 ++--- common/JackException.h | 67 +-- common/JackNetDriver.cpp | 690 +++++++++++++++++++++++ common/JackNetDriver.h | 88 +++ common/JackNetManager.cpp | 673 ++++++++++++++++++++++ common/JackNetManager.h | 118 ++++ common/JackNetTool.cpp | 312 ++++++++++ common/JackNetTool.h | 174 ++++++ common/SConscript | 2 + common/wscript | 10 +- linux/SConscript | 4 + linux/wscript | 3 + macosx/Jackdmp.xcodeproj/project.pbxproj | 439 ++++++++++++++ macosx/install_jackdmp | 4 + 16 files changed, 2595 insertions(+), 144 deletions(-) create mode 100644 common/JackNetDriver.cpp create mode 100644 common/JackNetDriver.h create mode 100644 common/JackNetManager.cpp create mode 100644 common/JackNetManager.h create mode 100644 common/JackNetTool.cpp create mode 100644 common/JackNetTool.h diff --git a/SConstruct b/SConstruct index 33caf15b..73eff4dc 100644 --- a/SConstruct +++ b/SConstruct @@ -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" diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index 0a572b90..c92eaafa 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -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(); diff --git a/common/JackDriver.h b/common/JackDriver.h index 53002f29..ce48956f 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -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 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 diff --git a/common/JackException.h b/common/JackException.h index c044f439..0260a6d7 100644 --- a/common/JackException.h +++ b/common/JackException.h @@ -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 -#include -#include -#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 diff --git a/common/JackNetDriver.cpp b/common/JackNetDriver.cpp new file mode 100644 index 00000000..a4a3a94d --- /dev/null +++ b/common/JackNetDriver.cpp @@ -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 ( &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 ( &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 ( &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 ( &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 ( 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 ( 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 ( 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 ( 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 ( fGraphManager->GetBuffer ( fMidiCapturePortList[port_index], fEngineControl->fBufferSize ) ); + } + + JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer ( int port_index ) + { + return static_cast ( fGraphManager->GetBuffer ( fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize ) ); + } + + int JackNetDriver::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 ( 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 +} diff --git a/common/JackNetDriver.h b/common/JackNetDriver.h new file mode 100644 index 00000000..255e3cf2 --- /dev/null +++ b/common/JackNetDriver.h @@ -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 diff --git a/common/JackNetManager.cpp b/common/JackNetManager.cpp new file mode 100644 index 00000000..18c2ebbb --- /dev/null +++ b/common/JackNetManager.cpp @@ -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 ( 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 ( &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 ( &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 ( 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 ( fRxBuffer ); + + //buffers + for ( int port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) + fNetMidiCaptureBuffer->fPortBuffer[port_index] = + static_cast ( 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 ( 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 ( 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 ( 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 ( &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 ( 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 ( &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 ( &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 diff --git a/common/JackNetManager.h b/common/JackNetManager.h new file mode 100644 index 00000000..b0bdb5fa --- /dev/null +++ b/common/JackNetManager.h @@ -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 +#include +#include "thread.h" +#include +#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 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 diff --git a/common/JackNetTool.cpp b/common/JackNetTool.cpp new file mode 100644 index 00000000..0d5351d8 --- /dev/null +++ b/common/JackNetTool.cpp @@ -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(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 ( params->fFramesPerPacket ) / static_cast ( 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; + } +} diff --git a/common/JackNetTool.h b/common/JackNetTool.h new file mode 100644 index 00000000..2940a710 --- /dev/null +++ b/common/JackNetTool.h @@ -0,0 +1,174 @@ + +#include "types.h" +#include "JackConstants.h" +#include "JackMidiPort.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 ); +} diff --git a/common/SConscript b/common/SConscript index c87591a9..6db1fb7a 100644 --- a/common/SConscript +++ b/common/SConscript @@ -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']) diff --git a/common/wscript b/common/wscript index 7d2683ac..0727b199 100644 --- a/common/wscript +++ b/common/wscript @@ -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') diff --git a/linux/SConscript b/linux/SConscript index 1309ad90..9d73d300 100644 --- a/linux/SConscript +++ b/linux/SConscript @@ -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'] ) diff --git a/linux/wscript b/linux/wscript index 320400a9..76fea2db 100644 --- a/linux/wscript +++ b/linux/wscript @@ -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') + diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 4f8fd2ce..30f21580 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -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 = ""; }; /* 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 = ""; }; + BA222AEA0DC88379001A17F4 /* Net */ = { + isa = PBXGroup; + children = ( + BA222AEB0DC883B3001A17F4 /* JackNetManager.cpp */, + BA222AEC0DC883B3001A17F4 /* JackNetManager.h */, + BA222AD60DC88268001A17F4 /* JackNetTool.cpp */, + BA222AD70DC88268001A17F4 /* JackNetTool.h */, + ); + name = Net; + sourceTree = ""; + }; 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 */; diff --git a/macosx/install_jackdmp b/macosx/install_jackdmp index 85fe1e30..1bfce2f3 100755 --- a/macosx/install_jackdmp +++ b/macosx/install_jackdmp @@ -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