/* 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. */ #include "JackNetAdapter.h" #define DEFAULT_MULTICAST_IP "225.3.19.154" #define DEFAULT_PORT 19000 namespace Jack { JackNetAdapter::JackNetAdapter ( jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params ) :JackAudioAdapterInterface(buffer_size, sample_rate), fThread(this) { jack_log ( "JackNetAdapter::JackNetAdapter" ); if ( SocketAPIInit() < 0 ) jack_error ( "Can't init Socket API, exiting..." ); //global parametering fMulticastIP = new char[16]; strcpy ( fMulticastIP, DEFAULT_MULTICAST_IP ); fSocket.SetPort ( DEFAULT_PORT ); GetHostName ( fParams.fName, JACK_CLIENT_NAME_SIZE ); fSocket.GetName ( fParams.fSlaveNetName ); fParams.fMtu = 1500; fParams.fTransportSync = 1; fParams.fSendAudioChannels = 2; fParams.fReturnAudioChannels = 2; fParams.fSendMidiChannels = 0; fParams.fReturnMidiChannels = 0; fParams.fSampleRate = 48000; fParams.fPeriodSize = 128; fParams.fSlaveSyncMode = 1; fParams.fNetworkMode = 'n'; //options parsing 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' : fMulticastIP = strdup ( param->value.str ); break; case 'p': fSocket.SetPort ( param->value.ui ); break; case 'M': fParams.fMtu = param->value.i; break; case 'C': fParams.fSendAudioChannels = param->value.i; break; case 'P': fParams.fReturnAudioChannels = param->value.i; break; case 'n' : strncpy ( fParams.fName, param->value.str, JACK_CLIENT_NAME_SIZE ); break; case 't' : fParams.fTransportSync = param->value.ui; break; case 'f' : fParams.fNetworkMode = 'f'; break; case 'S' : fParams.fSlaveSyncMode = 1; } } fCaptureChannels = fParams.fSendAudioChannels; fPlaybackChannels = fParams.fReturnAudioChannels; fSoftCaptureBuffer = NULL; fSoftPlaybackBuffer = NULL; } JackNetAdapter::~JackNetAdapter() { int port_index; for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) delete[] fSoftCaptureBuffer[port_index]; delete[] fSoftCaptureBuffer; for ( port_index = 0; port_index < fPlaybackChannels; port_index++ ) delete[] fSoftPlaybackBuffer[port_index]; delete[] fSoftPlaybackBuffer; } int JackNetAdapter::Open() { jack_log ( "JackNetAdapter::Open()" ); int port_index; //display some additional infos jack_info ( "NetAdapter started in %s mode %s Master's transport sync.", ( fParams.fSlaveSyncMode ) ? "sync" : "async", ( fParams.fTransportSync ) ? "with" : "without" ); //init network connection if ( !JackNetSlaveInterface::Init() ) return -1; //then set global parameters SetParams(); //set buffers fSoftCaptureBuffer = new sample_t*[fCaptureChannels]; for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) { fSoftCaptureBuffer[port_index] = new sample_t[fParams.fPeriodSize]; fNetAudioCaptureBuffer->SetBuffer ( port_index, fSoftCaptureBuffer[port_index] ); } fSoftPlaybackBuffer = new sample_t*[fPlaybackChannels]; for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) { fSoftPlaybackBuffer[port_index] = new sample_t[fParams.fPeriodSize]; fNetAudioPlaybackBuffer->SetBuffer ( port_index, fSoftPlaybackBuffer[port_index] ); } fThread.AcquireRealTime(85); return fThread.StartSync(); } int JackNetAdapter::Close() { fSocket.Close(); return 0; } int JackNetAdapter::SetBufferSize ( jack_nframes_t buffer_size ) { fParams.fPeriodSize = buffer_size; return 0; } bool JackNetAdapter::Init() { jack_info ( "Starting NetAdapter." ); return true; } bool JackNetAdapter::Execute() { //the sync mode is the equivalent of driver sync mode : data are sent back right after being computed //TODO : verify async mode is appropriate here, because of the ringbuffer usage switch ( fParams.fSlaveSyncMode ) { case true : return ProcessSync(); case false : return ProcessAsync(); } return true; } bool JackNetAdapter::ProcessSync() { bool failure = false; int port_index; //receive if ( SyncRecv() == SOCKET_ERROR ) return true; if ( DataRecv() == SOCKET_ERROR ) return false; //resample jack_nframes_t time1, time2; ResampleFactor ( time1, time2 ); for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) { fCaptureRingBuffer[port_index]->SetRatio ( time1, time2 ); if ( fCaptureRingBuffer[port_index]->WriteResample ( fSoftCaptureBuffer[port_index], fBufferSize ) < fBufferSize ) failure = true; } for ( port_index = 0; port_index < fPlaybackChannels; port_index++ ) { fPlaybackRingBuffer[port_index]->SetRatio ( time2, time1 ); if ( fPlaybackRingBuffer[port_index]->ReadResample ( fSoftPlaybackBuffer[port_index], fBufferSize ) < fBufferSize ) failure = true; } //send if ( SyncSend() == SOCKET_ERROR ) return false; if ( DataSend() == SOCKET_ERROR ) return false; if ( failure ) { jack_error ( "JackNetAdapter::Execute ringbuffer failure...reset." ); ResetRingBuffers(); } return true; } bool JackNetAdapter::ProcessAsync() { bool failure = false; int port_index; //receive if ( SyncRecv() == SOCKET_ERROR ) return true; if ( DataRecv() == SOCKET_ERROR ) return false; //send if ( SyncSend() == SOCKET_ERROR ) return false; if ( DataSend() == SOCKET_ERROR ) return false; if ( failure ) { jack_error ( "JackNetAdapter::Execute ringbuffer failure...reset." ); ResetRingBuffers(); } //resample jack_nframes_t time1, time2; ResampleFactor ( time1, time2 ); for ( port_index = 0; port_index < fCaptureChannels; port_index++ ) { fCaptureRingBuffer[port_index]->SetRatio ( time1, time2 ); if ( fCaptureRingBuffer[port_index]->WriteResample ( fSoftCaptureBuffer[port_index], fBufferSize ) < fBufferSize ) failure = true; } for ( port_index = 0; port_index < fPlaybackChannels; port_index++ ) { fPlaybackRingBuffer[port_index]->SetRatio ( time2, time1 ); if ( fPlaybackRingBuffer[port_index]->ReadResample ( fSoftPlaybackBuffer[port_index], fBufferSize ) < fBufferSize ) failure = true; } return true; } } // namespace Jack #ifdef __cplusplus extern "C" { #endif #include "driver_interface.h" #include "JackAudioAdapter.h" using namespace Jack; EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); strcpy ( desc->name, "net" ); desc->nparams = 9; desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); int 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 = JackDriverParamInt; desc->params[i].value.i = 19000; 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, "mtu" ); desc->params[i].character = 'M'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.i = 1500; strcpy ( desc->params[i].short_desc, "MTU to the master" ); 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 = JackDriverParamInt; 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, "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 ); i++; strcpy ( desc->params[i].name, "transport_sync" ); desc->params[i].character = 't'; desc->params[i].type = JackDriverParamUInt; desc->params[i].value.ui = 1U; strcpy ( desc->params[i].short_desc, "Sync transport with master's" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "fast_mode" ); desc->params[i].character = 'f'; desc->params[i].type = JackDriverParamString; strcpy ( desc->params[i].value.str, "" ); strcpy ( desc->params[i].short_desc, "Fast mode allows a zero latency transmission." ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); i++; strcpy ( desc->params[i].name, "sync_mode" ); desc->params[i].character = 'S'; desc->params[i].type = JackDriverParamString; strcpy ( desc->params[i].value.str, "" ); strcpy ( desc->params[i].short_desc, "Sync mode (same as driver's sync mode) ?" ); strcpy ( desc->params[i].long_desc, desc->params[i].short_desc ); return desc; } EXPORT int jack_internal_initialize ( jack_client_t* jack_client, const JSList* params ) { jack_log ( "Loading netadapter" ); Jack::JackAudioAdapter* adapter; jack_nframes_t buffer_size = jack_get_buffer_size ( jack_client ); jack_nframes_t sample_rate = jack_get_sample_rate ( jack_client ); adapter = new Jack::JackAudioAdapter ( jack_client, new Jack::JackNetAdapter ( buffer_size, sample_rate, params ) ); assert ( adapter ); if ( adapter->Open() == 0 ) { return 0; } else { delete adapter; return 1; } } EXPORT int jack_initialize ( jack_client_t* jack_client, const char* load_init ) { JSList* params = NULL; jack_driver_desc_t *desc = jack_get_descriptor(); JackArgParser parser ( load_init ); if ( parser.GetArgc() > 0 ) { if ( parser.ParseParams ( desc, ¶ms ) != 0 ) jack_error ( "Internal client : JackArgParser::ParseParams error." ); } return jack_internal_initialize ( jack_client, params ); } EXPORT void jack_finish ( void* arg ) { Jack::JackAudioAdapter* adapter = static_cast ( arg ); if ( adapter ) { jack_log ( "Unloading netadapter" ); adapter->Close(); delete adapter; } } #ifdef __cplusplus } #endif