First Steinberg ASIO COM interface in pure FreeBASIC.

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
D.J.Peters
Posts: 8631
Joined: May 28, 2005 3:28
Contact:

First Steinberg ASIO COM interface in pure FreeBASIC.

Post by D.J.Peters »

Steinberg ASIO Windows COM interface.

For the next version of FBSound i add support for ASIO drivers.

http://en.wikipedia.org/wiki/Audio_Stream_Input/Output

The problem are how can i do it without C++
of course FBSound is written in FreeBASIC.

So what is the main trick ?
The COM interface pointer are not on the stack
like the hidden THIS param of classes
the pointer are in CPU register ECX.

thats all

would be nice if some of you with Asio drivers could test it.

Joshy

Code: Select all

#define WIN_INCLUDEALL
#include "windows.bi"
#include "crt\string.bi"

' #define DEBUG

#ifdef DEBUG
# ifdef UNICODE
#  define STRING_T WSTRING
#  define MKSTRING(s) wstr(s)
# else
#  define STRING_T ZSTRING
#  define MKSTRING(s) (s)
# endif
  dim shared as STRING_T * 1024 Buffer
  ' DbgPrint a messages
  #macro DMSG(msg)
    wsprintf(@Buffer,@MKSTRING("%s"),@MKSTRING(msg))
    OutputDebugString(@Buffer)
  #endmacro
#else
# define DMSG(msg)
#endif


#define MAXPATHLEN 512
#define MAXDRVNAMELEN 128
#define DRVERR -5000
#define DRVERR_INVALID_PARAM DRVERR-1
#define DRVERR_DEVICE_ALREADY_OPEN DRVERR-2
#define DRVERR_DEVICE_NOT_FOUND DRVERR-3
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#define ASIODRV_DESC  "description"
#define INPROC_SERVER "InprocServer32"
#define ASIO_PATH     "software\\asio"
#define COM_CLSID     "clsid"

type ASIOSamples field=4
  as uinteger hi ' note: hi,lo order
  as uinteger lo
end type

type ASIOTimeStamp field=4
  as uinteger hi ' note: hi,lo order
  as uinteger lo
end type

type ASIOSampleRate as double

enum ASIOBool
  ASIOFalse
  ASIOTrue
end enum

' Sample Types are expressed as long
enum ASIOSampleType
  ASIOSTInt16MSB    =  0
  ASIOSTInt24MSB    =  1    ' used for 20 bits as well
  ASIOSTInt32MSB    =  2
  ASIOSTFloat32MSB  =  3    ' IEEE 754 32 bit float
  ASIOSTFloat64MSB  =  4    ' IEEE 754 64 bit double float
  ' these are used for 32 bit data buffer, with different alignment of the data inside
  ' 32 bit PCI bus systems can be more easily used with these
  ASIOSTInt32MSB16  =  8    ' 32 bit data with 16 bit alignment
  ASIOSTInt32MSB18  =  9    ' 32 bit data with 18 bit alignment
  ASIOSTInt32MSB20  = 10    ' 32 bit data with 20 bit alignment
  ASIOSTInt32MSB24  = 11    ' 32 bit data with 24 bit alignment
  ASIOSTInt16LSB    = 16
  ASIOSTInt24LSB    = 17    ' used for 20 bits as well
  ASIOSTInt32LSB    = 18
  ASIOSTFloat32LSB  = 19    ' IEEE 754 32 bit float, as found on Intel x86 architecture
  ASIOSTFloat64LSB  = 20    ' IEEE 754 64 bit double float, as found on Intel x86 architecture
  ' 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
  ASIOSTInt32LSB16  = 24    ' 32 bit data with 18 bit alignment
  ASIOSTInt32LSB18  = 25    ' 32 bit data with 18 bit alignment
  ASIOSTInt32LSB20  = 26    ' 32 bit data with 20 bit alignment
  ASIOSTInt32LSB24  = 27    ' 32 bit data with 24 bit alignment
  '  ASIO DSD format.
  ASIOSTDSDInt8LSB1 = 32    ' DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
  ASIOSTDSDInt8MSB1 = 33    ' DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
  ASIOSTDSDInt8NER8 = 40    ' DSD 8 bit data, 1 sample per byte. No Endianness required.
  ASIOSTLastEntry
end enum

enum ASIOError
  ASE_OK = 0               ' This value will be returned whenever the call succeeded
  ASE_SUCCESS = &H3f4847a0 ' unique success return value for ASIOFuture calls
  ASE_NotPresent = -1000   ' hardware input or output is not present or available
  ASE_HWMalfunction        ' hardware is malfunctioning (can be returned by any ASIO function)
  ASE_InvalidParameter     ' input parameter invalid
  ASE_InvalidMode          ' hardware is in a bad mode or used in a bad mode
  ASE_SPNotAdvancing       ' hardware is not running when sample position is inquired
  ASE_NoClock              ' sample clock or rate cannot be determined or is not present
  ASE_NoMemory             ' not enough memory for completing the request
end enum

enum ASIOTimeCodeFlags
  kTcValid                = 1 shl 0
  kTcRunning              = 1 shl 1
  kTcReverse              = 1 shl 2
  kTcOnspeed              = 1 shl 3
  kTcStill                = 1 shl 4
  kTcSpeedValid           = 1 shl 8
end enum
type ASIOTimeCode field=4
  as double            speed           ' speed relation (fraction of nominal speed)
  as ASIOSamples       timeCodeSamples ' time in samples
  as ASIOTimeCodeFlags flags           ' some information flags
  as ubyte future(63)
end type

enum AsioTimeInfoFlags
  kSystemTimeValid        = 1 shl 0   ' must always be valid
  kSamplePositionValid    = 1 shl 1   ' must always be valid
  kSampleRateValid        = 1 shl 2
  kSpeedValid             = 1 shl 3
  kSampleRateChanged      = 1 shl 4
  kClockSourceChanged     = 1 shl 5
end enum
type AsioTimeInfo field=4
  as double            speed          ' absolute speed (1. = nominal)
  as ASIOTimeStamp     systemTime     ' system time related to samplePosition, in nanoseconds
  as ASIOSamples       samplePosition
  as ASIOSampleRate    sampleRate     ' current rate
  as AsioTimeInfoFlags flags
  as ubyte reserved(11)
end type



type ASIOTime field=4            ' both input/output
  as long         reserved(3)    ' must be 0
  as AsioTimeInfo timeInfo       ' required
  as ASIOTimeCode timeCode       ' optional, evaluated if (timeCode.flags and kTcValid)
end type

type ASIOClockSource field=4
  as long         index             ' as used for ASIOSetClockSource()
  as long         associatedChannel ' for instance, S/PDIF or AES/EBU
  as long         associatedGroup   ' see channel groups (ASIOGetChannelInfo())
  as ASIOBool     isCurrentSource   ' ASIOTrue if this is the current clock source
  as zstring * 32 name              ' for user selection
end type

type ASIOChannelInfo field=4
  as long           channel      ' on input, channel index
  as ASIOBool       isInput      ' on input
  as ASIOBool       isActive     ' on exit
  as long           channelGroup ' on exit
  as ASIOSampleType type         ' on exit
  as zstring * 32 name           ' on exit
end type

type ASIOBufferInfo field=4
  as ASIOBool isInput     ' on input  ASIOTrue input, else output
  as long     channelNum  ' on input  channel index
  as any ptr  buffers(1)  ' on output double buffer addresses
end type

type ASIOCallbacks field=4
  bufferSwitch         as sub      cdecl (DoubleBufferIndex as long, _
                                          DirectProcess     as ASIOBool)
  sampleRateDidChange  as sub      cdecl (sRate             as ASIOSampleRate)
  asioMessage          as function cdecl (Selector          as long, _
                                          Value             as long, _
                                          pMessage          as any ptr, _
                                          opt               as double ptr) as long
  bufferSwitchTimeInfo as function cdecl (pParams           as ASIOTime ptr, _
                                          DoubleBufferIndex as long, _
                                          DirectProcess     as long) as ASIOTime ptr
end type

enum asioMessageSelectors
  kAsioSelectorSupported = 1
  kAsioEngineVersion
  kAsioResetRequest
  kAsioBufferSizeChange
  kAsioResyncRequest
  kAsioLatenciesChanged
  kAsioSupportsTimeInfo
  kAsioSupportsTimeCode
  kAsioMMCCommand
  kAsioSupportsInputMonitor
  kAsioSupportsInputGain
  kAsioSupportsInputMeter
  kAsioSupportsOutputGain
  kAsioSupportsOutputMeter
  kAsioOverload
  kAsioNumMessageSelectors
end enum

type ASIODriverInfo
  as long          AsioVers     ' asioVersion currently, 2
  as long          DriverVers   ' driverVersion driver specific
  as zstring *  33 name
  as zstring * 124 errorMessage
  as any ptr       sysRef       ' on input: system reference (HWND)
end type

