/*
Copyright (C) 2006 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.

*/

#ifdef WIN32
#pragma warning (disable : 4786)
#endif

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <process.h>
#include "JackRouter.h"
#include "profport.h"

/*

	08/07/2007 SL : USe jack_client_open instead of jack_client_new (automatic client renaming).
	09/08/2007 SL : Add JackRouter.ini parameter file.
	09/20/2007 SL : Better error report in DllRegisterServer (for Vista).
	09/27/2007 SL : Add AUDO_CONNECT property in JackRouter.ini file.
	10/10/2007 SL : Use ASIOSTInt32LSB instead of ASIOSTInt16LSB.

 */

//------------------------------------------------------------------------------------------
// extern
void getNanoSeconds(ASIOTimeStamp *time);

// local
double AsioSamples2double (ASIOSamples* samples);

static const double twoRaisedTo32 = 4294967296.;
static const double twoRaisedTo32Reciprocal = 1. / twoRaisedTo32;

//------------------------------------------------------------------------------------------
// on windows, we do the COM stuff.

#if WINDOWS
#include "windows.h"
#include "mmsystem.h"
#include "psapi.h"

using namespace std;

// class id. 
// {838FE50A-C1AB-4b77-B9B6-0A40788B53F3}
CLSID IID_ASIO_DRIVER = { 0x838fe50a, 0xc1ab, 0x4b77, { 0xb9, 0xb6, 0xa, 0x40, 0x78, 0x8b, 0x53, 0xf3 } };


CFactoryTemplate g_Templates[1] = {
    {L"ASIOJACK", &IID_ASIO_DRIVER, JackRouter::CreateInstance} 
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

CUnknown* JackRouter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
	return (CUnknown*)new JackRouter(pUnk,phr);
};

