|
- /*
- Copyright (C) 2008-2011 Romain Moret at Grame
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include "JackNetAdapter.h"
- #include "JackException.h"
- #include "JackServerGlobals.h"
- #include "JackEngineControl.h"
- #include "JackArgParser.h"
- #include <assert.h>
-
- namespace Jack
- {
- JackNetAdapter::JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params)
- : JackAudioAdapterInterface(buffer_size, sample_rate), JackNetSlaveInterface(), fThread(this)
- {
- jack_log("JackNetAdapter::JackNetAdapter");
-
- /*
- Global parameter setting : we can't call JackNetSlaveInterface constructor with some parameters before,
- because we don't have full parametering right now, parameters will be parsed from the param list,
- and then JackNetSlaveInterface will be filled with proper values.
- */
- char multicast_ip[32];
- uint udp_port;
- GetHostName(fParams.fName, JACK_CLIENT_NAME_SIZE);
- fSocket.GetName(fParams.fSlaveNetName);
- fParams.fMtu = DEFAULT_MTU;
- // Deactivated for now...
- fParams.fTransportSync = 0;
- int send_audio = -1;
- int return_audio = -1;
- fParams.fSendMidiChannels = 0;
- fParams.fReturnMidiChannels = 0;
- fParams.fSampleRate = sample_rate;
- fParams.fPeriodSize = buffer_size;
- fParams.fSlaveSyncMode = 1;
- fParams.fNetworkLatency = NETWORK_DEFAULT_LATENCY;
- fParams.fSampleEncoder = JackFloatEncoder;
- fClient = jack_client;
-
- // Possibly use env variable
- const char* default_udp_port = getenv("JACK_NETJACK_PORT");
- udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT;
-
- const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST");
- if (default_multicast_ip) {
- strcpy(multicast_ip, default_multicast_ip);
- } else {
- strcpy(multicast_ip, DEFAULT_MULTICAST_IP);
- }
-
- //options parsing
- const JSList* node;
- const jack_driver_param_t* param;
- for (node = params; node; node = jack_slist_next(node))
- {
- param = (const jack_driver_param_t*) node->data;
-
- switch (param->character) {
- case 'a' :
- assert(strlen(param->value.str) < 32);
- strcpy(multicast_ip, param->value.str);
- break;
- case 'p' :
- udp_port = param->value.ui;
- break;
- case 'M' :
- fParams.fMtu = param->value.i;
- break;
- case 'C' :
- send_audio = param->value.i;
- break;
- case 'P' :
- return_audio = param->value.i;
- break;
- case 'n' :
- strncpy(fParams.fName, param->value.str, JACK_CLIENT_NAME_SIZE);
- break;
- case 't' :
- fParams.fTransportSync = param->value.ui;
- break;
- #if HAVE_CELT
- case 'c':
- if (param->value.i > 0) {
- fParams.fSampleEncoder = JackCeltEncoder;
- fParams.fKBps = param->value.i;
- }
- break;
- #endif
- #if HAVE_OPUS
- case 'O':
- if (param->value.i > 0) {
- fParams.fSampleEncoder = JackOpusEncoder;
- fParams.fKBps = param->value.i;
- }
- break;
- #endif
- case 'l' :
- fParams.fNetworkLatency = param->value.i;
- if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) {
- jack_error("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY);
- throw std::bad_alloc();
- }
- break;
- case 'q':
- fQuality = param->value.ui;
- break;
- case 'g':
- fRingbufferCurSize = param->value.ui;
- fAdaptative = false;
- break;
- }
- }
-
- strcpy(fMulticastIP, multicast_ip);
-
- // Set the socket parameters
- fSocket.SetPort(udp_port);
- fSocket.SetAddress(fMulticastIP, udp_port);
-
- // If not set, takes default
- fParams.fSendAudioChannels = (send_audio == -1) ? 2 : send_audio;
-
- // If not set, takes default
- fParams.fReturnAudioChannels = (return_audio == -1) ? 2 : return_audio;
-
- // Set the audio adapter interface channel values
- SetInputs(fParams.fSendAudioChannels);
- SetOutputs(fParams.fReturnAudioChannels);
-
- // Soft buffers will be allocated later (once network initialization done)
- fSoftCaptureBuffer = NULL;
- fSoftPlaybackBuffer = NULL;
- }
-
- JackNetAdapter::~JackNetAdapter()
- {
- jack_log("JackNetAdapter::~JackNetAdapter");
-
- if (fSoftCaptureBuffer) {
- for (int port_index = 0; port_index < fCaptureChannels; port_index++) {
- delete[] fSoftCaptureBuffer[port_index];
- }
- delete[] fSoftCaptureBuffer;
- }
- if (fSoftPlaybackBuffer) {
- for (int port_index = 0; port_index < fPlaybackChannels; port_index++) {
- delete[] fSoftPlaybackBuffer[port_index];
- }
- delete[] fSoftPlaybackBuffer;
- }
- }
-
- //open/close--------------------------------------------------------------------------
- int JackNetAdapter::Open()
- {
- jack_info("NetAdapter started in %s mode %s Master's transport sync.",
- (fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without");
-
- if (fThread.StartSync() < 0) {
- jack_error("Cannot start netadapter thread");
- return -1;
- }
-
- return 0;
- }
-
- int JackNetAdapter::Close()
- {
- int res = 0;
- jack_log("JackNetAdapter::Close");
-
- #ifdef JACK_MONITOR
- fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
- #endif
-
- if (fThread.Kill() < 0) {
- jack_error("Cannot kill thread");
- res = -1;
- }
-
- fSocket.Close();
- return res;
- }
-
- int JackNetAdapter::SetBufferSize(jack_nframes_t buffer_size)
- {
- JackAudioAdapterInterface::SetHostBufferSize(buffer_size);
- return 0;
- }
-
- //thread------------------------------------------------------------------------------
- // TODO : if failure, thread exist... need to restart ?
-
- bool JackNetAdapter::Init()
- {
- jack_log("JackNetAdapter::Init");
-
- //init network connection
- if (!JackNetSlaveInterface::Init()) {
- jack_error("JackNetSlaveInterface::Init() error...");
- return false;
- }
-
- //then set global parameters
- if (!SetParams()) {
- jack_error("SetParams error...");
- return false;
- }
-
- //set buffers
- if (fCaptureChannels > 0) {
- fSoftCaptureBuffer = new sample_t*[fCaptureChannels];
- for (int port_index = 0; port_index < fCaptureChannels; port_index++) {
- fSoftCaptureBuffer[port_index] = new sample_t[fParams.fPeriodSize];
- fNetAudioCaptureBuffer->SetBuffer(port_index, fSoftCaptureBuffer[port_index]);
- }
- }
-
- if (fPlaybackChannels > 0) {
- fSoftPlaybackBuffer = new sample_t*[fPlaybackChannels];
- for (int port_index = 0; port_index < fPlaybackChannels; port_index++) {
- fSoftPlaybackBuffer[port_index] = new sample_t[fParams.fPeriodSize];
- fNetAudioPlaybackBuffer->SetBuffer(port_index, fSoftPlaybackBuffer[port_index]);
- }
- }
-
- //set audio adapter parameters
- SetAdaptedBufferSize(fParams.fPeriodSize);
- SetAdaptedSampleRate(fParams.fSampleRate);
-
- // Will do "something" on OSX only...
- fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
-
- if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
- jack_error("AcquireSelfRealTime error");
- } else {
- set_threaded_log_function();
- }
-
- //init done, display parameters
- SessionParamsDisplay(&fParams);
- return true;
- }
-
- bool JackNetAdapter::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();
- jack_info("NetAdapter is restarted");
- Reset();
- fThread.DropSelfRealTime();
- fThread.SetStatus(JackThread::kIniting);
- if (Init()) {
- fThread.SetStatus(JackThread::kRunning);
- return true;
- } else {
- return false;
- }
- }
- }
-
- //transport---------------------------------------------------------------------------
- void JackNetAdapter::DecodeTransportData()
- {
- //TODO : we need here to get the actual timebase master to eventually release it from its duty (see JackNetDriver)
-
- //is there a new transport state ?
- if (fSendTransportData.fNewState &&(fSendTransportData.fState != jack_transport_query(fClient, NULL))) {
- switch (fSendTransportData.fState)
- {
- case JackTransportStopped :
- jack_transport_stop(fClient);
- jack_info("NetMaster : transport stops");
- break;
-
- case JackTransportStarting :
- jack_transport_reposition(fClient, &fSendTransportData.fPosition);
- jack_transport_start(fClient);
- jack_info("NetMaster : transport starts");
- break;
-
- case JackTransportRolling :
- // TODO, we need to :
- // - find a way to call TransportEngine->SetNetworkSync()
- // - turn the transport state to JackTransportRolling
- jack_info("NetMaster : transport rolls");
- break;
- }
- }
- }
-
- void JackNetAdapter::EncodeTransportData()
- {
- //is there a timebase master change ?
- int refnum = -1;
- bool conditional = 0;
- //TODO : get the actual timebase master
- if (refnum != fLastTimebaseMaster) {
- //timebase master has released its function
- if (refnum == -1) {
- fReturnTransportData.fTimebaseMaster = RELEASE_TIMEBASEMASTER;
- jack_info("Sending a timebase master release request.");
- } else {
- //there is a new timebase master
- fReturnTransportData.fTimebaseMaster = (conditional) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER;
- jack_info("Sending a %s timebase master request.", (conditional) ? "conditional" : "non-conditional");
- }
- fLastTimebaseMaster = refnum;
- } else {
- fReturnTransportData.fTimebaseMaster = NO_CHANGE;
- }
-
- //update transport state and position
- fReturnTransportData.fState = jack_transport_query(fClient, &fReturnTransportData.fPosition);
-
- //is it a new state (that the master need to know...) ?
- fReturnTransportData.fNewState = ((fReturnTransportData.fState != fLastTransportState) &&
- (fReturnTransportData.fState != fSendTransportData.fState));
- if (fReturnTransportData.fNewState) {
- jack_info("Sending transport state '%s'.", GetTransportState(fReturnTransportData.fState));
- }
- fLastTransportState = fReturnTransportData.fState;
- }
-
- //read/write operations---------------------------------------------------------------
- int JackNetAdapter::Read()
- {
- 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
- break;
-
- default:
- //decode sync
- int unused_frames;
- DecodeSyncPacket(unused_frames);
- break;
- }
-
- return DataRecv();
- }
-
- int JackNetAdapter::Write()
- {
- EncodeSyncPacket();
-
- if (SyncSend() == SOCKET_ERROR) {
- return SOCKET_ERROR;
- }
-
- return DataSend();
- }
-
- //process-----------------------------------------------------------------------------
- int JackNetAdapter::Process()
- {
- //read data from the network
- //in case of fatal network error, stop the process
- if (Read() == SOCKET_ERROR) {
- return SOCKET_ERROR;
- }
-
- PushAndPull(fSoftCaptureBuffer, fSoftPlaybackBuffer, fAdaptedBufferSize);
-
- //then write data to network
- //in case of failure, stop process
- if (Write() == SOCKET_ERROR) {
- return SOCKET_ERROR;
- }
-
- return 0;
- }
-
- } // namespace Jack
-
- //loader------------------------------------------------------------------------------
- #ifdef __cplusplus
- extern "C"
- {
- #endif
-
- #include "driver_interface.h"
- #include "JackAudioAdapter.h"
-
- using namespace Jack;
-
- SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor()
- {
- jack_driver_desc_t * desc;
- jack_driver_desc_filler_t filler;
- jack_driver_param_value_t value;
-
- desc = jack_driver_descriptor_construct("netadapter", JackDriverNone, "netjack net <==> audio backend adapter", &filler);
-
- strcpy(value.str, DEFAULT_MULTICAST_IP);
- jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL);
-
- value.i = DEFAULT_PORT;
- jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL);
-
- value.i = DEFAULT_MTU;
- jack_driver_descriptor_add_parameter(desc, &filler, "mtu", 'M', JackDriverParamInt, &value, NULL, "MTU to the master", NULL);
-
- value.i = 2;
- jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", NULL);
- jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", NULL);
-
- #if HAVE_CELT
- value.i = -1;
- jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamInt, &value, NULL, "Set CELT encoding and number of kBits per channel", NULL);
- #endif
-
- #if HAVE_OPUS
- value.i = -1;
- jack_driver_descriptor_add_parameter(desc, &filler, "opus", 'O', JackDriverParamInt, &value, NULL, "Set Opus encoding and number of kBits per channel", NULL);
- #endif
-
- strcpy(value.str, "'hostname'");
- jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL);
-
- value.ui = 0U;
- jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamUInt, &value, NULL, "Sync transport with master's", NULL);
-
- value.ui = 5U;
- jack_driver_descriptor_add_parameter(desc, &filler, "latency", 'l', JackDriverParamUInt, &value, NULL, "Network latency", NULL);
-
- value.i = 0;
- jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL);
-
- value.i = 32768;
- jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)");
-
- value.i = false;
- jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netadapter to system ports", NULL);
-
- return desc;
- }
-
- SERVER_EXPORT int jack_internal_initialize(jack_client_t* client, const JSList* params)
- {
- jack_log("Loading netadapter");
-
- Jack::JackAudioAdapter* adapter;
- jack_nframes_t buffer_size = jack_get_buffer_size(client);
- jack_nframes_t sample_rate = jack_get_sample_rate(client);
-
- try {
-
- adapter = new Jack::JackAudioAdapter(client, new Jack::JackNetAdapter(client, buffer_size, sample_rate, params), params);
- assert(adapter);
-
- if (adapter->Open() == 0) {
- return 0;
- } else {
- delete adapter;
- return 1;
- }
-
- } catch (...) {
- jack_info("netadapter allocation error");
- return 1;
- }
- }
-
- SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init)
- {
- JSList* params = NULL;
- bool parse_params = true;
- int res = 1;
- jack_driver_desc_t* desc = jack_get_descriptor();
-
- Jack::JackArgParser parser(load_init);
- if (parser.GetArgc() > 0) {
- parse_params = parser.ParseParams(desc, ¶ms);
- }
-
- if (parse_params) {
- res = jack_internal_initialize(jack_client, params);
- parser.FreeParams(params);
- }
- return res;
- }
-
- SERVER_EXPORT void jack_finish(void* arg)
- {
- Jack::JackAudioAdapter* adapter = static_cast<Jack::JackAudioAdapter*>(arg);
-
- if (adapter) {
- jack_log("Unloading netadapter");
- adapter->Close();
- delete adapter;
- }
- }
-
- #ifdef __cplusplus
- }
- #endif
|