type IASIO
  declare constructor               (pRealInterface   as any ptr)

  declare function Init             (sysHandle         as HWND) as ASIOBool
  declare sub      GetDriverName    (pName             as zstring ptr)
  declare function GetDriverVersion () as integer
  declare sub      GetErrorMessage  (pMsg              as zstring ptr)
  declare function Start            () as ASIOError
  declare function Stop             () as ASIOError
  declare function GetChannels      (numInputChannels  as long ptr, _
                                     numOutputChannels as long ptr) as ASIOError
  declare function GetLatencies     (inputLatency      as long ptr, _
                                     outputLatency     as long ptr) as ASIOError
  declare function GetBufferSize    (minSize           as long ptr, _
                                     maxSize           as long ptr, _
                                     preferredSize     as long ptr, _
                                     granularity       as long ptr) as ASIOError
  declare function CanSampleRate    (sampleRate        as ASIOSampleRate) as ASIOError
  declare function GetSampleRate    (sampleRate        as ASIOSampleRate ptr) as ASIOError
  declare function SetSampleRate    (sampleRate        as ASIOSampleRate) as ASIOError
  declare function GetClockSources  (clocks            as ASIOClockSource ptr, _
                                     numSources        as long ptr) as ASIOError
  declare function SetClockSource   (reference         as long) as ASIOError
  declare function GetSamplePosition(sPos              as ASIOSamples     ptr, _
                                     tStamp            as ASIOTimeStamp   ptr) as ASIOError
  declare function GetChannelInfo   (info              as ASIOChannelInfo ptr) as ASIOError
  declare function CreateBuffers    (bufferInfos       as ASIOBufferInfo  ptr, _
                                     numChannels       as long, _
                                     bufferSize        as long, _
                                     callbacks         as ASIOCallbacks   ptr) as ASIOError
  declare function DisposeBuffers   () as ASIOError
  declare function ControlPanel     () as ASIOError
  declare function Future           (selector          as long, _
                                     opt               as any ptr) as ASIOError
  declare function OutputReady      () as ASIOError

  private:
  enum VT_ASIO
  VT_QueryInterface    =  0
  VT_AddRef            =  4
  VT_Release           =  8
  VT_init              = 12
  VT_getDriverName     = 16
  VT_getDriverVersion  = 20
  VT_getErrorMessage   = 24
  VT_start             = 28
  VT_stop              = 32
  VT_getChannels       = 36
  VT_getLatencies      = 40
  VT_getBufferSize     = 44
  VT_canSampleRate     = 48
  VT_getSampleRate     = 52
  VT_setSampleRate     = 56
  VT_getClockSources   = 60
  VT_setClockSource    = 64
  VT_getSamplePosition = 68
  VT_getChannelInfo    = 72
  VT_createBuffers     = 76
  VT_disposeBuffers    = 80
  VT_controlPanel      = 84
  VT_future            = 88
  VT_outputReady       = 92
  end enum
  as any ptr pAsioThis
end type

' fetch the interface pointer
constructor IASIO(pRealInterface as any ptr)
  DMSG("IASIO()")
  pAsioThis = pRealInterface
end constructor

function IASIO.Init(sysHandle as HWND) as ASIOBool
  DMSG("IASIO.init()")
  if pAsioThis=NULL then beep:return ASIOFalse
  dim as any ptr pThis=pAsioThis
  dim as ASIOBool result
  asm
  push dword ptr [sysHandle]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_init]
  mov [result],eax
  end asm
  return result
end function

sub IASIO.GetDriverName(pName as zstring ptr)
  DMSG("IASIO.getDriverName()")
  if pAsioThis=NULL then beep:return
  if pName    =NULL then beep:return
  dim as any ptr pThis=pAsioThis
  asm 
  push dword ptr [pName]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getDriverName]
  end asm
end sub

function IASIO.GetDriverVersion() as integer
  DMSG("IASIO.getDriverVersion()")
  if pAsioThis=NULL then beep:return 0
  dim as any ptr pThis=pAsioThis
  dim as integer result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getDriverVersion]
  mov  [result],eax
  end asm
  return result
end function

sub IASIO.GetErrorMessage(pMsg as zstring ptr)
  DMSG("IASIO.getErrorMessage()")
  if pAsioThis=NULL then beep
  if pMsg     =NULL then beep
  dim as any ptr pThis=pAsioThis
  asm 
  push dword ptr [pMsg]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getErrorMessage]
  end asm
end sub

function IASIO.Start() as ASIOError
  DMSG("IASIO.start()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_start]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.Stop() as ASIOError
  DMSG("IASIO.stop()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm 
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_stop]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.GetChannels(pInChannels  as long ptr, _
                           pOutChannels as long ptr) as ASIOError
  DMSG("IASIO.getChannels()")
  if pAsioThis   =NULL then beep:return ASE_InvalidParameter
  if pInChannels =NULL then beep:return ASE_InvalidParameter
  if pOutChannels=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOutChannels]
  push dword ptr [pInChannels]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getChannels]
  mov [result],eax
  end asm
  return result
end function
function IASIO.GetLatencies(pInLatency  as long ptr, _
                            pOutLatency as long ptr) as ASIOError
  DMSG("IASIO.getLatencies()")
  if pAsioThis  =NULL then beep:return ASE_InvalidParameter
  if pInLatency =NULL then beep:return ASE_InvalidParameter
  if pOutLatency=NULL then beep:return ASE_InvalidParameter

  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOutLatency]
  push dword ptr [pInLatency]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getLatencies]
  mov [result],eax
  end asm
  return result
end function
function IASIO.GetBufferSize(pMinSize       as long ptr, _
                             pMaxSize       as long ptr, _
                             pPreferredSize as long ptr, _
                             pGranularity   as long ptr) as ASIOError
  DMSG("IASIO.getBufferSize()")
  if pAsioThis     =NULL then beep:return ASE_InvalidParameter
  if pMinSize      =NULL then beep:return ASE_InvalidParameter
  if pMaxSize      =NULL then beep:return ASE_InvalidParameter
  if pPreferredSize=NULL then beep:return ASE_InvalidParameter
  if pGranularity  =NULL then beep:return ASE_InvalidParameter

  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pGranularity]
  push dword ptr [pPreferredSize]
  push dword ptr [pMaxSize]
  push dword ptr [pMinSize]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getBufferSize]
  mov [result],eax
  end asm
  return result
end function

function IASIO.CanSampleRate(SampleRate as ASIOSampleRate) as ASIOError
  DMSG("IASIO.canSampleRate()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [SampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_canSampleRate]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetSampleRate(pSampleRate as ASIOSampleRate ptr) as ASIOError
  DMSG("IASIO.getSampleRate()")
  if pAsioThis  =NULL then beep:return ASE_InvalidParameter
  if pSampleRate=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pSampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getSampleRate]
  mov [result],eax
  end asm
  return result
end function
function IASIO.SetSampleRate(SampleRate as ASIOSampleRate) as ASIOError
  DMSG("IASIO.setSampleRate()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [SampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_setSampleRate]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetClockSources(pClocks   as ASIOClockSource ptr, _
                               pnSources as long ptr) as ASIOError
  DMSG("IASIO.getClockSources()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pClocks  =NULL then beep:return ASE_InvalidParameter
  if pnSources=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pnSources]
  push dword ptr [pClocks]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getClockSources]
  mov [result],eax
  end asm
  return result
end function