STDMETHODIMP JackRouter::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
{
	if (riid == IID_ASIO_DRIVER) {
		return GetInterface(this, ppv);
	}
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//		Register ASIO Driver
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
extern LONG RegisterAsioDriver(CLSID,char *,char *,char *,char *);
extern LONG UnregisterAsioDriver(CLSID,char *,char *);

//
// Server registration, called on REGSVR32.EXE "the dllname.dll"
//
HRESULT _stdcall DllRegisterServer()
{
	LONG	rc;
	char	errstr[128];

	rc = RegisterAsioDriver (IID_ASIO_DRIVER,"JackRouter.dll","JackRouter","JackRouter","Apartment");

	if (rc) {
		memset(errstr,0,128);
		sprintf(errstr,"Register Server failed ! (%d)",rc);
		MessageBox(0,(LPCTSTR)errstr,(LPCTSTR)"JackRouter",MB_OK);
		return -1;
	}

	return S_OK;
}

//
// Server unregistration
//
HRESULT _stdcall DllUnregisterServer()
{
	LONG	rc;
	char	errstr[128];

	rc = UnregisterAsioDriver (IID_ASIO_DRIVER,"JackRouter.dll","JackRouter");

	if (rc) {
		memset(errstr,0,128);
		sprintf(errstr,"Unregister Server failed ! (%d)",rc);
		MessageBox(0,(LPCTSTR)errstr,(LPCTSTR)"JackRouter",MB_OK);
		return -1;
	}

	return S_OK;
}

// Globals

list<pair<string, string> > JackRouter::fConnections;
bool JackRouter::fFirstActivate = true;

//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
JackRouter::JackRouter (LPUNKNOWN pUnk, HRESULT *phr)
	: CUnknown("ASIOJACK", pUnk, phr)

//------------------------------------------------------------------------------------------

#else

// when not on windows, we derive from AsioDriver
JackRouter::JackRouter() : AsioDriver()

#endif
{
	long i;

	fSamplePosition = 0;
	fActive = false;
	fStarted = false;
	fTimeInfoMode = false;
	fTcRead = false;
	fClient = NULL;
	fAutoConnectIn = true;
	fAutoConnectOut = true;

	for (i = 0; i < kNumInputs; i++) {
		fInputBuffers[i] = 0;
		fInputPorts[i] = 0;
		fInMap[i] = 0;
	}
	for (i = 0; i < kNumOutputs; i++) {
		fOutputBuffers[i] = 0;
		fOutputPorts[i] = 0;
		fOutMap[i] = 0;
	}
	fCallbacks = 0;
	fActiveInputs = fActiveOutputs = 0;
	fToggle = 0;
	fBufferSize = 512;
	fSampleRate = 44100;
	printf("Constructor\n");

	// Use "jackrouter.ini" parameters if available
	HMODULE handle = LoadLibrary("JackRouter.dll");

	if (handle) {

		// Get JackRouter.dll path
		char dllName[512];
		string confPath;
		DWORD res = GetModuleFileName(handle, dllName, 512);
		
		// Compute .ini file path
		string fullPath = dllName;
		int lastPos = fullPath.find_last_of(PATH_SEP);
		string  dllFolder =  fullPath.substr(0, lastPos);
		confPath = dllFolder + PATH_SEP + "JackRouter.ini";

		// Get parameters
		kNumInputs = get_private_profile_int("IO", "input", 2, confPath.c_str());
		kNumOutputs = get_private_profile_int("IO", "output", 2, confPath.c_str());

		fAutoConnectIn = get_private_profile_int("AUTO_CONNECT", "input", 1, confPath.c_str());
		fAutoConnectOut = get_private_profile_int("AUTO_CONNECT", "output", 1, confPath.c_str());

		FreeLibrary(handle);

	} else {
		printf("LoadLibrary error\n");
	}
}

//------------------------------------------------------------------------------------------
JackRouter::~JackRouter()
{
	stop ();
	disposeBuffers ();
	printf("Destructor\n");
	jack_client_close(fClient);
}

//------------------------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "psapi.h"

static bool GetEXEName(DWORD dwProcessID, char* name)
{
    DWORD aProcesses [1024], cbNeeded, cProcesses;
    unsigned int i;
        
    // Enumerate all processes
    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
        return false;

    // Calculate how many process identifiers were returned.
    cProcesses = cbNeeded / sizeof(DWORD);

    TCHAR szEXEName[MAX_PATH];
    // Loop through all process to find the one that matches
    // the one we are looking for

    for (i = 0; i < cProcesses; i++) {
        if (aProcesses [i] == dwProcessID) {
            // Get a handle to the process
            HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
                              PROCESS_VM_READ, FALSE, dwProcessID);
        
            // Get the process name
            if (NULL != hProcess) {
                HMODULE hMod;
                DWORD cbNeeded;
            
                if(EnumProcessModules(hProcess, &hMod, 
                                      sizeof(hMod), &cbNeeded)) {
                    //Get the name of the exe file
                    GetModuleBaseName(hProcess, hMod, szEXEName, 
                        sizeof(szEXEName)/sizeof(TCHAR));
					int len = strlen((char*)szEXEName) - 4; // remove ".exe"
					strncpy(name, (char*)szEXEName, len); 
					name[len] = '\0';
					return true;
                 }
            }
        }    
    }

    return false;
}

 //------------------------------------------------------------------------------------------
static inline float ClipFloat(float sample)
{
     return (sample < -1.0f) ? -1.0f : (sample > 1.0f) ? 1.0f : sample;
}

//------------------------------------------------------------------------------------------
void JackRouter::shutdown(void* arg)
{
	JackRouter* driver = (JackRouter*)arg;
	/*
	//exit(1);
	char errstr[128];

	memset(errstr,0,128);
	sprintf(errstr,"JACK server has quitted");
	MessageBox(0,(LPCTSTR)errstr,(LPCTSTR)"JackRouter",MB_OK);
	*/
}

