/* 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 "JackNetManager.h" #include "JackArgParser.h" #include "JackServerGlobals.h" #include "JackLockedEngine.h" #include "thread.h" using namespace std; namespace Jack { //JackNetMaster****************************************************************************************************** JackNetMaster::JackNetMaster(JackNetSocket& socket, session_params_t& params, const char* multicast_ip) : JackNetMasterInterface(params, socket, multicast_ip) { jack_log("JackNetMaster::JackNetMaster"); //settings fName = const_cast(fParams.fName); fClient = NULL; fSendTransportData.fState = -1; fReturnTransportData.fState = -1; fLastTransportState = -1; int port_index; //jack audio ports fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels]; for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { fAudioCapturePorts[port_index] = NULL; } fAudioPlaybackPorts = new jack_port_t* [fParams.fReturnAudioChannels]; for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { fAudioPlaybackPorts[port_index] = NULL; } //jack midi ports fMidiCapturePorts = new jack_port_t* [fParams.fSendMidiChannels]; for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { fMidiCapturePorts[port_index] = NULL; } fMidiPlaybackPorts = new jack_port_t* [fParams.fReturnMidiChannels]; for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { fMidiPlaybackPorts[port_index] = NULL; } //monitor #ifdef JACK_MONITOR fPeriodUsecs = (int)(1000000.f * ((float) fParams.fPeriodSize / (float) fParams.fSampleRate)); string plot_name; plot_name = string(fParams.fName); plot_name += string("_master"); plot_name += string((fParams.fSlaveSyncMode) ? "_sync" : "_async"); plot_name += string("_latency"); fNetTimeMon = new JackGnuPlotMonitor(128, 4, plot_name); string net_time_mon_fields[] = { string("sync send"), string("end of send"), string("sync recv"), string("end of cycle") }; string net_time_mon_options[] = { string("set xlabel \"audio cycles\""), string("set ylabel \"% of audio cycle\"") }; fNetTimeMon->SetPlotFile(net_time_mon_options, 2, net_time_mon_fields, 4); #endif } JackNetMaster::~JackNetMaster() { jack_log("JackNetMaster::~JackNetMaster ID = %u", fParams.fID); if (fClient) { jack_deactivate(fClient); FreePorts(); jack_client_close(fClient); } delete[] fAudioCapturePorts; delete[] fAudioPlaybackPorts; delete[] fMidiCapturePorts; delete[] fMidiPlaybackPorts; #ifdef JACK_MONITOR fNetTimeMon->Save(); delete fNetTimeMon; #endif } //init-------------------------------------------------------------------------------- bool JackNetMaster::Init(bool auto_connect) { //network init if (!JackNetMasterInterface::Init()) { jack_error("JackNetMasterInterface::Init() error..."); return false; } //set global parameters if (!SetParams()) { jack_error("SetParams error..."); return false; } //jack client and process jack_status_t status; if ((fClient = jack_client_open(fName, JackNullOption, &status, NULL)) == NULL) { jack_error("Can't open a new JACK client"); return false; } if (jack_set_process_callback(fClient, SetProcess, this) < 0) { goto fail; } if (jack_set_buffer_size_callback(fClient, SetBufferSize, this) < 0) { goto fail; } if (jack_set_sample_rate_callback(fClient, SetSampleRate, this) < 0) { goto fail; } if (jack_set_latency_callback(fClient, LatencyCallback, this) < 0) { goto fail; } /* if (jack_set_port_connect_callback(fClient, SetConnectCallback, this) < 0) { goto fail; } */ if (AllocPorts() != 0) { jack_error("Can't allocate JACK ports"); goto fail; } //process can now run fRunning = true; //finally activate jack client if (jack_activate(fClient) != 0) { jack_error("Can't activate JACK client"); goto fail; } if (auto_connect) { ConnectPorts(); } jack_info("New NetMaster started"); return true; fail: FreePorts(); jack_client_close(fClient); fClient = NULL; return false; } //jack ports-------------------------------------------------------------------------- int JackNetMaster::AllocPorts() { int i; char name[32]; jack_log("JackNetMaster::AllocPorts"); //audio for (i = 0; i < fParams.fSendAudioChannels; i++) { snprintf(name, sizeof(name), "to_slave_%d", i+1); if ((fAudioCapturePorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) { return -1; } } for (i = 0; i < fParams.fReturnAudioChannels; i++) { snprintf(name, sizeof(name), "from_slave_%d", i+1); if ((fAudioPlaybackPorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) { return -1; } } //midi for (i = 0; i < fParams.fSendMidiChannels; i++) { snprintf(name, sizeof(name), "midi_to_slave_%d", i+1); if ((fMidiCapturePorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) { return -1; } } for (i = 0; i < fParams.fReturnMidiChannels; i++) { snprintf(name, sizeof(name), "midi_from_slave_%d", i+1); if ((fMidiPlaybackPorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) { return -1; } } return 0; } void JackNetMaster::ConnectPorts() { const char** ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); if (ports != NULL) { for (int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) { jack_connect(fClient, ports[i], jack_port_name(fAudioCapturePorts[i])); } jack_free(ports); } ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); if (ports != NULL) { for (int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) { jack_connect(fClient, jack_port_name(fAudioPlaybackPorts[i]), ports[i]); } jack_free(ports); } } void JackNetMaster::FreePorts() { jack_log("JackNetMaster::FreePorts ID = %u", fParams.fID); int port_index; for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { if (fAudioCapturePorts[port_index]) { jack_port_unregister(fClient, fAudioCapturePorts[port_index]); } } for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { if (fAudioPlaybackPorts[port_index]) { jack_port_unregister(fClient, fAudioPlaybackPorts[port_index]); } } for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { if (fMidiCapturePorts[port_index]) { jack_port_unregister(fClient, fMidiCapturePorts[port_index]); } } for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { if (fMidiPlaybackPorts[port_index]) { jack_port_unregister(fClient, fMidiPlaybackPorts[port_index]); } } } //transport--------------------------------------------------------------------------- void JackNetMaster::EncodeTransportData() { //is there a new timebase master ? //TODO : check if any timebase callback has been called (and if it's conditional or not) and set correct value... fSendTransportData.fTimebaseMaster = NO_CHANGE; //update state and position fSendTransportData.fState = static_cast(jack_transport_query(fClient, &fSendTransportData.fPosition)); //is it a new state ? fSendTransportData.fNewState = ((fSendTransportData.fState != fLastTransportState) && (fSendTransportData.fState != fReturnTransportData.fState)); if (fSendTransportData.fNewState) { jack_info("Sending '%s' to '%s' frame = %ld", GetTransportState(fSendTransportData.fState), fParams.fName, fSendTransportData.fPosition.frame); } fLastTransportState = fSendTransportData.fState; } void JackNetMaster::DecodeTransportData() { //is there timebase master change ? if (fReturnTransportData.fTimebaseMaster != NO_CHANGE) { int timebase = 0; switch (fReturnTransportData.fTimebaseMaster) { case RELEASE_TIMEBASEMASTER : timebase = jack_release_timebase(fClient); if (timebase < 0) { jack_error("Can't release timebase master"); } else { jack_info("'%s' isn't the timebase master anymore", fParams.fName); } break; case TIMEBASEMASTER : timebase = jack_set_timebase_callback(fClient, 0, SetTimebaseCallback, this); if (timebase < 0) { jack_error("Can't set a new timebase master"); } else { jack_info("'%s' is the new timebase master", fParams.fName); } break; case CONDITIONAL_TIMEBASEMASTER : timebase = jack_set_timebase_callback(fClient, 1, SetTimebaseCallback, this); if (timebase != EBUSY) { if (timebase < 0) jack_error("Can't set a new timebase master"); else jack_info("'%s' is the new timebase master", fParams.fName); } break; } } //is the slave in a new transport state and is this state different from master's ? if (fReturnTransportData.fNewState && (fReturnTransportData.fState != jack_transport_query(fClient, NULL))) { switch (fReturnTransportData.fState) { case JackTransportStopped : jack_transport_stop(fClient); jack_info("'%s' stops transport", fParams.fName); break; case JackTransportStarting : if (jack_transport_reposition(fClient, &fReturnTransportData.fPosition) == EINVAL) jack_error("Can't set new position"); jack_transport_start(fClient); jack_info("'%s' starts transport frame = %d", fParams.fName, fReturnTransportData.fPosition.frame); break; case JackTransportNetStarting : jack_info("'%s' is ready to roll...", fParams.fName); break; case JackTransportRolling : jack_info("'%s' is rolling", fParams.fName); break; } } } void JackNetMaster::SetTimebaseCallback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg) { static_cast(arg)->TimebaseCallback(pos); } void JackNetMaster::TimebaseCallback(jack_position_t* pos) { pos->bar = fReturnTransportData.fPosition.bar; pos->beat = fReturnTransportData.fPosition.beat; pos->tick = fReturnTransportData.fPosition.tick; pos->bar_start_tick = fReturnTransportData.fPosition.bar_start_tick; pos->beats_per_bar = fReturnTransportData.fPosition.beats_per_bar; pos->beat_type = fReturnTransportData.fPosition.beat_type; pos->ticks_per_beat = fReturnTransportData.fPosition.ticks_per_beat; pos->beats_per_minute = fReturnTransportData.fPosition.beats_per_minute; } //sync-------------------------------------------------------------------------------- bool JackNetMaster::IsSlaveReadyToRoll() { return (fReturnTransportData.fState == JackTransportNetStarting); } int JackNetMaster::SetBufferSize(jack_nframes_t nframes, void* arg) { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fPeriodSize) { jack_error("Cannot currently handle buffer size change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; } int JackNetMaster::SetSampleRate(jack_nframes_t nframes, void* arg) { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fSampleRate) { jack_error("Cannot currently handle sample rate change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; } void JackNetMaster::LatencyCallback(jack_latency_callback_mode_t mode, void* arg) { JackNetMaster* obj = static_cast(arg); jack_nframes_t port_latency = jack_get_buffer_size(obj->fClient); jack_latency_range_t range; //audio for (int i = 0; i < obj->fParams.fSendAudioChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f; jack_port_set_latency_range(obj->fAudioCapturePorts[i], JackPlaybackLatency, &range); } //audio for (int i = 0; i < obj->fParams.fReturnAudioChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f + ((obj->fParams.fSlaveSyncMode) ? 0 : port_latency); jack_port_set_latency_range(obj->fAudioPlaybackPorts[i], JackCaptureLatency, &range); } //midi for (int i = 0; i < obj->fParams.fSendMidiChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f; jack_port_set_latency_range(obj->fMidiCapturePorts[i], JackPlaybackLatency, &range); } //midi for (int i = 0; i < obj->fParams.fReturnMidiChannels; i++) { //port latency range.min = range.max = obj->fParams.fNetworkLatency * port_latency + ((obj->fParams.fSlaveSyncMode) ? 0 : port_latency); jack_port_set_latency_range(obj->fMidiPlaybackPorts[i], JackCaptureLatency, &range); } } //process----------------------------------------------------------------------------- int JackNetMaster::SetProcess(jack_nframes_t nframes, void* arg) { try { return static_cast(arg)->Process(); } catch (JackNetException& e) { return 0; } } void JackNetMaster::SetConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg) { static_cast(arg)->ConnectCallback(a, b, connect); } void JackNetMaster::ConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect) { jack_info("JackNetMaster::ConnectCallback a = %d b = %d connect = %d", a, b, connect); if (connect) { jack_connect(fClient, jack_port_name(jack_port_by_id(fClient, a)), "system:playback_1"); } } int JackNetMaster::Process() { if (!fRunning) { return 0; } #ifdef JACK_MONITOR jack_time_t begin_time = GetMicroSeconds(); fNetTimeMon->New(); #endif //buffers for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fNetMidiCaptureBuffer->SetBuffer(midi_port_index, static_cast(jack_port_get_buffer(fMidiCapturePorts[midi_port_index], fParams.fPeriodSize))); } for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL if (fNetAudioCaptureBuffer->GetConnected(audio_port_index)) { // Port is connected on other side... fNetAudioCaptureBuffer->SetBuffer(audio_port_index, ((jack_port_connected(fAudioCapturePorts[audio_port_index]) > 0) ? static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], fParams.fPeriodSize)) : NULL)); } else { fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL); } #else fNetAudioCaptureBuffer->SetBuffer(audio_port_index, static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], fParams.fPeriodSize))); #endif // TODO } for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, static_cast(jack_port_get_buffer(fMidiPlaybackPorts[midi_port_index], fParams.fPeriodSize))); } for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL sample_t* out = (jack_port_connected(fAudioPlaybackPorts[audio_port_index]) > 0) ? static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)) : NULL; if (out) { memset(out, 0, sizeof(float) * fParams.fPeriodSize); } fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out); #else sample_t* out = static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)); if (out) { memset(out, 0, sizeof(float) * fParams.fPeriodSize); } fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out))); #endif } // encode the first packet EncodeSyncPacket(); if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // send data if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // receive sync int res = SyncRecv(); switch (res) { case NET_SYNCHING: case SOCKET_ERROR: return res; 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; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // receive data res = DataRecv(); switch (res) { case 0: case SOCKET_ERROR: return res; case DATA_PACKET_ERROR: // Well not a real XRun... JackServerGlobals::fInstance->GetEngine()->NotifyClientXRun(ALL_CLIENTS); break; } #ifdef JACK_MONITOR fNetTimeMon->AddLast((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif return 0; } void JackNetMaster::SaveConnections(connections_list_t& connections) { // Audio for (int i = 0; i < fParams.fSendAudioChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fAudioCapturePorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(connected_port[port], jack_port_name(fAudioCapturePorts[i]))); jack_log("INPUT %s ==> %s", connected_port[port], jack_port_name(fAudioCapturePorts[i])); } jack_free(connected_port); } } for (int i = 0; i < fParams.fReturnAudioChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fAudioPlaybackPorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(jack_port_name(fAudioPlaybackPorts[i]), connected_port[port])); jack_log("OUTPUT %s ==> %s", jack_port_name(fAudioPlaybackPorts[i]), connected_port[port]); } jack_free(connected_port); } } // MIDI for (int i = 0; i < fParams.fSendMidiChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fMidiCapturePorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(connected_port[port], jack_port_name(fMidiCapturePorts[i]))); jack_log("INPUT %s ==> %s", connected_port[port], jack_port_name(fMidiCapturePorts[i])); } jack_free(connected_port); } } for (int i = 0; i < fParams.fReturnMidiChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fMidiPlaybackPorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(jack_port_name(fMidiPlaybackPorts[i]), connected_port[port])); jack_log("OUTPUT %s ==> %s", jack_port_name(fMidiPlaybackPorts[i]), connected_port[port]); } jack_free(connected_port); } } } void JackNetMaster::LoadConnections(const connections_list_t& connections) { list >::const_iterator it; for (it = connections.begin(); it != connections.end(); it++) { pair connection = *it; jack_connect(fClient, connection.first.c_str(), connection.second.c_str()); } } //JackNetMasterManager*********************************************************************************************** JackNetMasterManager::JackNetMasterManager(jack_client_t* client, const JSList* params) : fSocket() { jack_log("JackNetMasterManager::JackNetMasterManager"); fClient = client; fName = jack_get_client_name(fClient); fGlobalID = 0; fRunning = true; fAutoConnect = false; fAutoSave = false; const JSList* node; const jack_driver_param_t* param; jack_on_shutdown(fClient, SetShutDown, this); // Possibly use env variable const char* default_udp_port = getenv("JACK_NETJACK_PORT"); fSocket.SetPort((default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT); const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); if (default_multicast_ip) { strcpy(fMulticastIP, default_multicast_ip); } else { strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); } for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'a' : if (strlen(param->value.str) < 32) { strcpy(fMulticastIP, param->value.str); } else { jack_error("Can't use multicast address %s, using default %s", param->value.ui, DEFAULT_MULTICAST_IP); } break; case 'p': fSocket.SetPort(param->value.ui); break; case 'c': fAutoConnect = true; break; case 's': fAutoSave = true; break; } } //set sync callback jack_set_sync_callback(fClient, SetSyncCallback, this); //activate the client (for sync callback) if (jack_activate(fClient) != 0) { jack_error("Can't activate the NetManager client, transport disabled"); } //launch the manager thread if (jack_client_create_thread(fClient, &fThread, 0, 0, NetManagerThread, this)) { jack_error("Can't create the NetManager control thread"); } } JackNetMasterManager::~JackNetMasterManager() { jack_log("JackNetMasterManager::~JackNetMasterManager"); ShutDown(); } int JackNetMasterManager::CountIO(const char* type, int flags) { int count = 0; const char** ports = jack_get_ports(fClient, NULL, type, flags); if (ports != NULL) { while (ports[count]) { count++; } jack_free(ports); } return count; } void JackNetMasterManager::SetShutDown(void* arg) { static_cast(arg)->ShutDown(); } void JackNetMasterManager::ShutDown() { jack_log("JackNetMasterManager::ShutDown"); if (fRunning) { jack_client_kill_thread(fClient, fThread); fRunning = false; } master_list_t::iterator it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { delete (*it); } fMasterList.clear(); fSocket.Close(); SocketAPIEnd(); } int JackNetMasterManager::SetSyncCallback(jack_transport_state_t state, jack_position_t* pos, void* arg) { return static_cast(arg)->SyncCallback(state, pos); } int JackNetMasterManager::SyncCallback(jack_transport_state_t state, jack_position_t* pos) { //check if each slave is ready to roll int res = 1; master_list_it_t it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { if (!(*it)->IsSlaveReadyToRoll()) { res = 0; } } jack_log("JackNetMasterManager::SyncCallback returns '%s'", (res) ? "true" : "false"); return res; } void* JackNetMasterManager::NetManagerThread(void* arg) { JackNetMasterManager* master_manager = static_cast(arg); jack_info("Starting Jack NetManager"); jack_info("Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort()); master_manager->Run(); return NULL; } void JackNetMasterManager::Run() { jack_log("JackNetMasterManager::Run"); //utility variables int attempt = 0; //data session_params_t host_params; int rx_bytes = 0; JackNetMaster* net_master; //init socket API (win32) if (SocketAPIInit() < 0) { jack_error("Can't init Socket API, exiting..."); return; } //socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create NetManager input socket : %s", StrError(NET_ERROR_CODE)); return; } //bind the socket to the local port if (fSocket.Bind() == SOCKET_ERROR) { jack_error("Can't bind NetManager socket : %s", StrError(NET_ERROR_CODE)); fSocket.Close(); return; } //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 do { session_params_t net_params; rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); SessionParamsNToH(&net_params, &host_params); 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"); return; } } if (rx_bytes == sizeof(session_params_t)) { switch (GetPacketType(&host_params)) { case SLAVE_AVAILABLE: if ((net_master = InitMaster(host_params))) { SessionParamsDisplay(&net_master->fParams); } else { jack_error("Can't init new NetMaster..."); } jack_info("Waiting for a slave..."); break; case KILL_MASTER: if (KillMaster(&host_params)) { jack_info("Waiting for a slave..."); } break; default: break; } } } while (fRunning); } JackNetMaster* JackNetMasterManager::InitMaster(session_params_t& params) { jack_log("JackNetMasterManager::InitMaster slave : %s", params.fName); //check MASTER <<==> SLAVE network protocol coherency if (params.fProtocolVersion != NETWORK_PROTOCOL) { jack_error("Error : slave '%s' is running with a different protocol %d != %d", params.fName, params.fProtocolVersion, NETWORK_PROTOCOL); return NULL; } //settings fSocket.GetName(params.fMasterNetName); params.fID = ++fGlobalID; params.fSampleRate = jack_get_sample_rate(fClient); params.fPeriodSize = jack_get_buffer_size(fClient); if (params.fSendAudioChannels == -1) { params.fSendAudioChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); jack_info("Takes physical %d audio input(s) for slave", params.fSendAudioChannels); } if (params.fReturnAudioChannels == -1) { params.fReturnAudioChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); jack_info("Takes physical %d audio output(s) for slave", params.fReturnAudioChannels); } if (params.fSendMidiChannels == -1) { params.fSendMidiChannels = CountIO(JACK_DEFAULT_MIDI_TYPE, JackPortIsPhysical | JackPortIsOutput); jack_info("Takes physical %d MIDI input(s) for slave", params.fSendMidiChannels); } if (params.fReturnMidiChannels == -1) { params.fReturnMidiChannels = CountIO(JACK_DEFAULT_MIDI_TYPE, JackPortIsPhysical | JackPortIsInput); jack_info("Takes physical %d MIDI output(s) for slave", params.fReturnMidiChannels); } //create a new master and add it to the list JackNetMaster* master = new JackNetMaster(fSocket, params, fMulticastIP); if (master->Init(fAutoConnect)) { fMasterList.push_back(master); if (fAutoSave && fMasterConnectionList.find(params.fName) != fMasterConnectionList.end()) { master->LoadConnections(fMasterConnectionList[params.fName]); } return master; } else { delete master; return NULL; } } master_list_it_t JackNetMasterManager::FindMaster(uint32_t id) { jack_log("JackNetMasterManager::FindMaster ID = %u", id); master_list_it_t it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { if ((*it)->fParams.fID == id) { return it; } } return it; } int JackNetMasterManager::KillMaster(session_params_t* params) { jack_log("JackNetMasterManager::KillMaster ID = %u", params->fID); master_list_it_t master_it = FindMaster(params->fID); if (master_it != fMasterList.end()) { if (fAutoSave) { fMasterConnectionList[params->fName].clear(); (*master_it)->SaveConnections(fMasterConnectionList[params->fName]); } fMasterList.erase(master_it); delete (*master_it); return 1; } return 0; } }//namespace static Jack::JackNetMasterManager* master_manager = NULL; #ifdef __cplusplus extern "C" { #endif 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("netmanager", JackDriverNone, "netjack multi-cast master component", &filler); strcpy(value.str, DEFAULT_MULTICAST_IP); jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address", NULL); value.i = DEFAULT_PORT; jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netmaster to system ports", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore netmaster connection state when restarted", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { if (master_manager) { jack_error("Master Manager already loaded"); return 1; } else { jack_log("Loading Master Manager"); master_manager = new Jack::JackNetMasterManager(jack_client, params); return (master_manager) ? 0 : 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) { if (master_manager) { jack_log("Unloading Master Manager"); delete master_manager; master_manager = NULL; } } #ifdef __cplusplus } #endif