|
|
@@ -1,720 +0,0 @@ |
|
|
|
/* |
|
|
|
Copyright (C) 2006 Grame |
|
|
|
|
|
|
|
Portable Audio I/O Library for ASIO Drivers |
|
|
|
Author: Stephane Letz |
|
|
|
Based on the Open Source API proposed by Ross Bencina |
|
|
|
Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina |
|
|
|
|
|
|
|
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 "pa_asio.h" |
|
|
|
#include "JackDriverLoader.h" |
|
|
|
#include "driver_interface.h" |
|
|
|
|
|
|
|
#include "JackASIODriver.h" |
|
|
|
#include "JackEngineControl.h" |
|
|
|
#include "JackGraphManager.h" |
|
|
|
#include "JackError.h" |
|
|
|
#include "JackClientControl.h" |
|
|
|
#include "JackGlobals.h" |
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
#include <windows.h> |
|
|
|
#include <mmsystem.h> |
|
|
|
|
|
|
|
#include "asiosys.h" |
|
|
|
#include "asio.h" |
|
|
|
#include "asiodrivers.h" |
|
|
|
#include "iasiothiscallresolver.h" |
|
|
|
|
|
|
|
/* external references */ |
|
|
|
extern AsioDrivers* asioDrivers ; |
|
|
|
bool loadAsioDriver(char *name); |
|
|
|
|
|
|
|
|
|
|
|
namespace Jack |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
load the asio driver named by <driverName> and return statistics about |
|
|
|
the driver in info. If no error occurred, the driver will remain open |
|
|
|
and must be closed by the called by calling ASIOExit() - if an error |
|
|
|
is returned the driver will already be closed. |
|
|
|
*/ |
|
|
|
static PaError LoadAsioDriver(const char *driverName, PaAsioDriverInfo *driverInfo, void *systemSpecific) |
|
|
|
{ |
|
|
|
PaError result = paNoError; |
|
|
|
ASIOError asioError; |
|
|
|
int asioIsInitialized = 0; |
|
|
|
|
|
|
|
if (!loadAsioDriver(const_cast<char*>(driverName))) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_HOST_ERROR(0, "Failed to load ASIO driver"); |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
memset(&driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo)); |
|
|
|
driverInfo->asioDriverInfo.asioVersion = 2; |
|
|
|
driverInfo->asioDriverInfo.sysRef = systemSpecific; |
|
|
|
if ((asioError = ASIOInit(&driverInfo->asioDriverInfo)) != ASE_OK) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_ASIO_ERROR(asioError); |
|
|
|
goto error; |
|
|
|
} else { |
|
|
|
asioIsInitialized = 1; |
|
|
|
} |
|
|
|
|
|
|
|
if ((asioError = ASIOGetChannels(&driverInfo->inputChannelCount, |
|
|
|
&driverInfo->outputChannelCount)) != ASE_OK) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_ASIO_ERROR(asioError); |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
if ((asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize, |
|
|
|
&driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize, |
|
|
|
&driverInfo->bufferGranularity)) != ASE_OK) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_ASIO_ERROR(asioError); |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
if (ASIOOutputReady() == ASE_OK) |
|
|
|
driverInfo->postOutput = true; |
|
|
|
else |
|
|
|
driverInfo->postOutput = false; |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
error: |
|
|
|
if (asioIsInitialized) |
|
|
|
ASIOExit(); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::bufferSwitch(long index, ASIOBool directProcess) |
|
|
|
{ |
|
|
|
JackASIODriver* driver = (JackASIODriver*)userData; |
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
|
|
|
|
driver->fLastWaitUst = GetMicroSeconds(); // Take callback date here |
|
|
|
driver->fInputBuffer = (float**)inputBuffer; |
|
|
|
driver->fOutputBuffer = (float**)outputBuffer; |
|
|
|
|
|
|
|
// Call the real callback |
|
|
|
bufferSwitchTimeInfo(&timeInfo, index, directProcess); |
|
|
|
|
|
|
|
return driver->Process(); |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Read() |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Write() |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) |
|
|
|
{ |
|
|
|
PaError result = paNoError; |
|
|
|
int i, driverCount; |
|
|
|
PaAsioHostApiRepresentation *asioHostApi; |
|
|
|
PaAsioDeviceInfo *deviceInfoArray; |
|
|
|
char **names; |
|
|
|
PaAsioDriverInfo paAsioDriverInfo; |
|
|
|
|
|
|
|
asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory(sizeof(PaAsioHostApiRepresentation)); |
|
|
|
if (!asioHostApi) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
asioHostApi->allocations = PaUtil_CreateAllocationGroup(); |
|
|
|
if (!asioHostApi->allocations) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
asioHostApi->systemSpecific = 0; |
|
|
|
asioHostApi->openAsioDeviceIndex = paNoDevice; |
|
|
|
|
|
|
|
*hostApi = &asioHostApi->inheritedHostApiRep; |
|
|
|
(*hostApi)->info.structVersion = 1; |
|
|
|
|
|
|
|
(*hostApi)->info.type = paASIO; |
|
|
|
(*hostApi)->info.name = "ASIO"; |
|
|
|
(*hostApi)->info.deviceCount = 0; |
|
|
|
|
|
|
|
#ifdef WINDOWS |
|
|
|
/* use desktop window as system specific ptr */ |
|
|
|
asioHostApi->systemSpecific = GetDesktopWindow(); |
|
|
|
CoInitialize(NULL); |
|
|
|
#endif |
|
|
|
|
|
|
|
/* MUST BE CHECKED : to force fragments loading on Mac */ |
|
|
|
loadAsioDriver("dummy"); |
|
|
|
|
|
|
|
/* driverCount is the number of installed drivers - not necessarily |
|
|
|
the number of installed physical devices. */ |
|
|
|
#if MAC |
|
|
|
driverCount = asioDrivers->getNumFragments(); |
|
|
|
#elif WINDOWS |
|
|
|
driverCount = asioDrivers->asioGetNumDev(); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (driverCount > 0) { |
|
|
|
names = GetAsioDriverNames(asioHostApi->allocations, driverCount); |
|
|
|
if (!names) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
/* allocate enough space for all drivers, even if some aren't installed */ |
|
|
|
|
|
|
|
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( |
|
|
|
asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount); |
|
|
|
if (!(*hostApi)->deviceInfos) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
/* allocate all device info structs in a contiguous block */ |
|
|
|
deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory( |
|
|
|
asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount); |
|
|
|
if (!deviceInfoArray) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
for (i = 0; i < driverCount; ++i) { |
|
|
|
|
|
|
|
PA_DEBUG(("ASIO names[%d]:%s\n", i, names[i])); |
|
|
|
|
|
|
|
// Since portaudio opens ALL ASIO drivers, and no one else does that, |
|
|
|
// we face fact that some drivers were not meant for it, drivers which act |
|
|
|
// like shells on top of REAL drivers, for instance. |
|
|
|
// so we get duplicate handles, locks and other problems. |
|
|
|
// so lets NOT try to load any such wrappers. |
|
|
|
// The ones i [davidv] know of so far are: |
|
|
|
|
|
|
|
if (strcmp (names[i], "ASIO DirectX Full Duplex Driver") == 0 |
|
|
|
|| strcmp (names[i], "ASIO Multimedia Driver") == 0 |
|
|
|
|| strncmp(names[i], "Premiere", 8) == 0 //"Premiere Elements Windows Sound 1.0" |
|
|
|
|| strncmp(names[i], "Adobe", 5) == 0) //"Adobe Default Windows Sound 1.5" |
|
|
|
{ |
|
|
|
PA_DEBUG(("BLACKLISTED!!!\n")); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
/* Attempt to load the asio driver... */ |
|
|
|
if (LoadAsioDriver(names[i], &paAsioDriverInfo, asioHostApi->systemSpecific) == paNoError) { |
|
|
|
PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; |
|
|
|
PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo; |
|
|
|
|
|
|
|
deviceInfo->structVersion = 2; |
|
|
|
deviceInfo->hostApi = hostApiIndex; |
|
|
|
|
|
|
|
deviceInfo->name = names[i]; |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", i, deviceInfo->name)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriverInfo.inputChannelCount)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriverInfo.outputChannelCount)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", i, paAsioDriverInfo.bufferMinSize)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", i, paAsioDriverInfo.bufferMaxSize)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriverInfo.bufferPreferredSize)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", i, paAsioDriverInfo.bufferGranularity)); |
|
|
|
|
|
|
|
deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount; |
|
|
|
deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount; |
|
|
|
|
|
|
|
deviceInfo->defaultSampleRate = 0.; |
|
|
|
bool foundDefaultSampleRate = false; |
|
|
|
for (int j = 0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j) { |
|
|
|
ASIOError asioError = ASIOCanSampleRate(defaultSampleRateSearchOrder_[j]); |
|
|
|
if (asioError != ASE_NoClock && asioError != ASE_NotPresent) { |
|
|
|
deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j]; |
|
|
|
foundDefaultSampleRate = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate)); |
|
|
|
|
|
|
|
if (foundDefaultSampleRate) { |
|
|
|
|
|
|
|
/* calculate default latency values from bufferPreferredSize |
|
|
|
for default low latency, and bufferPreferredSize * 3 |
|
|
|
for default high latency. |
|
|
|
use the default sample rate to convert from samples to |
|
|
|
seconds. Without knowing what sample rate the user will |
|
|
|
use this is the best we can do. |
|
|
|
*/ |
|
|
|
|
|
|
|
double defaultLowLatency = |
|
|
|
paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate; |
|
|
|
|
|
|
|
deviceInfo->defaultLowInputLatency = defaultLowLatency; |
|
|
|
deviceInfo->defaultLowOutputLatency = defaultLowLatency; |
|
|
|
|
|
|
|
long defaultHighLatencyBufferSize = |
|
|
|
paAsioDriverInfo.bufferPreferredSize * 3; |
|
|
|
|
|
|
|
if (defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize) |
|
|
|
defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize; |
|
|
|
|
|
|
|
double defaultHighLatency = |
|
|
|
defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate; |
|
|
|
|
|
|
|
if (defaultHighLatency < defaultLowLatency) |
|
|
|
defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */ |
|
|
|
|
|
|
|
deviceInfo->defaultHighInputLatency = defaultHighLatency; |
|
|
|
deviceInfo->defaultHighOutputLatency = defaultHighLatency; |
|
|
|
|
|
|
|
} else { |
|
|
|
deviceInfo->defaultLowInputLatency = 0.; |
|
|
|
deviceInfo->defaultLowOutputLatency = 0.; |
|
|
|
deviceInfo->defaultHighInputLatency = 0.; |
|
|
|
deviceInfo->defaultHighOutputLatency = 0.; |
|
|
|
} |
|
|
|
|
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency)); |
|
|
|
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency)); |
|
|
|
|
|
|
|
asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize; |
|
|
|
asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize; |
|
|
|
asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize; |
|
|
|
asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity; |
|
|
|
|
|
|
|
|
|
|
|
asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory( |
|
|
|
asioHostApi->allocations, |
|
|
|
sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels |
|
|
|
+ deviceInfo->maxOutputChannels)); |
|
|
|
if (!asioDeviceInfo->asioChannelInfos) { |
|
|
|
result = paInsufficientMemory; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
int a; |
|
|
|
|
|
|
|
for (a = 0; a < deviceInfo->maxInputChannels; ++a) { |
|
|
|
asioDeviceInfo->asioChannelInfos[a].channel = a; |
|
|
|
asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue; |
|
|
|
ASIOError asioError = ASIOGetChannelInfo(&asioDeviceInfo->asioChannelInfos[a]); |
|
|
|
if (asioError != ASE_OK) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_ASIO_ERROR(asioError); |
|
|
|
goto error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (a = 0; a < deviceInfo->maxOutputChannels; ++a) { |
|
|
|
int b = deviceInfo->maxInputChannels + a; |
|
|
|
asioDeviceInfo->asioChannelInfos[b].channel = a; |
|
|
|
asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse; |
|
|
|
ASIOError asioError = ASIOGetChannelInfo(&asioDeviceInfo->asioChannelInfos[b]); |
|
|
|
if (asioError != ASE_OK) { |
|
|
|
result = paUnanticipatedHostError; |
|
|
|
PA_ASIO_SET_LAST_ASIO_ERROR(asioError); |
|
|
|
goto error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* unload the driver */ |
|
|
|
ASIOExit(); |
|
|
|
|
|
|
|
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; |
|
|
|
++(*hostApi)->info.deviceCount; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ((*hostApi)->info.deviceCount > 0) { |
|
|
|
(*hostApi)->info.defaultInputDevice = 0; |
|
|
|
(*hostApi)->info.defaultOutputDevice = 0; |
|
|
|
} else { |
|
|
|
(*hostApi)->info.defaultInputDevice = paNoDevice; |
|
|
|
(*hostApi)->info.defaultOutputDevice = paNoDevice; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
(*hostApi)->Terminate = Terminate; |
|
|
|
(*hostApi)->OpenStream = OpenStream; |
|
|
|
(*hostApi)->IsFormatSupported = IsFormatSupported; |
|
|
|
|
|
|
|
PaUtil_InitializeStreamInterface(&asioHostApi->callbackStreamInterface, CloseStream, StartStream, |
|
|
|
StopStream, AbortStream, IsStreamStopped, IsStreamActive, |
|
|
|
GetStreamTime, GetStreamCpuLoad, |
|
|
|
PaUtil_DummyRead, PaUtil_DummyWrite, |
|
|
|
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable); |
|
|
|
|
|
|
|
PaUtil_InitializeStreamInterface(&asioHostApi->blockingStreamInterface, CloseStream, StartStream, |
|
|
|
StopStream, AbortStream, IsStreamStopped, IsStreamActive, |
|
|
|
GetStreamTime, PaUtil_DummyGetCpuLoad, |
|
|
|
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
error: |
|
|
|
if (asioHostApi) { |
|
|
|
if (asioHostApi->allocations) { |
|
|
|
PaUtil_FreeAllAllocations(asioHostApi->allocations); |
|
|
|
PaUtil_DestroyAllocationGroup(asioHostApi->allocations); |
|
|
|
} |
|
|
|
|
|
|
|
PaUtil_FreeMemory(asioHostApi); |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
void JackASIODriverTerminate(struct PaUtilHostApiRepresentation *hostApi) |
|
|
|
{ |
|
|
|
PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; |
|
|
|
|
|
|
|
/* |
|
|
|
IMPLEMENT ME: |
|
|
|
- clean up any resources not handled by the allocation group |
|
|
|
*/ |
|
|
|
|
|
|
|
if (asioHostApi->allocations) |
|
|
|
{ |
|
|
|
PaUtil_FreeAllAllocations(asioHostApi->allocations); |
|
|
|
PaUtil_DestroyAllocationGroup(asioHostApi->allocations); |
|
|
|
} |
|
|
|
|
|
|
|
PaUtil_FreeMemory(asioHostApi); |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Open(jack_nframes_t nframes, |
|
|
|
jack_nframes_t samplerate, |
|
|
|
int capturing, |
|
|
|
int playing, |
|
|
|
int inchannels, |
|
|
|
int outchannels, |
|
|
|
bool monitor, |
|
|
|
const char* capture_driver_uid, |
|
|
|
const char* playback_driver_uid, |
|
|
|
jack_nframes_t capture_latency, |
|
|
|
jack_nframes_t playback_latency) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
|
|
|
|
error: |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Close() |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Start() |
|
|
|
{ |
|
|
|
jack_log("JackASIODriver::Start"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::Stop() |
|
|
|
{ |
|
|
|
jack_log("JackASIODriver::Stop"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackASIODriver::SetBufferSize(jack_nframes_t nframes) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void JackASIODriver::PrintState() |
|
|
|
{ |
|
|
|
int i; |
|
|
|
std::cout << "JackASIODriver state" << std::endl; |
|
|
|
jack_port_id_t port_index; |
|
|
|
std::cout << "Input ports" << std::endl; |
|
|
|
|
|
|
|
/* |
|
|
|
for (i = 0; i < fPlaybackChannels; i++) { |
|
|
|
port_index = fCapturePortList[i]; |
|
|
|
JackPort* port = fGraphManager->GetPort(port_index); |
|
|
|
std::cout << port->GetName() << std::endl; |
|
|
|
if (fGraphManager->GetConnectionsNum(port_index)) {} |
|
|
|
} |
|
|
|
|
|
|
|
std::cout << "Output ports" << std::endl; |
|
|
|
|
|
|
|
for (i = 0; i < fCaptureChannels; i++) { |
|
|
|
port_index = fPlaybackPortList[i]; |
|
|
|
JackPort* port = fGraphManager->GetPort(port_index); |
|
|
|
std::cout << port->GetName() << std::endl; |
|
|
|
if (fGraphManager->GetConnectionsNum(port_index)) {} |
|
|
|
} |
|
|
|
*/ |
|
|
|
} |
|
|
|
|
|
|
|
} // end of namespace |
|
|
|
|
|
|
|
#ifdef __cplusplus |
|
|
|
extern "C" |
|
|
|
{ |
|
|
|
#endif |
|
|
|
|
|
|
|
#include "JackExports.h" |
|
|
|
|
|
|
|
EXPORT jack_driver_desc_t* driver_get_descriptor() { |
|
|
|
jack_driver_desc_t *desc; |
|
|
|
unsigned int i; |
|
|
|
desc = (jack_driver_desc_t*)calloc(1, sizeof(jack_driver_desc_t)); |
|
|
|
|
|
|
|
strcpy(desc->name, "ASIO"); |
|
|
|
|
|
|
|
desc->nparams = 13; |
|
|
|
desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t)); |
|
|
|
|
|
|
|
i = 0; |
|
|
|
strcpy(desc->params[i].name, "channels"); |
|
|
|
desc->params[i].character = 'c'; |
|
|
|
desc->params[i].type = JackDriverParamInt; |
|
|
|
desc->params[i].value.ui = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Maximum number of channels"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "inchannels"); |
|
|
|
desc->params[i].character = 'i'; |
|
|
|
desc->params[i].type = JackDriverParamInt; |
|
|
|
desc->params[i].value.ui = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Maximum number of input channels"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "outchannels"); |
|
|
|
desc->params[i].character = 'o'; |
|
|
|
desc->params[i].type = JackDriverParamInt; |
|
|
|
desc->params[i].value.ui = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Maximum number of output channels"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "capture"); |
|
|
|
desc->params[i].character = 'C'; |
|
|
|
desc->params[i].type = JackDriverParamString; |
|
|
|
strcpy(desc->params[i].value.str, "will take default PortAudio input device"); |
|
|
|
strcpy(desc->params[i].short_desc, "Provide capture ports. Optionally set PortAudio device name"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "playback"); |
|
|
|
desc->params[i].character = 'P'; |
|
|
|
desc->params[i].type = JackDriverParamString; |
|
|
|
strcpy(desc->params[i].value.str, "will take default PortAudio output device"); |
|
|
|
strcpy(desc->params[i].short_desc, "Provide playback ports. Optionally set PortAudio device name"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy (desc->params[i].name, "monitor"); |
|
|
|
desc->params[i].character = 'm'; |
|
|
|
desc->params[i].type = JackDriverParamBool; |
|
|
|
desc->params[i].value.i = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Provide monitor ports for the output"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "duplex"); |
|
|
|
desc->params[i].character = 'D'; |
|
|
|
desc->params[i].type = JackDriverParamBool; |
|
|
|
desc->params[i].value.i = TRUE; |
|
|
|
strcpy(desc->params[i].short_desc, "Provide both capture and playback ports"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "rate"); |
|
|
|
desc->params[i].character = 'r'; |
|
|
|
desc->params[i].type = JackDriverParamUInt; |
|
|
|
desc->params[i].value.ui = 44100U; |
|
|
|
strcpy(desc->params[i].short_desc, "Sample rate"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "period"); |
|
|
|
desc->params[i].character = 'p'; |
|
|
|
desc->params[i].type = JackDriverParamUInt; |
|
|
|
desc->params[i].value.ui = 128U; |
|
|
|
strcpy(desc->params[i].short_desc, "Frames per period"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "device"); |
|
|
|
desc->params[i].character = 'd'; |
|
|
|
desc->params[i].type = JackDriverParamString; |
|
|
|
desc->params[i].value.ui = 128U; |
|
|
|
strcpy(desc->params[i].value.str, "will take default CoreAudio device name"); |
|
|
|
strcpy(desc->params[i].short_desc, "CoreAudio device name"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "input-latency"); |
|
|
|
desc->params[i].character = 'I'; |
|
|
|
desc->params[i].type = JackDriverParamUInt; |
|
|
|
desc->params[i].value.i = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Extra input latency"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "output-latency"); |
|
|
|
desc->params[i].character = 'O'; |
|
|
|
desc->params[i].type = JackDriverParamUInt; |
|
|
|
desc->params[i].value.i = 0; |
|
|
|
strcpy(desc->params[i].short_desc, "Extra output latency"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
i++; |
|
|
|
strcpy(desc->params[i].name, "list-devices"); |
|
|
|
desc->params[i].character = 'l'; |
|
|
|
desc->params[i].type = JackDriverParamBool; |
|
|
|
desc->params[i].value.i = TRUE; |
|
|
|
strcpy(desc->params[i].short_desc, "Display available CoreAudio devices"); |
|
|
|
strcpy(desc->params[i].long_desc, desc->params[i].short_desc); |
|
|
|
|
|
|
|
return desc; |
|
|
|
} |
|
|
|
|
|
|
|
EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackEngine* engine, Jack::JackSynchro** table, const JSList* params) { |
|
|
|
jack_nframes_t srate = 44100; |
|
|
|
jack_nframes_t frames_per_interrupt = 512; |
|
|
|
int capture = FALSE; |
|
|
|
int playback = FALSE; |
|
|
|
int chan_in = 0; |
|
|
|
int chan_out = 0; |
|
|
|
bool monitor = false; |
|
|
|
char* capture_pcm_name = ""; |
|
|
|
char* playback_pcm_name = ""; |
|
|
|
const JSList *node; |
|
|
|
const jack_driver_param_t *param; |
|
|
|
jack_nframes_t systemic_input_latency = 0; |
|
|
|
jack_nframes_t systemic_output_latency = 0; |
|
|
|
|
|
|
|
for (node = params; node; node = jack_slist_next(node)) { |
|
|
|
param = (const jack_driver_param_t *) node->data; |
|
|
|
|
|
|
|
switch (param->character) { |
|
|
|
|
|
|
|
case 'd': |
|
|
|
capture_pcm_name = strdup(param->value.str); |
|
|
|
playback_pcm_name = strdup(param->value.str); |
|
|
|
break; |
|
|
|
|
|
|
|
case 'D': |
|
|
|
capture = TRUE; |
|
|
|
playback = TRUE; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'c': |
|
|
|
chan_in = chan_out = (int) param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'i': |
|
|
|
chan_in = (int) param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'o': |
|
|
|
chan_out = (int) param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'C': |
|
|
|
capture = TRUE; |
|
|
|
if (strcmp(param->value.str, "none") != 0) { |
|
|
|
capture_pcm_name = strdup(param->value.str); |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case 'P': |
|
|
|
playback = TRUE; |
|
|
|
if (strcmp(param->value.str, "none") != 0) { |
|
|
|
playback_pcm_name = strdup(param->value.str); |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case 'm': |
|
|
|
monitor = param->value.i; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'r': |
|
|
|
srate = param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'p': |
|
|
|
frames_per_interrupt = (unsigned int) param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'I': |
|
|
|
systemic_input_latency = param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'O': |
|
|
|
systemic_output_latency = param->value.ui; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'l': |
|
|
|
Jack::DisplayDeviceNames(); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// duplex is the default |
|
|
|
if (!capture && !playback) { |
|
|
|
capture = TRUE; |
|
|
|
playback = TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
Jack::JackDriverClientInterface* driver = new Jack::JackASIODriver("ASIO", engine, table); |
|
|
|
if (driver->Open(frames_per_interrupt, srate, capture, playback, chan_in, chan_out, monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency) == 0) { |
|
|
|
return driver; |
|
|
|
} else { |
|
|
|
delete driver; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef __cplusplus |
|
|
|
} |
|
|
|
#endif |