function IASIO.SetClockSource(reference as long) as ASIOError
  DMSG("IASIO.setClockSource()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [reference]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_setClockSource]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetSamplePosition(pPos   as ASIOSamples   ptr, _
                                 pStamp as ASIOTimeStamp ptr) as ASIOError
  DMSG("IASIO.getSamplePosition()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pPos     =NULL then beep:return ASE_InvalidParameter
  if pStamp   =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pStamp]
  push dword ptr [pPos]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getSamplePosition]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.GetChannelInfo(pInfo as ASIOChannelInfo ptr) as ASIOError
  DMSG("IASIO.getChannelInfo()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pInfo    =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pInfo]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getChannelInfo]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.CreateBuffers(pInfos     as ASIOBufferInfo ptr, _
                             nChannels  as long, _
                             BufferSize as long, _
                             pCallbacks as ASIOCallbacks ptr) as ASIOError
  DMSG("IASIO.CreateBuffers()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  if pInfos    =NULL then beep:return ASE_InvalidParameter
  if pCallbacks=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pCallbacks]
  push dword ptr [BufferSize]
  push dword ptr [nChannels]
  push dword ptr [pInfos]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_createBuffers]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.DisposeBuffers() as ASIOError
  DMSG("IASIO.DisposeBuffers()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_disposeBuffers]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.ControlPanel() as ASIOError
  DMSG("IASIO.ControlPanel()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_controlPanel]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.Future(selector as long, _
                      pOpt     as any ptr) as ASIOError
  DMSG("IASIO.Future()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pOpt     =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOpt]
  push dword ptr [selector]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_future]
  mov [result],eax
  end asm
  return result
end function

function IASIO.OutputReady() as ASIOError
  DMSG("IASIO.OutputReady()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_outputReady]
  mov  [result],eax
  end asm
  return result
end function

type ASIODRVSTRUCT
  as integer                 drvID
  as CLSID                   ClassID
  as zstring * MAXPATHLEN    dllpath
  as zstring * MAXDRVNAMELEN drvname
  as any ptr                 pRealThis
  as IASIO         ptr       pIAsio
  as ASIODRVSTRUCT ptr  next
end type
type LPASIODRVSTRUCT as ASIODRVSTRUCT ptr


function FindDrvPath (ClassIDstr  as zstring ptr, _
                      DllPath     as zstring ptr, _
                      DllPathSize as integer) as long
  dim as HKEY      hkEnum,hksub,hkpath
  dim as zstring * 512 databuf
  dim as LONG      cr,rc = -1
  dim as DWORD     datatype,datasize
  dim as DWORD     index
  dim as BOOL      found = FALSE
  CharLowerBuff(ClassIDstr,len(*ClassIDstr))
  cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,@hkEnum)
  if (cr = ERROR_SUCCESS) then
    index = 0
    while (cr = ERROR_SUCCESS) and (found=FALSE)
      cr = RegEnumKey(hkEnum,index,cptr(LPTSTR,@databuf),512)
      index+=1
      if (cr = ERROR_SUCCESS) then
        CharLowerBuff(@databuf,sizeof(databuf))
        if (0=lstrcmp(databuf,ClassIDStr)) then
          cr = RegOpenKeyEx(hkEnum,cptr(LPCTSTR,@databuf),0,KEY_READ,@hksub)
          if (cr = ERROR_SUCCESS) then
            cr = RegOpenKeyEx(hksub,cptr(LPCTSTR,@INPROC_SERVER),0,KEY_READ,@hkpath)
            if (cr = ERROR_SUCCESS) then
              datatype = REG_SZ
              datasize = dllpathsize
              cr = RegQueryValueEx(hkpath,0,0,@datatype,cptr(LPBYTE,dllpath),@datasize)
              if (cr = ERROR_SUCCESS) then
                ' ok we found the class id and dll path
                ' !!! be sure the dll file exist !!!
                dim as integer hFile = FreeFile()
                if open(*dllpath,for binary, access read, as #hFile) then
                  beep
                  rc = 1
                else
                  close #hFile
                  rc = 0
                end if
              end if
              RegCloseKey(hkpath)
            end if
            RegCloseKey(hksub)
          end if
          found = TRUE  ' break out 
        end if
      end if
    wend
    RegCloseKey(hkEnum)
  end if
  return rc
end function

function NewDrvStruct(hkey    as HKEY, _
                      KeyName as zstring ptr, _
                      DrvID   as integer, _
                      lpDrv   as LPASIODRVSTRUCT) as LPASIODRVSTRUCT
  dim as HKEY  hksub
  dim as zstring * 256 databuf
  dim as zstring * MAXPATHLEN dllpath
  dim as WORD  wData(99)
  dim as CLSID  clsid
  dim as DWORD  datatype,datasize
  dim as LONG  cr,rc
  if (lpdrv=NULL) then
    cr = RegOpenKeyEx(hkey,cptr(LPCTSTR,keyname),0,KEY_READ,@hksub)
    if (cr = ERROR_SUCCESS) then
      datatype = REG_SZ
       datasize = 256
      cr = RegQueryValueEx(hksub,COM_CLSID,0,@datatype,cptr(LPBYTE,@databuf),@datasize)
      if (cr = ERROR_SUCCESS) then
        rc = findDrvPath (databuf,dllpath,MAXPATHLEN)
        if (rc = 0) then
          lpdrv = new ASIODRVSTRUCT[1]
          if (lpdrv<>NULL) then
            memset(lpdrv,0,sizeof(ASIODRVSTRUCT))
            lpdrv->drvID = drvID
            strcpy(lpdrv->dllpath,@dllpath)
            MultiByteToWideChar(CP_ACP,0,cptr(LPCSTR,@databuf),-1,cptr(LPWSTR,@wData(0)),100)
            cr = CLSIDFromString(cptr(LPOLESTR,@wData(0)),cptr(LPCLSID,@clsid))
            if (cr = S_OK) then
              memcpy(@lpdrv->ClassID,@clsid,sizeof(CLSID))
            end if
            datatype = REG_SZ
            datasize = 256
            cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,@datatype,cptr(LPBYTE,@databuf),@datasize)
            if (cr = ERROR_SUCCESS) then
              strcpy(lpdrv->drvname,@databuf)
            else 
              strcpy(lpdrv->drvname,keyname)
            end if
          end if
        end if
      end if
      RegCloseKey(hksub)
    end if
  else 
   lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next)
  end if
  return lpdrv
end function

sub DeleteDrvStruct (lpdrv as LPASIODRVSTRUCT)
  if (lpdrv<>NULL) then
    deleteDrvStruct(lpdrv->next)
    if (lpdrv->piAsio<>NULL) then
      delete lpdrv->piAsio
    end if
    delete lpdrv
  end if
end sub

function GetDrvStruct(drvID as integer, _
                      lpdrv as LPASIODRVSTRUCT) as LPASIODRVSTRUCT
  while (lpdrv<>NULL) 
    if (lpdrv->drvID = drvID) then return lpdrv
    lpdrv = lpdrv->next
  wend
  return 0
end function

type ASIODRIVERLIST
  declare constructor
  declare destructor
  declare function OpenDriver     (index  as integer, _
                                   ppAsio as IASIO ptr ptr) as long
  declare function CloseDriver    (index  as integer) as long
  declare function GetNumDev      () as long
  declare function GetDriverName  (index     as integer, _
                                   pName     as zstring ptr, _
                                   nMaxChars as integer) as long
  declare function GetDriverPath  (index     as integer, _
                                   pPath     as zstring ptr, _
                                   nMaxChars as integer) as long
  declare function GetDriverCLSID (index     as integer, _
                                   pClassID  as CLSID ptr) as long
  private:
  as LPASIODRVSTRUCT  lpDrvList
  as integer          numdrv
end type
type LPASIODRIVERLIST as ASIODRIVERLIST ptr

constructor ASIODRIVERLIST
  DMSG("ASIODRIVERLIST()")
  dim as HKEY      hkEnum = 0
  dim as zstring * MAXDRVNAMELEN keyname ' char[MAXDRVNAMELEN]
  dim as LPASIODRVSTRUCT  pdl
  dim as LONG       cr
  dim as DWORD      index = 0
  dim as BOOL      fin = FALSE
  numdrv    = 0
  lpdrvlist = 0

  cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,@hkEnum)
  while (cr = ERROR_SUCCESS)
    cr = RegEnumKey(hkEnum,index,cptr(LPTSTR,@keyname),MAXDRVNAMELEN)
    index+=1
    if (cr = ERROR_SUCCESS) then
      lpdrvlist = newDrvStruct(hkEnum,keyname,0,lpdrvlist)
    else 
      fin = TRUE
    end if
  wend
  if (hkEnum) then RegCloseKey(hkEnum)

  pdl = lpdrvlist
  while (pdl)
    numdrv+=1
    pdl = pdl->next
  wend
  if (numdrv>0) then
    if CoInitialize(0)<>S_OK then
      beep
    end if
  end if
end constructor

destructor ASIODRIVERLIST
  if (numdrv>0) then
    deleteDrvStruct(lpdrvlist)
    CoUninitialize()
  end if
  DMSG("ASIODRIVERLIST~")
end destructor

function ASIODRIVERLIST.GetNumDev () as LONG
  DMSG("ASIODRIVERLIST.GetNumDev")
  return numdrv
end function

function ASIODRIVERLIST.OpenDriver(drvID    as integer, _
                                       ppAsioDrv as IASIO ptr ptr) as long
  DMSG("ASIODRIVERLIST.OpenDriver")
  dim as LPASIODRVSTRUCT lpdrv
  dim as integer            rc

  if (ppAsioDrv=NULL) then 
    beep
    return DRVERR_INVALID_PARAM
  end if
  lpdrv = getDrvStruct(drvID,lpdrvlist)
  if (lpdrv<>NULL) then
    if (lpdrv->pRealThis=NULL) then
      rc = CoCreateInstance(@lpdrv->ClassID,0,CLSCTX_INPROC_SERVER,@lpdrv->ClassID,@lpdrv->pRealThis)
      if (rc = S_OK) then
        lpdrv->pIAsio = new IASIO(lpdrv->pRealThis)
        *ppAsioDrv = lpdrv->pIAsio
        return 0
      else
        beep
      end if
    else 
      rc = DRVERR_DEVICE_ALREADY_OPEN
    end if
  else 
    rc = DRVERR_DEVICE_NOT_FOUND
  end if
  return rc
end function

