Code: Select all
;:=============================================================================
;;- ORIGINAL Source code
;:- PB_Asio.pb
;:- Author : Eric Nassen (www.raxntrax.com) / (www.raxntrax.com/modulys)
;:- Date : August 21, 2013
;:- Compiler : PureBasic 4.31
;:- Target OS : Windows x32, x64
;:=============================================================================
;:=============================================================================
;;- x64 ONLY. Updated for PB6
;:- PB_Asio.pb
;:- Author : AndyMK
;:- Date : August 15, 2022
;:- Compiler : PureBasic 6.0 LTS
;:- Target OS : Windows x64 Only
;:=============================================================================
;x64 only
#Stop=0
#Start=1
#TM_Refresh=0
#EngineOn=1
#Debug=0
#WindowDevice=0
#Gain8=127
#Gain16=32767
#Gain24=8388607
#Gain32=2147483648 - 0.5
#kMaxInputChannels=32
#kMaxOutputChannels=32
#ASIO_PATH = "software\ASIO"
#ERROR_NO_MORE_ITEMS = 259
#HKEY_LOCAL_MACHINE = $80000002
#KEY_ENUMERATE_SUB_KEYS = $8
#WM_ASIO = #WM_USER + 4096; // unique we hope
#WM_ASIO_Reset=#WM_ASIO+1
Enumeration
#ASE_OK = 0
#ASE_SUCCESS = $3F4847A0
#ASE_NotPresent = -1000
#ASE_HWMalfunction
#ASE_InvalidParameter
#ASE_InvalidMode
#ASE_SPNotAdvancing
#ASE_NoClock
#ASE_NoMemory
EndEnumeration
Enumeration
#ASIOSTInt16MSB = 0
#ASIOSTInt24MSB = 1
#ASIOSTInt32MSB = 2
#ASIOSTFloat32MSB = 3
#ASIOSTFloat64MSB = 4
#ASIOSTInt32MSB16 = 8
#ASIOSTInt32MSB18 = 9
#ASIOSTInt32MSB20 = 10
#ASIOSTInt32MSB24 = 11
#ASIOSTInt16LSB = 16
#ASIOSTInt24LSB = 17
#ASIOSTInt32LSB = 18
#ASIOSTFloat32LSB = 19
#ASIOSTFloat64LSB = 20
#ASIOSTInt32LSB16 = 24
#ASIOSTInt32LSB18 = 25
#ASIOSTInt32LSB20 = 26
#ASIOSTInt32LSB24 = 27
EndEnumeration
Enumeration
#kAsioSelectorSupported = 1
#kAsioEngineVersion = 2
#kAsioResetRequest = 3
#kAsioBufferSizeChange = 4
#kAsioResyncRequest = 5
#kAsioLatenciesChanged = 6
#kAsioSupportsTimeInfo = 7
#kAsioSupportsTimeCode = 8
#kAsioSupportsInputMonitor = 9
#kAsioNumMessageSelectors = 10
EndEnumeration
Enumeration
#kTcValid = 1;
#kTcRunning = 1 << 1;
#kTcReverse = 1 << 2;
#kTcOnspeed = 1 << 3;
#kTcStill = 1 << 4;
#kTcSpeedValid = 1 << 8;
EndEnumeration
Enumeration
#kSystemTimeValid = 1
#kSamplePositionValid = 1 << 1
#kSampleRateValid = 1 << 2
#kSpeedValid = 1 << 3
#kSampleRateChanged = 1 << 4
#kClockSourceChanged = 1 << 5
EndEnumeration
;Following can be changed (backwards compatibility)
Structure MyDouble
HI.l
Lo.l
EndStructure
Structure ASIOInt64
HI.l
Lo.l
EndStructure
Structure ASIOTimeStamp
HI.l
Lo.l
EndStructure
Structure ASIO64bitFloat
HI.l
Lo.l
EndStructure
Structure ASIOSampleRate
HI.l
Lo.l
EndStructure
Structure ASIOBool
l.l
EndStructure
Structure ASIOSamples
HI.l
Lo.l
EndStructure
Structure ASIOTimeCodeFlags
l.l
EndStructure
Structure AsioTimeInfoFlags
l.l
EndStructure
; --------------------------------------
;Engine simulation....
Structure ENG
Pause.b
BufferIndex.l
SampleRate.d
BlockSize.l
AsioReset.b
EndStructure
; you need the interface definition:
Interface MyUnknown
QueryInterface.l(*riid.IID, *ppv.Integer)
AddRef.l()
Release.l()
EndInterface
Interface MyTestClass Extends IUnknown
Init.l(sysHandle)
GetDriverName.l(*Name)
GetDriverVersion.l()
GetErrorMessage(*errorStrings)
Start()
Stop()
GetChannels(*numInputChannels.Long, *numOutputChannels.Long)
GetLatencies(inputLatency, outputLatency)
GetBufferSize(minSize, maxSize, preferredSize, granularity)
CanSampleRate(dbl.d)
GetSampleRate(*dbl)
SetSampleRate(dbl.d)
GetClockSources(clocks,numSources)
SetClockSource(reference)
GetSamplePosition(sPos,tStamp)
GetChannelInfo(info)
CreateBuffers(bufferInfos, numChannels, bufferSize,callbacks)
DisposeBuffers()
ControlPanel()
Future(selector,opt)
OutputReady()
EndInterface
Structure ASIOCallbacks
bufferSwitch.i
sampleRateDidChange.i
asioMessage.i
bufferSwitchTimeInfo.i
EndStructure
Structure AsioBufferInfo
isInput.l
channelNum.l
buffers.i[2]
EndStructure
Structure ASIOChannelInfo
channel.l
isInput.l
isActive.l
channelGroup.l
vType.l
Name.c[32]
EndStructure
Structure ASIOTimeCode
speed.MyDouble
timecodeSamples.ASIOSamples
flags.l
future.c[64]
EndStructure
Structure AsioTimeInfo
speed.MyDouble
SystemTime.ASIOTimeStamp
samplePosition.ASIOSamples
SampleRate.ASIOSampleRate
flags .l
reserved.c[12]
EndStructure
Structure ASIOTime
reserved.l[4]
timeInfo.AsioTimeInfo
timeCode.ASIOTimeCode
EndStructure
;User defined driver (can be changed)
Structure AsioDriver
inputChannels.i
outputChannels.i
channelInfos.ASIOChannelInfo[#kMaxInputChannels+#kMaxOutputChannels]
inputBuffers.l
outputBuffers.l
bufferInfos.AsioBufferInfo[#kMaxInputChannels+#kMaxOutputChannels]
minSize.l
maxSize.l
preferredSize.l
granularity.l
SampleRate.d
postOutput.l
inputLatency.l
outputLatency.l
nanoSeconds.d
samples.d
tcSamples.d
tInfo.ASIOTime
sysRefTime.l
stopped.l
ActiveInput.l
ActiveOutput.l
EndStructure
Structure AsioStruct
DeviceID.l
AsioName$
CLSID$
DLLName$
EndStructure
;Quick 'n dirty.....
Global *MyTestClassObject.MyTestClass = #Null
Global AsioTime.ASIOTime
Global AsioDriver.AsioDriver
Global callbacks.ASIOCallbacks
Global gDeviceID=-1
Global gAsioState
Global gTicks.l
Global Eng_Event.l
Global NewList AsioDeviceInfo.AsioStruct()
Global Eng.ENG
Eng\SampleRate=44100
Eng\BlockSize=512
Procedure myTimerCB(hwnd,uMsg,idEvent,dwTime)
If AsioTime And gAsioState=#Start
SetGadgetText(2,Str(AsioTime\timeInfo\samplePosition\HI)+"/"+Str(AsioTime\timeInfo\samplePosition\Lo))
Else
SetGadgetText(2,"Sampleposition")
EndIf
EndProcedure
Procedure FillAsioDeviceInfo()
hKey = 0 : count.l = 0
buffer$=Space(1024)
If RegOpenKeyEx_(#HKEY_LOCAL_MACHINE, #ASIO_PATH,0, #KEY_READ,@hKey)=#ERROR_SUCCESS
buffsize=255
index=0
While RegEnumKeyEx_ (hKey, index, @buffer$, @buffsize, #Null, #Null, #Null, @written.SYSTEMTIME)<>#ERROR_NO_MORE_ITEMS
AsioName$ = Left(buffer$,buffsize)
hKey2 = 0
If RegOpenKeyEx_(#HKEY_LOCAL_MACHINE, #ASIO_PATH+"\"+AsioName$, 0, #KEY_READ, @hKey2)=0
DataSize = 255
If RegQueryValueEx_(hKey2, "CLSID", 0, 0, @buffer$, @DataSize)=0
CLSID$ = Left(buffer$, DataSize - 1)
hKey3=0
If RegOpenKeyEx_(#HKEY_CLASSES_ROOT, "clsid"+"\"+CLSID$+"\"+"InprocServer32", 0, #KEY_READ, @hKey3)=0
buffsize=255
If RegQueryValueEx_(hKey3, "", 0, 0, @buffer$, @buffsize)=0
DLLName$=Left(buffer$,buffsize)
lSize=FileSize(DLLName$)
If lSize=-1
lLen=GetSystemDirectory_(@buffer$,255)
szDir.s=Left(buffer$,lLen)
DLLName$=szDir+"\"+DLLName$
lSize=FileSize(DLLName$)
EndIf
If lSize>0
AddElement(AsioDeviceInfo())
AsioDeviceInfo()\DeviceID=count
AsioDeviceInfo()\AsioName$=AsioName$
AsioDeviceInfo()\CLSID$=CLSID$
AsioDeviceInfo()\DLLName$=DLLName$
count+1
EndIf
EndIf
RegCloseKey_(hKey3)
EndIf
EndIf
RegCloseKey_(hKey2)
EndIf
index+1
buffsize = 255
Wend
RegCloseKey_(hKey)
EndIf
EndProcedure
Procedure WideStr(pointer)
widelen.w = Len(PeekS(pointer))+2
widebuf.i = AllocateMemory(widelen)
longlen.w = MultiByteToWideChar_(#CP_ACP,0,pointer,-1,widebuf,widelen)
ProcedureReturn widebuf
EndProcedure
Procedure BufferSwitchTimeInfo(lBufferNumber.l,*AsioTime.ASIOTime)
Eng\BufferIndex=lBufferNumber
;AudioIn
Select AsioDriver\channelInfos[AsioDriver\ActiveInput]\vType
Case #ASIOSTInt16LSB
wSample.w=PeekW(AsioDriver\bufferInfos[AsioDriver\ActiveInput]\buffers[Eng\BufferIndex])
Case #ASIOSTInt32LSB
lSample.l=PeekL(AsioDriver\bufferInfos[AsioDriver\ActiveInput]\buffers[Eng\BufferIndex])
EndSelect
If Eng\Pause=0
;here comes code to generate output
;fill outputbuffers
Select AsioDriver\channelInfos[AsioDriver\ActiveOutput]\vType
Case #ASIOSTInt16LSB
For j = 0 To Eng\BlockSize-1
Next
Case #ASIOSTInt32LSB
For j = 0 To Eng\BlockSize-1
Next
Case #ASIOSTFloat32LSB
;..........
EndSelect
EndIf
Tag_Asio_Ready:
If AsioDriver\postOutput
*MyTestClassObject\OutputReady() ; // some asio drivers require this
EndIf
EndProcedure
ProcedureCDLL.l AsioBufferSwitchTimeInfo(*AsioTime.ASIOTime,doubleBufferIndex.l,directProcess.l) ;: PASIOTime; cdecl;
Select directProcess
Case #False
;PostMessage_(WindowID(#WindowDevice), #WM_ASIO, doubleBufferIndex,*AsioTime);
;or setevent_() or .....
Case #True
BufferSwitchTimeInfo(doubleBufferIndex, *AsioTime);
EndSelect
ProcedureReturn 0
EndProcedure
ProcedureCDLL AsioBufferSwitch(doubleBufferIndex.l,directProcess.l)
If *MyTestClassObject\GetSamplePosition(@AsioTime\timeInfo\samplePosition, @AsioTime\timeInfo\SystemTime) = #ASE_OK
AsioTime\timeInfo\flags = #kSystemTimeValid | #kSamplePositionValid
EndIf
Select directProcess
Case #False
;PostMessage_(WindowID(#WindowDevice), #WM_ASIO, doubleBufferIndex,#Null);
Case #True
BufferSwitchTimeInfo(doubleBufferIndex, AsioTime);
EndSelect
EndProcedure
ProcedureCDLL.l AsioMessage(selector.l, Value.l, *message, *opt.MyDouble) ;: longint; cdecl;
CompilerIf #Debug:ShowSelector(selector,Value):CompilerEndIf
Result = 0
;ShowSelector(selector)
Select selector
Case #kAsioSelectorSupported
Select Value
Case #kAsioEngineVersion : Result = 1
Case #kAsioResetRequest : Result = 1
Case #kAsioBufferSizeChange : Result = 0
Case #kAsioResyncRequest : Result = 1
Case #kAsioLatenciesChanged : Result = 1
Case #kAsioSupportsTimeInfo : Result = 1
Case #kAsioSupportsTimeCode : Result = 0
Case #kAsioSupportsInputMonitor : Result = 0
EndSelect
Case #kAsioEngineVersion
Result=2
Case #kAsioResetRequest
Eng\AsioReset=2
PostMessage_(WindowID(#WindowDevice),#WM_ASIO_Reset,0,0)
Result=1
Case #kAsioBufferSizeChange
Eng\AsioReset=1
PostMessage_(WindowID(#WindowDevice),#WM_ASIO_Reset,0,0)
Result=1
Case #kAsioResyncRequest
Eng\AsioReset=2
PostMessage_(WindowID(#WindowDevice),#WM_ASIO_Reset,0,0)
Result=1
Case #kAsioLatenciesChanged
Eng\AsioReset=1
PostMessage_(WindowID(#WindowDevice),#WM_ASIO_Reset,0,0)
Result=1
Case #kAsioSupportsTimeInfo
Result=1
Case #kAsioSupportsTimeCode
Case #kAsioSupportsInputMonitor
EndSelect
ProcedureReturn Result
EndProcedure
ProcedureCDLL AsioSampleRateDidChange(sRateHI.l, sRateLO.l)
EndProcedure
Procedure.l Asio_static_data()
YnSampleRateOK=0
If *MyTestClassObject\GetChannels(@AsioDriver\inputChannels, @AsioDriver\outputChannels)=#ASE_OK
If *MyTestClassObject\GetBufferSize(@AsioDriver\minSize, @AsioDriver\maxSize, @AsioDriver\preferredSize, @AsioDriver\granularity) = #ASE_OK
If*MyTestClassObject\GetSampleRate(@AsioDriver\SampleRate) = #ASE_OK
If Eng\SampleRate=44100 Or Eng\SampleRate=48000 Or Eng\SampleRate=96000
;Force to Engine Samplerate
If *MyTestClassObject\CanSampleRate(Eng\SampleRate) = #ASE_OK
If *MyTestClassObject\SetSampleRate(Eng\SampleRate) = #ASE_OK
If *MyTestClassObject\GetSampleRate(@AsioDriver\SampleRate) = #ASE_OK
EndIf
EndIf
EndIf
EndIf
If AsioDriver\SampleRate <= 0 Or AsioDriver\SampleRate > 96000
;Try 44100
If *MyTestClassObject\CanSampleRate(44100) = #ASE_OK
If *MyTestClassObject\SetSampleRate(44100) = #ASE_OK
If*MyTestClassObject\GetSampleRate(@AsioDriver\SampleRate) = #ASE_OK
YnSampleRateOK=1
EndIf
EndIf
EndIf
If YnSampleRateOK=0
If*MyTestClassObject\CanSampleRate(48000) = #ASE_OK
If *MyTestClassObject\SetSampleRate(48000) = #ASE_OK
If*MyTestClassObject\GetSampleRate(@AsioDriver\SampleRate) = #ASE_OK
YnSampleRateOK=1
EndIf
EndIf
EndIf
EndIf
Else
YnSampleRateOK=1
EndIf
EndIf
If*MyTestClassObject\OutputReady() = #ASE_OK
AsioDriver\postOutput = #True
Else
AsioDriver\postOutput = #False
EndIf
EndIf
EndIf
If YnSampleRateOK
ProcedureReturn 0
Else
ProcedureReturn 1
EndIf
tag_end:
EndProcedure
Procedure.l Asio_create_buffers()
; // fill the bufferInfos from the start without a gap
*info.AsioBufferInfo = @AsioDriver\bufferInfos;
; Inputs
If AsioDriver\inputChannels > #kMaxInputChannels
AsioDriver\inputBuffers = #kMaxInputChannels;
Else
AsioDriver\inputBuffers = AsioDriver\inputChannels
EndIf
AsioDriver\ActiveInput=0
AsioDriver\ActiveOutput=AsioDriver\inputBuffers
For i = 0 To AsioDriver\inputBuffers-1
*info\isInput = #True
*info\channelNum = i
*info\buffers[0] = 0
*info\buffers[1] = 0
*info+SizeOf(AsioBufferInfo)
Next
; Outputs
If AsioDriver\outputChannels > #kMaxOutputChannels
AsioDriver\outputBuffers = #kMaxOutputChannels
Else
AsioDriver\outputBuffers = AsioDriver\outputChannels
EndIf
For i = 0 To AsioDriver\outputBuffers-1
*info\isInput = #False
*info\channelNum = i
*info\buffers[0] = 0
*info\buffers[1] = 0;
*info+SizeOf(AsioBufferInfo)
Next
Result=*MyTestClassObject\CreateBuffers(@AsioDriver\bufferInfos, AsioDriver\inputBuffers + AsioDriver\outputBuffers, AsioDriver\preferredSize, @callbacks)
If Result=#ASE_OK
For i = 0 To AsioDriver\inputBuffers + AsioDriver\outputBuffers -1
AsioDriver\channelInfos[i]\channel = AsioDriver\bufferInfos[i]\channelNum
AsioDriver\channelInfos[i]\isInput = AsioDriver\bufferInfos[i]\isInput
Result = *MyTestClassObject\GetChannelInfo(@AsioDriver\channelInfos[i])
If Result<>#ASE_OK
;Break
Else
EndIf
Next
If Result=#ASE_OK
Result=*MyTestClassObject\GetLatencies(@AsioDriver\inputLatency, @AsioDriver\outputLatency)
EndIf
EndIf
;AudioIn
Select AsioDriver\channelInfos[AsioDriver\ActiveInput]\vType
Case #ASIOSTInt16LSB
Case #ASIOSTInt32LSB
EndSelect
ProcedureReturn Result
EndProcedure
Procedure ASIO_Open()
SelectElement(AsioDeviceInfo(),gDeviceID)
sCLSID.s = AsioDeviceInfo()\CLSID$
Err.l=CLSIDFromString_(WideStr(@sCLSID),@clsid.GUID)
UuidFromString_(Mid(sCLSID,2, Len(sCLSID) - 2), @CLSID_MyTestClass)
CoInitialize_(0);
CoCreateInstance_(@CLSID_MyTestClass, #Null, 1, @CLSID_MyTestClass, @*MyTestClassObject)
hWnd=WindowID(#WindowDevice)
Retval= *MyTestClassObject\Init(hwnd)
If RetVal
If Asio_static_data() = 0
callbacks\bufferSwitch = @AsioBufferSwitch()
callbacks\sampleRateDidChange = @AsioSampleRateDidChange()
callbacks\asioMessage = @AsioMessage()
callbacks\bufferSwitchTimeInfo = @AsioBufferSwitchTimeInfo()
If Asio_create_buffers()=#ASE_OK
Eng\BlockSize=AsioDriver\preferredSize
Eng\SampleRate=AsioDriver\SampleRate
If #EngineOn
If *MyTestClassObject\Start()=#ASE_OK
Else
MessageRequester("Asio","Driver could not be started",#MB_TOPMOST)
EndIf
EndIf
Else
MessageRequester("Asio","Unable to create the buffers",#MB_TOPMOST)
EndIf
EndIf
EndIf
EndProcedure
Procedure ASIO_Close()
Eng\Pause=1
If *MyTestClassObject\Stop()=#ASE_OK
If *MyTestClassObject\DisposeBuffers()=#ASE_OK
AsioDriver\stopped=1
EndIf
EndIf
*MyTestClassObject\release()
CoUninitialize_()
EndProcedure
Procedure.s Info_Refresh()
With AsioDriver
info$="Driver info"+Chr(13)+Chr(13)
info$=info$+"inputChannels="+Str(\inputChannels)+Chr(13)
info$=info$+"outputChannels="+Str(\outputChannels)+Chr(13)
info$=info$+"minSize="+Str(\minSize)+Chr(13)
info$=info$+"maxsize="+Str(\maxSize)+Chr(13)
info$=info$+"preferredSize="+Str(\preferredSize)+Chr(13)
info$=info$+"granularity="+Str(\granularity)+Chr(13)
info$=info$+"samplerate="+StrD(\SampleRate)+Chr(13)
info$=info$+"postoutput="+Str(\postOutput)+Chr(13)
info$=info$+"inputLatency="+Str(\inputLatency)+Chr(13)
info$=info$+"outputLatency="+Str(\outputLatency)
EndWith
ProcedureReturn info$
EndProcedure
FillAsioDeviceInfo()
;Quick 'n dirty window implementation ;-)
OpenWindow(0,0,0,620,280,"Select Asio device",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
SetTimer_(WindowID(#WindowDevice),#TM_Refresh,25,@myTimerCB())
ListViewGadget(0,10,10,300,200)
ButtonGadget(1,10,220,300,25,"Start Asio device")
TextGadget(2,10,250,150,25,"Sampleposition")
TextGadget(3,320,10,300,180,"")
ButtonGadget(4,170,250,130,25,"Control Panel")
ResetList(AsioDeviceInfo()) : i = 0 :gDeviceID=-1
While NextElement(AsioDeviceInfo())
AddGadgetItem(0,-1,AsioDeviceInfo()\AsioName$)
i+1
Wend
Repeat
ev=WaitWindowEvent()
If ev=#PB_Event_Gadget
Select EventGadget()
Case 0
If gDeviceID<>GetGadgetState(0)
If gAsioState=#Start
ASIO_Close()
gAsioState=#Stop
SetGadgetText(1,"Start Asio device")
SetGadgetText(2,"Sampleposition")
SetGadgetText(3,"")
EndIf
gDeviceID=GetGadgetState(0)
EndIf
Case 1
If gAsioState=#Stop
If gDeviceID>=0
ASIO_Open()
gAsioState=#Start
SetGadgetText(1,"Stop Asio device")
SetGadgetText(3,Info_Refresh())
EndIf
Else
ASIO_Close()
gAsioState=#Stop
SetGadgetText(1,"Start Asio device")
SetGadgetText(2,"Sampleposition")
SetGadgetText(3,"")
EndIf
Case 4
If gAsioState=#Start
*MyTestClassObject\controlPanel()
EndIf
EndSelect
EndIf
If ev=#WM_ASIO_Reset
If gAsioState=#Start
Asio_close()
Eng\AsioReset=0
Asio_open()
SetGadgetText(3,Info_Refresh())
EndIf
EndIf
Until ev=#PB_Event_CloseWindow
If gAsioState=#Start
Asio_close()
EndIf