/* 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 measure ******************************************************************************** template struct NetMeasure { uint fTableSize;; T* fTable; NetMeasure ( uint table_size = 5 ) { fTableSize = table_size; fTable = new T[fTableSize]; } ~NetMeasure() { delete[] fTable; } }; // net monitor ******************************************************************************** template class NetMonitor { private: uint fMeasureCnt; uint fMeasurePoints; NetMeasure* fMeasureTable; uint fTablePos; void DisplayMeasure ( NetMeasure& measure ) { string display; for ( uint m_id = 0; m_id < measure.fTableSize; m_id++ ) { char* value; sprintf ( value, "%lu ", measure.fTable[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; fMeasureTable = new NetMeasure[fMeasureCnt]; fTablePos = 0; for ( uint i = 0; i < fMeasureCnt; i++ ) InitTable(); } ~NetMonitor() { jack_log ( "NetMonitor::~NetMonitor" ); delete fMeasureTable; } uint InitTable() { uint measure_id; for ( measure_id = 0; measure_id < fMeasureTable[fTablePos].fTableSize; measure_id++ ) fMeasureTable[fTablePos].fTable[measure_id] = 0; if ( ++fTablePos == fMeasureCnt ) fTablePos = 0; return fTablePos; } uint Write ( NetMeasure& measure ) { for ( uint m_id = 0; m_id < measure.fTableSize; m_id++ ) fMeasureTable[fTablePos].fTable[m_id] = measure.fTable[m_id]; if ( ++fTablePos == fMeasureCnt ) fTablePos = 0; //DisplayMeasure ( fMeasureTable[fTablePos] ); 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" ); //printf each measure with tab separated values for ( uint id = 0; id < fMeasureCnt; id++ ) { for ( uint m_id = 0; m_id < fMeasureTable[id].fTableSize; m_id++ ) fprintf ( file, "%lu \t ", fMeasureTable[id].fTable[m_id] ); 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 ) { 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 ); }