function ASIODRIVERLIST.CloseDriver(drvID as integer) as long
  DMSG("ASIODRIVERLIST.CloseDriver")
  dim as LPASIODRVSTRUCT  lpdrv
  dim as IASIO ptr        piAsio
  lpdrv = getDrvStruct(drvID,lpdrvlist)
  if (lpdrv<>NULL) then
    if (lpdrv->pIAsio<>NULL) then
      lpdrv->pIAsio    = NULL
      lpdrv->pRealThis = NULL
    end if
  end if
  return 0
end function

function ASIODRIVERLIST.GetDriverName (DrvID    as integer, _
                                       pDrvName as zstring ptr, _
                                       NameSize as integer) as long
  DMSG("ASIODRIVERLIST.GetDriverName")
  dim as LPASIODRVSTRUCT pDrv

  if (pDrvName = NULL) then beep:return DRVERR_INVALID_PARAM
  if (NameSize < 5   ) then beep:return DRVERR_INVALID_PARAM

  pDrv = getDrvStruct(drvID,lpdrvlist)
  if (pDrv<>NULL) then
    if (len(pDrv->DrvName) < NameSize) then
      strcpy(pDrvName,pDrv->DrvName)
    else 
      memcpy(pDrvName,@pDrv->DrvName,NameSize-4)
      pDrvName[NameSize-4] = "."
      pDrvName[NameSize-3] = "."
      pDrvName[NameSize-2] = "."
      pDrvName[NameSize-1] = 0
    end if
    return 0
  else
    beep
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.GetDriverPath (DrvID       as integer, _
                                       DllPath     as zstring ptr, _
                                       DllPathsize as integer) as long
  DMSG("ASIODRIVERLIST.GetDriverPath")
  dim as LPASIODRVSTRUCT lpdrv

  if (dllpath=NULL) then 
    beep
    return DRVERR_INVALID_PARAM
  end if

  lpdrv = getDrvStruct(drvID,lpdrvlist)
  if (lpdrv<>NULL) then
    if (sizeof(lpdrv->dllpath) < =dllpathsize) then
      strcpy(dllpath,lpdrv->dllpath)
      return 0
    end if
    dllpath[0] = 0
    return DRVERR_INVALID_PARAM
  else
    beep
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.GetDriverCLSID (drvID    as integer, _
                                        pClassID as CLSID ptr) as long 
  DMSG("ASIODRIVERLIST.GetDriverCLSID")
  dim as LPASIODRVSTRUCT lpdrv
  if (pClassID = NULL) then return DRVERR_INVALID_PARAM
  lpdrv = getDrvStruct(drvID,lpdrvlist)
  if (lpdrv <>NULL) then
    memcpy(pClassID,@lpdrv->ClassID,sizeof(CLSID))
    return 0
  else
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function



dim as zstring * MAXDRVNAMELEN DrvName
dim as zstring * MAXPATHLEN    DrvPath
dim as IASIO ptr      Asio
dim as CLSID          ClassID
dim as ASIODRIVERLIST DrvList
dim as ASIOSampleRate SampleRate
dim as integer        nDrv,nInChannels,nOutChannels
dim as integer        MinSize,MaxSize,PreferredSize,Granularity 

nDrv = DrvList.GetNumDev
if nDrv<1 then
  print "sorry no ASIO driver installed !"
  beep:sleep:end 1
end if

for i as integer=0 to nDrv-1
  DrvList.GetDriverPath (i,@DrvPath,sizeof(DrvPath))
  DrvList.GetDriverName (i,@DrvName,sizeof(DrvName))
  DrvList.GetDriverCLSID(i,@ClassID)
  print "[" & i & "] " & DrvPath
  print "[" & i & "] " & DrvName
  print "[" & i & "] " & hex(ClassID.Data1,8) & "-" _
                       & hex(ClassID.Data2,4) & "-" _
                       & hex(ClassID.Data3,4) & "-" _
                       & hex(ClassID.Data4(0),2) & hex(ClassID.Data4(1),2) & "-" _
                       & hex(ClassID.Data4(2),2) & hex(ClassID.Data4(3),2) _
                       & hex(ClassID.Data4(4),2) & hex(ClassID.Data4(5),2) _
                       & hex(ClassID.Data4(6),2) & hex(ClassID.Data4(7),2)
  DrvList.OpenDriver(i,@ASIO)
  if ASIO<>NULL then
    ? "Asio.Init(NULL)   " & Asio->Init(NULL)
    Asio->getChannels(@nInChannels,@nOutChannels)
    ? "capture  channels " & nInChannels
    ? "playback channels " & nOutChannels

    Asio->getSampleRate(@SampleRate)
    ? "Samplerate        " & SampleRate
    Asio->getBufferSize(@MinSize, _
                        @MaxSize, _
                        @PreferredSize, _
                        @Granularity)
    ? "MinSize           " & MinSize
    ? "MaxSize           " & MaxSize
    ? "PreferredSize     " & PreferredSize
    if Granularity<>-1 then
      ? "Granularity       " & Granularity
    end if
    ? "press any key to open the conrol panel"
    sleep
    Asio->controlPanel()
    print "ok"
  end if
  DrvList.CloseDriver(i)
next
sleep
Image
Last edited by D.J.Peters on Oct 03, 2017 5:32, edited 5 times in total.
D.J.Peters
Posts: 8631
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

With ASIO 4 ALL you can use your "normal" WDM Soundcard as ASIO device too.

it's for 98SE/ME/2k/XP/MCE/2003/XP64 and Vista/Windows 7 x86/x64

http://www.asio4all.com/

Joshy
D.J.Peters
Posts: 8631
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

First test with capture / play callback.

Joshy
file: "FBASIO.bi"

Code: Select all

#ifndef __FBASIO_BI__
#define __FBASIO_BI__

#define WIN_INCLUDEALL
#include "windows.bi"
#include "crt\string.bi"

#ifdef ASIODEBUG
# ifdef UNICODE
#  define STRING_T WSTRING
#  define MKSTRING(s) wstr(s)
# else
#  define STRING_T ZSTRING
#  define MKSTRING(s) (s)
# endif
  dim shared as STRING_T * 1024 Buffer
  ' DbgPrint a messages
  #macro ASIO_DEBUG_MSG(msg)
    wsprintf(@Buffer,@MKSTRING("%s"),@MKSTRING(msg))
    OutputDebugString(@Buffer)
  #endmacro
#else
# define ASIO_DEBUG_MSG(msg)
#endif


#define MAXPATHLEN 512
#define MAXDRVNAMELEN 128

#define DRVERR_OK                  (0)
#define DRVERR_INVALID_PARAM       (DRVERR_OK-1)
#define DRVERR_DEVICE_ALREADY_OPEN (DRVERR_OK-2)
#define DRVERR_DEVICE_NOT_FOUND    (DRVERR_OK-3)
#define DRVERR_DEVICE_NOT_CREATED  (DRVERR_OK-4)

#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#define ASIODRV_DESC  "description"
#define INPROC_SERVER "InprocServer32"
#define ASIO_PATH     "software\\asio"
#define COM_CLSID     "clsid"

type ASIOSamples field=4
  as uinteger hi ' note: hi,lo order
  as uinteger lo
end type

type ASIOTimeStamp field=4
  as uinteger hi ' note: hi,lo order
  as uinteger lo
end type

type ASIOSampleRate as double

enum ASIOBool
  ASIOFalse
  ASIOTrue
end enum

' Sample Types are expressed as long
enum ASIOSampleType
  ASIOSTInt16MSB    =  0
  ASIOSTInt24MSB    =  1    ' used for 20 bits as well
  ASIOSTInt32MSB    =  2
  ASIOSTFloat32MSB  =  3    ' IEEE 754 32 bit float
  ASIOSTFloat64MSB  =  4    ' IEEE 754 64 bit double float
  ' these are used for 32 bit data buffer, with different alignment of the data inside
  ' 32 bit PCI bus systems can be more easily used with these
  ASIOSTInt32MSB16  =  8    ' 32 bit data with 16 bit alignment
  ASIOSTInt32MSB18  =  9    ' 32 bit data with 18 bit alignment
  ASIOSTInt32MSB20  = 10    ' 32 bit data with 20 bit alignment
  ASIOSTInt32MSB24  = 11    ' 32 bit data with 24 bit alignment
  ASIOSTInt16LSB    = 16
  ASIOSTInt24LSB    = 17    ' used for 20 bits as well
  ASIOSTInt32LSB    = 18
  ASIOSTFloat32LSB  = 19    ' IEEE 754 32 bit float, as found on Intel x86 architecture
  ASIOSTFloat64LSB  = 20    ' IEEE 754 64 bit double float, as found on Intel x86 architecture
  ' 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
  ASIOSTInt32LSB16  = 24    ' 32 bit data with 18 bit alignment
  ASIOSTInt32LSB18  = 25    ' 32 bit data with 18 bit alignment
  ASIOSTInt32LSB20  = 26    ' 32 bit data with 20 bit alignment
  ASIOSTInt32LSB24  = 27    ' 32 bit data with 24 bit alignment
  '  ASIO DSD format.
  ASIOSTDSDInt8LSB1 = 32    ' DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
  ASIOSTDSDInt8MSB1 = 33    ' DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
  ASIOSTDSDInt8NER8 = 40    ' DSD 8 bit data, 1 sample per byte. No Endianness required.
  ASIOSTLastEntry
