From 76fd95b7f6f178bfcb2dff1e02c0015257cb6431 Mon Sep 17 00:00:00 2001 From: moret Date: Tue, 22 Jul 2008 11:44:30 +0000 Subject: [PATCH] Add NetMonitor to trunk git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2717 0c269be4-1314-0410-8aa9-9f06e86f4224 --- common/JackNetDriver.cpp | 59 ++++++++++++- common/JackNetDriver.h | 25 ++++-- common/JackNetManager.cpp | 75 +++++++++++++---- common/JackNetManager.h | 23 +++-- common/JackNetTool.cpp | 8 +- common/JackNetTool.h | 153 ++++++++++++++++++++++++++++++++-- windows/jack_audioadapter.cbp | 51 ++++++++++++ windows/jack_connect.cbp | 45 ++++++++++ windows/jack_disconnect.cbp | 45 ++++++++++ windows/jack_dummy.cbp | 46 ++++++++++ windows/jack_load.cbp | 46 ++++++++++ windows/jack_lsp.cbp | 24 ++++++ windows/jack_metro.cbp | 51 ++++++++++++ windows/jack_netdriver.cbp | 52 ++++++++++++ windows/jack_netmanager.cbp | 51 ++++++++++++ windows/jack_portaudio.cbp | 54 ++++++++++++ windows/jack_unload.cbp | 45 ++++++++++ windows/jackdmp.cbp | 51 ++++++++++++ windows/jackdmp.workspace | 4 +- windows/libjackmp.cbp | 52 ++++++++++++ windows/libjackservermp.cbp | 55 ++++++++++++ 21 files changed, 972 insertions(+), 43 deletions(-) diff --git a/common/JackNetDriver.cpp b/common/JackNetDriver.cpp index e920a465..bf579714 100644 --- a/common/JackNetDriver.cpp +++ b/common/JackNetDriver.cpp @@ -32,6 +32,22 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { +#ifdef NETMONITOR + std::string JackNetDriver::fMonitorPlotOptions[] = + { + std::string ( "set xlabel \"audio cycles\"" ), + std::string ( "set ylabel \"usecs\"" ) + }; + std::string JackNetDriver::fMonitorFieldNames[] = + { + std::string ( "cyclestart" ), + std::string ( "read end" ), + std::string ( "write start" ), + std::string ( "sync end" ), + std::string ( "send end" ) + }; +#endif + JackNetDriver::JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, const char* net_name, uint transport_sync ) : JackAudioDriver ( name, alias, engine, table ), fSocket ( ip, port ) @@ -44,6 +60,12 @@ namespace Jack strcpy ( fParams.fName, net_name ); fSocket.GetName ( fParams.fSlaveNetName ); fParams.fTransportSync = transport_sync; + + //monitor +#ifdef NETMONITOR + std::string plot_file_name = std::string ( fParams.fName ); + fMonitor.SetPlotFile ( plot_file_name, JackNetDriver::fMonitorPlotOptions, 2, JackNetDriver::fMonitorFieldNames, 5 ); +#endif } JackNetDriver::~JackNetDriver() @@ -76,6 +98,16 @@ namespace Jack return res; } + +#ifdef NETMONITOR + int JackNetDriver::Close() + { + std::string filename = string ( fParams.fName ); + fMonitor.Save ( filename ); + return JackDriver::Close(); + } +#endif + int JackNetDriver::Attach() { return 0; @@ -290,7 +322,7 @@ namespace Jack fAudioTxLen = sizeof ( packet_header_t ) + fNetAudioPlaybackBuffer->GetSize(); fAudioRxLen = sizeof ( packet_header_t ) + fNetAudioCaptureBuffer->GetSize(); - //payload size + //payload size fPayloadSize = fParams.fMtu - sizeof ( packet_header_t ); return 0; @@ -485,6 +517,11 @@ namespace Jack //take the time at the beginning of the cycle JackDriver::CycleTakeBeginTime(); +#ifdef NETMONITOR + fUsecCycleStart = GetMicroSeconds(); + fMeasure.fTable[0] = GetMicroSeconds() - fUsecCycleStart; +#endif + //audio, midi or sync if driver is late if ( fParams.fSendMidiChannels || fParams.fSendAudioChannels ) { @@ -522,6 +559,12 @@ namespace Jack while ( fRxHeader.fIsLastPckt != 'y' ); } fRxHeader.fCycle = rx_head->fCycle; + + +#ifdef NETMONITOR + fMeasure.fTable[1] = GetMicroSeconds() - fUsecCycleStart; +#endif + return 0; } @@ -540,6 +583,10 @@ namespace Jack for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer ( audio_port_index )); +#ifdef NETMONITOR + fMeasure.fTable[2] = GetMicroSeconds() - fUsecCycleStart; +#endif + //sync fTxHeader.fDataType = 's'; if ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels ) @@ -548,6 +595,10 @@ namespace Jack SetSyncPacket(); tx_bytes = Send ( fParams.fMtu, 0 ); +#ifdef NETMONITOR + fMeasure.fTable[3] = GetMicroSeconds() - fUsecCycleStart; +#endif + //midi if ( fParams.fReturnMidiChannels ) { @@ -579,6 +630,12 @@ namespace Jack tx_bytes = Send ( fAudioTxLen, 0 ); } } + +#ifdef NETMONITOR + fMeasure.fTable[4] = GetMicroSeconds() - fUsecCycleStart; + fMonitor.Write ( fMeasure ); +#endif + return 0; } diff --git a/common/JackNetDriver.h b/common/JackNetDriver.h index 9a993f10..ac8cddb4 100644 --- a/common/JackNetDriver.h +++ b/common/JackNetDriver.h @@ -35,31 +35,40 @@ namespace Jack uint fNSubProcess; net_transport_data_t fTransportData; - //jack ports + //jack ports jack_port_id_t* fMidiCapturePortList; jack_port_id_t* fMidiPlaybackPortList; - //headers + //headers packet_header_t fTxHeader; packet_header_t fRxHeader; - //network buffers + //network buffers char* fTxBuffer; char* fRxBuffer; char* fTxData; char* fRxData; - //jack buffers + //jack buffers NetMidiBuffer* fNetMidiCaptureBuffer; NetMidiBuffer* fNetMidiPlaybackBuffer; NetAudioBuffer* fNetAudioCaptureBuffer; NetAudioBuffer* fNetAudioPlaybackBuffer; - //sizes + //sizes int fAudioRxLen; int fAudioTxLen; int fPayloadSize; + //monitoring +#ifdef NETMONITOR + static std::string fMonitorPlotOptions[]; + static std::string fMonitorFieldNames[]; + jack_time_t fUsecCycleStart; + NetMeasure fMeasure; + NetMonitor fMonitor; +#endif + bool Init(); net_status_t GetNetMaster(); net_status_t SendMasterStartSync(); @@ -74,7 +83,7 @@ namespace Jack int Recv ( size_t size, int flags ); int Send ( size_t size, int flags ); - int SetSyncPacket(); + int SetSyncPacket(); int TransportSync(); public: @@ -86,6 +95,10 @@ namespace Jack 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 ); +#ifdef NETMONITOR + int Close(); +#endif + int Attach(); int Detach(); diff --git a/common/JackNetManager.cpp b/common/JackNetManager.cpp index fd89c639..51cfab02 100644 --- a/common/JackNetManager.cpp +++ b/common/JackNetManager.cpp @@ -27,6 +27,21 @@ using namespace std; namespace Jack { //JackNetMaster****************************************************************************************************** +#ifdef NETMONITOR + std::string JackNetMaster::fMonitorPlotOptions[] = + { + std::string ( "set xlabel \"audio cycles\"" ), + std::string ( "set ylabel \"audio frames\"" ) + }; + std::string JackNetMaster::fMonitorFieldNames[] = + { + std::string ( "cycle start" ), + std::string ( "sync send" ), + std::string ( "send end" ), + std::string ( "sync recv" ), + std::string ( "end of cycle" ) + }; +#endif JackNetMaster::JackNetMaster ( JackNetMasterManager* manager, session_params_t& params ) : fSocket() { @@ -98,6 +113,12 @@ namespace Jack //payload size fPayloadSize = fParams.fMtu - sizeof ( packet_header_t ); + + //monitor +#ifdef NETMONITOR + std::string plot_file_name = std::string ( fParams.fName ); + fMonitor.SetPlotFile ( plot_file_name, fMonitorPlotOptions, 2, fMonitorFieldNames, 5 ); +#endif } JackNetMaster::~JackNetMaster() @@ -120,6 +141,10 @@ namespace Jack delete[] fMidiPlaybackPorts; delete[] fTxBuffer; delete[] fRxBuffer; +#ifdef NETMONITOR + std::string filename = string ( fParams.fName ); + fMonitor.Save ( filename ); +#endif } bool JackNetMaster::Init() @@ -360,20 +385,29 @@ fail: fTxHeader.fIsLastPckt = 'n'; packet_header_t* rx_head = reinterpret_cast ( fRxBuffer ); +#ifdef NETMONITOR + NetMeasure measure; + measure.fTable[0] = jack_frames_since_cycle_start( fJackClient ); +#endif + //buffers uint port_index; for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ ) fNetMidiCaptureBuffer->SetBuffer(port_index, - static_cast ( jack_port_get_buffer ( fMidiCapturePorts[port_index], fParams.fPeriodSize ))); + static_cast ( jack_port_get_buffer ( fMidiCapturePorts[port_index], + fParams.fPeriodSize ))); for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ ) fNetAudioCaptureBuffer->SetBuffer(port_index, - static_cast ( jack_port_get_buffer ( fAudioCapturePorts[port_index], fParams.fPeriodSize ))); + static_cast ( jack_port_get_buffer ( fAudioCapturePorts[port_index], + fParams.fPeriodSize ))); for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ ) fNetMidiPlaybackBuffer->SetBuffer(port_index, - static_cast ( jack_port_get_buffer ( fMidiPlaybackPorts[port_index], fParams.fPeriodSize ))); + static_cast ( jack_port_get_buffer ( fMidiPlaybackPorts[port_index], + fParams.fPeriodSize ))); for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ ) fNetAudioPlaybackBuffer->SetBuffer(port_index, - static_cast ( jack_port_get_buffer ( fAudioPlaybackPorts[port_index], fParams.fPeriodSize ))); + static_cast ( jack_port_get_buffer ( fAudioPlaybackPorts[port_index], + fParams.fPeriodSize ))); //send ------------------------------------------------------------------------------------------------------------------ //sync @@ -386,6 +420,10 @@ fail: if ( ( tx_bytes == 0 ) || ( tx_bytes == SOCKET_ERROR ) ) return tx_bytes; +#ifdef NETMONITOR + measure.fTable[1] = jack_frames_since_cycle_start( fJackClient ); +#endif + //midi if ( fParams.fSendMidiChannels ) { @@ -422,20 +460,23 @@ fail: } } - //receive ( if there is stg to receive...)------------------------------------------------------------------------------------- +#ifdef NETMONITOR + measure.fTable[2] = jack_frames_since_cycle_start( fJackClient ); +#endif + + //receive -------------------------------------------------------------------------------------------------------------------- //sync - if ( fParams.fTransportSync ) + do { - do - { - rx_bytes = Recv ( fParams.fMtu, 0 ); - if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) ) - return rx_bytes; - } - while ( !rx_bytes && ( rx_head->fDataType != 's' ) ); - //then update fSyncState from received sync packet + rx_bytes = Recv ( fParams.fMtu, 0 ); + if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) ) + return rx_bytes; } + while ( !rx_bytes && ( rx_head->fDataType != 's' ) ); +#ifdef NETMONITOR + measure.fTable[3] = jack_frames_since_cycle_start( fJackClient ); +#endif if ( fParams.fReturnMidiChannels || fParams.fReturnAudioChannels ) { @@ -474,6 +515,11 @@ fail: } while ( fRxHeader.fIsLastPckt != 'y' ); } + +#ifdef NETMONITOR + measure.fTable[4] = jack_frames_since_cycle_start( fJackClient ); + fMonitor.Write ( measure ); +#endif return 0; } @@ -542,6 +588,7 @@ fail: if ( ( *it )->fSyncState == 0 ) ret = 0; jack_log ( "JackNetMasterManager::SyncCallback returns '%s'", ( ret ) ? "true" : "false" ); + return ret; } void* JackNetMasterManager::NetManagerThread ( void* arg ) diff --git a/common/JackNetManager.h b/common/JackNetManager.h index 442e59bc..f5e8e114 100644 --- a/common/JackNetManager.h +++ b/common/JackNetManager.h @@ -43,46 +43,53 @@ namespace Jack int fNetJumpCnt; bool fRunning; - //jack client + //jack client jack_client_t* fJackClient; const char* fClientName; - //jack ports + //jack ports jack_port_t** fAudioCapturePorts; jack_port_t** fAudioPlaybackPorts; jack_port_t** fMidiCapturePorts; jack_port_t** fMidiPlaybackPorts; - //sync and transport + //sync and transport int fSyncState; net_transport_data_t fTransportData; - //network headers + //network headers packet_header_t fTxHeader; packet_header_t fRxHeader; - //network buffers + //network buffers char* fTxBuffer; char* fRxBuffer; char* fTxData; char* fRxData; - //jack buffers + //jack buffers NetAudioBuffer* fNetAudioCaptureBuffer; NetAudioBuffer* fNetAudioPlaybackBuffer; NetMidiBuffer* fNetMidiCaptureBuffer; NetMidiBuffer* fNetMidiPlaybackBuffer; - //sizes + //sizes int fAudioTxLen; int fAudioRxLen; int fPayloadSize; + //monitoring +#ifdef NETMONITOR + static std::string fMonitorPlotOptions[]; + static std::string fMonitorFieldNames[]; + NetMonitor fMonitor; +#endif + bool Init(); void FreePorts(); void Exit(); - int SetSyncPacket(); + int SetSyncPacket(); int Send ( char* buffer, size_t size, int flags ); int Recv ( size_t size, int flags ); diff --git a/common/JackNetTool.cpp b/common/JackNetTool.cpp index d1880436..0a24b3f6 100644 --- a/common/JackNetTool.cpp +++ b/common/JackNetTool.cpp @@ -188,8 +188,8 @@ namespace Jack EXPORT void SessionParamsDisplay ( session_params_t* params ) { - char bitdepth[16]; - ( params->fBitdepth ) ? sprintf ( bitdepth, "%u", params->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); + char bitdepth[16]; + ( params->fBitdepth ) ? sprintf ( bitdepth, "%u", params->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); jack_info ( "**************** Network parameters ****************" ); jack_info ( "Name : %s", params->fName ); jack_info ( "Protocol revision : %c", params->fProtocolVersion ); @@ -274,8 +274,8 @@ namespace Jack EXPORT void PacketHeaderDisplay ( packet_header_t* header ) { - char bitdepth[16]; - ( header->fBitdepth ) ? sprintf ( bitdepth, "%u", header->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); + char bitdepth[16]; + ( header->fBitdepth ) ? sprintf ( bitdepth, "%u", header->fBitdepth ) : sprintf ( bitdepth, "%s", "float" ); jack_info ( "********************Header********************" ); jack_info ( "Data type : %c", header->fDataType ); jack_info ( "Data stream : %c", header->fDataStream ); diff --git a/common/JackNetTool.h b/common/JackNetTool.h index 39d7c3e3..17f8af54 100644 --- a/common/JackNetTool.h +++ b/common/JackNetTool.h @@ -29,6 +29,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include +using namespace std; + namespace Jack { typedef struct _session_params session_params_t; @@ -61,7 +63,6 @@ namespace Jack char fName[JACK_CLIENT_NAME_SIZE]; //slave's name }; - //net status ********************************************************************************** enum _net_status @@ -111,12 +112,12 @@ namespace Jack //transport data ****************************************************************************** - struct _net_transport_data - { - char fTransportType[10]; //test value ('transport') - jack_position_t fCurPos; - jack_transport_state_t fCurState; - }; + struct _net_transport_data + { + char fTransportType[10]; //test value ('transport') + jack_position_t fCurPos; + jack_transport_state_t fCurState; + }; //midi data *********************************************************************************** @@ -145,7 +146,7 @@ namespace Jack int RenderFromNetwork ( int subcycle, size_t copy_size ); int RenderToNetwork ( int subcycle, size_t total_size ); - void SetBuffer(int index, JackMidiBuffer* buffer); + void SetBuffer(int index, JackMidiBuffer* buffer); }; // audio data ********************************************************************************* @@ -171,6 +172,142 @@ namespace Jack 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 ) + { + 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 diff --git a/windows/jack_audioadapter.cbp b/windows/jack_audioadapter.cbp index 9279a106..9a9b818f 100644 --- a/windows/jack_audioadapter.cbp +++ b/windows/jack_audioadapter.cbp @@ -72,10 +72,61 @@ + + + + + + + + + + + + + + + + + + + diff --git a/windows/jack_connect.cbp b/windows/jack_connect.cbp index 3b13433f..2fe6daac 100644 --- a/windows/jack_connect.cbp +++ b/windows/jack_connect.cbp @@ -64,10 +64,55 @@ + + + + + + + + + + + + + + + + + diff --git a/windows/jack_disconnect.cbp b/windows/jack_disconnect.cbp index 6287d76b..98d9bccc 100644 --- a/windows/jack_disconnect.cbp +++ b/windows/jack_disconnect.cbp @@ -64,10 +64,55 @@ + + + + + + + + + + + + + + + + + diff --git a/windows/jack_dummy.cbp b/windows/jack_dummy.cbp index 92b7911a..afd3b2a4 100644 --- a/windows/jack_dummy.cbp +++ b/windows/jack_dummy.cbp @@ -66,10 +66,56 @@ + + + + + + + + + + + + + + + + + diff --git a/windows/jack_load.cbp b/windows/jack_load.cbp index 000baf33..abc8ea3f 100644 --- a/windows/jack_load.cbp +++ b/windows/jack_load.cbp @@ -65,10 +65,56 @@ + + + + + + + + + + + + + + + + + diff --git a/windows/jack_lsp.cbp b/windows/jack_lsp.cbp index e93ddf16..2de18c6e 100644 --- a/windows/jack_lsp.cbp +++ b/windows/jack_lsp.cbp @@ -61,12 +61,36 @@ + + + + + + + + + + + + + + + + diff --git a/windows/jack_metro.cbp b/windows/jack_metro.cbp index e4081433..5e246c83 100644 --- a/windows/jack_metro.cbp +++ b/windows/jack_metro.cbp @@ -66,10 +66,56 @@ + + + + + + + + + + + + + + + + + @@ -78,6 +124,11 @@ + + + + + diff --git a/windows/jack_netdriver.cbp b/windows/jack_netdriver.cbp index 8fab71f5..8acbea09 100644 --- a/windows/jack_netdriver.cbp +++ b/windows/jack_netdriver.cbp @@ -20,6 +20,7 @@ + @@ -51,6 +52,7 @@ + @@ -68,10 +70,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/jackdmp.cbp b/windows/jackdmp.cbp index acd49dc3..627a1ff3 100644 --- a/windows/jackdmp.cbp +++ b/windows/jackdmp.cbp @@ -75,10 +75,61 @@ + + + + + + + + + + + + + + + + +