//------------------------------------------------------------------------------------------
int JackRouter::process(jack_nframes_t nframes, void* arg)
{
	JackRouter* driver = (JackRouter*)arg;
	int i,j;
	int pos = (driver->fToggle) ? 0 : driver->fBufferSize ;	
	
	for (i = 0; i < driver->fActiveInputs; i++) {	
		
#ifdef LONG_SAMPLE		
		float* buffer = (float*)jack_port_get_buffer(driver->fInputPorts[i], nframes);
		long* in = driver->fInputBuffers[i] + pos;
		for (j = 0; j < nframes; j++) {
			in[j] = buffer[j] * float(0x7fffffff);
		}
#else
		memcpy(driver->fInputBuffers[i] + pos,
				jack_port_get_buffer(driver->fInputPorts[i], nframes),
				nframes * sizeof(float));
#endif

	}	

	driver->bufferSwitch();

	for (i = 0; i < driver->fActiveOutputs; i++) {

#ifdef LONG_SAMPLE
		float* buffer = (float*)jack_port_get_buffer(driver->fOutputPorts[i], nframes);
		long* out = driver->fOutputBuffers[i] + pos;
		float gain = 1.f/float(0x7fffffff);
		for (j = 0; j < nframes; j++) {
			buffer[j] = out[j] * gain;
		}
#else
		memcpy(jack_port_get_buffer(driver->fOutputPorts[i], nframes),
				driver->fOutputBuffers[i] + pos,
				nframes * sizeof(float));
#endif
	}
	
	return 0;
}

//------------------------------------------------------------------------------------------
void JackRouter::getDriverName(char *name)
{
	strcpy (name, "JackRouter");
}

//------------------------------------------------------------------------------------------
long JackRouter::getDriverVersion()
{
	return 0x00000001L;
}

//------------------------------------------------------------------------------------------
void JackRouter::getErrorMessage(char *string)
{
	strcpy (string, fErrorMessage);
}