end enum

function ASIOSampleTypeString(typ as ASIOSampleType) as string
  select case typ
  case ASIOSTInt16MSB   :return "Int 16 MSB"
  case ASIOSTInt24MSB   :return "Int 24 MSB"
  case ASIOSTInt32MSB   :return "Int 32 MSB"
  case ASIOSTFloat32MSB :return "Float 32 MSB"
  case ASIOSTFloat64MSB :return "Float 64 MSB"
  case ASIOSTInt32MSB16 :return "Int 32 MSB 16"
  case ASIOSTInt32MSB18 :return "Int 32 MSB 18"
  case ASIOSTInt32MSB20 :return "Int 32 MSB 20"
  case ASIOSTInt32MSB24 :return "Int 32 MSB 24"
  case ASIOSTInt16LSB   :return "Int 16 LSB"
  case ASIOSTInt24LSB   :return "Int 24 LSB"
  case ASIOSTInt32LSB   :return "Int 32 LSB"
  case ASIOSTFloat32LSB :return "Float 32 LSB"
  case ASIOSTFloat64LSB :return "Float 64 LSB"
  case ASIOSTInt32LSB16 :return "Int 32 LSB 16"
  case ASIOSTInt32LSB18 :return "Int 32 LSB 18"
  case ASIOSTInt32LSB20 :return "Int 32 LSB 20"
  case ASIOSTInt32LSB24 :return "Int 32 LSB 24"
  case ASIOSTDSDInt8LSB1:return "DSD Int 8 LSB 1"
  case ASIOSTDSDInt8MSB1:return "DSD Int 8 MSB 1"
  case ASIOSTDSDInt8NER8:return "DSD Int 8 NER 8"
  case else
    return "UNKNOW SAMPLE TYPE" & str(typ)
  end select
end function

enum ASIOError
  ASE_OK = 0               ' This value will be returned whenever the call succeeded
  ASE_SUCCESS = &H3f4847a0 ' unique success return value for ASIOFuture calls
  ASE_NotPresent = -1000   ' hardware input or output is not present or available
  ASE_HWMalfunction        ' hardware is malfunctioning (can be returned by any ASIO function)
  ASE_InvalidParameter     ' input parameter invalid
  ASE_InvalidMode          ' hardware is in a bad mode or used in a bad mode
  ASE_SPNotAdvancing       ' hardware is not running when sample position is inquired
  ASE_NoClock              ' sample clock or rate cannot be determined or is not present
  ASE_NoMemory             ' not enough memory for completing the request
end enum

enum ASIOTimeCodeFlags
  kTcValid                = 1 shl 0
  kTcRunning              = 1 shl 1
  kTcReverse              = 1 shl 2
  kTcOnspeed              = 1 shl 3
  kTcStill                = 1 shl 4
  kTcSpeedValid           = 1 shl 8
end enum
type ASIOTimeCode field=4
  as double            speed           ' speed relation (fraction of nominal speed)
  as ASIOSamples       timeCodeSamples ' time in samples
  as ASIOTimeCodeFlags flags           ' some information flags
  as ubyte future(63)
end type

enum AsioTimeInfoFlags
  kSystemTimeValid        = 1 shl 0   ' must always be valid
  kSamplePositionValid    = 1 shl 1   ' must always be valid
  kSampleRateValid        = 1 shl 2
  kSpeedValid             = 1 shl 3
  kSampleRateChanged      = 1 shl 4
  kClockSourceChanged     = 1 shl 5
end enum
type AsioTimeInfo field=4
  as double            speed          ' absolute speed (1. = nominal)
  as ASIOTimeStamp     systemTime     ' system time related to samplePosition, in nanoseconds
  as ASIOSamples       samplePosition
  as ASIOSampleRate    sampleRate     ' current rate
  as AsioTimeInfoFlags flags
  as ubyte reserved(11)
end type

type ASIOTime field=4            ' both input/output
  as long         reserved(3)    ' must be 0
  as AsioTimeInfo timeInfo       ' required
  as ASIOTimeCode timeCode       ' optional, evaluated if (timeCode.flags and kTcValid)
end type

type ASIOClockSource field=4
  as long         index             ' as used for ASIOSetClockSource()
  as long         associatedChannel ' for instance, S/PDIF or AES/EBU
  as long         associatedGroup   ' see channel groups (ASIOGetChannelInfo())
  as ASIOBool     isCurrentSource   ' ASIOTrue if this is the current clock source
  as zstring * 32 name              ' for user selection
end type

type ASIOChannelInfo field=4
  as long           channel      ' on input, channel index
  as ASIOBool       isInput      ' on input
  as ASIOBool       isActive     ' on exit
  as long           channelGroup ' on exit
  as ASIOSampleType type         ' on exit
  as zstring * 32   name         ' on exit
end type

type ASIOBufferInfo field=4
  as ASIOBool isInput     ' on input  ASIOTrue input, else output
  as long     channelNum  ' on input  channel index
  as any ptr  buffers(1)  ' on output double buffer addresses
end type

type ASIOCallbacks field=4
  BufferSwitchProc     as sub      cdecl (BufferIndex as long, _
                                          ProcessIt   as ASIOBool)
  SampleRateChangedProc as sub      cdecl (sRate       as ASIOSampleRate)
  MessageProc           as function cdecl (Selector    as long, _
                                          Value       as long, _
                                          pMsg        as any ptr, _
                                          pOpt        as double ptr) as long
  BufferSwitchTimeInfoProc as function cdecl (pParams     as ASIOTime ptr, _
                                          BufferIndex as long, _
                                          ProcessIt   as ASIOBool) as ASIOTime ptr
end type

enum asioMessageSelectors
  kAsioSelectorSupported = 1
  kAsioEngineVersion
  kAsioResetRequest
  kAsioBufferSizeChange
  kAsioResyncRequest
  kAsioLatenciesChanged
  kAsioSupportsTimeInfo
  kAsioSupportsTimeCode
  kAsioMMCCommand
  kAsioSupportsInputMonitor
  kAsioSupportsInputGain
  kAsioSupportsInputMeter
  kAsioSupportsOutputGain
  kAsioSupportsOutputMeter
  kAsioOverload
  kAsioNumMessageSelectors
end enum

type ASIODriverInfo
  as long          AsioVers     ' asioVersion currently, 2
  as long          DriverVers   ' driverVersion driver specific
  as zstring *  33 name
  as zstring * 124 errorMessage
  as any ptr       sysRef       ' on input: system reference (HWND)
end type

type IASIO
  declare constructor               (pRealInterface   as any ptr)

  declare function Init             (sysHandle         as HWND) as ASIOBool
  declare sub      GetDriverName    (pName             as zstring ptr)
  declare function GetDriverVersion () as integer
  declare sub      GetErrorMessage  (pMsg              as zstring ptr)
  declare function Start            () as ASIOError
  declare function Stop             () as ASIOError
  declare function GetChannels      (numInputChannels  as long ptr, _
                                     numOutputChannels as long ptr) as ASIOError
  declare function GetLatencies     (inputLatency      as long ptr, _
                                     outputLatency     as long ptr) as ASIOError
  declare function GetBufferSize    (minSize           as long ptr, _
                                     maxSize           as long ptr, _
                                     preferredSize     as long ptr, _
                                     granularity       as long ptr) as ASIOError
  declare function CanSampleRate    (sampleRate        as ASIOSampleRate) as ASIOError
  declare function GetSampleRate    (sampleRate        as ASIOSampleRate ptr) as ASIOError
  declare function SetSampleRate    (sampleRate        as ASIOSampleRate) as ASIOError
  declare function GetClockSources  (clocks            as ASIOClockSource ptr, _
                                     numSources        as long ptr) as ASIOError
  declare function SetClockSource   (reference         as long) as ASIOError
  declare function GetSamplePosition(sPos              as ASIOSamples     ptr, _
                                     tStamp            as ASIOTimeStamp   ptr) as ASIOError
  declare function GetChannelInfo   (info              as ASIOChannelInfo ptr) as ASIOError
  declare function CreateBuffers    (bufferInfos       as ASIOBufferInfo  ptr, _
                                     numChannels       as long, _
                                     bufferSize        as long, _
                                     callbacks         as ASIOCallbacks   ptr) as ASIOError
  declare function DisposeBuffers   () as ASIOError
  declare function ControlPanel     () as ASIOError
  declare function Future           (selector          as long, _
                                     opt               as any ptr) as ASIOError
  declare function OutputReady      () as ASIOError

  private:
  enum VT_ASIO
  VT_QueryInterface    =  0
  VT_AddRef            =  4
  VT_Release           =  8
  VT_init              = 12
  VT_getDriverName     = 16
  VT_getDriverVersion  = 20
  VT_getErrorMessage   = 24
  VT_start             = 28
  VT_stop              = 32
  VT_getChannels       = 36
  VT_getLatencies      = 40
  VT_getBufferSize     = 44
  VT_canSampleRate     = 48
  VT_getSampleRate     = 52
  VT_setSampleRate     = 56
  VT_getClockSources   = 60
  VT_setClockSource    = 64
  VT_getSamplePosition = 68
  VT_getChannelInfo    = 72
  VT_createBuffers     = 76
  VT_disposeBuffers    = 80
  VT_controlPanel      = 84
  VT_future            = 88
  VT_outputReady       = 92
  end enum
  as any ptr pAsioThis
