JackNetAPI.cpp
/*
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 <assert.h>
#include <stdarg.h>
#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();
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 + 1];
// 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);
}
Jack::JackNetExtMaster::InitMaster
int InitMaster(jack_slave_t *result)
Definition: JackNetAPI.cpp:298
Jack::JackRingBuffer
Base class for RingBuffer in frames.
Definition: JackResampler.h:41
Jack
Definition: JackAC3Encoder.cpp:30
Jack::JackNetSlaveInterface
Definition: JackNetInterface.h:199
Jack::JackRunnableInterface
The base class for runnable objects, that have an Init and Execute method to be called in a threa...
Definition: JackThread.h:34
jack_master_t
Definition: JackNetAPI.cpp:57
jack_error
LIB_EXPORT void jack_error(const char *fmt,...)
Definition: JackNetAPI.cpp:1231
Jack::JackResampler
Base class for Resampler.
Definition: JackResampler.h:79
jack_info
LIB_EXPORT void jack_info(const char *fmt,...)
Definition: JackNetAPI.cpp:1239
Jack::JackAudioAdapterInterface
Base class for audio adapters.
Definition: JackAudioAdapterInterface.h:65
Jack::JackNetExtSlave::Init
bool Init()
Definition: JackNetAPI.cpp:772
jack_slave_t
Definition: JackNetAPI.cpp:43
Jack::JackNetExtMaster
Definition: JackNetAPI.cpp:148
Jack::JackNetExtSlave
Definition: JackNetAPI.cpp:478
Jack::JackNetAdapter
Net adapter.
Definition: JackNetAdapter.h:33
Jack::JackMidiBuffer
Definition: JackMidiPort.h:75
Jack::JackNetMasterInterface
Definition: JackNetInterface.h:143
Jack::JackNetException
Exception possibly thrown by Net slaves.
Definition: JackException.h:103
Jack::_session_params
This structure containes master/slave connection parameters, it's used to setup the whole system...
Definition: JackNetTool.h:92
jack_log
LIB_EXPORT void jack_log(const char *fmt,...)
Definition: JackNetAPI.cpp:1247
Jack::JackMachThread
Darwin threads. Real-time threads are actually "time constraint" threads.
Definition: JackMachThread.h:92