Jack2  1.9.10
JackNetAPI.cpp
00001 /*
00002 Copyright (C) 2009-2013 Grame
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU Lesser General Public License as published by
00006 the Free Software Foundation; either version 2.1 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU Lesser General Public License for more details.
00013 
00014 You should have received a copy of the GNU Lesser General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 
00018 */
00019 
00020 #include <assert.h>
00021 #include <stdarg.h>
00022 
00023 #include "JackNetInterface.h"
00024 #include "JackAudioAdapterInterface.h"
00025 
00026 #ifdef __cplusplus
00027 extern "C"
00028 {
00029 #endif
00030 
00031     // NetJack common API
00032 
00033     #define MASTER_NAME_SIZE 256
00034 
00035     enum JackNetEncoder {
00036 
00037         JackFloatEncoder = 0,
00038         JackIntEncoder = 1,
00039         JackCeltEncoder = 2,
00040         JackOpusEncoder = 3
00041     };
00042 
00043     typedef struct {
00044 
00045         int audio_input;
00046         int audio_output;
00047         int midi_input;
00048         int midi_output;
00049         int mtu;
00050         int time_out;   // in millisecond, -1 means in infinite
00051         int encoder;    // one of JackNetEncoder
00052         int kbps;       // KB per second for CELT encoder
00053         int latency;    // network cycles
00054 
00055     } jack_slave_t;
00056 
00057     typedef struct {
00058 
00059         int audio_input;
00060         int audio_output;
00061         int midi_input;
00062         int midi_output;
00063         jack_nframes_t buffer_size;
00064         jack_nframes_t sample_rate;
00065         char master_name[MASTER_NAME_SIZE];
00066         int time_out;
00067         int partial_cycle;                   
00068 
00069     } jack_master_t;
00070 
00071     // NetJack slave API
00072 
00073     typedef struct _jack_net_slave jack_net_slave_t;
00074 
00075     typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size,
00076                                             int audio_input,
00077                                             float** audio_input_buffer,
00078                                             int midi_input,
00079                                             void** midi_input_buffer,
00080                                             int audio_output,
00081                                             float** audio_output_buffer,
00082                                             int midi_output,
00083                                             void** midi_output_buffer,
00084                                             void* data);
00085 
00086     typedef int (*JackNetSlaveBufferSizeCallback) (jack_nframes_t nframes, void *arg);
00087     typedef int (*JackNetSlaveSampleRateCallback) (jack_nframes_t nframes, void *arg);
00088     typedef void (*JackNetSlaveShutdownCallback) (void* arg);
00089     typedef int (*JackNetSlaveRestartCallback) (void* arg);
00090     typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg);
00091 
00092     LIB_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result);
00093     LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net);
00094 
00095     LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net);
00096     LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net);
00097     LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net);
00098 
00099     LIB_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg);
00100     LIB_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t* net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg);
00101     LIB_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t* net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg);
00102     LIB_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t* net, JackNetSlaveShutdownCallback shutdown_callback, void *arg);
00103     LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t* net, JackNetSlaveRestartCallback restart_callback, void *arg);
00104     LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t* net, JackNetSlaveErrorCallback error_callback, void *arg);
00105 
00106     // NetJack master API
00107 
00108     typedef struct _jack_net_master jack_net_master_t;
00109 
00110     LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result);
00111     LIB_EXPORT int jack_net_master_close(jack_net_master_t* net);
00112 
00113     LIB_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer);
00114     LIB_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer);
00115 
00116     LIB_EXPORT int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames);
00117     LIB_EXPORT int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames);
00118 
00119     // NetJack adapter API
00120 
00121     typedef struct _jack_adapter jack_adapter_t;
00122 
00123     LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output,
00124                                                     jack_nframes_t host_buffer_size,
00125                                                     jack_nframes_t host_sample_rate,
00126                                                     jack_nframes_t adapted_buffer_size,
00127                                                     jack_nframes_t adapted_sample_rate);
00128     LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter);
00129     LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter);
00130 
00131     LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
00132     LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
00133 
00134     #define LOG_LEVEL_INFO   1
00135     #define LOG_LEVEL_ERROR  2
00136 
00137     LIB_EXPORT void jack_error(const char *fmt, ...);
00138     LIB_EXPORT void jack_info(const char *fmt, ...);
00139     LIB_EXPORT void jack_log(const char *fmt, ...);
00140 
00141 #ifdef __cplusplus
00142 }
00143 #endif
00144 
00145 namespace Jack
00146 {
00147 
00148 struct JackNetExtMaster : public JackNetMasterInterface {
00149 
00150     jack_master_t fRequest;
00151     
00152     JackRingBuffer** fRingBuffer;
00153 
00154     JackNetExtMaster(const char* ip,
00155                     int port,
00156                     const char* name,
00157                     jack_master_t* request)
00158     {
00159         fRunning = true;
00160         assert(strlen(ip) < 32);
00161         strcpy(fMulticastIP, ip);
00162         fSocket.SetPort(port);
00163         fRequest.buffer_size = request->buffer_size;
00164         fRequest.sample_rate = request->sample_rate;
00165         fRequest.audio_input = request->audio_input;
00166         fRequest.audio_output = request->audio_output;
00167         fRequest.time_out = request->time_out;
00168         fRequest.partial_cycle = request->partial_cycle;
00169         fRingBuffer = NULL;
00170     }
00171 
00172     virtual ~JackNetExtMaster()
00173     {
00174         if (fRingBuffer) {
00175             for (int i = 0; i < fParams.fReturnAudioChannels; i++) {
00176                 delete fRingBuffer[i];
00177             }
00178             delete [] fRingBuffer;
00179         }
00180     }
00181 
00182     int Open(jack_slave_t* result)
00183     {
00184         // Check buffer_size
00185         if (fRequest.buffer_size == 0) {
00186             jack_error("Incorrect buffer_size...");
00187             return -1;
00188         }
00189         // Check sample_rate
00190         if (fRequest.sample_rate == 0) {
00191             jack_error("Incorrect sample_rate...");
00192             return -1;
00193         }
00194                    
00195         // Init socket API (win32)
00196         if (SocketAPIInit() < 0) {
00197             jack_error("Can't init Socket API, exiting...");
00198             return -1;
00199         }
00200 
00201         // Request socket
00202         if (fSocket.NewSocket() == SOCKET_ERROR) {
00203             jack_error("Can't create the network management input socket : %s", StrError(NET_ERROR_CODE));
00204             return -1;
00205         }
00206 
00207         // Bind the socket to the local port
00208         if (fSocket.Bind() == SOCKET_ERROR) {
00209             jack_error("Can't bind the network manager socket : %s", StrError(NET_ERROR_CODE));
00210             fSocket.Close();
00211             return -1;
00212         }
00213 
00214         // Join multicast group
00215         if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) {
00216             jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE));
00217         }
00218 
00219         // Local loop
00220         if (fSocket.SetLocalLoop() == SOCKET_ERROR) {
00221             jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE));
00222         }
00223 
00224         // Set a timeout on the multicast receive (the thread can now be cancelled)
00225         if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) {
00226             jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE));
00227         }
00228 
00229          // Main loop, wait for data, deal with it and wait again
00230         int attempt = 0;
00231         int rx_bytes = 0;
00232         int try_count = (fRequest.time_out > 0) ? int((1000000.f * float(fRequest.time_out)) / float(MANAGER_INIT_TIMEOUT)) : INT_MAX;
00233        
00234         do
00235         {
00236             session_params_t net_params;
00237             rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0);
00238             SessionParamsNToH(&net_params, &fParams);
00239 
00240             if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) {
00241                 jack_error("Error in receive : %s", StrError(NET_ERROR_CODE));
00242                 if (++attempt == 10) {
00243                     jack_error("Can't receive on the socket, exiting net manager" );
00244                     goto error;
00245                 }
00246             }
00247 
00248             if (rx_bytes == sizeof(session_params_t))  {
00249                 switch (GetPacketType(&fParams)) {
00250 
00251                     case SLAVE_AVAILABLE:
00252                         if (InitMaster(result) == 0) {
00253                             SessionParamsDisplay(&fParams);
00254                             fRunning = false;
00255                         } else {
00256                             jack_error("Can't init new net master...");
00257                             goto error;
00258                         }
00259                         jack_info("Waiting for a slave...");
00260                         break;
00261 
00262                     case KILL_MASTER:
00263                          break;
00264 
00265                     default:
00266                         break;
00267                 }
00268             }
00269         }
00270         while (fRunning && (--try_count > 0));
00271         
00272         if (try_count == 0) {
00273             jack_error("Time out error in connect");
00274             return -1;
00275         }
00276  
00277         // Set result parameters
00278         result->audio_input = fParams.fSendAudioChannels;
00279         result->audio_output = fParams.fReturnAudioChannels;
00280         result->midi_input = fParams.fSendMidiChannels;
00281         result->midi_output = fParams.fReturnMidiChannels;
00282         result->mtu = fParams.fMtu;
00283         result->latency = fParams.fNetworkLatency;
00284         
00285         // Use ringbuffer in case of partial cycle and latency > 0
00286         if (fRequest.partial_cycle && result->latency > 0) {
00287             fRingBuffer = new JackRingBuffer*[fParams.fReturnAudioChannels];
00288             for (int i = 0; i < fParams.fReturnAudioChannels; i++) {
00289                 fRingBuffer[i] = new JackRingBuffer(fRequest.buffer_size * result->latency * 2);
00290             }
00291         }
00292         return 0;
00293 
00294     error:
00295         fSocket.Close();
00296         return -1;
00297     }
00298 
00299     int InitMaster(jack_slave_t* result)
00300     {
00301         // Check MASTER <==> SLAVE network protocol coherency
00302         if (fParams.fProtocolVersion != NETWORK_PROTOCOL) {
00303             jack_error("Error : slave '%s' is running with a different protocol %d != %d", fParams.fName, fParams.fProtocolVersion, NETWORK_PROTOCOL);
00304             return -1;
00305         }
00306 
00307         // Settings
00308         fSocket.GetName(fParams.fMasterNetName);
00309         fParams.fID = 1;
00310         fParams.fPeriodSize = fRequest.buffer_size;
00311         fParams.fSampleRate = fRequest.sample_rate;
00312         
00313         if (fRequest.audio_input == -1) {
00314             if (fParams.fSendAudioChannels == -1) {
00315                 jack_error("Error : master and slave use -1 for wanted inputs...");
00316                 return -1;
00317             } else {
00318                 result->audio_input = fParams.fSendAudioChannels;
00319                 jack_info("Takes slave %d inputs", fParams.fSendAudioChannels);
00320             }
00321         } else if (fParams.fSendAudioChannels == -1) {
00322             fParams.fSendAudioChannels = fRequest.audio_input;
00323             jack_info("Takes master %d inputs", fRequest.audio_input);
00324         } else if (fParams.fSendAudioChannels != fRequest.audio_input) {
00325             jack_error("Error : master wants %d inputs and slave wants %d inputs...", fRequest.audio_input, fParams.fSendAudioChannels);
00326             return -1;
00327         } 
00328                 
00329         if (fRequest.audio_output == -1) {
00330             if (fParams.fReturnAudioChannels == -1) {
00331                 jack_error("Error : master and slave use -1 for wanted outputs...");
00332                 return -1;
00333             } else {
00334                 result->audio_output = fParams.fReturnAudioChannels;
00335                 jack_info("Takes slave %d outputs", fParams.fReturnAudioChannels);
00336             }
00337         } else if (fParams.fReturnAudioChannels == -1) {
00338             fParams.fReturnAudioChannels = fRequest.audio_output;
00339             jack_info("Takes master %d outputs", fRequest.audio_output);
00340         } else if (fParams.fReturnAudioChannels != fRequest.audio_output) {
00341             jack_error("Error : master wants %d outputs and slave wants %d outputs...", fRequest.audio_output, fParams.fReturnAudioChannels);
00342             return -1;
00343         }
00344         
00345         // Close request socket
00346         fSocket.Close();
00347 
00349         if (!JackNetMasterInterface::Init()) {
00350             return -1;
00351         }
00352 
00353         // Set global parameters
00354         if (!SetParams()) {
00355             return -1;
00356         }
00357 
00358         return 0;
00359     }
00360 
00361     int Close()
00362     {
00363         fSocket.Close();
00364         return 0;
00365     }
00366     
00367     void UseRingBuffer(int audio_input, float** audio_input_buffer, int write, int read)
00368     {
00369         // Possibly use ringbuffer...
00370         if (fRingBuffer) {
00371             for (int i = 0; i < audio_input; i++) {
00372                 fRingBuffer[i]->Write(audio_input_buffer[i], write);
00373                 fRingBuffer[i]->Read(audio_input_buffer[i], read);
00374             }
00375         }
00376     }
00377   
00378     int Read(int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames)
00379     {
00380         try {
00381             
00382             // frames = -1 means : entire buffer
00383             if (frames < 0) frames = fParams.fPeriodSize;
00384            
00385             int read_frames = 0;
00386             assert(audio_input == fParams.fReturnAudioChannels);
00387 
00388             for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) {
00389                 fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, audio_input_buffer[audio_port_index]);
00390             }
00391 
00392             for (int midi_port_index = 0; midi_port_index < midi_input; midi_port_index++) {
00393                 fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_input_buffer)[midi_port_index]);
00394             }
00395          
00396             int res1 = SyncRecv();
00397             switch (res1) {
00398             
00399                 case NET_SYNCHING:
00400                     // Data will not be received, so cleanup buffers...
00401                     for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) {
00402                         memset(audio_input_buffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize);
00403                     }
00404                     UseRingBuffer(audio_input, audio_input_buffer, fParams.fPeriodSize, frames);
00405                     return res1;
00406                     
00407                 case SOCKET_ERROR:
00408                     return res1;
00409                     
00410                 case SYNC_PACKET_ERROR:
00411                     // since sync packet is incorrect, don't decode it and continue with data
00412                     break;
00413                     
00414                 default:
00415                     // decode sync
00416                     DecodeSyncPacket(read_frames);
00417                     break;
00418             }
00419           
00420             int res2 = DataRecv();
00421             UseRingBuffer(audio_input, audio_input_buffer, read_frames, frames);
00422             return res2;
00423 
00424         } catch (JackNetException& e) {
00425             jack_error(e.what());
00426             return -1;
00427         }
00428     }
00429 
00430     int Write(int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames)
00431     {
00432         try {
00433         
00434             // frames = -1 means : entire buffer
00435             if (frames < 0) frames = fParams.fPeriodSize;
00436             
00437             assert(audio_output == fParams.fSendAudioChannels);
00438 
00439             for (int audio_port_index = 0; audio_port_index < audio_output; audio_port_index++) {
00440                 fNetAudioCaptureBuffer->SetBuffer(audio_port_index, audio_output_buffer[audio_port_index]);
00441             }
00442 
00443             for (int midi_port_index = 0; midi_port_index < midi_output; midi_port_index++) {
00444                 fNetMidiCaptureBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_output_buffer)[midi_port_index]);
00445             }
00446             
00447             EncodeSyncPacket(frames);
00448     
00449             // send sync
00450             if (SyncSend() == SOCKET_ERROR) {
00451                 return SOCKET_ERROR;
00452             }
00453 
00454             // send data
00455             if (DataSend() == SOCKET_ERROR) {
00456                 return SOCKET_ERROR;
00457             }
00458             return 0;
00459 
00460         } catch (JackNetException& e) {
00461             jack_error(e.what());
00462             return -1;
00463         }
00464     }
00465 
00466     // Transport
00467     void EncodeTransportData()
00468     {}
00469 
00470     void DecodeTransportData()
00471     {}
00472 
00473 };
00474 
00475 struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface {
00476 
00477     // Data buffers
00478     float** fAudioCaptureBuffer;
00479     float** fAudioPlaybackBuffer;
00480 
00481     JackMidiBuffer** fMidiCaptureBuffer;
00482     JackMidiBuffer** fMidiPlaybackBuffer;
00483    
00484     JackThread fThread;
00485 
00486     JackNetSlaveProcessCallback fProcessCallback;
00487     void* fProcessArg;
00488 
00489     JackNetSlaveShutdownCallback fShutdownCallback;
00490     void* fShutdownArg;
00491     
00492     JackNetSlaveRestartCallback fRestartCallback;
00493     void* fRestartArg;
00494     
00495     JackNetSlaveErrorCallback fErrorCallback;
00496     void* fErrorArg;
00497 
00498     JackNetSlaveBufferSizeCallback fBufferSizeCallback;
00499     void* fBufferSizeArg;
00500 
00501     JackNetSlaveSampleRateCallback fSampleRateCallback;
00502     void* fSampleRateArg;
00503 
00504     int fConnectTimeOut;
00505     int fFrames;
00506    
00507     JackNetExtSlave(const char* ip,
00508                     int port,
00509                     const char* name,
00510                     jack_slave_t* request)
00511         :fThread(this),
00512         fProcessCallback(NULL),fProcessArg(NULL),
00513         fShutdownCallback(NULL), fShutdownArg(NULL),
00514         fRestartCallback(NULL), fRestartArg(NULL),
00515         fErrorCallback(NULL), fErrorArg(NULL),
00516         fBufferSizeCallback(NULL), fBufferSizeArg(NULL),
00517         fSampleRateCallback(NULL), fSampleRateArg(NULL)
00518    {
00519         char host_name[JACK_CLIENT_NAME_SIZE];
00520 
00521         // Request parameters
00522         assert(strlen(ip) < 32);
00523         strcpy(fMulticastIP, ip);
00524         fParams.fMtu = request->mtu;
00525         fParams.fTransportSync = 0;
00526         fParams.fSendAudioChannels = request->audio_input;
00527         fParams.fReturnAudioChannels = request->audio_output;
00528         fParams.fSendMidiChannels = request->midi_input;
00529         fParams.fReturnMidiChannels = request->midi_output;
00530         fParams.fNetworkLatency = request->latency;
00531         fParams.fSampleEncoder = request->encoder;
00532         fParams.fKBps = request->kbps;
00533         fParams.fSlaveSyncMode = 1;
00534         fConnectTimeOut = request->time_out;
00535 
00536         // Create name with hostname and client name
00537         GetHostName(host_name, JACK_CLIENT_NAME_SIZE);
00538         snprintf(fParams.fName, JACK_CLIENT_NAME_SIZE, "%s_%s", host_name, name);
00539         fSocket.GetName(fParams.fSlaveNetName);
00540 
00541         // Set the socket parameters
00542         fSocket.SetPort(port);
00543         fSocket.SetAddress(fMulticastIP, port);
00544         
00545         fAudioCaptureBuffer = NULL;
00546         fAudioPlaybackBuffer = NULL;
00547         fMidiCaptureBuffer = NULL;
00548         fMidiPlaybackBuffer = NULL;
00549     }
00550 
00551     virtual ~JackNetExtSlave()
00552     {}
00553      
00554     void AllocPorts()
00555     {
00556         // Set buffers
00557         if (fParams.fSendAudioChannels > 0) {
00558             fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels];
00559             for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) {
00560                 fAudioCaptureBuffer[audio_port_index] = new float[fParams.fPeriodSize];
00561                 memset(fAudioCaptureBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize);
00562                 fNetAudioCaptureBuffer->SetBuffer(audio_port_index, fAudioCaptureBuffer[audio_port_index]);
00563             }
00564         }
00565 
00566         if (fParams.fSendMidiChannels > 0) {
00567             fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels];
00568             for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
00569                 fMidiCaptureBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize];
00570                 memset(fMidiCaptureBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize);
00571                 fNetMidiCaptureBuffer->SetBuffer(midi_port_index, fMidiCaptureBuffer[midi_port_index]);
00572             }
00573         }
00574 
00575         if (fParams.fReturnAudioChannels > 0) {
00576             fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels];
00577             for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) {
00578                 fAudioPlaybackBuffer[audio_port_index] = new float[fParams.fPeriodSize];
00579                 memset(fAudioPlaybackBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize);
00580                 fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, fAudioPlaybackBuffer[audio_port_index]);
00581             }
00582         }
00583 
00584         if (fParams.fReturnMidiChannels > 0) {
00585             fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels];
00586             for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
00587                 fMidiPlaybackBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize];
00588                 memset(fMidiPlaybackBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize);
00589                 fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, fMidiPlaybackBuffer[midi_port_index]);
00590             }
00591         }
00592     }
00593 
00594     void FreePorts()
00595     {
00596         if (fAudioCaptureBuffer) {
00597             for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) {
00598                 delete[] fAudioCaptureBuffer[audio_port_index];
00599             }
00600             delete[] fAudioCaptureBuffer;
00601             fAudioCaptureBuffer = NULL;
00602         }
00603         
00604         if (fMidiCaptureBuffer) {
00605             for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
00606                 delete[] fMidiCaptureBuffer[midi_port_index];
00607             }
00608             delete[] fMidiCaptureBuffer;
00609             fMidiCaptureBuffer = NULL;
00610         }
00611         
00612         if (fAudioPlaybackBuffer) {
00613             for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) {
00614                 delete[] fAudioPlaybackBuffer[audio_port_index];
00615             }
00616             delete[] fAudioPlaybackBuffer;
00617             fAudioPlaybackBuffer = NULL;
00618         }
00619 
00620         if (fMidiPlaybackBuffer) {
00621             for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
00622                 delete[] (fMidiPlaybackBuffer[midi_port_index]);
00623             }
00624             delete[] fMidiPlaybackBuffer;
00625             fMidiPlaybackBuffer = NULL;
00626         }
00627     }
00628 
00629     int Open(jack_master_t* result)
00630     {
00631         // Check audio/midi parameters
00632         if (fParams.fSendAudioChannels == 0
00633             && fParams.fReturnAudioChannels == 0
00634             && fParams.fSendMidiChannels == 0
00635             && fParams.fReturnMidiChannels == 0) {
00636             jack_error("Incorrect audio/midi channels number...");
00637             return -1;
00638         }
00639         
00640         // Check MTU parameters
00641         if ((fParams.fMtu < DEFAULT_MTU) && (fParams.fMtu > MAX_MTU)) {
00642             jack_error("MTU is not in the expected range [%d ... %d]", DEFAULT_MTU, MAX_MTU);
00643             return -1;
00644         }
00645             
00646         // Check CELT encoder parameters
00647         if ((fParams.fSampleEncoder == JackCeltEncoder) && (fParams.fKBps == 0)) {
00648             jack_error("CELT encoder with 0 for kps...");
00649             return -1;
00650         }
00651         
00652         if ((fParams.fSampleEncoder == JackOpusEncoder) && (fParams.fKBps == 0)) {
00653             jack_error("Opus encoder with 0 for kps...");
00654             return -1;
00655         }
00656 
00657         // Check latency
00658         if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) {
00659             jack_error("Network latency is limited to %d", NETWORK_MAX_LATENCY);
00660             return -1;
00661         }
00662 
00663         // Init network connection
00664         if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) {
00665             jack_error("Initing network fails...");
00666             return -1;
00667         }
00668 
00669         // Finish connection...
00670         if (!JackNetSlaveInterface::InitRendering()) {
00671             jack_error("Starting network fails...");
00672             return -1;
00673         }
00674 
00675         // Then set global parameters
00676         if (!SetParams()) {
00677             jack_error("SetParams error...");
00678             return -1;
00679         }
00680 
00681         // Set result
00682         if (result != NULL) {
00683             result->buffer_size = fParams.fPeriodSize;
00684             result->sample_rate = fParams.fSampleRate;
00685             result->audio_input = fParams.fSendAudioChannels;
00686             result->audio_output = fParams.fReturnAudioChannels;
00687             result->midi_input = fParams.fSendMidiChannels;
00688             result->midi_output = fParams.fReturnMidiChannels;
00689             strcpy(result->master_name, fParams.fMasterNetName);
00690         }
00691         
00692         // By default fFrames is fPeriodSize
00693         fFrames = fParams.fPeriodSize;
00694         
00695         SessionParamsDisplay(&fParams);
00696      
00697         AllocPorts();
00698         return 0;
00699     }
00700 
00701     int Restart() 
00702     {
00703        // Do it until client possibly decides to stop trying to connect...
00704         while (true) {
00705         
00706             // If restart cb is set, then call it
00707             if (fRestartCallback) {
00708                 if (fRestartCallback(fRestartArg) != 0) {
00709                     return -1;
00710                 }
00711             // Otherwise if shutdown cb is set, then call it
00712             } else if (fShutdownCallback) {
00713                 fShutdownCallback(fShutdownArg);
00714             }
00715 
00716             // Init network connection
00717             if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) {
00718                 jack_error("Initing network fails after time_out, retry...");
00719             } else {
00720                 break;
00721             }
00722         }
00723 
00724          // Finish connection
00725         if (!JackNetSlaveInterface::InitRendering()) {
00726             jack_error("Starting network fails...");
00727             return -1;
00728         }
00729 
00730         // Then set global parameters
00731         if (!SetParams()) {
00732             jack_error("SetParams error...");
00733             return -1;
00734         }
00735 
00736         // We need to notify possibly new buffer size and sample rate (see Execute)
00737         if (fBufferSizeCallback) {
00738             if (fBufferSizeCallback(fParams.fPeriodSize, fBufferSizeArg) != 0) {
00739                 jack_error("New buffer size = %d cannot be used...", fParams.fPeriodSize);
00740                 return -1;
00741             }
00742         }
00743 
00744         if (fSampleRateCallback) {
00745             if (fSampleRateCallback(fParams.fSampleRate, fSampleRateArg) != 0) {
00746                 jack_error("New sample rate = %d cannot be used...", fParams.fSampleRate);
00747                 return -1;
00748             }
00749         }
00750 
00751         AllocPorts();
00752         return 0;
00753     }
00754 
00755     int Close()
00756     {
00757         fSocket.Close();
00758         FreePorts();
00759         return 0;
00760     }
00761 
00762     // Transport
00763     void EncodeTransportData()
00764     {}
00765 
00766     void DecodeTransportData()
00767     {}
00768 
00769     bool Init()
00770     {
00771         // Will do "something" on OSX only...
00772         UInt64 period, constraint;
00773         period = constraint = UInt64(1000000000.f * (float(fParams.fPeriodSize) / float(fParams.fSampleRate)));
00774         UInt64 computation = JackTools::ComputationMicroSec(fParams.fPeriodSize) * 1000;
00775         fThread.SetParams(period, computation, constraint);
00776 
00777         return (fThread.AcquireSelfRealTime(80) == 0);      // TODO: get a value from the server
00778     }
00779     
00780     bool IsRunning()
00781     {
00782         return (fThread.GetStatus() == JackThread::kRunning);
00783     }
00784 
00785     bool Execute()
00786     {
00787         try {
00788             /*
00789                 Fist cycle use an INT_MAX time out, so that connection
00790                 is considered established (with PACKET_TIMEOUT later on)
00791                 when the first cycle has been done.
00792             */
00793             DummyProcess();
00794             // keep running even in case of error
00795             while (fThread.GetStatus() == JackThread::kRunning) {
00796                 if (Process() == SOCKET_ERROR) {
00797                     return false;
00798                 }
00799             }
00800             return false;
00801         } catch (JackNetException& e) {
00802             // otherwise just restart...
00803             e.PrintMessage();
00804             jack_info("NetSlave is restarted");
00805             fThread.DropRealTime();
00806             fThread.SetStatus(JackThread::kIniting);
00807             FreePorts();
00808             if (Restart() == 0 && Init()) {
00809                 fThread.SetStatus(JackThread::kRunning);
00810                 return true;
00811             } else {
00812                 return false;
00813             }
00814         }
00815     }
00816 
00817     int Read()
00818     {
00819         // receive sync (launch the cycle)
00820         switch (SyncRecv()) {
00821         
00822             case SOCKET_ERROR:
00823                 return SOCKET_ERROR;
00824                 
00825             case SYNC_PACKET_ERROR:
00826                 // since sync packet is incorrect, don't decode it and continue with data
00827                 if (fErrorCallback) {
00828                     fErrorCallback(SYNC_PACKET_ERROR, fErrorArg);
00829                 }
00830                 break;
00831                 
00832             default:
00833                 // decode sync
00834                 DecodeSyncPacket(fFrames);
00835                 break;
00836         }
00837 
00838         int res = DataRecv();
00839         if (res == DATA_PACKET_ERROR && fErrorCallback) {
00840             fErrorCallback(DATA_PACKET_ERROR, fErrorArg);
00841         }
00842         return res;
00843     }
00844     
00845     int Write()
00846     {
00847         EncodeSyncPacket(fFrames);
00848       
00849         if (SyncSend() == SOCKET_ERROR) {
00850             return SOCKET_ERROR;
00851         }
00852 
00853         return DataSend();
00854     }
00855     
00856     void DummyProcess()
00857     {
00858         // First cycle with INT_MAX time out
00859         SetPacketTimeOut(INT_MAX);
00860         
00861         // One cycle
00862         Process();
00863         
00864         // Then use PACKET_TIMEOUT * fParams.fNetworkLatency for next cycles
00865         SetPacketTimeOut(PACKET_TIMEOUT * fParams.fNetworkLatency);
00866     }
00867 
00868     int Process()
00869     {
00870         // Read data from the network, throw JackNetException in case of network error...
00871         if (Read() == SOCKET_ERROR) {
00872             return SOCKET_ERROR;
00873         }
00874         
00875         if (fFrames < 0) fFrames = fParams.fPeriodSize;
00876         
00877         fProcessCallback(fFrames,
00878                         fParams.fSendAudioChannels,
00879                         fAudioCaptureBuffer,
00880                         fParams.fSendMidiChannels,
00881                         (void**)fMidiCaptureBuffer,
00882                         fParams.fReturnAudioChannels,
00883                         fAudioPlaybackBuffer,
00884                         fParams.fReturnMidiChannels,
00885                         (void**)fMidiPlaybackBuffer,
00886                         fProcessArg);
00887        
00888         // Then write data to network, throw JackNetException in case of network error...
00889         if (Write() == SOCKET_ERROR) {
00890             return SOCKET_ERROR;
00891         }
00892 
00893         return 0;
00894     }
00895 
00896     int Start()
00897     {
00898         return (fProcessCallback == 0) ? -1 : fThread.StartSync();
00899     }
00900 
00901     int Stop()
00902     {
00903         return (fProcessCallback == 0) ? -1 : fThread.Kill();
00904     }
00905 
00906     // Callback
00907     int SetProcessCallback(JackNetSlaveProcessCallback net_callback, void *arg)
00908     {
00909         if (fThread.GetStatus() == JackThread::kRunning) {
00910             return -1;
00911         } else {
00912             fProcessCallback = net_callback;
00913             fProcessArg = arg;
00914             return 0;
00915         }
00916     }
00917 
00918     int SetShutdownCallback(JackNetSlaveShutdownCallback shutdown_callback, void *arg)
00919     {
00920         if (fThread.GetStatus() == JackThread::kRunning) {
00921             return -1;
00922         } else {
00923             fShutdownCallback = shutdown_callback;
00924             fShutdownArg = arg;
00925             return 0;
00926         }
00927     }
00928     
00929     int SetRestartCallback(JackNetSlaveRestartCallback restart_callback, void *arg)
00930     {
00931         if (fThread.GetStatus() == JackThread::kRunning) {
00932             return -1;
00933         } else {
00934             fRestartCallback = restart_callback;
00935             fRestartArg = arg;
00936             return 0;
00937         }
00938     }
00939     
00940     int SetErrorCallback(JackNetSlaveErrorCallback error_callback, void *arg)
00941     {
00942         if (fThread.GetStatus() == JackThread::kRunning) {
00943             return -1;
00944         } else {
00945             fErrorCallback = error_callback;
00946             fErrorArg = arg;
00947             return 0;
00948         }
00949     }
00950 
00951     int SetBufferSizeCallback(JackNetSlaveBufferSizeCallback bufsize_callback, void *arg)
00952     {
00953         if (fThread.GetStatus() == JackThread::kRunning) {
00954             return -1;
00955         } else {
00956             fBufferSizeCallback = bufsize_callback;
00957             fBufferSizeArg = arg;
00958             return 0;
00959         }
00960     }
00961 
00962     int SetSampleRateCallback(JackNetSlaveSampleRateCallback samplerate_callback, void *arg)
00963     {
00964         if (fThread.GetStatus() == JackThread::kRunning) {
00965             return -1;
00966         } else {
00967             fSampleRateCallback = samplerate_callback;
00968             fSampleRateArg = arg;
00969             return 0;
00970         }
00971     }
00972 
00973 };
00974 
00975 struct JackNetAdapter : public JackAudioAdapterInterface {
00976 
00977     JackNetAdapter(int input, int output,
00978                     jack_nframes_t host_buffer_size,
00979                     jack_nframes_t host_sample_rate,
00980                     jack_nframes_t adapted_buffer_size,
00981                     jack_nframes_t adapted_sample_rate)
00982         :JackAudioAdapterInterface(host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate)
00983     {
00984         fCaptureChannels = input;
00985         fPlaybackChannels = output;
00986         Create();
00987     }
00988 
00989     void Create()
00990     {
00991         //ringbuffers
00992 
00993         if (fCaptureChannels > 0) {
00994             fCaptureRingBuffer = new JackResampler*[fCaptureChannels];
00995         }
00996         if (fPlaybackChannels > 0) {
00997             fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels];
00998         }
00999 
01000         if (fAdaptative) {
01001             AdaptRingBufferSize();
01002             jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize);
01003         } else {
01004             if (fRingbufferCurSize > DEFAULT_RB_SIZE) {
01005                 fRingbufferCurSize = DEFAULT_RB_SIZE;
01006             }
01007             jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize);
01008         }
01009 
01010         for (int i = 0; i < fCaptureChannels; i++ ) {
01011             fCaptureRingBuffer[i] = new JackResampler();
01012             fCaptureRingBuffer[i]->Reset(fRingbufferCurSize);
01013         }
01014         for (int i = 0; i < fPlaybackChannels; i++ ) {
01015             fPlaybackRingBuffer[i] = new JackResampler();
01016             fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize);
01017         }
01018 
01019         if (fCaptureChannels > 0) {
01020             jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace());
01021         }
01022         if (fPlaybackChannels > 0) {
01023             jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace());
01024         }
01025     }
01026 
01027     virtual ~JackNetAdapter()
01028     {
01029         Destroy();
01030     }
01031 
01032     void Flush()
01033     {
01034         for (int i = 0; i < fCaptureChannels; i++ ) {
01035             fCaptureRingBuffer[i]->Reset(fRingbufferCurSize);
01036         }
01037         for (int i = 0; i < fPlaybackChannels; i++ ) {
01038             fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize);
01039         }
01040     }
01041 
01042 };
01043 
01044 
01045 } // end of namespace
01046 
01047 using namespace Jack;
01048 
01049 LIB_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result)
01050 {
01051     JackNetExtSlave* slave = new JackNetExtSlave(ip, port, name, request);
01052     if (slave->Open(result) == 0) {
01053         return (jack_net_slave_t*)slave;
01054     } else {
01055         delete slave;
01056         return NULL;
01057     }
01058 }
01059 
01060 LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net)
01061 {
01062     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01063     slave->Close();
01064     delete slave;
01065     return 0;
01066 }
01067 
01068 LIB_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg)
01069 {
01070      JackNetExtSlave* slave = (JackNetExtSlave*)net;
01071      return slave->SetProcessCallback(net_callback, arg);
01072 }
01073 
01074 LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net)
01075 {
01076     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01077     return slave->Start();
01078 }
01079 
01080 LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net)
01081 {
01082     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01083     return slave->Stop();
01084 }
01085 
01086 LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net)
01087 {
01088     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01089     return slave->IsRunning();
01090 }
01091 
01092 LIB_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg)
01093 {
01094     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01095     return slave->SetBufferSizeCallback(bufsize_callback, arg);
01096 }
01097 
01098 LIB_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg)
01099 {
01100     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01101     return slave->SetSampleRateCallback(samplerate_callback, arg);
01102 }
01103 
01104 LIB_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg)
01105 {
01106     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01107     return slave->SetShutdownCallback(shutdown_callback, arg);
01108 }
01109 
01110 LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg)
01111 {
01112     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01113     return slave->SetRestartCallback(restart_callback, arg);
01114 }
01115 
01116 LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg)
01117 {
01118     JackNetExtSlave* slave = (JackNetExtSlave*)net;
01119     return slave->SetErrorCallback(error_callback, arg);
01120 }
01121 
01122 // Master API
01123 
01124 LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result)
01125 {
01126     JackNetExtMaster* master = new JackNetExtMaster(ip, port, name, request);
01127     if (master->Open(result) == 0) {
01128         return (jack_net_master_t*)master;
01129     } else {
01130         delete master;
01131         return NULL;
01132     }
01133 }
01134 
01135 LIB_EXPORT int jack_net_master_close(jack_net_master_t* net)
01136 {
01137     JackNetExtMaster* master = (JackNetExtMaster*)net;
01138     master->Close();
01139     delete master;
01140     return 0;
01141 }
01142 
01143 LIB_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer)
01144 {
01145     JackNetExtMaster* master = (JackNetExtMaster*)net;
01146     return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, -1);
01147 }
01148 
01149 LIB_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer)
01150 {
01151     JackNetExtMaster* master = (JackNetExtMaster*)net;
01152     return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, -1);
01153 }
01154 
01155 LIB_EXPORT int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames)
01156 {
01157     JackNetExtMaster* master = (JackNetExtMaster*)net;
01158     return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, frames);
01159 }
01160 
01161 LIB_EXPORT int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames)
01162 {
01163     JackNetExtMaster* master = (JackNetExtMaster*)net;
01164     return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, frames);
01165 }
01166 
01167 // Adapter API
01168 
01169 LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output,
01170                                                 jack_nframes_t host_buffer_size,
01171                                                 jack_nframes_t host_sample_rate,
01172                                                 jack_nframes_t adapted_buffer_size,
01173                                                 jack_nframes_t adapted_sample_rate)
01174 {
01175     try {
01176         return (jack_adapter_t*)new JackNetAdapter(input, output, host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate);
01177     } catch (...) {
01178         return NULL;
01179     }
01180 }
01181 
01182 LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter)
01183 {
01184     delete((JackNetAdapter*)adapter);
01185     return 0;
01186 }
01187 
01188 LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter)
01189 {
01190     JackNetAdapter* slave = (JackNetAdapter*)adapter;
01191     slave->Flush();
01192 }
01193 
01194 LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames)
01195 {
01196     JackNetAdapter* slave = (JackNetAdapter*)adapter;
01197     return slave->PushAndPull(input, output, frames);
01198 }
01199 
01200 LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames)
01201 {
01202     JackNetAdapter* slave = (JackNetAdapter*)adapter;
01203     return slave->PullAndPush(input, output, frames);
01204 }
01205 
01206 static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap)
01207 {
01208     static const char* netjack_log = getenv("JACK_NETJACK_LOG");
01209     static bool is_netjack_log = (netjack_log) ? atoi(netjack_log) : 0;
01210 
01211     if (is_netjack_log) {
01212         char buffer[300];
01213         size_t len;
01214 
01215         if (prefix != NULL) {
01216             len = strlen(prefix);
01217             memcpy(buffer, prefix, len);
01218         } else {
01219             len = 0;
01220         }
01221 
01222         vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap);
01223         printf("%s", buffer);
01224         printf("\n");
01225     }
01226 }
01227 
01228 LIB_EXPORT void jack_error(const char *fmt, ...)
01229 {
01230     va_list ap;
01231     va_start(ap, fmt);
01232     jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap);
01233     va_end(ap);
01234 }
01235 
01236 LIB_EXPORT void jack_info(const char *fmt, ...)
01237 {
01238     va_list ap;
01239     va_start(ap, fmt);
01240     jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap);
01241     va_end(ap);
01242 }
01243 
01244 LIB_EXPORT void jack_log(const char *fmt, ...)
01245 {
01246     va_list ap;
01247     va_start(ap, fmt);
01248     jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap);
01249     va_end(ap);
01250 }