end type

' fetch the interface pointer
constructor IASIO(pRealInterface as any ptr)
  ASIO_DEBUG_MSG("IASIO()")
  pAsioThis = pRealInterface
end constructor

function IASIO.Init(sysHandle as HWND) as ASIOBool
  ASIO_DEBUG_MSG("IASIO.init()")
  if pAsioThis=NULL then beep:return ASIOFalse
  dim as any ptr pThis=pAsioThis
  dim as ASIOBool result
  asm
  push dword ptr [sysHandle]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_init]
  mov [result],eax
  end asm
  return result
end function

sub IASIO.GetDriverName(pName as zstring ptr)
  ASIO_DEBUG_MSG("IASIO.getDriverName()")
  if pAsioThis=NULL then beep:return
  if pName    =NULL then beep:return
  dim as any ptr pThis=pAsioThis
  asm 
  push dword ptr [pName]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getDriverName]
  end asm
end sub

function IASIO.GetDriverVersion() as integer
  ASIO_DEBUG_MSG("IASIO.getDriverVersion()")
  if pAsioThis=NULL then beep:return 0
  dim as any ptr pThis=pAsioThis
  dim as integer result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getDriverVersion]
  mov  [result],eax
  end asm
  return result
end function

sub IASIO.GetErrorMessage(pMsg as zstring ptr)
  ASIO_DEBUG_MSG("IASIO.getErrorMessage()")
  if pAsioThis=NULL then beep
  if pMsg     =NULL then beep
  dim as any ptr pThis=pAsioThis
  asm 
  push dword ptr [pMsg]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getErrorMessage]
  end asm
end sub

function IASIO.Start() as ASIOError
  ASIO_DEBUG_MSG("IASIO.start()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_start]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.Stop() as ASIOError
  ASIO_DEBUG_MSG("IASIO.stop()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm 
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_stop]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.GetChannels(pInChannels  as long ptr, _
                           pOutChannels as long ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getChannels()")
  if pAsioThis   =NULL then beep:return ASE_InvalidParameter
  if pInChannels =NULL then beep:return ASE_InvalidParameter
  if pOutChannels=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOutChannels]
  push dword ptr [pInChannels]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getChannels]
  mov [result],eax
  end asm
  return result
end function
function IASIO.GetLatencies(pInLatency  as long ptr, _
                            pOutLatency as long ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getLatencies()")
  if pAsioThis  =NULL then beep:return ASE_InvalidParameter
  if pInLatency =NULL then beep:return ASE_InvalidParameter
  if pOutLatency=NULL then beep:return ASE_InvalidParameter

  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOutLatency]
  push dword ptr [pInLatency]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getLatencies]
  mov [result],eax
  end asm
  return result
end function
function IASIO.GetBufferSize(pMinSize       as long ptr, _
                             pMaxSize       as long ptr, _
                             pPreferredSize as long ptr, _
                             pGranularity   as long ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getBufferSize()")
  if pAsioThis     =NULL then beep:return ASE_InvalidParameter
  if pMinSize      =NULL then beep:return ASE_InvalidParameter
  if pMaxSize      =NULL then beep:return ASE_InvalidParameter
  if pPreferredSize=NULL then beep:return ASE_InvalidParameter
  if pGranularity  =NULL then beep:return ASE_InvalidParameter

  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pGranularity]
  push dword ptr [pPreferredSize]
  push dword ptr [pMaxSize]
  push dword ptr [pMinSize]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getBufferSize]
  mov [result],eax
  end asm
  return result
end function

function IASIO.CanSampleRate(SampleRate as ASIOSampleRate) as ASIOError
  ASIO_DEBUG_MSG("IASIO.canSampleRate()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [SampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_canSampleRate]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetSampleRate(pSampleRate as ASIOSampleRate ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getSampleRate()")
  if pAsioThis  =NULL then beep:return ASE_InvalidParameter
  if pSampleRate=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pSampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getSampleRate]
  mov [result],eax
  end asm
  return result
end function
function IASIO.SetSampleRate(SampleRate as ASIOSampleRate) as ASIOError
  ASIO_DEBUG_MSG("IASIO.setSampleRate()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [SampleRate]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_setSampleRate]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetClockSources(pClocks   as ASIOClockSource ptr, _
                               pnSources as long ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getClockSources()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pClocks  =NULL then beep:return ASE_InvalidParameter
  if pnSources=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pnSources]
  push dword ptr [pClocks]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getClockSources]
  mov [result],eax
  end asm
  return result
end function

function IASIO.SetClockSource(reference as long) as ASIOError
  ASIO_DEBUG_MSG("IASIO.setClockSource()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [reference]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_setClockSource]
  mov [result],eax
  end asm
  return result
end function

function IASIO.GetSamplePosition(pPos   as ASIOSamples   ptr, _
                                 pStamp as ASIOTimeStamp ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getSamplePosition()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pPos     =NULL then beep:return ASE_InvalidParameter
  if pStamp   =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pStamp]
  push dword ptr [pPos]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getSamplePosition]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.GetChannelInfo(pInfo as ASIOChannelInfo ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.getChannelInfo()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pInfo    =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pInfo]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_getChannelInfo]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.CreateBuffers(pInfos     as ASIOBufferInfo ptr, _
                             nChannels  as long, _
                             BufferSize as long, _
                             pCallbacks as ASIOCallbacks ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.CreateBuffers()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  if pInfos    =NULL then beep:return ASE_InvalidParameter
  if pCallbacks=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pCallbacks]
  push dword ptr [BufferSize]
  push dword ptr [nChannels]
  push dword ptr [pInfos]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_createBuffers]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.DisposeBuffers() as ASIOError
  ASIO_DEBUG_MSG("IASIO.DisposeBuffers()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_disposeBuffers]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.ControlPanel() as ASIOError
  ASIO_DEBUG_MSG("IASIO.ControlPanel()")
  if pAsioThis =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_controlPanel]
  mov  [result],eax
  end asm
  return result
end function

function IASIO.Future(selector as long, _
                      pOpt     as any ptr) as ASIOError
  ASIO_DEBUG_MSG("IASIO.Future()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  if pOpt     =NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  push dword ptr [pOpt]
  push dword ptr [selector]
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_future]
  mov [result],eax
  end asm
  return result
end function

function IASIO.OutputReady() as ASIOError
  ASIO_DEBUG_MSG("IASIO.OutputReady()")
  if pAsioThis=NULL then beep:return ASE_InvalidParameter
  dim as any ptr pThis=pAsioThis
  dim as ASIOError result
  asm
  mov  ecx,[pThis]
  mov  eax,[ecx]
  call [eax+VT_outputReady]
  mov  [result],eax
  end asm
  return result
end function

type ASIODRVSTRUCT
  as integer                 drvID
  as CLSID                   ClassID
  as zstring * MAXPATHLEN    dllpath
  as zstring * MAXDRVNAMELEN drvname
  as any ptr                 pRealThis
  as IASIO         ptr       pIAsio
  as ASIODRVSTRUCT ptr  next
end type
type LPASIODRVSTRUCT as ASIODRVSTRUCT ptr


