831 lines
34 KiB
C++
831 lines
34 KiB
C++
/*
|
|
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 "JackCompilerDeps.h"
|
|
#include "driver_interface.h"
|
|
#include "JackNetDriver.h"
|
|
#include "JackEngineControl.h"
|
|
#include "JackLockedEngine.h"
|
|
#include "JackWaitThreadedDriver.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Jack
|
|
{
|
|
JackNetDriver::JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
|
|
const char* ip, int udp_port, int mtu, int midi_input_ports, int midi_output_ports,
|
|
char* net_name, uint transport_sync, int network_latency,
|
|
int celt_encoding, int opus_encoding, bool auto_save)
|
|
: JackWaiterDriver(name, alias, engine, table), JackNetSlaveInterface(ip, udp_port)
|
|
{
|
|
jack_log("JackNetDriver::JackNetDriver ip %s, port %d", ip, udp_port);
|
|
|
|
// Use the hostname if no name parameter was given
|
|
if (strcmp(net_name, "") == 0) {
|
|
GetHostName(net_name, JACK_CLIENT_NAME_SIZE);
|
|
}
|
|
|
|
fParams.fMtu = mtu;
|
|
|
|
fWantedMIDICaptureChannels = midi_input_ports;
|
|
fWantedMIDIPlaybackChannels = midi_output_ports;
|
|
|
|
if (celt_encoding > 0) {
|
|
fParams.fSampleEncoder = JackCeltEncoder;
|
|
fParams.fKBps = celt_encoding;
|
|
} else if (opus_encoding > 0) {
|
|
fParams.fSampleEncoder = JackOpusEncoder;
|
|
fParams.fKBps = opus_encoding;
|
|
} else {
|
|
fParams.fSampleEncoder = JackFloatEncoder;
|
|
//fParams.fSampleEncoder = JackIntEncoder;
|
|
}
|
|
strcpy(fParams.fName, net_name);
|
|
fSocket.GetName(fParams.fSlaveNetName);
|
|
fParams.fTransportSync = transport_sync;
|
|
fParams.fNetworkLatency = network_latency;
|
|
fSendTransportData.fState = -1;
|
|
fReturnTransportData.fState = -1;
|
|
fLastTransportState = -1;
|
|
fLastTimebaseMaster = -1;
|
|
fMidiCapturePortList = NULL;
|
|
fMidiPlaybackPortList = NULL;
|
|
fWantedAudioCaptureChannels = -1;
|
|
fWantedAudioPlaybackChannels = -1;
|
|
fAutoSave = auto_save;
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon = NULL;
|
|
fRcvSyncUst = 0;
|
|
#endif
|
|
}
|
|
|
|
JackNetDriver::~JackNetDriver()
|
|
{
|
|
delete[] fMidiCapturePortList;
|
|
delete[] fMidiPlaybackPortList;
|
|
#ifdef JACK_MONITOR
|
|
delete fNetTimeMon;
|
|
#endif
|
|
}
|
|
|
|
//open, close, attach and detach------------------------------------------------------
|
|
|
|
int JackNetDriver::Open(jack_nframes_t buffer_size,
|
|
jack_nframes_t samplerate,
|
|
bool capturing,
|
|
bool playing,
|
|
int inchannels,
|
|
int outchannels,
|
|
bool monitor,
|
|
const char* capture_driver_name,
|
|
const char* playback_driver_name,
|
|
jack_nframes_t capture_latency,
|
|
jack_nframes_t playback_latency)
|
|
{
|
|
// Keep initial wanted values
|
|
fWantedAudioCaptureChannels = inchannels;
|
|
fWantedAudioPlaybackChannels = outchannels;
|
|
return JackWaiterDriver::Open(buffer_size, samplerate,
|
|
capturing, playing,
|
|
inchannels, outchannels,
|
|
monitor,
|
|
capture_driver_name, playback_driver_name,
|
|
capture_latency, playback_latency);
|
|
}
|
|
|
|
int JackNetDriver::Close()
|
|
{
|
|
#ifdef JACK_MONITOR
|
|
if (fNetTimeMon) {
|
|
fNetTimeMon->Save();
|
|
}
|
|
#endif
|
|
FreeAll();
|
|
return JackWaiterDriver::Close();
|
|
}
|
|
|
|
// Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init)
|
|
int JackNetDriver::Attach()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int JackNetDriver::Detach()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//init and restart--------------------------------------------------------------------
|
|
/*
|
|
JackNetDriver is wrapped in a JackWaitThreadedDriver decorator that behaves
|
|
as a "dummy driver, until Init method returns.
|
|
*/
|
|
|
|
bool JackNetDriver::Initialize()
|
|
{
|
|
jack_log("JackNetDriver::Initialize");
|
|
if (fAutoSave) {
|
|
SaveConnections(0);
|
|
}
|
|
FreePorts();
|
|
|
|
// New loading, but existing socket, restart the driver
|
|
if (fSocket.IsSocket()) {
|
|
jack_info("Restarting driver...");
|
|
FreeAll();
|
|
}
|
|
|
|
// Set the parameters to send
|
|
fParams.fSendAudioChannels = fWantedAudioCaptureChannels;
|
|
fParams.fReturnAudioChannels = fWantedAudioPlaybackChannels;
|
|
|
|
fParams.fSendMidiChannels = fWantedMIDICaptureChannels;
|
|
fParams.fReturnMidiChannels = fWantedMIDIPlaybackChannels;
|
|
|
|
fParams.fSlaveSyncMode = fEngineControl->fSyncMode;
|
|
|
|
// Display some additional infos
|
|
jack_info("NetDriver started in %s mode %s Master's transport sync.",
|
|
(fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without");
|
|
|
|
// Init network
|
|
if (!JackNetSlaveInterface::Init()) {
|
|
jack_error("Starting network fails...");
|
|
return false;
|
|
}
|
|
|
|
// Set global parameters
|
|
if (!SetParams()) {
|
|
jack_error("SetParams error...");
|
|
return false;
|
|
}
|
|
|
|
// If -1 at connection time for audio, in/out audio channels count is sent by the master
|
|
fCaptureChannels = fParams.fSendAudioChannels;
|
|
fPlaybackChannels = fParams.fReturnAudioChannels;
|
|
|
|
// If -1 at connection time for MIDI, in/out MIDI channels count is sent by the master (in fParams struct)
|
|
|
|
// Allocate midi ports lists
|
|
delete[] fMidiCapturePortList;
|
|
delete[] fMidiPlaybackPortList;
|
|
|
|
if (fParams.fSendMidiChannels > 0) {
|
|
fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels];
|
|
assert(fMidiCapturePortList);
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
|
|
fMidiCapturePortList[midi_port_index] = 0;
|
|
}
|
|
}
|
|
|
|
if (fParams.fReturnMidiChannels > 0) {
|
|
fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels];
|
|
assert(fMidiPlaybackPortList);
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
|
|
fMidiPlaybackPortList[midi_port_index] = 0;
|
|
}
|
|
}
|
|
|
|
// Register jack ports
|
|
if (AllocPorts() != 0) {
|
|
jack_error("Can't allocate ports.");
|
|
return false;
|
|
}
|
|
|
|
// Init done, display parameters
|
|
SessionParamsDisplay(&fParams);
|
|
|
|
// Monitor
|
|
#ifdef JACK_MONITOR
|
|
string plot_name;
|
|
// NetTimeMon
|
|
plot_name = string(fParams.fName);
|
|
plot_name += string("_slave");
|
|
plot_name += (fEngineControl->fSyncMode) ? string("_sync") : string("_async");
|
|
plot_name += string("_latency");
|
|
fNetTimeMon = new JackGnuPlotMonitor<float>(128, 5, plot_name);
|
|
string net_time_mon_fields[] =
|
|
{
|
|
string("sync decoded"),
|
|
string("end of read"),
|
|
string("start of write"),
|
|
string("sync send"),
|
|
string("end of write")
|
|
};
|
|
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, 5);
|
|
#endif
|
|
// Driver parametering
|
|
JackTimedDriver::SetBufferSize(fParams.fPeriodSize);
|
|
JackTimedDriver::SetSampleRate(fParams.fSampleRate);
|
|
|
|
JackDriver::NotifyBufferSize(fParams.fPeriodSize);
|
|
JackDriver::NotifySampleRate(fParams.fSampleRate);
|
|
|
|
// Transport engine parametering
|
|
fEngineControl->fTransport.SetNetworkSync(fParams.fTransportSync);
|
|
|
|
if (fAutoSave) {
|
|
LoadConnections(0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void JackNetDriver::FreeAll()
|
|
{
|
|
FreePorts();
|
|
|
|
delete[] fTxBuffer;
|
|
delete[] fRxBuffer;
|
|
delete fNetAudioCaptureBuffer;
|
|
delete fNetAudioPlaybackBuffer;
|
|
delete fNetMidiCaptureBuffer;
|
|
delete fNetMidiPlaybackBuffer;
|
|
delete[] fMidiCapturePortList;
|
|
delete[] fMidiPlaybackPortList;
|
|
|
|
fTxBuffer = NULL;
|
|
fRxBuffer = NULL;
|
|
fNetAudioCaptureBuffer = NULL;
|
|
fNetAudioPlaybackBuffer = NULL;
|
|
fNetMidiCaptureBuffer = NULL;
|
|
fNetMidiPlaybackBuffer = NULL;
|
|
fMidiCapturePortList = NULL;
|
|
fMidiPlaybackPortList = NULL;
|
|
|
|
#ifdef JACK_MONITOR
|
|
delete fNetTimeMon;
|
|
fNetTimeMon = NULL;
|
|
#endif
|
|
}
|
|
|
|
void JackNetDriver::UpdateLatencies()
|
|
{
|
|
jack_latency_range_t input_range;
|
|
jack_latency_range_t output_range;
|
|
jack_latency_range_t monitor_range;
|
|
|
|
for (int i = 0; i < fCaptureChannels; i++) {
|
|
input_range.max = input_range.min = float(fParams.fNetworkLatency * fEngineControl->fBufferSize) / 2.f;
|
|
fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range);
|
|
}
|
|
|
|
for (int i = 0; i < fPlaybackChannels; i++) {
|
|
output_range.max = output_range.min = float(fParams.fNetworkLatency * fEngineControl->fBufferSize) / 2.f;
|
|
if (!fEngineControl->fSyncMode) {
|
|
output_range.max = output_range.min += fEngineControl->fBufferSize;
|
|
}
|
|
fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range);
|
|
if (fWithMonitorPorts) {
|
|
monitor_range.min = monitor_range.max = 0;
|
|
fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range);
|
|
}
|
|
}
|
|
}
|
|
|
|
//jack ports and buffers--------------------------------------------------------------
|
|
int JackNetDriver::AllocPorts()
|
|
{
|
|
jack_log("JackNetDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate);
|
|
|
|
/*
|
|
fNetAudioCaptureBuffer fNetAudioPlaybackBuffer
|
|
fSendAudioChannels fReturnAudioChannels
|
|
|
|
fCapturePortList fPlaybackPortList
|
|
fCaptureChannels ==> SLAVE ==> fPlaybackChannels
|
|
"capture_" "playback_"
|
|
*/
|
|
|
|
JackPort* port;
|
|
jack_port_id_t port_index;
|
|
char name[REAL_JACK_PORT_NAME_SIZE+1];
|
|
char alias[REAL_JACK_PORT_NAME_SIZE+1];
|
|
int audio_port_index;
|
|
int midi_port_index;
|
|
|
|
//audio
|
|
for (audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) {
|
|
snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, audio_port_index + 1);
|
|
snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, audio_port_index + 1);
|
|
if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE,
|
|
CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) {
|
|
jack_error("driver: cannot register port for %s", name);
|
|
return -1;
|
|
}
|
|
|
|
port = fGraphManager->GetPort(port_index);
|
|
port->SetAlias(alias);
|
|
fCapturePortList[audio_port_index] = port_index;
|
|
jack_log("JackNetDriver::AllocPorts() fCapturePortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency());
|
|
}
|
|
|
|
for (audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) {
|
|
snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, audio_port_index + 1);
|
|
snprintf(name, sizeof(name), "%s:playback_%d",fClientControl.fName, audio_port_index + 1);
|
|
if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE,
|
|
PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) {
|
|
jack_error("driver: cannot register port for %s", name);
|
|
return -1;
|
|
}
|
|
|
|
port = fGraphManager->GetPort(port_index);
|
|
port->SetAlias(alias);
|
|
fPlaybackPortList[audio_port_index] = port_index;
|
|
jack_log("JackNetDriver::AllocPorts() fPlaybackPortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency());
|
|
}
|
|
|
|
//midi
|
|
for (midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
|
|
snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, midi_port_index + 1);
|
|
snprintf(name, sizeof (name), "%s:midi_capture_%d", fClientControl.fName, midi_port_index + 1);
|
|
if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE,
|
|
CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) {
|
|
jack_error("driver: cannot register port for %s", name);
|
|
return -1;
|
|
}
|
|
|
|
port = fGraphManager->GetPort(port_index);
|
|
fMidiCapturePortList[midi_port_index] = port_index;
|
|
jack_log("JackNetDriver::AllocPorts() fMidiCapturePortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency());
|
|
}
|
|
|
|
for (midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
|
|
snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, midi_port_index + 1);
|
|
snprintf(name, sizeof(name), "%s:midi_playback_%d", fClientControl.fName, midi_port_index + 1);
|
|
if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE,
|
|
PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) {
|
|
jack_error("driver: cannot register port for %s", name);
|
|
return -1;
|
|
}
|
|
|
|
port = fGraphManager->GetPort(port_index);
|
|
fMidiPlaybackPortList[midi_port_index] = port_index;
|
|
jack_log("JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency());
|
|
}
|
|
|
|
UpdateLatencies();
|
|
return 0;
|
|
}
|
|
|
|
int JackNetDriver::FreePorts()
|
|
{
|
|
jack_log("JackNetDriver::FreePorts");
|
|
|
|
for (int audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) {
|
|
if (fCapturePortList[audio_port_index] > 0) {
|
|
fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[audio_port_index]);
|
|
fCapturePortList[audio_port_index] = 0;
|
|
}
|
|
}
|
|
|
|
for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) {
|
|
if (fPlaybackPortList[audio_port_index] > 0) {
|
|
fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[audio_port_index]);
|
|
fPlaybackPortList[audio_port_index] = 0;
|
|
}
|
|
}
|
|
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
|
|
if (fMidiCapturePortList && fMidiCapturePortList[midi_port_index] > 0) {
|
|
fGraphManager->ReleasePort(fClientControl.fRefNum, fMidiCapturePortList[midi_port_index]);
|
|
fMidiCapturePortList[midi_port_index] = 0;
|
|
}
|
|
}
|
|
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
|
|
if (fMidiPlaybackPortList && fMidiPlaybackPortList[midi_port_index] > 0) {
|
|
fEngine->PortUnRegister(fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index]);
|
|
fMidiPlaybackPortList[midi_port_index] = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void JackNetDriver::SaveConnections(int alias)
|
|
{
|
|
JackDriver::SaveConnections(alias);
|
|
const char** connections;
|
|
|
|
if (fMidiCapturePortList) {
|
|
for (int i = 0; i < fParams.fSendMidiChannels; ++i) {
|
|
if (fMidiCapturePortList[i] && (connections = fGraphManager->GetConnections(fMidiCapturePortList[i])) != 0) {
|
|
for (int j = 0; connections[j]; j++) {
|
|
JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j]));
|
|
fConnections.push_back(make_pair(port_id->GetType(), make_pair(fGraphManager->GetPort(fMidiCapturePortList[i])->GetName(), connections[j])));
|
|
jack_info("Save connection: %s %s", fGraphManager->GetPort(fMidiCapturePortList[i])->GetName(), connections[j]);
|
|
}
|
|
free(connections);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fMidiPlaybackPortList) {
|
|
for (int i = 0; i < fParams.fReturnMidiChannels; ++i) {
|
|
if (fMidiPlaybackPortList[i] && (connections = fGraphManager->GetConnections(fMidiPlaybackPortList[i])) != 0) {
|
|
for (int j = 0; connections[j]; j++) {
|
|
JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j]));
|
|
fConnections.push_back(make_pair(port_id->GetType(), make_pair(connections[j], fGraphManager->GetPort(fMidiPlaybackPortList[i])->GetName())));
|
|
jack_info("Save connection: %s %s", connections[j], fGraphManager->GetPort(fMidiPlaybackPortList[i])->GetName());
|
|
}
|
|
free(connections);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
JackMidiBuffer* JackNetDriver::GetMidiInputBuffer(int port_index)
|
|
{
|
|
return static_cast<JackMidiBuffer*>(fGraphManager->GetBuffer(fMidiCapturePortList[port_index], fEngineControl->fBufferSize));
|
|
}
|
|
|
|
JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer(int port_index)
|
|
{
|
|
return static_cast<JackMidiBuffer*>(fGraphManager->GetBuffer(fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize));
|
|
}
|
|
|
|
//transport---------------------------------------------------------------------------
|
|
void JackNetDriver::DecodeTransportData()
|
|
{
|
|
//is there a new timebase master on the net master ?
|
|
// - release timebase master only if it's a non-conditional request
|
|
// - no change or no request : don't do anything
|
|
// - conditional request : don't change anything too, the master will know if this slave is actually the timebase master
|
|
int refnum;
|
|
bool conditional;
|
|
if (fSendTransportData.fTimebaseMaster == TIMEBASEMASTER) {
|
|
fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional);
|
|
if (refnum != -1) {
|
|
fEngineControl->fTransport.ResetTimebase(refnum);
|
|
}
|
|
jack_info("The NetMaster is now the new timebase master.");
|
|
}
|
|
|
|
//is there a transport state change to handle ?
|
|
if (fSendTransportData.fNewState &&(fSendTransportData.fState != fEngineControl->fTransport.GetState())) {
|
|
|
|
switch (fSendTransportData.fState)
|
|
{
|
|
case JackTransportStopped :
|
|
fEngineControl->fTransport.SetCommand(TransportCommandStop);
|
|
jack_info("Master stops transport.");
|
|
break;
|
|
|
|
case JackTransportStarting :
|
|
fEngineControl->fTransport.RequestNewPos(&fSendTransportData.fPosition);
|
|
fEngineControl->fTransport.SetCommand(TransportCommandStart);
|
|
jack_info("Master starts transport frame = %d", fSendTransportData.fPosition.frame);
|
|
break;
|
|
|
|
case JackTransportRolling :
|
|
//fEngineControl->fTransport.SetCommand(TransportCommandStart);
|
|
fEngineControl->fTransport.SetState(JackTransportRolling);
|
|
jack_info("Master is rolling.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void JackNetDriver::EncodeTransportData()
|
|
{
|
|
// is there a timebase master change ?
|
|
int refnum;
|
|
bool conditional;
|
|
fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional);
|
|
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 = fEngineControl->fTransport.Query(&fReturnTransportData.fPosition);
|
|
|
|
// is it a new state (that the master need to know...) ?
|
|
fReturnTransportData.fNewState = ((fReturnTransportData.fState == JackTransportNetStarting) &&
|
|
(fReturnTransportData.fState != fLastTransportState) &&
|
|
(fReturnTransportData.fState != fSendTransportData.fState));
|
|
if (fReturnTransportData.fNewState) {
|
|
jack_info("Sending '%s'.", GetTransportState(fReturnTransportData.fState));
|
|
}
|
|
fLastTransportState = fReturnTransportData.fState;
|
|
}
|
|
|
|
//driver processes--------------------------------------------------------------------
|
|
|
|
int JackNetDriver::Read()
|
|
{
|
|
// buffers
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) {
|
|
fNetMidiCaptureBuffer->SetBuffer(midi_port_index, GetMidiInputBuffer(midi_port_index));
|
|
}
|
|
|
|
for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) {
|
|
#ifdef OPTIMIZED_PROTOCOL
|
|
if (fGraphManager->GetConnectionsNum(fCapturePortList[audio_port_index]) > 0) {
|
|
fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index));
|
|
} else {
|
|
fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL);
|
|
}
|
|
#else
|
|
fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index));
|
|
#endif
|
|
}
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->New();
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef JACK_MONITOR
|
|
// For timing
|
|
fRcvSyncUst = GetMicroSeconds();
|
|
#endif
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->Add(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f);
|
|
#endif
|
|
// audio, midi or sync if driver is late
|
|
switch (DataRecv()) {
|
|
|
|
case SOCKET_ERROR:
|
|
return SOCKET_ERROR;
|
|
|
|
case DATA_PACKET_ERROR:
|
|
jack_time_t cur_time = GetMicroSeconds();
|
|
NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing...
|
|
break;
|
|
}
|
|
|
|
// take the time at the beginning of the cycle
|
|
JackDriver::CycleTakeBeginTime();
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->Add(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int JackNetDriver::Write()
|
|
{
|
|
// buffers
|
|
for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) {
|
|
fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, GetMidiOutputBuffer(midi_port_index));
|
|
}
|
|
|
|
for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) {
|
|
#ifdef OPTIMIZED_PROTOCOL
|
|
// Port is connected on other side...
|
|
if (fNetAudioPlaybackBuffer->GetConnected(audio_port_index)
|
|
&& (fGraphManager->GetConnectionsNum(fPlaybackPortList[audio_port_index]) > 0)) {
|
|
fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index));
|
|
} else {
|
|
fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, NULL);
|
|
}
|
|
#else
|
|
fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index));
|
|
#endif
|
|
}
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->AddLast(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f);
|
|
#endif
|
|
|
|
EncodeSyncPacket();
|
|
|
|
// send sync
|
|
if (SyncSend() == SOCKET_ERROR) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->Add(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f);
|
|
#endif
|
|
|
|
// send data
|
|
if (DataSend() == SOCKET_ERROR) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
#ifdef JACK_MONITOR
|
|
fNetTimeMon->AddLast(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
//driver loader-----------------------------------------------------------------------
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
|
|
{
|
|
jack_driver_desc_t * desc;
|
|
jack_driver_desc_filler_t filler;
|
|
jack_driver_param_value_t value;
|
|
|
|
desc = jack_driver_descriptor_construct("net", JackDriverMaster, "netjack slave backend component", &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 = -1;
|
|
jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master");
|
|
jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master");
|
|
|
|
value.i = -1;
|
|
jack_driver_descriptor_add_parameter(desc, &filler, "midi-in-ports", 'i', JackDriverParamInt, &value, NULL, "Number of midi input ports", "Number of MIDI input ports. If -1, MIDI physical input from the master");
|
|
jack_driver_descriptor_add_parameter(desc, &filler, "midi-out-ports", 'o', JackDriverParamInt, &value, NULL, "Number of midi output ports", "Number of MIDI output ports. If -1, MIDI physical output from the master");
|
|
|
|
#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.i = false;
|
|
jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore connection state when restarting", NULL);
|
|
|
|
|
|
/*
|
|
Deactivated for now..
|
|
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);
|
|
|
|
return desc;
|
|
}
|
|
|
|
SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
|
|
{
|
|
char multicast_ip[32];
|
|
char net_name[JACK_CLIENT_NAME_SIZE+1] = {0};
|
|
int udp_port;
|
|
int mtu = DEFAULT_MTU;
|
|
// Deactivated for now...
|
|
uint transport_sync = 0;
|
|
jack_nframes_t period_size = 1024; // to be used while waiting for master period_size
|
|
jack_nframes_t sample_rate = 48000; // to be used while waiting for master sample_rate
|
|
int audio_capture_ports = -1;
|
|
int audio_playback_ports = -1;
|
|
int midi_input_ports = -1;
|
|
int midi_output_ports = -1;
|
|
int celt_encoding = -1;
|
|
int opus_encoding = -1;
|
|
bool monitor = false;
|
|
int network_latency = 5;
|
|
const JSList* node;
|
|
const jack_driver_param_t* param;
|
|
bool auto_save = false;
|
|
|
|
// Possibly use env variable for UDP port
|
|
const char* default_udp_port = getenv("JACK_NETJACK_PORT");
|
|
udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT;
|
|
|
|
// Possibly use env variable for multicast IP
|
|
const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST");
|
|
strcpy(multicast_ip, (default_multicast_ip) ? default_multicast_ip : 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' :
|
|
assert(strlen(param->value.str) < 32);
|
|
strcpy(multicast_ip, param->value.str);
|
|
break;
|
|
case 'p':
|
|
udp_port = param->value.ui;
|
|
break;
|
|
case 'M':
|
|
mtu = param->value.i;
|
|
break;
|
|
case 'C':
|
|
audio_capture_ports = param->value.i;
|
|
break;
|
|
case 'P':
|
|
audio_playback_ports = param->value.i;
|
|
break;
|
|
case 'i':
|
|
midi_input_ports = param->value.i;
|
|
break;
|
|
case 'o':
|
|
midi_output_ports = param->value.i;
|
|
break;
|
|
#if HAVE_CELT
|
|
case 'c':
|
|
celt_encoding = param->value.i;
|
|
break;
|
|
#endif
|
|
#if HAVE_OPUS
|
|
case 'O':
|
|
opus_encoding = param->value.i;
|
|
break;
|
|
#endif
|
|
case 'n' :
|
|
strncpy(net_name, param->value.str, JACK_CLIENT_NAME_SIZE);
|
|
break;
|
|
case 's':
|
|
auto_save = true;
|
|
break;
|
|
/*
|
|
Deactivated for now..
|
|
case 't' :
|
|
transport_sync = param->value.ui;
|
|
break;
|
|
*/
|
|
case 'l' :
|
|
network_latency = param->value.ui;
|
|
if (network_latency > NETWORK_MAX_LATENCY) {
|
|
printf("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY);
|
|
return NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
try {
|
|
|
|
Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver(
|
|
new Jack::JackNetDriver("system", "net_pcm", engine, table, multicast_ip, udp_port, mtu,
|
|
midi_input_ports, midi_output_ports,
|
|
net_name, transport_sync,
|
|
network_latency, celt_encoding, opus_encoding, auto_save));
|
|
if (driver->Open(period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports, monitor, "from_master_", "to_master_", 0, 0) == 0) {
|
|
return driver;
|
|
} else {
|
|
delete driver;
|
|
return NULL;
|
|
}
|
|
|
|
} catch (...) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
}
|