git-svn-id: http://moon:8086/svn/software/trunk/libsrc/asiosdk2@1 b431acfa-c32f-4a4a-93f1-934dc6c82436
527 lines
20 KiB
C++
Executable File
527 lines
20 KiB
C++
Executable File
// hostsample.cpp : a simple ASIO host example.
|
|
// - instantiates the driver
|
|
// - get the information from the driver
|
|
// - built up some audio channels
|
|
// - plays silence for 20 seconds
|
|
// - destruct the driver
|
|
// Note: This sample cannot work with the "ASIO DirectX Driver" as it does
|
|
// not have a valid Application Window handle, which is used as sysRef
|
|
// on the Windows platform.
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "asiosys.h"
|
|
#include "asio.h"
|
|
#include "asiodrivers.h"
|
|
|
|
// name of the ASIO device to be used
|
|
#if WINDOWS
|
|
// #define ASIO_DRIVER_NAME "ASIO Multimedia Driver"
|
|
#define ASIO_DRIVER_NAME "ASIO Sample"
|
|
#elif MAC
|
|
// #define ASIO_DRIVER_NAME "Apple Sound Manager"
|
|
#define ASIO_DRIVER_NAME "ASIO Sample"
|
|
#endif
|
|
|
|
#define TEST_RUN_TIME 20.0 // run for 20 seconds
|
|
|
|
|
|
enum {
|
|
// number of input and outputs supported by the host application
|
|
// you can change these to higher or lower values
|
|
kMaxInputChannels = 32,
|
|
kMaxOutputChannels = 32
|
|
};
|
|
|
|
|
|
// internal data storage
|
|
typedef struct DriverInfo
|
|
{
|
|
// ASIOInit()
|
|
ASIODriverInfo driverInfo;
|
|
|
|
// ASIOGetChannels()
|
|
long inputChannels;
|
|
long outputChannels;
|
|
|
|
// ASIOGetBufferSize()
|
|
long minSize;
|
|
long maxSize;
|
|
long preferredSize;
|
|
long granularity;
|
|
|
|
// ASIOGetSampleRate()
|
|
ASIOSampleRate sampleRate;
|
|
|
|
// ASIOOutputReady()
|
|
bool postOutput;
|
|
|
|
// ASIOGetLatencies ()
|
|
long inputLatency;
|
|
long outputLatency;
|
|
|
|
// ASIOCreateBuffers ()
|
|
long inputBuffers; // becomes number of actual created input buffers
|
|
long outputBuffers; // becomes number of actual created output buffers
|
|
ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
|
|
|
|
// ASIOGetChannelInfo()
|
|
ASIOChannelInfo channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
|
|
// The above two arrays share the same indexing, as the data in them are linked together
|
|
|
|
// Information from ASIOGetSamplePosition()
|
|
// data is converted to double floats for easier use, however 64 bit integer can be used, too
|
|
double nanoSeconds;
|
|
double samples;
|
|
double tcSamples; // time code samples
|
|
|
|
// bufferSwitchTimeInfo()
|
|
ASIOTime tInfo; // time info state
|
|
unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
|
|
|
|
// Signal the end of processing in this example
|
|
bool stopped;
|
|
} DriverInfo;
|
|
|
|
|
|
DriverInfo asioDriverInfo = {0};
|
|
ASIOCallbacks asioCallbacks;
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// some external references
|
|
extern AsioDrivers* asioDrivers;
|
|
bool loadAsioDriver(char *name);
|
|
|
|
// internal prototypes (required for the Metrowerks CodeWarrior compiler)
|
|
int main(int argc, char* argv[]);
|
|
long init_asio_static_data (DriverInfo *asioDriverInfo);
|
|
ASIOError create_asio_buffers (DriverInfo *asioDriverInfo);
|
|
unsigned long get_sys_reference_time();
|
|
|
|
|
|
// callback prototypes
|
|
void bufferSwitch(long index, ASIOBool processNow);
|
|
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
|
|
void sampleRateChanged(ASIOSampleRate sRate);
|
|
long asioMessages(long selector, long value, void* message, double* opt);
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
long init_asio_static_data (DriverInfo *asioDriverInfo)
|
|
{ // collect the informational data of the driver
|
|
// get the number of available channels
|
|
if(ASIOGetChannels(&asioDriverInfo->inputChannels, &asioDriverInfo->outputChannels) == ASE_OK)
|
|
{
|
|
printf ("ASIOGetChannels (inputs: %d, outputs: %d);\n", asioDriverInfo->inputChannels, asioDriverInfo->outputChannels);
|
|
|
|
// get the usable buffer sizes
|
|
if(ASIOGetBufferSize(&asioDriverInfo->minSize, &asioDriverInfo->maxSize, &asioDriverInfo->preferredSize, &asioDriverInfo->granularity) == ASE_OK)
|
|
{
|
|
printf ("ASIOGetBufferSize (min: %d, max: %d, preferred: %d, granularity: %d);\n",
|
|
asioDriverInfo->minSize, asioDriverInfo->maxSize,
|
|
asioDriverInfo->preferredSize, asioDriverInfo->granularity);
|
|
|
|
// get the currently selected sample rate
|
|
if(ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK)
|
|
{
|
|
printf ("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate);
|
|
if (asioDriverInfo->sampleRate <= 0.0 || asioDriverInfo->sampleRate > 96000.0)
|
|
{
|
|
// Driver does not store it's internal sample rate, so set it to a know one.
|
|
// Usually you should check beforehand, that the selected sample rate is valid
|
|
// with ASIOCanSampleRate().
|
|
if(ASIOSetSampleRate(44100.0) == ASE_OK)
|
|
{
|
|
if(ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK)
|
|
printf ("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate);
|
|
else
|
|
return -6;
|
|
}
|
|
else
|
|
return -5;
|
|
}
|
|
|
|
// check wether the driver requires the ASIOOutputReady() optimization
|
|
// (can be used by the driver to reduce output latency by one block)
|
|
if(ASIOOutputReady() == ASE_OK)
|
|
asioDriverInfo->postOutput = true;
|
|
else
|
|
asioDriverInfo->postOutput = false;
|
|
printf ("ASIOOutputReady(); - %s\n", asioDriverInfo->postOutput ? "Supported" : "Not supported");
|
|
|
|
return 0;
|
|
}
|
|
return -3;
|
|
}
|
|
return -2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
|
|
#if NATIVE_INT64
|
|
#define ASIO64toDouble(a) (a)
|
|
#else
|
|
const double twoRaisedTo32 = 4294967296.;
|
|
#define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
|
|
#endif
|
|
|
|
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
|
|
{ // the actual processing callback.
|
|
// Beware that this is normally in a seperate thread, hence be sure that you take care
|
|
// about thread synchronization. This is omitted here for simplicity.
|
|
static processedSamples = 0;
|
|
|
|
// store the timeInfo for later use
|
|
asioDriverInfo.tInfo = *timeInfo;
|
|
|
|
// get the time stamp of the buffer, not necessary if no
|
|
// synchronization to other media is required
|
|
if (timeInfo->timeInfo.flags & kSystemTimeValid)
|
|
asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
|
|
else
|
|
asioDriverInfo.nanoSeconds = 0;
|
|
|
|
if (timeInfo->timeInfo.flags & kSamplePositionValid)
|
|
asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
|
|
else
|
|
asioDriverInfo.samples = 0;
|
|
|
|
if (timeInfo->timeCode.flags & kTcValid)
|
|
asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
|
|
else
|
|
asioDriverInfo.tcSamples = 0;
|
|
|
|
// get the system reference time
|
|
asioDriverInfo.sysRefTime = get_sys_reference_time();
|
|
|
|
#if WINDOWS && _DEBUG
|
|
// a few debug messages for the Windows device driver developer
|
|
// tells you the time when driver got its interrupt and the delay until the app receives
|
|
// the event notification.
|
|
static double last_samples = 0;
|
|
char tmp[128];
|
|
sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
|
|
OutputDebugString (tmp);
|
|
last_samples = asioDriverInfo.samples;
|
|
#endif
|
|
|
|
// buffer size in samples
|
|
long buffSize = asioDriverInfo.preferredSize;
|
|
|
|
// perform the processing
|
|
for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++)
|
|
{
|
|
if (asioDriverInfo.bufferInfos[i].isInput == false)
|
|
{
|
|
// OK do processing for the outputs only
|
|
switch (asioDriverInfo.channelInfos[i].type)
|
|
{
|
|
case ASIOSTInt16LSB:
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2);
|
|
break;
|
|
case ASIOSTInt24LSB: // used for 20 bits as well
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3);
|
|
break;
|
|
case ASIOSTInt32LSB:
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8);
|
|
break;
|
|
|
|
// these are used for 32 bit data buffer, with different alignment of the data inside
|
|
// 32 bit PCI bus systems can more easily used with these
|
|
case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment
|
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
|
|
case ASIOSTInt16MSB:
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2);
|
|
break;
|
|
case ASIOSTInt24MSB: // used for 20 bits as well
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3);
|
|
break;
|
|
case ASIOSTInt32MSB:
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8);
|
|
break;
|
|
|
|
// these are used for 32 bit data buffer, with different alignment of the data inside
|
|
// 32 bit PCI bus systems can more easily used with these
|
|
case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
|
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
|
memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
|
|
if (asioDriverInfo.postOutput)
|
|
ASIOOutputReady();
|
|
|
|
if (processedSamples >= asioDriverInfo.sampleRate * TEST_RUN_TIME) // roughly measured
|
|
asioDriverInfo.stopped = true;
|
|
else
|
|
processedSamples += buffSize;
|
|
|
|
return 0L;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void bufferSwitch(long index, ASIOBool processNow)
|
|
{ // the actual processing callback.
|
|
// Beware that this is normally in a seperate thread, hence be sure that you take care
|
|
// about thread synchronization. This is omitted here for simplicity.
|
|
|
|
// as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
|
|
// though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
|
|
ASIOTime timeInfo;
|
|
memset (&timeInfo, 0, sizeof (timeInfo));
|
|
|
|
// get the time stamp of the buffer, not necessary if no
|
|
// synchronization to other media is required
|
|
if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
|
|
timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
|
|
|
|
bufferSwitchTimeInfo (&timeInfo, index, processNow);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void sampleRateChanged(ASIOSampleRate sRate)
|
|
{
|
|
// do whatever you need to do if the sample rate changed
|
|
// usually this only happens during external sync.
|
|
// Audio processing is not stopped by the driver, actual sample rate
|
|
// might not have even changed, maybe only the sample rate status of an
|
|
// AES/EBU or S/PDIF digital input at the audio device.
|
|
// You might have to update time/sample related conversion routines, etc.
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
long asioMessages(long selector, long value, void* message, double* opt)
|
|
{
|
|
// currently the parameters "value", "message" and "opt" are not used.
|
|
long ret = 0;
|
|
switch(selector)
|
|
{
|
|
case kAsioSelectorSupported:
|
|
if(value == kAsioResetRequest
|
|
|| value == kAsioEngineVersion
|
|
|| value == kAsioResyncRequest
|
|
|| value == kAsioLatenciesChanged
|
|
// the following three were added for ASIO 2.0, you don't necessarily have to support them
|
|
|| value == kAsioSupportsTimeInfo
|
|
|| value == kAsioSupportsTimeCode
|
|
|| value == kAsioSupportsInputMonitor)
|
|
ret = 1L;
|
|
break;
|
|
case kAsioResetRequest:
|
|
// defer the task and perform the reset of the driver during the next "safe" situation
|
|
// You cannot reset the driver right now, as this code is called from the driver.
|
|
// Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
|
|
// Afterwards you initialize the driver again.
|
|
asioDriverInfo.stopped; // In this sample the processing will just stop
|
|
ret = 1L;
|
|
break;
|
|
case kAsioResyncRequest:
|
|
// This informs the application, that the driver encountered some non fatal data loss.
|
|
// It is used for synchronization purposes of different media.
|
|
// Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
|
|
// Windows Multimedia system, which could loose data because the Mutex was hold too long
|
|
// by another thread.
|
|
// However a driver can issue it in other situations, too.
|
|
ret = 1L;
|
|
break;
|
|
case kAsioLatenciesChanged:
|
|
// This will inform the host application that the drivers were latencies changed.
|
|
// Beware, it this does not mean that the buffer sizes have changed!
|
|
// You might need to update internal delay data.
|
|
ret = 1L;
|
|
break;
|
|
case kAsioEngineVersion:
|
|
// return the supported ASIO version of the host application
|
|
// If a host applications does not implement this selector, ASIO 1.0 is assumed
|
|
// by the driver
|
|
ret = 2L;
|
|
break;
|
|
case kAsioSupportsTimeInfo:
|
|
// informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
|
|
// is supported.
|
|
// For compatibility with ASIO 1.0 drivers the host application should always support
|
|
// the "old" bufferSwitch method, too.
|
|
ret = 1;
|
|
break;
|
|
case kAsioSupportsTimeCode:
|
|
// informs the driver wether application is interested in time code info.
|
|
// If an application does not need to know about time code, the driver has less work
|
|
// to do.
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
ASIOError create_asio_buffers (DriverInfo *asioDriverInfo)
|
|
{ // create buffers for all inputs and outputs of the card with the
|
|
// preferredSize from ASIOGetBufferSize() as buffer size
|
|
long i;
|
|
ASIOError result;
|
|
|
|
// fill the bufferInfos from the start without a gap
|
|
ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
|
|
|
|
// prepare inputs (Though this is not necessaily required, no opened inputs will work, too
|
|
if (asioDriverInfo->inputChannels > kMaxInputChannels)
|
|
asioDriverInfo->inputBuffers = kMaxInputChannels;
|
|
else
|
|
asioDriverInfo->inputBuffers = asioDriverInfo->inputChannels;
|
|
for(i = 0; i < asioDriverInfo->inputBuffers; i++, info++)
|
|
{
|
|
info->isInput = ASIOTrue;
|
|
info->channelNum = i;
|
|
info->buffers[0] = info->buffers[1] = 0;
|
|
}
|
|
|
|
// prepare outputs
|
|
if (asioDriverInfo->outputChannels > kMaxOutputChannels)
|
|
asioDriverInfo->outputBuffers = kMaxOutputChannels;
|
|
else
|
|
asioDriverInfo->outputBuffers = asioDriverInfo->outputChannels;
|
|
for(i = 0; i < asioDriverInfo->outputBuffers; i++, info++)
|
|
{
|
|
info->isInput = ASIOFalse;
|
|
info->channelNum = i;
|
|
info->buffers[0] = info->buffers[1] = 0;
|
|
}
|
|
|
|
// create and activate buffers
|
|
result = ASIOCreateBuffers(asioDriverInfo->bufferInfos,
|
|
asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers,
|
|
asioDriverInfo->preferredSize, &asioCallbacks);
|
|
if (result == ASE_OK)
|
|
{
|
|
// now get all the buffer details, sample word length, name, word clock group and activation
|
|
for (i = 0; i < asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers; i++)
|
|
{
|
|
asioDriverInfo->channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
|
|
asioDriverInfo->channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
|
|
result = ASIOGetChannelInfo(&asioDriverInfo->channelInfos[i]);
|
|
if (result != ASE_OK)
|
|
break;
|
|
}
|
|
|
|
if (result == ASE_OK)
|
|
{
|
|
// get the input and output latencies
|
|
// Latencies often are only valid after ASIOCreateBuffers()
|
|
// (input latency is the age of the first sample in the currently returned audio block)
|
|
// (output latency is the time the first sample in the currently returned audio block requires to get to the output)
|
|
result = ASIOGetLatencies(&asioDriverInfo->inputLatency, &asioDriverInfo->outputLatency);
|
|
if (result == ASE_OK)
|
|
printf ("ASIOGetLatencies (input: %d, output: %d);\n", asioDriverInfo->inputLatency, asioDriverInfo->outputLatency);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
// load the driver, this will setup all the necessary internal data structures
|
|
if (loadAsioDriver (ASIO_DRIVER_NAME))
|
|
{
|
|
// initialize the driver
|
|
if (ASIOInit (&asioDriverInfo.driverInfo) == ASE_OK)
|
|
{
|
|
printf ("asioVersion: %d\n"
|
|
"driverVersion: %d\n"
|
|
"Name: %s\n"
|
|
"ErrorMessage: %s\n",
|
|
asioDriverInfo.driverInfo.asioVersion, asioDriverInfo.driverInfo.driverVersion,
|
|
asioDriverInfo.driverInfo.name, asioDriverInfo.driverInfo.errorMessage);
|
|
if (init_asio_static_data (&asioDriverInfo) == 0)
|
|
{
|
|
// ASIOControlPanel(); you might want to check wether the ASIOControlPanel() can open
|
|
|
|
// set up the asioCallback structure and create the ASIO data buffer
|
|
asioCallbacks.bufferSwitch = &bufferSwitch;
|
|
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
|
|
asioCallbacks.asioMessage = &asioMessages;
|
|
asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
|
|
if (create_asio_buffers (&asioDriverInfo) == ASE_OK)
|
|
{
|
|
if (ASIOStart() == ASE_OK)
|
|
{
|
|
// Now all is up and running
|
|
fprintf (stdout, "\nASIO Driver started succefully.\n\n");
|
|
while (!asioDriverInfo.stopped)
|
|
{
|
|
#if WINDOWS
|
|
Sleep(100); // goto sleep for 100 milliseconds
|
|
#elif MAC
|
|
unsigned long dummy;
|
|
Delay (6, &dummy);
|
|
#endif
|
|
fprintf (stdout, "%d ms / %d ms / %d samples", asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)asioDriverInfo.samples);
|
|
|
|
// create a more readable time code format (the quick and dirty way)
|
|
double remainder = asioDriverInfo.tcSamples;
|
|
long hours = (long)(remainder / (asioDriverInfo.sampleRate * 3600));
|
|
remainder -= hours * asioDriverInfo.sampleRate * 3600;
|
|
long minutes = (long)(remainder / (asioDriverInfo.sampleRate * 60));
|
|
remainder -= minutes * asioDriverInfo.sampleRate * 60;
|
|
long seconds = (long)(remainder / asioDriverInfo.sampleRate);
|
|
remainder -= seconds * asioDriverInfo.sampleRate;
|
|
fprintf (stdout, " / TC: %2.2d:%2.2d:%2.2d:%5.5d", (long)hours, (long)minutes, (long)seconds, (long)remainder);
|
|
|
|
fprintf (stdout, " \r");
|
|
#if !MAC
|
|
fflush (stdout);
|
|
#endif
|
|
}
|
|
ASIOStop();
|
|
}
|
|
ASIODisposeBuffers();
|
|
}
|
|
}
|
|
ASIOExit();
|
|
}
|
|
asioDrivers->removeCurrentDriver();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
unsigned long get_sys_reference_time()
|
|
{ // get the system reference time
|
|
#if WINDOWS
|
|
return timeGetTime();
|
|
#elif MAC
|
|
static const double twoRaisedTo32 = 4294967296.;
|
|
UnsignedWide ys;
|
|
Microseconds(&ys);
|
|
double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
|
|
return (unsigned long)(r / 1000.);
|
|
#endif
|
|
}
|