function FindDrvPath (ClassIDstr  as zstring ptr, _
                      DllPath     as zstring ptr, _
                      DllPathSize as integer) as long
  dim as HKEY      hkEnum,hksub,hkpath
  dim as zstring * 512 databuf
  dim as LONG      cr,rc = -1
  dim as DWORD     datatype,datasize
  dim as DWORD     index
  dim as BOOL      found = FALSE
  CharLowerBuff(ClassIDstr,len(*ClassIDstr))
  cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,@hkEnum)
  if (cr = ERROR_SUCCESS) then
    index = 0
    while (cr = ERROR_SUCCESS) and (found=FALSE)
      cr = RegEnumKey(hkEnum,index,cptr(LPTSTR,@databuf),512)
      index+=1
      if (cr = ERROR_SUCCESS) then
        CharLowerBuff(@databuf,sizeof(databuf))
        if (0=lstrcmp(databuf,ClassIDStr)) then
          cr = RegOpenKeyEx(hkEnum,cptr(LPCTSTR,@databuf),0,KEY_READ,@hksub)
          if (cr = ERROR_SUCCESS) then
            cr = RegOpenKeyEx(hksub,cptr(LPCTSTR,@INPROC_SERVER),0,KEY_READ,@hkpath)
            if (cr = ERROR_SUCCESS) then
              datatype = REG_SZ
              datasize = dllpathsize
              cr = RegQueryValueEx(hkpath,0,0,@datatype,cptr(LPBYTE,dllpath),@datasize)
              if (cr = ERROR_SUCCESS) then
                ' ok we found the class id and dll path
                ' !!! be sure the dll file exist !!!
                dim as integer hFile = FreeFile()
                if open(*dllpath,for binary, access read, as #hFile) then
                  beep
                  rc = 1
                else
                  close #hFile
                  rc = 0
                end if
              end if
              RegCloseKey(hkpath)
            end if
            RegCloseKey(hksub)
          end if
          found = TRUE  ' break out 
        end if
      end if
    wend
    RegCloseKey(hkEnum)
  end if
  return rc
end function

function NewDrvStruct(hkey    as HKEY, _
                      KeyName as zstring ptr, _
                      DrvID   as integer, _
                      pDrv    as LPASIODRVSTRUCT) as LPASIODRVSTRUCT
  dim as HKEY  hksub
  dim as zstring * 256 databuf
  dim as zstring * MAXPATHLEN DllPath
  dim as WORD   wData(99)
  dim as CLSID  clsid
  dim as DWORD  datatype,datasize
  dim as LONG    cr,rc

  if (pDrv=NULL) then
    cr = RegOpenKeyEx(hkey,cptr(LPCTSTR,KeyName),0,KEY_READ,@hksub)
    if (cr = ERROR_SUCCESS) then
      datatype = REG_SZ
      datasize = 256
      cr = RegQueryValueEx(hksub,COM_CLSID,0,@DataType,cptr(LPBYTE,@DataBuf),@DataSize)
      if (cr = ERROR_SUCCESS) then
        rc = FindDrvPath(DataBuf,DllPath,MAXPATHLEN)
        if (rc = 0) then
          pDrv = new ASIODRVSTRUCT
          if (pDrv<>NULL) then
            memset(pDrv,0,sizeof(ASIODRVSTRUCT))
            pDrv->DrvID = DrvID
            strcpy(pDrv->DllPath,@DllPath)
            MultiByteToWideChar(CP_ACP,0,cptr(LPCSTR,@DataBuf),-1,cptr(LPWSTR,@wData(0)),100)
            cr = CLSIDFromString(cptr(LPOLESTR,@wData(0)),cptr(LPCLSID,@clsid))
            if (cr = S_OK) then
              memcpy(@pDrv->ClassID,@clsid,sizeof(CLSID))
            end if
            datatype = REG_SZ
            datasize = 256
            cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,@datatype,cptr(LPBYTE,@databuf),@datasize)
            if (cr = ERROR_SUCCESS) then
              strcpy(pDrv->DrvName,@DataBuf)
            else 
              strcpy(pDrv->DrvName,KeyName)
            end if
          end if
        end if
      end if
      RegCloseKey(hksub)
    end if
  else 
   pDrv->next = newDrvStruct(hkey,keyname,DrvID+1,pDrv->next)
  end if
  return pDrv
end function

sub DeleteDrvStruct (pDrv as LPASIODRVSTRUCT)
  if (pDrv<>NULL) then
    deleteDrvStruct(pDrv->next)
    if (pDrv->piAsio<>NULL) then
      delete pDrv->piAsio
      pDrv->piAsio = NULL
    end if
    delete pDrv
  end if
end sub

function GetDrvStruct(drvID as integer, _
                      lpdrv as LPASIODRVSTRUCT) as LPASIODRVSTRUCT
  while (lpdrv<>NULL) 
    if (lpdrv->drvID = drvID) then return lpdrv
    lpdrv = lpdrv->next
  wend
  return 0
end function

type ASIODRIVERLIST
  declare constructor
  declare destructor
  declare function OpenDriver     (index  as integer, _
                                   ppAsio as IASIO ptr ptr) as long
  declare function CloseDriver    (index  as integer) as long
  declare function GetNumDev      () as long
  declare function GetDriverName  (index     as integer, _
                                   pName     as zstring ptr, _
                                   nMaxChars as integer) as long
  declare function GetDriverPath  (index     as integer, _
                                   pPath     as zstring ptr, _
                                   nMaxChars as integer) as long
  declare function GetDriverCLSID (index     as integer, _
                                   pClassID  as CLSID ptr) as long
  private:
  as LPASIODRVSTRUCT  lpDrvList
  as integer          numdrv
end type
type LPASIODRIVERLIST as ASIODRIVERLIST ptr

constructor ASIODRIVERLIST
  ASIO_DEBUG_MSG("ASIODRIVERLIST()")
  dim as HKEY      hkEnum = 0
  dim as zstring * MAXDRVNAMELEN KeyName
  dim as LPASIODRVSTRUCT  pdl
  dim as LONG      cr
  dim as DWORD     index = 0
  dim as BOOL      fin = FALSE
  NumDrv    = 0
  lpDrvList = 0

  cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,@hkEnum)
  while (cr = ERROR_SUCCESS)
    cr = RegEnumKey(hkEnum,index,cptr(LPTSTR,@keyname),MAXDRVNAMELEN)
    index+=1
    if (cr = ERROR_SUCCESS) then
      lpDrvList = newDrvStruct(hkEnum,KeyName,0,lpDrvList)
    else 
      fin = TRUE
    end if
  wend
  if (hkEnum) then RegCloseKey(hkEnum)

  pdl = lpDrvList
  while (pdl)
    NumDrv+=1
    pdl = pdl->next
  wend
  if (NumDrv>0) then
    if CoInitialize(0)<>S_OK then
      beep
    end if
  end if
end constructor

destructor ASIODRIVERLIST
  if (NumDrv>0) then
    deleteDrvStruct(lpDrvList)
    CoUninitialize()
  end if
  ASIO_DEBUG_MSG("ASIODRIVERLIST~")
end destructor

function ASIODRIVERLIST.GetNumDev () as LONG
  ASIO_DEBUG_MSG("ASIODRIVERLIST.GetNumDev")
  return NumDrv
end function

function ASIODRIVERLIST.OpenDriver(DrvID    as integer, _
                                   ppAsioDrv as IASIO ptr ptr) as long
  ASIO_DEBUG_MSG("ASIODRIVERLIST.OpenDriver")
  if (ppAsioDrv=NULL) then return DRVERR_INVALID_PARAM

  dim as LPASIODRVSTRUCT pDrv = getDrvStruct(DrvID,lpDrvList)
  if (pDrv<>NULL) then
    if (pDrv->pRealThis=NULL) then
      dim as integer rc = CoCreateInstance(@pDrv->ClassID, _
                                           0, _
                                           CLSCTX_INPROC_SERVER, _
                                           @pDrv->ClassID, _
                                           @pDrv->pRealThis)
      if (rc = S_OK) then
        pDrv->pIAsio = new IASIO(pDrv->pRealThis)
        *ppAsioDrv = pDrv->pIAsio
        return DRVERR_OK
      else
        *ppAsioDrv=NULL
        return DRVERR_DEVICE_NOT_CREATED
      end if
    else 
      return DRVERR_DEVICE_ALREADY_OPEN
    end if
  else 
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.CloseDriver(DrvID as integer) as long
  ASIO_DEBUG_MSG("ASIODRIVERLIST.CloseDriver")
  dim as LPASIODRVSTRUCT pDrv = getDrvStruct(DrvID,lpDrvList)
  if (pDrv<>NULL) then
    if (pDrv->pIAsio<>NULL) then
      delete pDrv->pIAsio
      pDrv->pIAsio    = NULL
      pDrv->pRealThis = NULL
      return DRVERR_OK
    else
      return DRVERR_DEVICE_NOT_CREATED
    end if
  else
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.GetDriverName (DrvID    as integer    , _
                                       pDrvName as zstring ptr, _
                                       NameSize as integer) as long
  ASIO_DEBUG_MSG("ASIODRIVERLIST.GetDriverName")
  dim as LPASIODRVSTRUCT pDrv
  if (pDrvName = NULL) then beep:return DRVERR_INVALID_PARAM
  if (NameSize < 5   ) then beep:return DRVERR_INVALID_PARAM
  pDrv = getDrvStruct(drvID,lpdrvlist)
  if (pDrv<>NULL) then
    if (len(pDrv->DrvName) < NameSize) then
      strcpy(pDrvName,pDrv->DrvName)
    else 
      memcpy(pDrvName,@pDrv->DrvName,NameSize-4)
      pDrvName[NameSize-4] = "."
      pDrvName[NameSize-3] = "."
      pDrvName[NameSize-2] = "."
      pDrvName[NameSize-1] = 0
    end if
    return DRVERR_OK
  else
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.GetDriverPath (DrvID       as integer, _
                                       DllPath     as zstring ptr, _
                                       DllPathsize as integer) as long
  ASIO_DEBUG_MSG("ASIODRIVERLIST.GetDriverPath")
  if (dllpath=NULL) then 
    return DRVERR_INVALID_PARAM
  end if
  dim as LPASIODRVSTRUCT pDrv = getDrvStruct(DrvID,lpDrvList)
  if (pDrv<>NULL) then
    if (sizeof(pDrv->DllPath) <= DllPathSize) then
      strcpy(DllPath,pDrv->DllPath)
      return DRVERR_OK
    else
      dllpath[0] = 0
      return DRVERR_INVALID_PARAM
    end if
  else
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

