/* Copyright (C) 2008 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackConstants.h" #include "JackMidiPort.h" #include "JackExports.h" #include "JackError.h" #include "JackTools.h" #include "JackPlatformNetSocket.h" #include "types.h" #include #include #include using namespace std; namespace Jack { typedef struct _session_params session_params_t; typedef struct _packet_header packet_header_t; typedef struct _net_transport_data net_transport_data_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 uint32_t fPacketID; //indicates the packet type char fMasterNetName[256]; //master hostname (network) char fSlaveNetName[256]; //slave hostname (network) uint32_t fMtu; //connection mtu uint32_t fID; //slave's ID uint32_t fTransportSync; //is the transport synced ? uint32_t fSendAudioChannels; //number of master->slave channels uint32_t fReturnAudioChannels; //number of slave->master channels uint32_t fSendMidiChannels; //number of master->slave midi channels uint32_t fReturnMidiChannels; //number of slave->master midi channels uint32_t fSampleRate; //session sample rate uint32_t fPeriodSize; //period size uint32_t fFramesPerPacket; //complete frames per packet uint32_t fBitdepth; //samples bitdepth (unused) char fName[JACK_CLIENT_NAME_SIZE]; //slave's name }; //net status ********************************************************************************** enum _net_status { NET_SOCKET_ERROR = 0, NET_CONNECT_ERROR, NET_ERROR, NET_SEND_ERROR, NET_RECV_ERROR, NET_CONNECTED, NET_ROLLING }; typedef enum _net_status net_status_t; //sync packet type **************************************************************************** enum _sync_packet_type { INVALID = 0, //... 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 uint32_t fID; //to identify the slave uint32_t fBitdepth; //bitdepth of the data samples uint32_t fMidiDataSize; //size of midi data (if packet is 'midi typed') in bytes uint32_t fNMidiPckt; //number of midi packets of the cycle uint32_t fCycle; //process cycle counter uint32_t fSubCycle; //midi/audio subcycle counter char fIsLastPckt; //is it the last packet of a given cycle ('y' or 'n') char fFree[13]; //unused }; //transport data ****************************************************************************** struct _net_transport_data { char fTransportType[10]; //test value ('transport') jack_position_t fCurPos; jack_transport_state_t fCurState; }; //midi data *********************************************************************************** class EXPORT NetMidiBuffer { private: int fNPorts; size_t fMaxBufsize; int fMaxPcktSize; char* fBuffer; char* fNetBuffer; JackMidiBuffer** fPortBuffer; public: NetMidiBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ); ~NetMidiBuffer(); void Reset(); size_t GetSize(); //utility void DisplayEvents(); //jack<->buffer int RenderFromJackPorts(); int RenderToJackPorts(); //network<->buffer int RenderFromNetwork ( int subcycle, size_t copy_size ); int RenderToNetwork ( int subcycle, size_t total_size ); void SetBuffer(int index, JackMidiBuffer* buffer); }; // audio data ********************************************************************************* class EXPORT NetAudioBuffer { private: int fNPorts; jack_nframes_t fPeriodSize; jack_nframes_t fSubPeriodSize; size_t fSubPeriodBytesSize; char* fNetBuffer; sample_t** fPortBuffer; public: NetAudioBuffer ( session_params_t* params, uint32_t nports, char* net_buffer ); ~NetAudioBuffer(); size_t GetSize(); //jack<->buffer void RenderFromJackPorts ( int subcycle ); void RenderToJackPorts ( int subcycle ); void SetBuffer(int index, sample_t* buffer); }; // net monitor ******************************************************************************** template class NetMonitor { private: uint fMeasureCnt; uint fMeasurePoints; T** fMeasureTable; uint fTablePos; void DisplayMeasure ( T* measure ) { string display; for ( uint m_id = 0; m_id < fMeasurePoints; m_id++ ) { char* value; sprintf ( value, "%llu ", measure[m_id] ); display += string ( value ); } cout << "NetMonitor:: '" << display << "'" << endl; } public: NetMonitor ( uint measure_cnt = 512, uint measure_points = 5 ) { jack_log ( "JackNetMonitor::JackNetMonitor measure_cnt %u measure_points %u", measure_cnt, measure_points ); fMeasureCnt = measure_cnt; fMeasurePoints = measure_points; fTablePos = 0; //allocate measure table fMeasureTable = new T*[fMeasureCnt]; for ( uint i = 0; i < fMeasureCnt; i++ ) fMeasureTable[i] = new T[fMeasurePoints]; //init measure table for ( uint cnt = 0; cnt < fMeasureCnt; cnt++ ) for ( uint point = 0; point < fMeasurePoints; point++ ) fMeasureTable[cnt][point] = 0; } ~NetMonitor() { jack_log ( "NetMonitor::~NetMonitor" ); for ( uint cnt = 0; cnt < fMeasureCnt; cnt++ ) delete[] fMeasureTable[cnt]; delete[] fMeasureTable; } void InitTable() { for ( uint cnt = 0; cnt < fMeasureCnt; cnt++ ) for ( uint point = 0; point < fMeasurePoints; point++ ) fMeasureTable[cnt][point] = 0; } uint Write ( T* measure ) { for ( uint point = 0; point < fMeasurePoints; point++ ) fMeasureTable[fTablePos][point] = measure[point]; if ( ++fTablePos == fMeasureCnt ) fTablePos = 0; return fTablePos; } int Save ( string& filename ) { filename += "_netmonitor.log"; jack_log ( "JackNetMonitor::Save filename %s", filename.c_str() ); FILE* file = fopen ( filename.c_str(), "w" ); //print each measure with tab separated values for ( uint cnt = 0; cnt < fMeasureCnt; cnt++ ) { for ( uint pt = 0; pt < fMeasurePoints; pt++ ) fprintf ( file, "%llu \t ", fMeasureTable[cnt][pt] ); fprintf ( file, "\n" ); } fclose(file); return 0; } int SetPlotFile ( string& name, string* options_list = NULL, uint options_number = 0, string* field_names = NULL, uint field_number = 0 ) { //names and file string title = name + "_netmonitor"; string plot_filename = title + ".plt"; string data_filename = title + ".log"; FILE* file = fopen ( plot_filename.c_str(), "w" ); //base options fprintf ( file, "set multiplot\n" ); fprintf ( file, "set grid\n" ); fprintf ( file, "set title \"%s\"\n", title.c_str() ); //additional options for ( uint i = 0; i < options_number; i++ ) { jack_log ( "JackNetMonitor::SetPlotFile - Add plot option : '%s'", options_list[i].c_str() ); fprintf ( file, "%s\n", options_list[i].c_str() ); } //plot fprintf ( file, "plot " ); for ( uint row = 1; row <= field_number; row++ ) { jack_log ( "JackNetMonitor::SetPlotFile - Add plot : file '%s' row '%d' title '%s' field '%s'", data_filename.c_str(), row, name.c_str(), field_names[row-1].c_str() ); fprintf ( file, "\"%s\" using %u title \"%s : %s\" with lines", data_filename.c_str(), row, name.c_str(), field_names[row-1].c_str() ); fprintf ( file, ( row < field_number ) ? "," : "\n" ); } jack_log ( "JackNetMonitor::SetPlotFile - Saving GnuPlot '.plt' file to '%s'", plot_filename.c_str() ); fclose ( file ); return 0; } }; //utility ************************************************************************************* //socket API management EXPORT int SocketAPIInit(); EXPORT int SocketAPIEnd(); //n<-->h functions EXPORT void SessionParamsHToN ( session_params_t* params ); EXPORT void SessionParamsNToH ( session_params_t* params ); EXPORT void PacketHeaderHToN ( packet_header_t* header ); EXPORT void PacketHeaderNToH ( packet_header_t* header ); //display session parameters EXPORT void SessionParamsDisplay ( session_params_t* params ); //display packet header EXPORT void PacketHeaderDisplay ( packet_header_t* header ); //get the packet type from a sesion parameters EXPORT sync_packet_type_t GetPacketType ( session_params_t* params ); //set the packet type in a session parameters EXPORT int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type ); //step of network initialization EXPORT jack_nframes_t SetFramesPerPacket ( session_params_t* params ); //get the midi packet number for a given cycle EXPORT int GetNMidiPckt ( session_params_t* params, size_t data_size ); //set the recv timeout on a socket EXPORT int SetRxTimeout ( JackNetSocket* socket, session_params_t* params ); //check if 'next' packet is really the next after 'previous' EXPORT bool IsNextPacket ( packet_header_t* previous, packet_header_t* next, uint subcycles ); }