/* Copyright (C) 2009-2013 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 #include "JackNetInterface.h" #include "JackAudioAdapterInterface.h" #ifdef __cplusplus extern "C" { #endif // NetJack common API #define MASTER_NAME_SIZE 256 enum JackNetEncoder { JackFloatEncoder = 0, JackIntEncoder = 1, JackCeltEncoder = 2, JackOpusEncoder = 3 }; typedef struct { int audio_input; int audio_output; int midi_input; int midi_output; int mtu; int time_out; // in millisecond, -1 means in infinite int encoder; // one of JackNetEncoder int kbps; // KB per second for CELT encoder int latency; // network cycles } jack_slave_t; typedef struct { int audio_input; int audio_output; int midi_input; int midi_output; jack_nframes_t buffer_size; jack_nframes_t sample_rate; char master_name[MASTER_NAME_SIZE]; int time_out; int partial_cycle; } 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* arg); typedef int (*JackNetSlaveRestartCallback) (void* arg); typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg); 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); LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net); LIB_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg); LIB_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t* net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); LIB_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t* net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); LIB_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t* net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t* net, JackNetSlaveRestartCallback restart_callback, void *arg); LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t* net, JackNetSlaveErrorCallback error_callback, void *arg); // NetJack master API typedef struct _jack_net_master jack_net_master_t; LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result); LIB_EXPORT int jack_net_master_close(jack_net_master_t* net); 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); 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); 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); 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); // NetJack adapter API typedef struct _jack_adapter jack_adapter_t; LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate); LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter); LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter); LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_ERROR 2 LIB_EXPORT void jack_error(const char *fmt, ...); LIB_EXPORT void jack_info(const char *fmt, ...); LIB_EXPORT void jack_log(const char *fmt, ...); #ifdef __cplusplus } #endif namespace Jack { struct JackNetExtMaster : public JackNetMasterInterface { jack_master_t fRequest; JackRingBuffer** fRingBuffer; JackNetExtMaster(const char* ip, int port, 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; fRequest.audio_input = request->audio_input; fRequest.audio_output = request->audio_output; fRequest.time_out = request->time_out; fRequest.partial_cycle = request->partial_cycle; fRingBuffer = NULL; } virtual ~JackNetExtMaster() { if (fRingBuffer) { for (int i = 0; i < fParams.fReturnAudioChannels; i++) { delete fRingBuffer[i]; } delete [] fRingBuffer; } } int Open(jack_slave_t* result) { // Check buffer_size if (fRequest.buffer_size == 0) { jack_error("Incorrect buffer_size..."); return -1; } // Check sample_rate if (fRequest.sample_rate == 0) { jack_error("Incorrect sample_rate..."); return -1; } // Init socket API (win32) if (SocketAPIInit() < 0) { jack_error("Can't init Socket API, exiting..."); return -1; } // Request socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create the network manager input socket : %s", StrError(NET_ERROR_CODE)); return -1; } // Bind the socket to the local port if (fSocket.Bind() == SOCKET_ERROR) { jack_error("Can't bind the network manager socket : %s", StrError(NET_ERROR_CODE)); fSocket.Close(); return -1; } // Join multicast group if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); } // Local loop if (fSocket.SetLocalLoop() == SOCKET_ERROR) { jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE)); } // Set a timeout on the multicast receive (the thread can now be cancelled) if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) { jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); } // Main loop, wait for data, deal with it and wait again int attempt = 0; int rx_bytes = 0; int try_count = (fRequest.time_out > 0) ? int((1000000.f * float(fRequest.time_out)) / float(MANAGER_INIT_TIMEOUT)) : INT_MAX; 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)) { jack_error("Error in receive : %s", StrError(NET_ERROR_CODE)); if (++attempt == 10) { jack_error("Can't receive on the socket, exiting net manager" ); goto error; } } if (rx_bytes == sizeof(session_params_t)) { switch (GetPacketType(&fParams)) { case SLAVE_AVAILABLE: if (InitMaster(result) == 0) { SessionParamsDisplay(&fParams); fRunning = false; } else { jack_error("Can't init new net master..."); goto error; } jack_info("Waiting for a slave..."); break; case KILL_MASTER: break; default: break; } } } while (fRunning && (--try_count > 0)); if (try_count == 0) { jack_error("Time out error in connect"); return -1; } // Set result parameters result->audio_input = fParams.fSendAudioChannels; result->audio_output = fParams.fReturnAudioChannels; result->midi_input = fParams.fSendMidiChannels; result->midi_output = fParams.fReturnMidiChannels; result->mtu = fParams.fMtu; result->latency = fParams.fNetworkLatency; // Use ringbuffer in case of partial cycle and latency > 0 if (fRequest.partial_cycle && result->latency > 0) { fRingBuffer = new JackRingBuffer*[fParams.fReturnAudioChannels]; for (int i = 0; i < fParams.fReturnAudioChannels; i++) { fRingBuffer[i] = new JackRingBuffer(fRequest.buffer_size * result->latency * 2); } } return 0; error: fSocket.Close(); return -1; } int InitMaster(jack_slave_t* result) { // Check MASTER <==> SLAVE network protocol coherency if (fParams.fProtocolVersion != NETWORK_PROTOCOL) { jack_error("Error : slave '%s' is running with a different protocol %d != %d", fParams.fName, fParams.fProtocolVersion, NETWORK_PROTOCOL); return -1; } // Settings fSocket.GetName(fParams.fMasterNetName); fParams.fID = 1; fParams.fPeriodSize = fRequest.buffer_size; fParams.fSampleRate = fRequest.sample_rate; if (fRequest.audio_input == -1) { if (fParams.fSendAudioChannels == -1) { jack_error("Error : master and slave use -1 for wanted inputs..."); return -1; } else { result->audio_input = fParams.fSendAudioChannels; jack_info("Takes slave %d inputs", fParams.fSendAudioChannels); } } else if (fParams.fSendAudioChannels == -1) { fParams.fSendAudioChannels = fRequest.audio_input; jack_info("Takes master %d inputs", fRequest.audio_input); } else if (fParams.fSendAudioChannels != fRequest.audio_input) { jack_error("Error : master wants %d inputs and slave wants %d inputs...", fRequest.audio_input, fParams.fSendAudioChannels); return -1; } if (fRequest.audio_output == -1) { if (fParams.fReturnAudioChannels == -1) { jack_error("Error : master and slave use -1 for wanted outputs..."); return -1; } else { result->audio_output = fParams.fReturnAudioChannels; jack_info("Takes slave %d outputs", fParams.fReturnAudioChannels); } } else if (fParams.fReturnAudioChannels == -1) { fParams.fReturnAudioChannels = fRequest.audio_output; jack_info("Takes master %d outputs", fRequest.audio_output); } else if (fParams.fReturnAudioChannels != fRequest.audio_output) { jack_error("Error : master wants %d outputs and slave wants %d outputs...", fRequest.audio_output, fParams.fReturnAudioChannels); return -1; } // Close request socket fSocket.Close(); /// Network init if (!JackNetMasterInterface::Init()) { return -1; } // Set global parameters if (!SetParams()) { return -1; } return 0; } int Close() { fSocket.Close(); return 0; } void UseRingBuffer(int audio_input, float** audio_input_buffer, int write, int read) { // Possibly use ringbuffer... if (fRingBuffer) { for (int i = 0; i < audio_input; i++) { fRingBuffer[i]->Write(audio_input_buffer[i], write); fRingBuffer[i]->Read(audio_input_buffer[i], read); } } } int Read(int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames) { try { // frames = -1 means : entire buffer if (frames < 0) frames = fParams.fPeriodSize; int read_frames = 0; assert(audio_input == fParams.fReturnAudioChannels); for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) { assert(audio_input_buffer[audio_port_index]); fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, audio_input_buffer[audio_port_index]); } for (int midi_port_index = 0; midi_port_index < midi_input; midi_port_index++) { assert(((JackMidiBuffer**)midi_input_buffer)[midi_port_index]); fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_input_buffer)[midi_port_index]); } int res1 = SyncRecv(); switch (res1) { case NET_SYNCHING: // Data will not be received, so cleanup buffers... for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) { memset(audio_input_buffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); } UseRingBuffer(audio_input, audio_input_buffer, fParams.fPeriodSize, frames); return res1; case SOCKET_ERROR: return res1; case SYNC_PACKET_ERROR: // since sync packet is incorrect, don't decode it and continue with data break; default: // decode sync DecodeSyncPacket(read_frames); break; } int res2 = DataRecv(); UseRingBuffer(audio_input, audio_input_buffer, read_frames, frames); return res2; } catch (JackNetException& e) { jack_error(e.what()); return -1; } } int Write(int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames) { try { // frames = -1 means : entire buffer if (frames < 0) frames = fParams.fPeriodSize; assert(audio_output == fParams.fSendAudioChannels); for (int audio_port_index = 0; audio_port_index < audio_output; audio_port_index++) { assert(audio_output_buffer[audio_port_index]); fNetAudioCaptureBuffer->SetBuffer(audio_port_index, audio_output_buffer[audio_port_index]); } for (int midi_port_index = 0; midi_port_index < midi_output; midi_port_index++) { assert(((JackMidiBuffer**)midi_output_buffer)[midi_port_index]); fNetMidiCaptureBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_output_buffer)[midi_port_index]); } EncodeSyncPacket(frames); // send sync if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } // send data if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } catch (JackNetException& e) { jack_error(e.what()); return -1; } } // Transport void EncodeTransportData() {} void DecodeTransportData() {} }; struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface { // Data buffers float** fAudioCaptureBuffer; float** fAudioPlaybackBuffer; JackMidiBuffer** fMidiCaptureBuffer; JackMidiBuffer** fMidiPlaybackBuffer; JackThread fThread; JackNetSlaveProcessCallback fProcessCallback; void* fProcessArg; JackNetSlaveShutdownCallback fShutdownCallback; void* fShutdownArg; JackNetSlaveRestartCallback fRestartCallback; void* fRestartArg; JackNetSlaveErrorCallback fErrorCallback; void* fErrorArg; JackNetSlaveBufferSizeCallback fBufferSizeCallback; void* fBufferSizeArg; JackNetSlaveSampleRateCallback fSampleRateCallback; void* fSampleRateArg; int fConnectTimeOut; int fFrames; JackNetExtSlave(const char* ip, int port, const char* name, jack_slave_t* request) :fThread(this), fProcessCallback(NULL),fProcessArg(NULL), fShutdownCallback(NULL), fShutdownArg(NULL), fRestartCallback(NULL), fRestartArg(NULL), fErrorCallback(NULL), fErrorArg(NULL), fBufferSizeCallback(NULL), fBufferSizeArg(NULL), fSampleRateCallback(NULL), fSampleRateArg(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_output; fParams.fNetworkLatency = request->latency; fParams.fSampleEncoder = request->encoder; fParams.fKBps = request->kbps; fParams.fSlaveSyncMode = 1; fConnectTimeOut = request->time_out; // 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); fAudioCaptureBuffer = NULL; fAudioPlaybackBuffer = NULL; fMidiCaptureBuffer = NULL; fMidiPlaybackBuffer = NULL; } virtual ~JackNetExtSlave() {} void AllocPorts() { // Set buffers if (fParams.fSendAudioChannels > 0) { fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels]; for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { fAudioCaptureBuffer[audio_port_index] = new float[fParams.fPeriodSize]; memset(fAudioCaptureBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetAudioCaptureBuffer->SetBuffer(audio_port_index, fAudioCaptureBuffer[audio_port_index]); } } if (fParams.fSendMidiChannels > 0) { fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fMidiCaptureBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; memset(fMidiCaptureBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetMidiCaptureBuffer->SetBuffer(midi_port_index, fMidiCaptureBuffer[midi_port_index]); } } if (fParams.fReturnAudioChannels > 0) { fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels]; for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { fAudioPlaybackBuffer[audio_port_index] = new float[fParams.fPeriodSize]; memset(fAudioPlaybackBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, fAudioPlaybackBuffer[audio_port_index]); } } if (fParams.fReturnMidiChannels > 0) { fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fMidiPlaybackBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; memset(fMidiPlaybackBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, fMidiPlaybackBuffer[midi_port_index]); } } } void FreePorts() { if (fAudioCaptureBuffer) { for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { delete[] fAudioCaptureBuffer[audio_port_index]; } delete[] fAudioCaptureBuffer; fAudioCaptureBuffer = NULL; } if (fMidiCaptureBuffer) { for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { delete[] fMidiCaptureBuffer[midi_port_index]; } delete[] fMidiCaptureBuffer; fMidiCaptureBuffer = NULL; } if (fAudioPlaybackBuffer) { for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { delete[] fAudioPlaybackBuffer[audio_port_index]; } delete[] fAudioPlaybackBuffer; fAudioPlaybackBuffer = NULL; } if (fMidiPlaybackBuffer) { for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { delete[] (fMidiPlaybackBuffer[midi_port_index]); } delete[] fMidiPlaybackBuffer; fMidiPlaybackBuffer = NULL; } } int Open(jack_master_t* result) { // Check audio/midi parameters if (fParams.fSendAudioChannels == 0 && fParams.fReturnAudioChannels == 0 && fParams.fSendMidiChannels == 0 && fParams.fReturnMidiChannels == 0) { jack_error("Incorrect audio/midi channels number..."); return -1; } // Check MTU parameters if ((fParams.fMtu < DEFAULT_MTU) && (fParams.fMtu > MAX_MTU)) { jack_error("MTU is not in the expected range [%d ... %d]", DEFAULT_MTU, MAX_MTU); return -1; } // Check CELT encoder parameters if ((fParams.fSampleEncoder == JackCeltEncoder) && (fParams.fKBps == 0)) { jack_error("CELT encoder with 0 for kps..."); return -1; } if ((fParams.fSampleEncoder == JackOpusEncoder) && (fParams.fKBps == 0)) { jack_error("Opus encoder with 0 for kps..."); return -1; } // Check latency if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { jack_error("Network latency is limited to %d", NETWORK_MAX_LATENCY); return -1; } // Init network connection if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { jack_error("Initing network fails..."); return -1; } // Finish connection... if (!JackNetSlaveInterface::InitRendering()) { jack_error("Starting network fails..."); return -1; } // Then set global parameters if (!SetParams()) { jack_error("SetParams error..."); return -1; } // Set result if (result != NULL) { result->buffer_size = fParams.fPeriodSize; result->sample_rate = fParams.fSampleRate; result->audio_input = fParams.fSendAudioChannels; result->audio_output = fParams.fReturnAudioChannels; result->midi_input = fParams.fSendMidiChannels; result->midi_output = fParams.fReturnMidiChannels; strcpy(result->master_name, fParams.fMasterNetName); } // By default fFrames is fPeriodSize fFrames = fParams.fPeriodSize; SessionParamsDisplay(&fParams); AllocPorts(); return 0; } int Restart() { // Do it until client possibly decides to stop trying to connect... while (true) { // If restart cb is set, then call it if (fRestartCallback) { if (fRestartCallback(fRestartArg) != 0) { return -1; } // Otherwise if shutdown cb is set, then call it } else if (fShutdownCallback) { fShutdownCallback(fShutdownArg); } // Init network connection if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { jack_error("Initing network fails after time_out, retry..."); } else { break; } } // Finish connection if (!JackNetSlaveInterface::InitRendering()) { jack_error("Starting network fails..."); return -1; } // Then set global parameters if (!SetParams()) { jack_error("SetParams error..."); return -1; } // We need to notify possibly new buffer size and sample rate (see Execute) if (fBufferSizeCallback) { if (fBufferSizeCallback(fParams.fPeriodSize, fBufferSizeArg) != 0) { jack_error("New buffer size = %d cannot be used...", fParams.fPeriodSize); return -1; } } if (fSampleRateCallback) { if (fSampleRateCallback(fParams.fSampleRate, fSampleRateArg) != 0) { jack_error("New sample rate = %d cannot be used...", fParams.fSampleRate); return -1; } } AllocPorts(); return 0; } int Close() { fSocket.Close(); FreePorts(); return 0; } // Transport void EncodeTransportData() {} void DecodeTransportData() {} bool Init() { // Will do "something" on OSX only... UInt64 period, constraint; period = constraint = UInt64(1000000000.f * (float(fParams.fPeriodSize) / float(fParams.fSampleRate))); UInt64 computation = JackTools::ComputationMicroSec(fParams.fPeriodSize) * 1000; fThread.SetParams(period, computation, constraint); return (fThread.AcquireSelfRealTime(80) == 0); // TODO: get a value from the server } bool IsRunning() { return (fThread.GetStatus() == JackThread::kRunning); } bool Execute() { try { /* Fist cycle use an INT_MAX time out, so that connection is considered established (with PACKET_TIMEOUT later on) when the first cycle has been done. */ DummyProcess(); // 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(); jack_info("NetSlave is restarted"); fThread.DropRealTime(); fThread.SetStatus(JackThread::kIniting); FreePorts(); if (Restart() == 0 && Init()) { fThread.SetStatus(JackThread::kRunning); return true; } else { return false; } } } int Read() { // receive sync (launch the cycle) switch (SyncRecv()) { case SOCKET_ERROR: return SOCKET_ERROR; case SYNC_PACKET_ERROR: // since sync packet is incorrect, don't decode it and continue with data if (fErrorCallback) { fErrorCallback(SYNC_PACKET_ERROR, fErrorArg); } break; default: // decode sync DecodeSyncPacket(fFrames); break; } int res = DataRecv(); if (res == DATA_PACKET_ERROR && fErrorCallback) { fErrorCallback(DATA_PACKET_ERROR, fErrorArg); } return res; } int Write() { EncodeSyncPacket(fFrames); if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } return DataSend(); } void DummyProcess() { // First cycle with INT_MAX time out SetPacketTimeOut(INT_MAX); // One cycle Process(); // Then use PACKET_TIMEOUT * fParams.fNetworkLatency for next cycles SetPacketTimeOut(std::max(int(PACKET_TIMEOUT), int(PACKET_TIMEOUT * fParams.fNetworkLatency))); } int Process() { // Read data from the network, throw JackNetException in case of network error... if (Read() == SOCKET_ERROR) { return SOCKET_ERROR; } if (fFrames < 0) fFrames = fParams.fPeriodSize; fProcessCallback(fFrames, fParams.fSendAudioChannels, fAudioCaptureBuffer, fParams.fSendMidiChannels, (void**)fMidiCaptureBuffer, fParams.fReturnAudioChannels, fAudioPlaybackBuffer, fParams.fReturnMidiChannels, (void**)fMidiPlaybackBuffer, fProcessArg); // Then write data to network, throw JackNetException in case of network error... if (Write() == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } int Start() { 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 SetRestartCallback(JackNetSlaveRestartCallback restart_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fRestartCallback = restart_callback; fRestartArg = arg; return 0; } } int SetErrorCallback(JackNetSlaveErrorCallback error_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fErrorCallback = error_callback; fErrorArg = 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(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate) :JackAudioAdapterInterface(host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate) { fCaptureChannels = input; fPlaybackChannels = output; Create(); } void Create() { //ringbuffers if (fCaptureChannels > 0) { fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; } if (fPlaybackChannels > 0) { fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; } if (fAdaptative) { AdaptRingBufferSize(); jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); } else { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); } for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i] = new JackResampler(); fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i] = new JackResampler(); fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } if (fCaptureChannels > 0) { jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); } if (fPlaybackChannels > 0) { jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); } } virtual ~JackNetAdapter() { Destroy(); } void Flush() { for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } } }; } // end of namespace using namespace Jack; 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) { JackNetExtSlave* slave = new JackNetExtSlave(ip, port, name, request); if (slave->Open(result) == 0) { return (jack_net_slave_t*)slave; } else { delete slave; return NULL; } } LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; slave->Close(); delete slave; return 0; } LIB_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); } LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->Start(); } LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->Stop(); } LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->IsRunning(); } LIB_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); } LIB_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); } LIB_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); } LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetRestartCallback(restart_callback, arg); } LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetErrorCallback(error_callback, arg); } // Master API LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result) { JackNetExtMaster* master = new JackNetExtMaster(ip, port, request); if (master->Open(result) == 0) { return (jack_net_master_t*)master; } else { delete master; return NULL; } } LIB_EXPORT int jack_net_master_close(jack_net_master_t* net) { JackNetExtMaster* master = (JackNetExtMaster*)net; master->Close(); delete master; return 0; } 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) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, -1); } 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) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, -1); } 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) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, frames); } 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) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, frames); } // Adapter API LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate) { try { return (jack_adapter_t*)new JackNetAdapter(input, output, host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate); } catch (...) { return NULL; } } LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter) { delete((JackNetAdapter*)adapter); return 0; } LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter) { JackNetAdapter* slave = (JackNetAdapter*)adapter; slave->Flush(); } LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) { JackNetAdapter* slave = (JackNetAdapter*)adapter; return slave->PushAndPull(input, output, frames); } LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) { JackNetAdapter* slave = (JackNetAdapter*)adapter; return slave->PullAndPush(input, output, frames); } static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap) { static const char* netjack_log = getenv("JACK_NETJACK_LOG"); static bool is_netjack_log = (netjack_log) ? atoi(netjack_log) : 0; if (is_netjack_log) { char buffer[300]; size_t len; if (prefix != NULL) { len = strlen(prefix); memcpy(buffer, prefix, len); } else { len = 0; } vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap); printf("%s", buffer); printf("\n"); } } LIB_EXPORT void jack_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } LIB_EXPORT void jack_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } LIB_EXPORT void jack_log(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); }