function ASIODRIVERLIST.GetDriverCLSID (DrvID    as integer, _
                                        pClassID as CLSID ptr) as long 
  ASIO_DEBUG_MSG("ASIODRIVERLIST.GetDriverCLSID")
  if (pClassID = NULL) then return DRVERR_INVALID_PARAM
  dim as LPASIODRVSTRUCT pDrv = getDrvStruct(DrvID,lpDrvList)
  if (pDrv<>NULL) then
    memcpy(pClassID,@pDrv->ClassID,sizeof(CLSID))
    return DRVERR_OK
  else
    return DRVERR_DEVICE_NOT_FOUND
  end if
end function

#endif ' __FBASIO_BI__
file: "test01.bas"

Code: Select all

' test01.bas

'#define ASIODEBUG
#include "FBASIO.bi"

#define APPDEBUG
#ifdef APPDEBUG
# define APP_DEBUG_MSG(msg) ? msg
#else
# define APP_DEBUG_MSG(msg)
#endif


' our ASIO Device
enum DEVICESTATE
  UNPREPARED
  PREPARED
  RUNNING
  STOPPED
end enum

type ASIODEVICE
  const MAX_INPUTS   = 16
  const MAX_OUTPUTS  = 16
  const MAX_CHANNELS = MAX_INPUTS + MAX_OUTPUTS
  declare constructor(DriverIndex as integer=0)
  declare destructor
  declare sub        Start
  declare sub        Stop
  declare sub        ControlPanel
  
  as integer         DriverID
  as IASIO ptr       Asio
  as DEVICESTATE     State
  as integer         nInChannels,nOutChannels,nChannels
  as integer         InLatency,OutLatency
  as integer         MinSize,MaxSize
  as integer         PreferredSize,Granularity
  as integer         UseOutputReady
  as integer         nInBuffers,nOutBuffers,nBuffers
  as ASIOSampleRate  Rate
  as ASIOTime        TimeInfo
  as ASIOBufferInfo  BufferInfos(MAX_CHANNELS-1)
  as ASIOChannelInfo ChannelInfos(MAX_CHANNELS-1)
  as ASIOCallbacks   Callbacks
end type

' enumerate all ASIO drivers
dim shared as ASIODRIVERLIST DriverList
' a global copy of our device
' we need it inside of device callbacks
dim shared as ASIODEVICE ptr pGlobalDevice


' the ASIO 2.x SDK buffer callback
function BufferSwitchTimeInfoProc cdecl ( _
  pTI         as ASIOTime ptr, _
  BufferIndex as long, _
  ProcessIt   as ASIOBool) as ASIOTime ptr
  ? "callback samplepos = " & pTI->TimeInfo.SamplePosition.lo
  if (pGlobalDevice<>NULL) then
    if pGlobalDevice->UseOutputReady then 
      pGlobalDevice->Asio->OutputReady()
    end if
  end if
  return 0
end function

' the old ASIO SDK buffer callback
sub BufferSwitchProc cdecl ( _
  BufferIndex as long, _
  ProcessIt   as ASIOBool)
  dim as ASIOTime TI
  if (pGlobalDevice<>NULL) then
    if (pGlobalDevice->Asio->GetSamplePosition( _
      @TI.TimeInfo.SamplePosition, _
      @TI.TimeInfo.SystemTime) = ASE_OK) then
      TI.TimeInfo.flags = kSystemTimeValid or kSamplePositionValid
    end if
  end if
  ' we simulate the new ASIO SDK callback
  BufferSwitchTimeInfoProc(@TI,BufferIndex,ProcessIt)
end sub

sub SampleRateChangedProc cdecl ( _
  NewRate as ASIOSampleRate)
  APP_DEBUG_MSG("SampleRateChangedProc")
end sub

function MessageProc cdecl ( _
  Selector as long   , _
  Value    as long   , _
  pMsg     as any ptr, _
  pOpt     as double ptr) as long
  APP_DEBUG_MSG("MessageProc")
  select case Selector
    case kAsioSelectorSupported
      if (value = kAsioResetRequest     or _
          value = kAsioEngineVersion    or _
          value = kAsioResyncRequest    or _
          value = kAsioLatenciesChanged or _
          value = kAsioSupportsTimeInfo or _
          value = kAsioSupportsTimeCode or _
          value = kAsioSupportsInputMonitor) then return 1
    case kAsioResetRequest
      return 1
    case kAsioResyncRequest
      return 1
    case kAsioLatenciesChanged
      return 1
    case kAsioEngineVersion
      return 2
    case kAsioSupportsTimeInfo
      return 1
    case kAsioSupportsTimeCode
      return 0
    case else
      return 0
  end select
end function


constructor ASIODEVICE(DriverIndex as integer=0)
  APP_DEBUG_MSG("ASIODEVICE()")
  DriverID = DriverIndex
  DriverList.OpenDriver(DriverID,@Asio)
  State = UNPREPARED
  if (Asio<>NULL) then
    with Callbacks
      .BufferSwitchProc         = @BufferSwitchProc
      .SampleRateChangedProc    = @SampleRateChangedProc
      .MessageProc              = @MessageProc
      .BufferSwitchTimeInfoProc = @BufferSwitchTimeInfoProc
    end with
    with *Asio
      .Init(NULL)
      .GetChannels(@nInChannels,@nOutChannels)
      nChannels  =nInChannels+nOutChannels
      nInBuffers =nInChannels
      nOutBuffers=nOutChannels
      .GetBufferSize(@MinSize,@MaxSize,@PreferredSize,@Granularity)
      .GetSampleRate(@Rate)
      if (Rate <= 0.0) or  (Rate > 196000.0) then
        .SetSampleRate(44100.0)
        .GetSampleRate(@Rate)
      end if
      UseOutputReady = (.OutputReady()=ASE_OK)
      ' ############################
      '  prepare in and out buffers
      ' ############################
      if nInChannels >MAX_INPUTS  then nInBuffers =MAX_INPUTS
      if nOutChannels>MAX_OUTPUTS then nOutBuffers=MAX_OUTPUTS
      nBuffers=0
      if nInBuffers>0 then
        for i as integer = 0 to nInBuffers-1
          with BufferInfos(nBuffers)
            .IsInput = ASIOTrue
            .ChannelNum = i
          end with
          nBuffers+=1
        next
      end if
      if nOutBuffers>0 then
        for i as integer = 0 to nOutBuffers-1
          with BufferInfos(nBuffers)
            .IsInput    = ASIOFalse
            .ChannelNum = i
          end with
          nBuffers+=1
        next
      end if
      ' ########################################
      '  create the prepared in and out buffers
      ' ########################################
      if (Asio->CreateBuffers(@BufferInfos(0),nBuffers,MaxSize,@Callbacks)=ASE_OK) then
        State = PREPARED
        ' get all channel infos
        for i as integer = 0 to nBuffers-1
          ChannelInfos(i).Channel = BufferInfos(i).ChannelNum
          ChannelInfos(i).IsInput = BufferInfos(i).IsInput
          .GetChannelInfo(@ChannelInfos(i))
          APP_DEBUG_MSG(ASIOSampleTypeString(ChannelInfos(i).type))
        next
        .GetLatencies(@InLatency,@OutLatency)
      end if
    end with
  end if
end constructor

destructor ASIODEVICE
  if (Asio<>NULL) then
    if State = RUNNING then
      Asio->Stop()
      State  = STOPPED
    end if
    if State = STOPPED then
      State = PREPARED
    end if
    if State=PREPARED then
      Asio->DisposeBuffers()
      State = UNPREPARED 
    end if
    DriverList.CloseDriver(DriverID)
  end if
  APP_DEBUG_MSG("ASIODEVICE~")
end destructor

sub ASIODEVICE.Start
  if (State=PREPARED) or (State=STOPPED) then
    if (Asio->Start()=ASE_OK) then
      State=RUNNING
    end if
  end if
end sub

sub ASIODEVICE.Stop
  if (State=RUNNING) then
    if (Asio->Stop()=ASE_OK) then
      State=STOPPED
    end if
  end if
end sub

sub ASIODEVICE.ControlPanel
  if (Asio<>NULL) then Asio->ControlPanel()
end sub

'
' main
'
if DriverList.GetNumDev=0 then
  print "Sorry no ASIO device found !"
  sleep:end
end if

dim as ASIODEVICE Device
' make it usable for callbacks
pGlobalDevice = @Device

' record playback for ~2 seconds
Device.Start
sleep(2000,1)
Device.Stop
sleep
Post Reply