//------------------------------------------------------------------------------------------
ASIOBool JackRouter::init(void* sysRef)
{
	char name[MAX_PATH];
	sysRef = sysRef;

	if (fActive)
		return true;

	HANDLE win = (HANDLE)sysRef;
	int	my_pid = _getpid();
	
	if (!GetEXEName(my_pid, name)) { // If getting the .exe name fails, takes a generic one.
		_snprintf(name, sizeof(name) - 1, "JackRouter_%d", my_pid);
	}

	if (fClient) {
		printf("Error: jack client still present...\n");
		return true;
	}
		
	fClient = jack_client_open(name, JackNullOption, NULL);
	if (fClient == NULL) {
		strcpy (fErrorMessage, "Open error: is jack server running?");
		printf("Open error: is jack server running?\n");
		return false;
	}
	
	fBufferSize = jack_get_buffer_size(fClient);
	fSampleRate = jack_get_sample_rate(fClient);
	jack_set_process_callback(fClient, process, this);
	jack_on_shutdown(fClient, shutdown, this);
  	
	fInputLatency = fBufferSize;		// typically
	fOutputLatency = fBufferSize * 2;
	fMilliSeconds = (long)((double)(fBufferSize * 1000) / fSampleRate);

	// Typically fBufferSize * 2; try to get 1 by offering direct buffer
	// access, and using asioPostOutput for lower latency

	printf("Init ASIO Jack\n");
	fActive = true;
	return true;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::start()
{
	if (fCallbacks) {
		fSamplePosition = 0;
		fTheSystemTime.lo = fTheSystemTime.hi = 0;
		fToggle = 0;
		fStarted = true;
		printf("Start ASIO Jack\n");
	
		if (jack_activate(fClient) == 0) {

			if (fFirstActivate) {
				AutoConnect();
				fFirstActivate = false;
			} else {
				RestoreConnections();
			}

			return ASE_OK;

		} else {
			return ASE_NotPresent;
		}		
	}
	
	return ASE_NotPresent;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::stop()
{
	fStarted = false;
	printf("Stop ASIO Jack\n");
	SaveConnections();
	jack_deactivate(fClient);
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getChannels(long *numInputChannels, long *numOutputChannels)
{
	*numInputChannels = kNumInputs;
	*numOutputChannels = kNumOutputs;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getLatencies(long *_inputLatency, long *_outputLatency)
{
	*_inputLatency = fInputLatency;
	*_outputLatency = fOutputLatency;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity)
{
	*minSize = *maxSize = *preferredSize = fBufferSize;		// allow this size only
	*granularity = 0;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::canSampleRate(ASIOSampleRate sampleRate)
{
	return (sampleRate == fSampleRate) ? ASE_OK : ASE_NoClock;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getSampleRate(ASIOSampleRate *sampleRate)
{
	*sampleRate = fSampleRate;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::setSampleRate(ASIOSampleRate sampleRate)
{
	return (sampleRate == fSampleRate) ? ASE_OK : ASE_NoClock;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getClockSources(ASIOClockSource *clocks, long *numSources)
{
	// Internal
	if (clocks && numSources) {
		clocks->index = 0;
		clocks->associatedChannel = -1;
		clocks->associatedGroup = -1;
		clocks->isCurrentSource = ASIOTrue;
		strcpy(clocks->name, "Internal");
		*numSources = 1;
		return ASE_OK;
	} else {
		return ASE_InvalidParameter;
	}
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::setClockSource(long index)
{
	if (!index) {
		fAsioTime.timeInfo.flags |= kClockSourceChanged;
		return ASE_OK;
	} else {
		return ASE_NotPresent;
	}
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
	tStamp->lo = fTheSystemTime.lo;
	tStamp->hi = fTheSystemTime.hi;

	if (fSamplePosition >= twoRaisedTo32) {
		sPos->hi = (unsigned long)(fSamplePosition * twoRaisedTo32Reciprocal);
		sPos->lo = (unsigned long)(fSamplePosition - (sPos->hi * twoRaisedTo32));
	} else {
		sPos->hi = 0;
		sPos->lo = (unsigned long)fSamplePosition;
	}
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::getChannelInfo(ASIOChannelInfo *info)
{
	if (info->channel < 0 || (info->isInput ? info->channel >= kNumInputs : info->channel >= kNumOutputs))
		return ASE_InvalidParameter;
#ifdef LONG_SAMPLE
	info->type = ASIOSTInt32LSB;
#else
	info->type = ASIOSTFloat32LSB;
#endif

	info->channelGroup = 0;
	info->isActive = ASIOFalse;
	long i;
	char buf[32];

	if (info->isInput) {
		for (i = 0; i < fActiveInputs; i++) {		
			if (fInMap[i] == info->channel) { 
				info->isActive = ASIOTrue;
				//_snprintf(buf, sizeof(buf) - 1, "Jack::In%d ", info->channel);
				//strcpy(info->name, buf);
				//strcpy(info->name, jack_port_name(fInputPorts[i]));
				break;
			}
		}
		_snprintf(buf, sizeof(buf) - 1, "In%d ", info->channel);
		strcpy(info->name, buf);
	} else {
		for (i = 0; i < fActiveOutputs; i++) {	
			if (fOutMap[i] == info->channel) {  //NOT USED !!
				info->isActive = ASIOTrue;
				//_snprintf(buf, sizeof(buf) - 1, "Jack::Out%d ", info->channel);
				//strcpy(info->name, buf);
				//strcpy(info->name, jack_port_name(fOutputPorts[i]));
				break;
			}
		}
		_snprintf(buf, sizeof(buf) - 1, "Out%d ", info->channel);
		strcpy(info->name, buf);
	}
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError JackRouter::createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
	long bufferSize, ASIOCallbacks *callbacks)
{
	ASIOBufferInfo *info = bufferInfos;
	long i;
	bool notEnoughMem = false;
	char buf[256];
	fActiveInputs = 0;
	fActiveOutputs = 0;

	for (i = 0; i < numChannels; i++, info++) {
		if (info->isInput) {
			if (info->channelNum < 0 || info->channelNum >= kNumInputs)
				goto error;
			fInMap[fActiveInputs] = info->channelNum;
		#ifdef LONG_SAMPLE
			fInputBuffers[fActiveInputs] = new long[fBufferSize * 2];	// double buffer
		#else
			fInputBuffers[fActiveInputs] = new float[fBufferSize * 2];	// double buffer
		#endif
			if (fInputBuffers[fActiveInputs]) {
				info->buffers[0] = fInputBuffers[fActiveInputs];
				info->buffers[1] = fInputBuffers[fActiveInputs] + fBufferSize;
			} else {
				info->buffers[0] = info->buffers[1] = 0;
				notEnoughMem = true;
			}
			
			_snprintf(buf, sizeof(buf) - 1, "in%d", fActiveInputs + 1);
			fInputPorts[fActiveInputs] 
				= jack_port_register(fClient, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0);
			if (fInputPorts[fActiveInputs] == NULL)
				goto error;

			fActiveInputs++;
			if (fActiveInputs > kNumInputs) {
error:
				disposeBuffers();
				return ASE_InvalidParameter;
			}
		} else {	// output			
			if (info->channelNum < 0 || info->channelNum >= kNumOutputs)
				goto error;
			fOutMap[fActiveOutputs] = info->channelNum;
	
		#ifdef LONG_SAMPLE
			fOutputBuffers[fActiveOutputs] = new long[fBufferSize * 2];	// double buffer
		#else
			fOutputBuffers[fActiveOutputs] = new float[fBufferSize * 2];	// double buffer
		#endif
	
			if (fOutputBuffers[fActiveOutputs]) {
				info->buffers[0] = fOutputBuffers[fActiveOutputs];
				info->buffers[1] = fOutputBuffers[fActiveOutputs] + fBufferSize;
			} else {
				info->buffers[0] = info->buffers[1] = 0;
				notEnoughMem = true;
			}
			
			_snprintf(buf, sizeof(buf) - 1, "out%d", fActiveOutputs + 1);
			fOutputPorts[fActiveOutputs] 
				= jack_port_register(fClient, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput,0);
			if (fOutputPorts[fActiveOutputs] == NULL)
				goto error;

			fActiveOutputs++;
			if (fActiveOutputs > kNumOutputs) {
				fActiveOutputs--;
				disposeBuffers();
				return ASE_InvalidParameter;
			}
		}
	}		

	if (notEnoughMem) {
		disposeBuffers();
		return ASE_NoMemory;
	}

	this->fCallbacks = callbacks;
	if (callbacks->asioMessage (kAsioSupportsTimeInfo, 0, 0, 0)) {
		fTimeInfoMode = true;
		fAsioTime.timeInfo.speed = 1.;
		fAsioTime.timeInfo.systemTime.hi = fAsioTime.timeInfo.systemTime.lo = 0;
		fAsioTime.timeInfo.samplePosition.hi = fAsioTime.timeInfo.samplePosition.lo = 0;
		fAsioTime.timeInfo.sampleRate = fSampleRate;
		fAsioTime.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;

		fAsioTime.timeCode.speed = 1.;
		fAsioTime.timeCode.timeCodeSamples.lo = fAsioTime.timeCode.timeCodeSamples.hi = 0;
		fAsioTime.timeCode.flags = kTcValid | kTcRunning ;
	} else {
		fTimeInfoMode = false;	
	}
	
	return ASE_OK;
}

//---------------------------------------------------------------------------------------------
ASIOError JackRouter::disposeBuffers()
{
	long i;
	
	fCallbacks = 0;
	stop();

	for (i = 0; i < fActiveInputs; i++) {
		delete[] fInputBuffers[i];
		jack_port_unregister(fClient, fInputPorts[i]);
	}		
	fActiveInputs = 0;

	for (i = 0; i < fActiveOutputs; i++) {
		delete[] fOutputBuffers[i];
		jack_port_unregister(fClient, fOutputPorts[i]);
	}
	fActiveOutputs = 0;

	return ASE_OK;
}

//---------------------------------------------------------------------------------------------
ASIOError JackRouter::controlPanel()
{
	return ASE_NotPresent;
}

//---------------------------------------------------------------------------------------------
ASIOError JackRouter::future(long selector, void* opt)	// !!! check properties 
{
	ASIOTransportParameters* tp = (ASIOTransportParameters*)opt;
	switch (selector)
	{
		case kAsioEnableTimeCodeRead:	fTcRead = true;	return ASE_SUCCESS;
		case kAsioDisableTimeCodeRead:	fTcRead = false; return ASE_SUCCESS;
		case kAsioSetInputMonitor:		return ASE_SUCCESS;	// for testing!!!
		case kAsioCanInputMonitor:		return ASE_SUCCESS;	// for testing!!!
		case kAsioCanTimeInfo:			return ASE_SUCCESS;
		case kAsioCanTimeCode:			return ASE_SUCCESS;
	}
	return ASE_NotPresent;
}

//--------------------------------------------------------------------------------------------------------
// private methods
//--------------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------
void JackRouter::bufferSwitch()
{
	if (fStarted && fCallbacks) {
		getNanoSeconds(&fTheSystemTime);			// latch system time
		fSamplePosition += fBufferSize;
		if (fTimeInfoMode) {
			bufferSwitchX ();
		} else {
			fCallbacks->bufferSwitch (fToggle, ASIOFalse);
		}
		fToggle = fToggle ? 0 : 1;
	}
}

//---------------------------------------------------------------------------------------------
// asio2 buffer switch
void JackRouter::bufferSwitchX ()
{
	getSamplePosition (&fAsioTime.timeInfo.samplePosition, &fAsioTime.timeInfo.systemTime);
	long offset = fToggle ? fBufferSize : 0;
	if (fTcRead) {
		// Create a fake time code, which is 10 minutes ahead of the card's sample position
		// Please note that for simplicity here time code will wrap after 32 bit are reached
		fAsioTime.timeCode.timeCodeSamples.lo = fAsioTime.timeInfo.samplePosition.lo + 600.0 * fSampleRate;
		fAsioTime.timeCode.timeCodeSamples.hi = 0;
	}
	fCallbacks->bufferSwitchTimeInfo (&fAsioTime, fToggle, ASIOFalse);
	fAsioTime.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged);
}

//---------------------------------------------------------------------------------------------
ASIOError JackRouter::outputReady()
{
	return ASE_NotPresent;
}

//---------------------------------------------------------------------------------------------
double AsioSamples2double(ASIOSamples* samples)
{
	double a = (double)(samples->lo);
	if (samples->hi)
		a += (double)(samples->hi) * twoRaisedTo32;
	return a;
}

//---------------------------------------------------------------------------------------------
void getNanoSeconds(ASIOTimeStamp* ts)
{
	double nanoSeconds = (double)((unsigned long)timeGetTime ()) * 1000000.;
	ts->hi = (unsigned long)(nanoSeconds / twoRaisedTo32);
	ts->lo = (unsigned long)(nanoSeconds - (ts->hi * twoRaisedTo32));
}

//------------------------------------------------------------------------
void JackRouter::SaveConnections()
{
    const char** connections;
 	int i;

    for (i = 0; i < fActiveInputs; ++i) {
        if (fInputPorts[i] && (connections = jack_port_get_connections(fInputPorts[i])) != 0) {
            for (int j = 0; connections[j]; j++) {
                fConnections.push_back(make_pair(connections[j], jack_port_name(fInputPorts[i])));
            }
            jack_free(connections);
        }
    }

    for (i = 0; i < fActiveOutputs; ++i) {
        if (fOutputPorts[i] && (connections = jack_port_get_connections(fOutputPorts[i])) != 0) {
            for (int j = 0; connections[j]; j++) {
                fConnections.push_back(make_pair(jack_port_name(fOutputPorts[i]), connections[j]));
            }
            jack_free(connections);
        }
    }
}

//------------------------------------------------------------------------
void JackRouter::RestoreConnections()
{
    list<pair<string, string> >::const_iterator it;

    for (it = fConnections.begin(); it != fConnections.end(); it++) {
        pair<string, string> connection = *it;
        jack_connect(fClient, connection.first.c_str(), connection.second.c_str());
    }

    fConnections.clear();
}

//------------------------------------------------------------------------------------------
void JackRouter::AutoConnect()
{
	const char** ports;
	
	if ((ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput)) == NULL) {
		printf("Cannot find any physical capture ports\n");
	} else {
		if (fAutoConnectIn) {
			for (int i = 0; i < fActiveInputs; i++) {
				if (!ports[i]) {
					printf("source port is null i = %ld\n", i);
					break;
				} else if (jack_connect(fClient, ports[i], jack_port_name(fInputPorts[i])) != 0) {
					printf("Cannot connect input ports\n");
				}
			}
		}
		jack_free(ports); 
	}
	
	if ((ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput)) == NULL) {
		printf("Cannot find any physical playback ports");
	} else {
		if (fAutoConnectOut) {
			for (int i = 0; i < fActiveOutputs; i++) {
				if (!ports[i]){
					printf("destination port is null i = %ld\n", i);
					break;
				} else if (jack_connect(fClient, jack_port_name(fOutputPorts[i]), ports[i]) != 0) {
					printf("Cannot connect output ports\n");
				}
			}
		}
		jack_free(ports); 
	}
}

