diff --git a/ChangeLog b/ChangeLog index ef1336e4..632cec72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,10 @@ Michael Voigt Jackdmp changes log --------------------------- +2009-02-24 Stephane Letz + + * New libjacknet library with net.h and JackNetAPI.cpp files. New netmaster.c and netmaster.c examples. + 2009-02-23 Stephane Letz * Another fix in systemdeps.h and types.h: jack_time_t now uniquely defined in types.h. diff --git a/common/JackNetAPI.cpp b/common/JackNetAPI.cpp new file mode 100644 index 00000000..300357ac --- /dev/null +++ b/common/JackNetAPI.cpp @@ -0,0 +1,1029 @@ +/* +Copyright (C) 2009 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include "JackNetInterface.h" +#include "JackPlatformPlug.h" +#include "JackError.h" +#include "JackTime.h" +#include "JackException.h" + +#include "JackAudioAdapterInterface.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + // NetJack common API + + #define MASTER_NAME_SIZE 256 + + enum JackNetMode { + + JackFastMode = 'f', + JackNormalMode = 'n', + JackSlowMode = 's', + }; + + typedef struct { + + int audio_input; + int audio_output; + int midi_input; + int midi_ouput; + int mtu; + int time_out; // in millisecond, -1 means in infinite + char mode; + + } jack_slave_t; + + typedef struct { + + jack_nframes_t buffer_size; + jack_nframes_t sample_rate; + char master_name[MASTER_NAME_SIZE]; + + } jack_master_t; + + // NetJack slave API + + typedef struct _jack_net_slave jack_net_slave_t; + + typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, + int audio_input, + float** audio_input_buffer, + int midi_input, + void** midi_input_buffer, + int audio_output, + float** audio_output_buffer, + int midi_output, + void** midi_output_buffer, + void* data); + + typedef int (*JackNetSlaveBufferSizeCallback) (jack_nframes_t nframes, void *arg); + typedef int (*JackNetSlaveSampleRateCallback) (jack_nframes_t nframes, void *arg); + typedef void (*JackNetSlaveShutdownCallback) (void* data); + + SERVER_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); + SERVER_EXPORT int jack_net_slave_close(jack_net_slave_t* net); + + SERVER_EXPORT int jack_net_slave_activate(jack_net_slave_t* net); + SERVER_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net); + + SERVER_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); + SERVER_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); + + // NetJack master API + + typedef struct _jack_net_master jack_net_master_t; + + SERVER_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); + SERVER_EXPORT int jack_net_master_close(jack_net_master_t* net); + + SERVER_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); + SERVER_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); + + // NetJack adapter API + + typedef struct _jack_adapter jack_adapter_t; + + SERVER_EXPORT jack_adapter_t* jack_create_adapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate); + SERVER_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter); + + SERVER_EXPORT int jack_adapter_push_input(jack_adapter_t* adapter, int channels, float** buffers); + SERVER_EXPORT int jack_adapter_pull_input(jack_adapter_t* adapter, int channels, float** buffers); + + SERVER_EXPORT int jack_adapter_push_output(jack_adapter_t* adapter, int channels, float** buffers); + SERVER_EXPORT int jack_adapter_pull_output(jack_adapter_t* adapter, int channels, float** buffers); + +#ifdef __cplusplus +} +#endif + +namespace Jack +{ + +struct JackNetExtMaster : public JackNetMasterInterface { + + //sample buffers + float** fAudioCaptureBuffer; + float** fAudioPlaybackBuffer; + + JackMidiBuffer** fMidiCaptureBuffer; + JackMidiBuffer** fMidiPlaybackBuffer; + + jack_master_t fRequest; + + JackNetExtMaster(const char* ip, + int port, + const char* name, + jack_master_t* request) + { + fRunning = true; + assert(strlen(ip) < 32); + strcpy(fMulticastIP, ip); + fSocket.SetPort(port); + fRequest.buffer_size = request->buffer_size; + fRequest.sample_rate = request->sample_rate; + } + + virtual ~JackNetExtMaster() + {} + + int Open(jack_slave_t* result) + { + // Init socket API (win32) + if (SocketAPIInit() < 0) { + fprintf(stderr, "Can't init Socket API, exiting...\n"); + return -1; + } + + // Request socket + if (fSocket.NewSocket() == SOCKET_ERROR) { + fprintf(stderr, "Can't create the network management input socket : %s\n", StrError(NET_ERROR_CODE)); + return -1; + } + + // Bind the socket to the local port + if (fSocket.Bind() == SOCKET_ERROR) { + fprintf(stderr, "Can't bind the network manager socket : %s\n", StrError(NET_ERROR_CODE)); + fSocket.Close(); + return -1; + } + + // Join multicast group + if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) + fprintf(stderr, "Can't join multicast group : %s\n", StrError(NET_ERROR_CODE)); + + // Local loop + if (fSocket.SetLocalLoop() == SOCKET_ERROR) + fprintf(stderr, "Can't set local loop : %s\n", StrError(NET_ERROR_CODE)); + + // Set a timeout on the multicast receive (the thread can now be cancelled) + if (fSocket.SetTimeOut(2000000) == SOCKET_ERROR) + fprintf(stderr, "Can't set timeout : %s\n", StrError(NET_ERROR_CODE)); + + //main loop, wait for data, deal with it and wait again + //utility variables + int attempt = 0; + int rx_bytes = 0; + + do + { + session_params_t net_params; + rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); + SessionParamsNToH(&net_params, &fParams); + + if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { + fprintf(stderr, "Error in receive : %s\n", StrError(NET_ERROR_CODE)); + if (++attempt == 10) { + fprintf(stderr, "Can't receive on the socket, exiting net manager.\n" ); + goto error; + } + } + + if (rx_bytes == sizeof(session_params_t )) { + + switch (GetPacketType(&fParams)) { + + case SLAVE_AVAILABLE: + if (MasterInit() == 0) { + SessionParamsDisplay(&fParams); + fRunning = false; + } else { + fprintf(stderr, "Can't init new net master...\n"); + goto error; + } + break; + + case KILL_MASTER: + break; + + default: + break; + } + } + } + while (fRunning); + + // Set result paramaters + result->audio_input = fParams.fSendAudioChannels; + result->audio_output = fParams.fReturnAudioChannels; + result->midi_input = fParams.fSendMidiChannels; + result->midi_ouput = fParams.fReturnMidiChannels; + result->midi_ouput = fParams.fMtu; + result->mode = fParams.fNetworkMode; + return 0; + + error: + fSocket.Close(); + return -1; + } + + int MasterInit() + { + // Check MASTER <<==> SLAVE network protocol coherency + if (fParams.fProtocolVersion != MASTER_PROTOCOL) { + fprintf(stderr, "Error : slave is running with a different protocol %s\n", fParams.fName); + return -1; + } + + // Settings + fSocket.GetName(fParams.fMasterNetName); + fParams.fID = 1; + fParams.fBitdepth = 0; + fParams.fPeriodSize = fRequest.buffer_size; + fParams.fSampleRate = fRequest.sample_rate; + + // Close request socket + fSocket.Close(); + + // Network slave init + if (!JackNetMasterInterface::Init()) + return -1; + + // Set global parameters + SetParams(); + AllocPorts(); + return 0; + } + + int Close() + { + fSocket.Close(); + FreePorts(); + return 0; + } + + void AllocPorts() + { + unsigned int port_index; + + // Set buffers + fAudioPlaybackBuffer = new float*[fParams.fSendAudioChannels]; + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { + fAudioPlaybackBuffer[port_index] = new float[fParams.fPeriodSize]; + fNetAudioPlaybackBuffer->SetBuffer(port_index, fAudioPlaybackBuffer[port_index]); + } + + fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { + fMidiPlaybackBuffer[port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiPlaybackBuffer->SetBuffer(port_index, fMidiPlaybackBuffer[port_index]); + } + + fAudioCaptureBuffer = new float*[fParams.fReturnAudioChannels]; + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { + fAudioCaptureBuffer[port_index] = new float[fParams.fPeriodSize]; + fNetAudioCaptureBuffer->SetBuffer(port_index, fAudioCaptureBuffer[port_index]); + } + + fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { + fMidiCaptureBuffer[port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiCaptureBuffer->SetBuffer(port_index, fMidiCaptureBuffer[port_index]); + } + } + + void FreePorts() + { + unsigned int port_index; + + if (fAudioPlaybackBuffer) { + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) + delete[] fAudioPlaybackBuffer[port_index]; + delete[] fAudioPlaybackBuffer; + fAudioPlaybackBuffer = NULL; + } + + if (fMidiPlaybackBuffer) { + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) + delete[] (fMidiPlaybackBuffer[port_index]); + delete[] fMidiPlaybackBuffer; + fMidiPlaybackBuffer = NULL; + } + + if (fAudioCaptureBuffer) { + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) + delete[] fAudioCaptureBuffer[port_index]; + delete[] fAudioCaptureBuffer; + fAudioCaptureBuffer = NULL; + } + + if (fMidiCaptureBuffer) { + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) + delete[] fMidiCaptureBuffer[port_index]; + delete[] fMidiCaptureBuffer; + fMidiCaptureBuffer = NULL; + } + } + + int Read(int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer) + { + assert((unsigned int)audio_input == fParams.fSendAudioChannels); + int port_index; + + for (port_index = 0; port_index < audio_input; port_index++) { + fNetAudioPlaybackBuffer->SetBuffer(port_index, audio_input_buffer[port_index]); + } + + for (port_index = 0; port_index < midi_input; port_index++) { + fNetMidiPlaybackBuffer->SetBuffer(port_index, ((JackMidiBuffer**)midi_input_buffer)[port_index]); + } + + if (SyncRecv() == SOCKET_ERROR) + return 0; + + if (DecodeSyncPacket() < 0) + return 0; + + return DataRecv(); + } + + int Write(int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer) + { + assert((unsigned int)audio_output == fParams.fReturnAudioChannels); + int port_index; + + for (port_index = 0; port_index < audio_output; port_index++) { + fNetAudioCaptureBuffer->SetBuffer(port_index, audio_output_buffer[port_index]); + } + + for (port_index = 0; port_index < midi_output; port_index++) { + fNetMidiCaptureBuffer->SetBuffer(port_index, ((JackMidiBuffer**)midi_output_buffer)[port_index]); + } + + if (EncodeSyncPacket() < 0) + return 0; + + if (SyncSend() == SOCKET_ERROR) + return SOCKET_ERROR; + + return DataSend(); + } + + // Transport + int EncodeTransportData() + { + return 0; + } + + int DecodeTransportData() + { + return 0; + } + + +}; + +struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface { + + JackThread fThread; + + JackNetSlaveProcessCallback fProcessCallback; + void* fProcessArg; + + JackNetSlaveShutdownCallback fShutdownCallback; + void* fShutdownArg; + + JackNetSlaveBufferSizeCallback fBufferSizeCallback; + void* fBufferSizeArg; + + JackNetSlaveSampleRateCallback fSampleRateCallback; + void* fSampleRateArg; + + //sample buffers + float** fAudioCaptureBuffer; + float** fAudioPlaybackBuffer; + + JackMidiBuffer** fMidiCaptureBuffer; + JackMidiBuffer** fMidiPlaybackBuffer; + + JackNetExtSlave(const char* ip, + int port, + const char* name, + jack_slave_t* request) + :fThread(this), + fProcessCallback(NULL),fProcessArg(NULL), + fShutdownCallback(NULL), fShutdownArg(NULL), + fBufferSizeCallback(NULL), fBufferSizeArg(NULL), + fSampleRateCallback(NULL), fSampleRateArg(NULL), + fAudioCaptureBuffer(NULL), fAudioPlaybackBuffer(NULL), + fMidiCaptureBuffer(NULL), fMidiPlaybackBuffer(NULL) + { + char host_name[JACK_CLIENT_NAME_SIZE]; + + // Request parameters + assert(strlen(ip) < 32); + strcpy(fMulticastIP, ip); + + fParams.fMtu = request->mtu; + fParams.fTransportSync = 0; + fParams.fSendAudioChannels = request->audio_input; + fParams.fReturnAudioChannels = request->audio_output; + fParams.fSendMidiChannels = request->midi_input; + fParams.fReturnMidiChannels = request->midi_ouput; + fParams.fNetworkMode = request->mode; + fParams.fSlaveSyncMode = 1; + + // Create name with hostname and client name + GetHostName(host_name, JACK_CLIENT_NAME_SIZE); + snprintf(fParams.fName, JACK_CLIENT_NAME_SIZE, "%s_%s", host_name, name); + fSocket.GetName(fParams.fSlaveNetName); + + // Set the socket parameters + fSocket.SetPort(port); + fSocket.SetAddress(fMulticastIP, port); + } + + virtual ~JackNetExtSlave() + {} + + int Open(jack_master_t* result) + { + // Init network connection + if (!JackNetSlaveInterface::InitConnection()){ + return -1; + } + + // Then set global parameters + SetParams(); + + // Set result + if (result != NULL) { + result->buffer_size = fParams.fPeriodSize; + result->sample_rate = fParams.fSampleRate; + strcpy(result->master_name, fParams.fMasterNetName); + } + + AllocPorts(); + return 0; + } + + int Restart() + { + // If shutdown cb is set, then call it + if (fShutdownCallback) + fShutdownCallback(fShutdownArg); + + // Init complete network connection + if (!JackNetSlaveInterface::Init()) + return -1; + + // Then set global parameters + SetParams(); + + // We need to notify possibly new buffer size and sample rate (see Execute) + if (fBufferSizeCallback) + fBufferSizeCallback(fParams.fPeriodSize, fBufferSizeArg); + + if (fSampleRateCallback) + fSampleRateCallback(fParams.fSampleRate, fSampleRateArg); + + AllocPorts(); + return 0; + } + + int Close() + { + fSocket.Close(); + FreePorts(); + return 0; + } + + void AllocPorts() + { + unsigned int port_index; + + // Set buffers + fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels]; + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { + fAudioCaptureBuffer[port_index] = new float[fParams.fPeriodSize]; + fNetAudioCaptureBuffer->SetBuffer(port_index, fAudioCaptureBuffer[port_index]); + } + + fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { + fMidiCaptureBuffer[port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiCaptureBuffer->SetBuffer(port_index, fMidiCaptureBuffer[port_index]); + } + + fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels]; + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { + fAudioPlaybackBuffer[port_index] = new float[fParams.fPeriodSize]; + fNetAudioPlaybackBuffer->SetBuffer(port_index, fAudioPlaybackBuffer[port_index]); + } + + fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { + fMidiPlaybackBuffer[port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; + fNetMidiPlaybackBuffer->SetBuffer(port_index, fMidiPlaybackBuffer[port_index]); + } + } + + void FreePorts() + { + unsigned int port_index; + + if (fAudioCaptureBuffer) { + for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) + delete[] fAudioCaptureBuffer[port_index]; + delete[] fAudioCaptureBuffer; + fAudioCaptureBuffer = NULL; + } + + if (fMidiCaptureBuffer) { + for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) + delete[] (fMidiCaptureBuffer[port_index]); + delete[] fMidiCaptureBuffer; + fMidiCaptureBuffer = NULL; + } + + if (fAudioPlaybackBuffer) { + for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) + delete[] fAudioPlaybackBuffer[port_index]; + delete[] fAudioPlaybackBuffer; + fAudioPlaybackBuffer = NULL; + } + + if (fMidiPlaybackBuffer) { + for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) + delete[] fMidiPlaybackBuffer[port_index]; + delete[] fMidiPlaybackBuffer; + fMidiPlaybackBuffer = NULL; + } + } + + // Transport + int EncodeTransportData() + { + return 0; + } + + int DecodeTransportData() + { + return 0; + } + + bool Init() + { + // Will do "something" on OSX only... + fThread.SetParams(float(fParams.fPeriodSize) / float(fParams.fSampleRate) * 1000000, 100 * 1000, 500 * 1000); + return (fThread.AcquireRealTime(80) == 0); // TODO: get a value from the server + } + + bool Execute() + { + try { + // Keep running even in case of error + while (fThread.GetStatus() == JackThread::kRunning) { + if (Process() == SOCKET_ERROR) + return false; + } + return false; + } catch (JackNetException& e) { + + // Otherwise just restart... + e.PrintMessage(); + fThread.DropRealTime(); + fThread.SetStatus(JackThread::kIniting); + FreePorts(); + Restart(); + if (Init()) { + fThread.SetStatus(JackThread::kRunning); + return true; + } else { + return false; + } + } + } + + int Read() + { + // Don't return -1 in case of sync recv failure + // we need the process to continue for network error detection + if (SyncRecv() == SOCKET_ERROR) + return 0; + + if (DecodeSyncPacket() < 0) + return 0; + + return DataRecv(); + } + + int Write() + { + if (EncodeSyncPacket() < 0) + return 0; + + if (SyncSend() == SOCKET_ERROR) + return SOCKET_ERROR; + + return DataSend(); + } + + int Process() + { + // Read data from the network + // in case of fatal network error, stop the process + if (Read() == SOCKET_ERROR) + return SOCKET_ERROR; + + fProcessCallback(fParams.fPeriodSize, + fParams.fSendAudioChannels, + fAudioCaptureBuffer, + fParams.fSendMidiChannels, + (void**)fMidiCaptureBuffer, + fParams.fReturnAudioChannels, + fAudioPlaybackBuffer, + fParams.fReturnMidiChannels, + (void**)fMidiPlaybackBuffer, + fProcessArg); + + // Then write data to network + // in case of failure, stop process + if (Write() == SOCKET_ERROR) + return SOCKET_ERROR; + + return 0; + } + + int Start() + { + // Finish connection.. + if (!JackNetSlaveInterface::InitRendering()) { + return -1; + } + + return (fProcessCallback == 0) ? -1 : fThread.StartSync(); + } + + int Stop() + { + return (fProcessCallback == 0) ? -1 : fThread.Kill(); + } + + // Callback + int SetProcessCallback(JackNetSlaveProcessCallback net_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fProcessCallback = net_callback; + fProcessArg = arg; + return 0; + } + } + + int SetShutdownCallback(JackNetSlaveShutdownCallback shutdown_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fShutdownCallback = shutdown_callback; + fShutdownArg = arg; + return 0; + } + } + + int SetBufferSizeCallback(JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fBufferSizeCallback = bufsize_callback; + fBufferSizeArg = arg; + return 0; + } + } + + int SetSampleRateCallback(JackNetSlaveSampleRateCallback samplerate_callback, void *arg) + { + if (fThread.GetStatus() == JackThread::kRunning) { + return -1; + } else { + fSampleRateCallback = samplerate_callback; + fSampleRateArg = arg; + return 0; + } + } + +}; + +struct JackNetAdapter : public JackAudioAdapterInterface { + + + JackNetAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate) + :JackAudioAdapterInterface(buffer_size, sample_rate) + { + fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; + fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; + + /* + for (i = 0; i < fCaptureChannels; i++) + fCaptureRingBuffer[i] = new JackLibSampleRateResampler(fAudioAdapter->GetQuality()); + for (i = 0; i < fPlaybackChannels; i++) + fPlaybackRingBuffer[i] = new JackLibSampleRateResampler(fAudioAdapter->GetQuality()); + */ + + int i; + for (i = 0; i < fCaptureChannels; i++) + fCaptureRingBuffer[i] = new JackResampler(); + for (i = 0; i < fPlaybackChannels; i++) + fPlaybackRingBuffer[i] = new JackResampler(); + } + + virtual ~JackNetAdapter() + { + int i; + for (i = 0; i < fCaptureChannels; i++) + delete (fCaptureRingBuffer[i]); + for (i = 0; i < fPlaybackChannels; i++) + delete(fPlaybackRingBuffer[i] ); + + delete[] fCaptureRingBuffer; + delete[] fPlaybackRingBuffer; + } + + void Reset() + { + int i; + for (i = 0; i < fCaptureChannels; i++) + fCaptureRingBuffer[i]->Reset(); + for (i = 0; i < fPlaybackChannels; i++) + fPlaybackRingBuffer[i]->Reset(); + } + + int PushInput(int audio_input, float** audio_input_buffer) + { + bool failure = false; + int port_index; + + // Get the resample factor, + jack_nframes_t time1, time2; + ResampleFactor(time1, time2); + + // Resample input data, + for (port_index = 0; port_index < audio_input; port_index++) { + fCaptureRingBuffer[port_index]->SetRatio(time1, time2); + if (fCaptureRingBuffer[port_index]->WriteResample(audio_input_buffer[port_index], fAdaptedBufferSize) < fAdaptedBufferSize) + failure = true; + } + + if (failure) { + ResetRingBuffers(); + return -1; + } + + return 0; + } + + int PullInput(int audio_input, float** audio_input_buffer) + { + bool failure = false; + int port_index; + + // DLL + SetCallbackTime(GetMicroSeconds()); + + // Push/pull from ringbuffer + for (port_index = 0; port_index < audio_input; port_index++) { + if (fCaptureRingBuffer[port_index]->Read(audio_input_buffer[port_index], fHostBufferSize) < fHostBufferSize) + failure = true; + } + + // Reset all ringbuffers in case of failure + if (failure) { + Reset(); + return -1; + } + + return 0; + } + + int PushOutput(int audio_input, float** audio_input_buffer) + { + bool failure = false; + int port_index; + + // DLL + SetCallbackTime(GetMicroSeconds()); + + // Push/pull from ringbuffer + for (port_index = 0; port_index < audio_input; port_index++) { + if (fPlaybackRingBuffer[port_index]->Write(audio_input_buffer[port_index], fHostBufferSize) < fHostBufferSize) + failure = true; + } + + // Reset all ringbuffers in case of failure + if (failure) { + Reset(); + return -1; + } + + return 0; + } + + int PullOutput(int audio_output, float** audio_output_buffer) + { + bool failure = false; + int port_index; + + //get the resample factor, + jack_nframes_t time1, time2; + ResampleFactor(time1, time2); + + //resample output data, + for (port_index = 0; port_index < fPlaybackChannels; port_index++) { + fPlaybackRingBuffer[port_index]->SetRatio(time2, time1); + if (fPlaybackRingBuffer[port_index]->ReadResample(audio_output_buffer[port_index], fAdaptedBufferSize) < fAdaptedBufferSize) + failure = true; + } + + if (failure) { + ResetRingBuffers(); + return -1; + } + + return 0; + } + +}; + + +} // end of namespace + +using namespace Jack; + +SERVER_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) +{ + JackNetExtSlave* slave = new JackNetExtSlave(ip, port, name, request); + if (slave->Open(result) == 0) { + return (jack_net_slave_t*)slave; + } else { + delete slave; + return NULL; + } +} + +SERVER_EXPORT int jack_net_slave_close(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + slave->Close(); + delete slave; + return 0; +} + +SERVER_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetProcessCallback(net_callback, arg); +} + +SERVER_EXPORT int jack_net_slave_activate(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->Start(); +} + +SERVER_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->Stop(); +} + +SERVER_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetBufferSizeCallback(bufsize_callback, arg); +} + +SERVER_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetSampleRateCallback(samplerate_callback, arg); +} + +SERVER_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) +{ + JackNetExtSlave* slave = (JackNetExtSlave*)net; + return slave->SetShutdownCallback(shutdown_callback, arg); +} + +// Master API + +SERVER_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) +{ + JackNetExtMaster* master = new JackNetExtMaster(ip, port, name, request); + if (master->Open(result) == 0) { + return (jack_net_master_t*)master; + } else { + delete master; + return NULL; + } +} + +SERVER_EXPORT int jack_net_master_close(jack_net_master_t* net) +{ + JackNetExtMaster* master = (JackNetExtMaster*)net; + master->Close(); + delete master; + return 0; +} +SERVER_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) +{ + JackNetExtMaster* slave = (JackNetExtMaster*)net; + return slave->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer); +} + +SERVER_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) +{ + JackNetExtMaster* slave = (JackNetExtMaster*)net; + return slave->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer); +} + +// Adapter API + +SERVER_EXPORT jack_adapter_t* jack_create_adapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate) +{ + return (jack_adapter_t*)new JackNetAdapter(buffer_size, sample_rate); +} + +SERVER_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter) +{ + delete((JackNetAdapter*)adapter); + return 0; +} + +SERVER_EXPORT int jack_adapter_push_input(jack_adapter_t * adapter, int channels, float** buffers) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PushInput(channels, buffers); +} + +SERVER_EXPORT int jack_adapter_pull_input(jack_adapter_t * adapter, int channels, float** buffers) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PullInput(channels, buffers); +} + +SERVER_EXPORT int jack_adapter_push_output(jack_adapter_t * adapter, int channels, float** buffers) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PushOutput(channels, buffers); +} + +SERVER_EXPORT int jack_adapter_pull_output(jack_adapter_t * adapter, int channels, float** buffers) +{ + JackNetAdapter* slave = (JackNetAdapter*)adapter; + return slave->PullOutput(channels, buffers); +} + + +// Empty code for now.. +//#ifdef TARGET_OS_IPHONE + +SERVER_EXPORT void jack_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + // TODO + va_end(ap); +} + +SERVER_EXPORT void jack_info(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + // TODO + va_end(ap); +} + +SERVER_EXPORT void jack_log(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + // TODO + va_end(ap); +} + +//#endif diff --git a/common/jack/net.h b/common/jack/net.h new file mode 100644 index 00000000..c8671608 --- /dev/null +++ b/common/jack/net.h @@ -0,0 +1,300 @@ +/* + Copyright (C) 2009 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __net_h__ +#define __net_h__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#define DEFAULT_MULTICAST_IP "225.3.19.154" +#define DEFAULT_PORT 19000 +#define DEFAULT_MTU 1500 +#define MASTER_NAME_SIZE 256 + +#define SOCKET_ERROR -1 + + enum JackNetMode { + + JackFastMode = 'f', + JackNormalMode = 'n', + JackSlowMode = 's', +}; + +typedef struct { + + int audio_input; + int audio_output; + int midi_input; + int midi_ouput; + int mtu; + int time_out; // in millisecond, -1 means in infinite + char mode; + +} jack_slave_t; + +typedef struct { + + jack_nframes_t buffer_size; + jack_nframes_t sample_rate; + char master_name[MASTER_NAME_SIZE]; + +} jack_master_t; + +/** + * jack_net_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_net_slave jack_net_slave_t; + + /** + * Open a network connection with the master machine. + * @param ip the multicast address of the master + * @param port the connection port + * @param request a connection request structure + * @param result a connection result structure + * + * @return Opaque net handle if successful or NULL in case of error. + */ +jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result); + +/** + * Close the network connection with the master machine. + * @param net the network connection to be closed + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_close(jack_net_slave_t* net); + +/** + * Prototype for Process callback. + * @param nframes buffer size + * @param audio_input number of audio inputs + * @param audio_input_buffer an array of audio input buffers + * @param midi_input number of MIDI inputs + * @param midi_input_buffer an array of MIDI input buffers + * @param audio_output number of audio outputs + * @param audio_output_buffer an array of audio output buffers + * @param midi_output number of MIDI outputs + * @param midi_output_buffer an array of MIDI output buffers + * @param arg pointer to a client supplied structure supplied by jack_set_net_process_callback(). + * + * @return zero on success, non-zero on error + */ +typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, + int audio_input, + float** audio_input_buffer, + int midi_input, + void** midi_input_buffer, + int audio_output, + float** audio_output_buffer, + int midi_output, + void** midi_output_buffer, + void* data); + +/** + * Set network process callback. + * @param net the network connection + * @param net_callback the process callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg); + +/** + * Start processing thread, the net_callback will start to be called. + * @param net the network connection + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_activate(jack_net_slave_t* net); + +/** + * Stop processing thread. + * @param net the network connection + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_slave_deactivate(jack_net_slave_t* net); + +/** + * Prototype for BufferSize callback. + * @param nframes buffer size + * @param arg pointer to a client supplied structure supplied by jack_set_net_buffer_size_callback(). + * + * @return zero on success, non-zero on error + */ +typedef int (*JackNetSlaveBufferSizeCallback)(jack_nframes_t nframes, void *arg); + +/** + * Prototype for SampleRate callback + * @param nframes sample rate + * @param arg pointer to a client supplied structure supplied by jack_set_net_sample_rate_callback(). + * + * @return zero on success, non-zero on error + */ +typedef int (*JackNetSlaveSampleRateCallback)(jack_nframes_t nframes, void *arg); + +/** + * Set network buffer size callback. + * @param net the network connection + * @param bufsize_callback the buffer size callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); + +/** + * Set network sample rate callback. + * @param net the network connection + * @param samplerate_callback the sample rate callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); + +/** + * Prototype for server Shutdown callback (if not set, the client will just restart, waiting for an available master again.) + * @param arg pointer to a client supplied structure supplied by jack_set_net_shutdown_callback(). + */ +typedef void (*JackNetSlaveShutdownCallback)(void* data); + +/** + * Set network shutdown callback. + * @param net the network connection + * @param shutdown_callback the shutdown callback + * @param arg pointer to a client supplied structure + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); + +/** + * jack_net_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_net_master jack_net_master_t; + + /** + * Open a network connection with the slave machine. + * @param ip the multicast address of the master + * @param port the connection port + * @param request a connection request structure + * @param result a connection result structure + * + * @return Opaque net handle if successful or NULL in case of error. + */ +jack_net_master_t* jack_net_master_open(const char* ip, int port, const char* name, jack_master_t* request, jack_slave_t* result); + +/** + * Close the network connection with the master machine. + * @param net the network connection to be closed + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_net_master_close(jack_net_master_t* net); + +/** + * Receive sync and data from the network + * @param net the network connection + * @param audio_input number of audio inputs + * @param audio_input_buffer an array of audio input buffers + * @param midi_input number of MIDI inputs + * @param midi_input_buffer an array of MIDI input buffers + * + * @return zero on success, non-zero on error + */ +int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer); + +/** + * Send sync and data to the network + * @param net the network connection + * @param audio_output number of audio ouputs + * @param audio_output_buffer an array of audio output buffers + * @param midi_output number of MIDI ouputs + * @param midi_output_buffer an array of MIDI output buffers + * + * @return zero on success, non-zero on error + */ +int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer); + +// Experimental Adapter API + +/** + * jack_adapter_t is an opaque type. You may only access it using the + * API provided. + */ +typedef struct _jack_adapter jack_adapter_t; + +/** + * Create an adapter. + * + * @return 0 on success, otherwise a non-zero error code + */ +jack_adapter_t* jack_create_adapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate); + +/** + * Destroy an adapter. + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_destroy_adapter(jack_adapter_t* adapter); + +/** + * Push input to ringbuffer + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_adapter_push_input(jack_adapter_t * adapter, int channels, float** buffers); + +/** + * Pull input from ringbuffer + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_adapter_pull_input(jack_adapter_t * adapter, int channels, float** buffers); + +/** + * Push output to ringbuffer + * + * @return error code. + */ +int jack_adapter_push_output(jack_adapter_t * adapter, int channels, float** buffers); + +/** + * Pull output from ringbuffer + * + * @return 0 on success, otherwise a non-zero error code + */ +int jack_adapter_pull_output(jack_adapter_t * adapter, int channels, float** buffers); + + +#ifdef __cplusplus +} +#endif + +#endif /* __net_h__ */ diff --git a/common/wscript b/common/wscript index 8c576a15..df784d79 100644 --- a/common/wscript +++ b/common/wscript @@ -173,6 +173,31 @@ def build(bld): if bld.env['IS_SUN']: serverlib.env.append_value("LINKFLAGS", "-lnsl -lsocket") + netlib = bld.new_task_gen('cxx', 'shlib') + netlib.features.append('cc') + netlib.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] + netlib.includes = includes + netlib.name = 'netlib' + netlib.target = 'jacknet' + netlib.uselib = uselib + netlib.install_path = '${LIBDIR}' + netlib.source = ['JackNetAPI.cpp', 'JackNetInterface.cpp', 'JackNetTool.cpp', 'JackAudioAdapterInterface.cpp', 'JackResampler.cpp', 'ringbuffer.c'] + + if bld.env['IS_LINUX']: + netlib.source += ['./posix/JackNetUnixSocket.cpp','./posix/JackPosixThread.cpp'] + netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + + if bld.env['IS_SUN']: + netlib.source += ['./posix/JackNetUnixSocket.cpp','./posix/JackPosixThread.cpp'] + netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + + + if bld.env['IS_MACOSX']: + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../macosx/JackMachThread.cpp', '../macosx/JackMachTime.c'] + netlib.env.append_value("LINKFLAGS", "-framework CoreAudio -single_module") + + netlib.vnum = bld.env['JACK_API_VERSION'] + clientlib = bld.new_task_gen('cxx', 'shlib') clientlib.features.append('cc') clientlib.defines = 'HAVE_CONFIG_H' diff --git a/example-clients/netmaster.c b/example-clients/netmaster.c new file mode 100644 index 00000000..863715df --- /dev/null +++ b/example-clients/netmaster.c @@ -0,0 +1,165 @@ +/* + Copyright (C) 2009 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 +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include + +#include + +jack_net_master_t* net; + +#define BUFFER_SIZE 512 +#define SAMPLE_RATE 44100 + +static void signal_handler(int sig) +{ + jack_net_master_close(net); + fprintf(stderr, "signal received, exiting ...\n"); + exit(0); +} + +static void +usage () +{ + fprintf (stderr, "\n" +"usage: jack_net_master \n" +" [ -b buffer size (default = %d) ]\n" +" [ -r sample rate (default = %d) ]\n" +" [ -a hostname (default = %s) ]\n" +" [ -p port (default = %d) ]\n", BUFFER_SIZE, SAMPLE_RATE, DEFAULT_MULTICAST_IP, DEFAULT_PORT); +} + +int +main (int argc, char *argv[]) +{ + int buffer_size = BUFFER_SIZE; + int sample_rate = SAMPLE_RATE; + int port = DEFAULT_PORT; + char* multicast_ip = DEFAULT_MULTICAST_IP; + const char *options = "b:r:a:p:"; + int option_index; + int opt; + + struct option long_options[] = + { + {"buffer size", 1, 0, 'b'}, + {"sample rate", 1, 0, 'r'}, + {"hostname", 1, 0, 'a'}, + {"port", 1, 0, 'p'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) { + + switch (opt) { + + case 'b': + buffer_size = atoi(optarg); + break; + + case 'r': + sample_rate = atoi(optarg); + break; + + case 'a': + multicast_ip = strdup(optarg); + break; + + case 'p': + port = atoi(optarg); + break; + + case 'h': + usage(); + return -1; + } + } + + int i; + jack_master_t request = { buffer_size, sample_rate, "master" }; + jack_slave_t result; + float** audio_input_buffer; + float** audio_output_buffer; + int wait_usec = (int) ((((float)buffer_size) * 1000000) / ((float)sample_rate)); + + printf("Waiting for a slave...\n"); + + if ((net = jack_net_master_open(DEFAULT_MULTICAST_IP, DEFAULT_PORT, "net_master", &request, &result)) == 0) { + fprintf(stderr, "jack server not running?\n"); + return 1; + } + + /* install a signal handler to properly quits jack client */ +#ifdef WIN32 + signal(SIGINT, signal_handler); + signal(SIGABRT, signal_handler); + signal(SIGTERM, signal_handler); +#else + signal(SIGQUIT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); +#endif + + // Allocate buffers + audio_input_buffer = calloc(result.audio_input, sizeof(float*)); + for (i = 0; i < result.audio_input; i++) { + audio_input_buffer[i] = calloc(buffer_size, sizeof(float)); + } + + audio_output_buffer = calloc(result.audio_output, sizeof(float*)); + for (i = 0; i < result.audio_output; i++) { + audio_output_buffer[i] = calloc(buffer_size, sizeof(float)); + } + + // Run until interrupted + while (1) { + + // Copy input to output + for (i = 0; i < result.audio_input; i++) { + memcpy(audio_output_buffer[i], audio_input_buffer[i], buffer_size * sizeof(float)); + } + + jack_net_master_send(net, result.audio_output, audio_output_buffer, 0, NULL); + jack_net_master_recv(net, result.audio_input, audio_input_buffer, 0, NULL); + usleep(wait_usec); + }; + + // Wait for application end + jack_net_master_close(net); + + for (i = 0; i < result.audio_input; i++) { + free(audio_input_buffer[i]); + } + free(audio_input_buffer); + + for (i = 0; i < result.audio_output; i++) { + free(audio_output_buffer[i]); + } + free(audio_output_buffer); + + exit (0); +} diff --git a/example-clients/netslave.c b/example-clients/netslave.c new file mode 100644 index 00000000..165e334f --- /dev/null +++ b/example-clients/netslave.c @@ -0,0 +1,160 @@ +/* + Copyright (C) 2009 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 +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include + +#include + +jack_net_slave_t* net; + +static void signal_handler(int sig) +{ + jack_net_slave_close(net); + fprintf(stderr, "signal received, exiting ...\n"); + exit(0); +} + +static void +usage () +{ + fprintf (stderr, "\n" +"usage: jack_net_slave \n" +" [ -C capture channels (default = 2)]\n" +" [ -P playback channels (default = 2) ]\n" +" [ -a hostname (default = %s) ]\n" +" [ -p port (default = %d)]\n", DEFAULT_MULTICAST_IP, DEFAULT_PORT); +} + + +static int net_process(jack_nframes_t buffer_size, + int audio_input, + float** audio_input_buffer, + int midi_input, + void** midi_input_buffer, + int audio_output, + float** audio_output_buffer, + int midi_output, + void** midi_output_buffer, + void* data) +{ + int i; + + // Copy input to output + for (i = 0; i < audio_input; i++) { + memcpy(audio_output_buffer[i], audio_input_buffer[i], buffer_size * sizeof(float)); + } + return 0; +} + +int +main (int argc, char *argv[]) +{ + int audio_input = 2; + int audio_output = 2; + int port = DEFAULT_PORT; + char* multicast_ip = DEFAULT_MULTICAST_IP; + const char *options = "C:P:a:p:"; + int option_index; + int opt; + + struct option long_options[] = + { + {"audio input", 1, 0, 'C'}, + {"audio output", 1, 0, 'P'}, + {"hostname", 1, 0, 'a'}, + {"port", 1, 0, 'p'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) { + + switch (opt) { + + case 'C': + audio_input = atoi(optarg); + break; + + case 'P': + audio_output = atoi(optarg); + break; + + case 'a': + multicast_ip = strdup(optarg); + break; + + case 'p': + port = atoi(optarg); + break; + + case 'h': + usage(); + return -1; + } + } + + jack_slave_t request = { audio_input, audio_output, 0, 0, DEFAULT_MTU, -1, JackSlowMode }; + jack_master_t result; + + printf("Waiting for a master...\n"); + + if ((net = jack_net_slave_open(DEFAULT_MULTICAST_IP, DEFAULT_PORT, "net_slave", &request, &result)) == 0) { + fprintf(stderr, "jack server not running?\n"); + return 1; + } + + jack_set_net_slave_process_callback(net, net_process, NULL); + if (jack_net_slave_activate(net) != 0) { + fprintf(stderr, "Cannot sactivate client\n"); + return 1; + } + + /* install a signal handler to properly quits jack client */ +#ifdef WIN32 + signal(SIGINT, signal_handler); + signal(SIGABRT, signal_handler); + signal(SIGTERM, signal_handler); +#else + signal(SIGQUIT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); +#endif + + /* run until interrupted */ + while (1) { + #ifdef WIN32 + Sleep(1000); + #else + sleep(1); + #endif + }; + + // Wait for application end + jack_net_slave_deactivate(net); + jack_net_slave_close(net); + exit (0); +} diff --git a/example-clients/wscript b/example-clients/wscript index 03886af4..1a5dcd69 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -21,6 +21,8 @@ example_programs = { 'jack_thru' : 'thru_client.c', 'jack_cpu_load' : 'cpu_load.c', 'jack_server_control' : 'server_control.cpp', + 'jack_net_slave' : 'netslave.c', + 'jack_net_master' : 'netmaster.c', } example_libs = { @@ -69,8 +71,13 @@ def build(bld): prog.env.append_value("LINKFLAGS", "-lm") if example_program == 'jack_server_control': prog.uselib_local = 'serverlib' + elif example_program == 'jack_net_slave': + prog.uselib_local = 'netlib' + elif example_program == 'jack_net_master': + prog.uselib_local = 'netlib' else: - prog.uselib_local = 'clientlib' + prog.uselib_local = 'clientlib' + prog.target = example_program #if bld.env['BUILD_EXAMPLE_CLIENT_TRANSPORT']