#include <WinAPI.au3>
#include <Memory.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>


;#include "FASM.au3"
#AutoIt3Wrapper_UseX64=n

;Global $pASM_Sin1 = _FASM_StructCreateBCA16(_FASM_Compile("_ASM_Sin1_32"))
Global $pASM_Sin1 = _StructCreateBCA16("0x9BDBE38B5424048B4C2408D944240CD9EED8C1D9C0D9FED91A83C20483E9047FF0DED9C20C00")
;Global $pASM_Sin2 = _FASM_StructCreateBCA16(_FASM_Compile("_ASM_Sin2_32"))
Global $pASM_Sin2 = _StructCreateBCA16("0x575653558B7C24148B4C24188B6C241CB800000080F30F2AD0B801000000F30F2AC8F30F5ECA31F689F38D841B00000080C1FB1FF7E88D84120000008031D8F30F2AC0F30F59C1660F7E0701EE83C70483E9047FD35D5B5E5FC20C00")
;Global $pASM_Sin3 = _FASM_StructCreateBCA16(_FASM_Compile("_ASM_Sin3_32"))
Global $pASM_Sin3 = _StructCreateBCA16("0x575653558B7C24148B4C24188B6C241CB8008059F6F30F2AD0B801000000F30F2AC8F30F5ECA5131F689F38D841B00000080C1F802F7E8C1FB1F8D8412000000908D8CD2000000DCF7EA31D98D84D20000A044F7E9F30F2AC2F30F59C1660F7E0701EE83C704832C24047FBD83C4045D5B5E5FC20C00")


#ASM _ASM_Sin3_32
#	use32
#	push edi
#	push esi
#	push ebx
#	push ebp

#	mov edi, [esp+20] ;tSamples
#	mov ecx, [esp+24] ;iBytes
#	mov ebp, [esp+28] ;iPhaseInc

;eax = Sin
;ebx = Sin
;ecx = Sin
;edx = Sin

;edi = Samples
;esi = Phase
;ebp = PhaseInc

#	mov eax, 0xF6598000
#	cvtsi2ss xmm2, eax
#	mov eax, 1
#	cvtsi2ss xmm1, eax
#	divss xmm1, xmm2 ;1 / -161906688

#	push ecx
#	xor esi, esi
#	_Loop:
#		mov ebx, esi ;Phase

#		lea  eax, [ebx*2+0x80000000]
#		sar  eax, 2
#		imul eax
#		sar  ebx, 31
#		lea  eax, [edx*2-0x70000000]
#		lea  ecx, [edx*9-0x24000000]
#		imul edx
#		xor  ecx, ebx
#		lea  eax, [edx*9+0x44A00000]
#		imul ecx

#		cvtsi2ss xmm0, edx
#		mulss xmm0, xmm1
#		movd [edi], xmm0

#		add esi, ebp

#		add edi, 4
#		sub dword[esp], 4
#		jg _Loop

#	add esp, 4

#	pop ebp
#	pop ebx
#	pop esi
#	pop edi
#	ret 12
#ASMEND

Func _Sin3($tSamples, $fFreq)
	Local $fPhaseInc = 2 / 44100 * $fFreq * 2 ^ 31
	Local $iTimer = TimerInit()
	DllCallAddress("int", $pASM_Sin3, "struct*", $tSamples, "uint", DllStructGetSize($tSamples), "uint", $fPhaseInc)
	Return TimerDiff($iTimer)
EndFunc   ;==>_Sin3





#ASM _ASM_Sin2_32
#	use32
#	push edi
#	push esi
#	push ebx
#	push ebp

#	mov edi, [esp+20] ;tSamples
#	mov ecx, [esp+24] ;iBytes
#	mov ebp, [esp+28] ;iPhaseInc

;eax = Sin
;ebx = Sin
;ecx = Cnt
;edx = Sin

;edi = Samples
;esi = Phase
;ebp = PhaseInc

#	mov eax, 2147483648
#	cvtsi2ss xmm2, eax
#	mov eax, 1
#	cvtsi2ss xmm1, eax
#	divss xmm1, xmm2 ;1 / 2^31


#	xor esi, esi
#	_Loop:
#		mov ebx, esi ;Phase
#		lea eax, [0x80000000+ebx*2]
#		sar ebx, 31
#		imul eax
#		lea eax, [0x80000000+edx*2]
#		xor eax, ebx

#		cvtsi2ss xmm0, eax
#		mulss xmm0, xmm1
#		movd [edi], xmm0

#		add esi, ebp

#		add edi, 4
#		sub ecx, 4
#		jg _Loop

#	pop ebp
#	pop ebx
#	pop esi
#	pop edi
#	ret 12
#ASMEND

Func _Sin2($tSamples, $fFreq)
	Local $fPhaseInc = 2 / 44100 * $fFreq * 2 ^ 31
	Local $iTimer = TimerInit()
	DllCallAddress("int", $pASM_Sin2, "struct*", $tSamples, "uint", DllStructGetSize($tSamples), "uint", $fPhaseInc)
	Return TimerDiff($iTimer)
EndFunc   ;==>_Sin2






#ASM _ASM_Sin1_32
#	use32
#	finit

#	mov edx, [esp+4] ;tSamples
#	mov ecx, [esp+8] ;iBytes

#	fld dword[esp+12] ;fPhaseInc
#	fldz ;[Phs][PInc]
#	_Loop:
#		fadd st0, st1
#		fld st0 ;[Phs][Phs][PInc]
#		fsin ;[Smp][Phs][PInc]
#		fstp dword[edx] ;[Phs][PInc]
#		add edx, 4
#		sub ecx, 4
#		jg _Loop
#	fcompp

#	ret 12
#ASMEND

Func _Sin1($tSamples, $fFreq)
	Local $fPhaseInc = (ATan(1) * 8) / 44100 * $fFreq
	Local $iTimer = TimerInit()
	DllCallAddress("int", $pASM_Sin1, "struct*", $tSamples, "uint", DllStructGetSize($tSamples), "float", $fPhaseInc)
	Return TimerDiff($iTimer)
EndFunc   ;==>_Sin1







Opt("MustDeclareVars", 1)
Opt("GUIOnEventMode", 1)

Global $hGui = GUICreate("Test", 340, 210)
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
GUICtrlCreateLabel("Seconds:", 10, 10, 100, 20, BitOR(0x0002, 0x0200))
Global $cIP_Seconds = GUICtrlCreateInput("60", 120, 10, 100, 20, BitOR(0x0800, 0x2000, 0x01))
GUICtrlSetOnEvent(-1, "_SetSeconds")
GUICtrlCreateUpdown(-1)
GUICtrlSetLimit(-1, 600, 1)

GUICtrlCreateLabel("Frequency:", 10, 40, 100, 20, BitOR(0x0002, 0x0200))
Global $cIP_Freq = GUICtrlCreateInput("440", 120, 40, 100, 20, BitOR(0x0800, 0x2000, 0x01))
GUICtrlSetOnEvent(-1, "_SetFreq")
GUICtrlCreateUpdown(-1)
GUICtrlSetLimit(-1, 2000, 20)

Global $cBT_Create1 = GUICtrlCreateButton("FPU Sin", 10, 70, 100, 20)
GUICtrlSetOnEvent(-1, "_Calc")
Global $cBT_Create2 = GUICtrlCreateButton("Parabolic", 120, 70, 100, 20)
GUICtrlSetOnEvent(-1, "_Calc")
Global $cBT_Create3 = GUICtrlCreateButton("6th Order", 230, 70, 100, 20)
GUICtrlSetOnEvent(-1, "_Calc")

GUICtrlCreateLabel("CalcTime:", 10, 100, 100, 20, BitOR(0x0002, 0x0200))
Global $cIP_Time = GUICtrlCreateInput("0.0", 120, 100, 100, 20, BitOR(0x0800, 0x01))

Global $cPG_Play = GUICtrlCreateProgress(10, 130, 320, 10)
Global $cBT_Stop = GUICtrlCreateButton("STOP", 120, 150, 100, 20)
GUICtrlSetOnEvent(-1, "_StopPlaying")

GUICtrlCreateLabel("Volume:", 10, 180, 100, 20, BitOR(0x0002, 0x0200))
Global $cSL_Volume = GUICtrlCreateSlider(120, 170, 100, 30, 0x0004)
GUICtrlSetOnEvent(-1, "_SetVolume")
GUICtrlSetLimit(-1, 0, -40)
GUICtrlSetData(-1, -18)

GUIRegisterMsg($WM_HSCROLL, "WM_HSCROLL")
GUISetState()



Global $iVolume = -18 ;dB
Global $iFreq = 440
Global $iSeconds = 60
Global $tSamples = DllStructCreate("float[" & Ceiling(44100 * $iSeconds) & "];")
Global $tSND

Global $iTimer = 0
While Sleep(40)
	If $iTimer Then
		If TimerDiff($iTimer) < $iSeconds * 1000 Then
			GUICtrlSetData($cPG_Play, TimerDiff($iTimer) / 10 / $iSeconds)
		Else
			$iTimer = 0
			GUICtrlSetData($cPG_Play, 0)
		EndIf
	EndIf
WEnd




Func _SetVolume()
	$iVolume = GUICtrlRead($cSL_Volume)
	If IsDllStruct($tSND) Then
		Local $pDSBuffer = DllStructGetData($tSND, "DSBuffer")
		Local $tDSBuffer = DllStructCreate("ptr VTBL;", $pDSBuffer)
		Local $tDSBufferM = DllStructCreate("ptr[21];", DllStructGetData($tDSBuffer, 1))
		DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 16), "struct*", $tDSBuffer, "int", $iVolume * 100)
	EndIf
EndFunc   ;==>_SetVolume

Func WM_HSCROLL($hWnd, $iMsg, $wParam, $lParam)
	#forceref $hWnd, $iMsg, $wParam, $lParam
	Switch $hWnd
		Case $hGui
			If BitAND($wParam, 0x0000FFFF) = 5 Then
				Switch $lParam
					Case GUICtrlGetHandle($cSL_Volume)
						_SetVolume()
				EndSwitch
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_HSCROLL



Func _SetFreq()
	$iFreq = GUICtrlRead($cIP_Freq)
EndFunc   ;==>_SetFreq


Func _SetSeconds()
	$iSeconds = GUICtrlRead($cIP_Seconds)
	$tSamples = DllStructCreate("float[" & Ceiling(44100 * $iSeconds) & "];")
	_StopPlaying()
EndFunc   ;==>_SetSeconds


Func _Calc()
	Local $fTime
	Switch @GUI_CtrlId
		Case $cBT_Create1
			$fTime = _Sin1($tSamples, $iFreq)
			_SaveWave(@ScriptDir & "\Sine_1.Wav", $tSamples)
		Case $cBT_Create2
			$fTime = _Sin2($tSamples, $iFreq)
			_SaveWave(@ScriptDir & "\Sine_2.Wav", $tSamples)
		Case $cBT_Create3
			$fTime = _Sin3($tSamples, $iFreq)
			_SaveWave(@ScriptDir & "\Sine_3.Wav", $tSamples)
	EndSwitch
	GUICtrlSetData($cIP_Time, StringFormat("%.1f", $fTime))
	$iTimer = TimerInit()
	If IsDllStruct($tSND) Then _Stop($tSND)
	$tSND = _Play($tSamples)
EndFunc   ;==>_Calc


Func _StopPlaying()
	_Stop($tSND)
	$iTimer = 0
	GUICtrlSetData($cPG_Play, 0)
EndFunc   ;==>_StopPlaying


Func _Exit()
	_Stop($tSND)
	Exit
EndFunc   ;==>_Exit






Func _Play($tSamples)
	Local $tSND = DllStructCreate("uint Dll; ptr DSound; ptr DSBuffer")
	DllStructSetData($tSND, "DLL", DllOpen("dsound.dll"))

	DllStructSetData($tSND, "DSound", DllCall(DllStructGetData($tSND, "Dll"), "uint", "DirectSoundCreate8", "struct*", Null, "ptr*", 0, "ptr", Null)[2])
	Local $tDSound = DllStructCreate("ptr VTBL;", DllStructGetData($tSND, "DSound"))
	Local $tDSoundM = DllStructCreate("ptr[12];", DllStructGetData($tDSound, 1))
	DllCallAddress("uint", DllStructGetData($tDSoundM, 1, 7), "struct*", $tDSound, "hwnd", _WinAPI_GetDesktopWindow(), "uint", 0x00000002)

	Local $tWavEx = DllStructCreate("struct; align 2; struct; align 2; word FormatTag; word Channels; uint SamplesPerSec; uint AvgBytesPerSec; word BlockAlign; word BitsPerSample; word Size; endstruct; ushort ValidBitsPerSample; uint ChannelMask; byte SubFormat[16]; endstruct;")
	Local $vTmp = DllStructSetData($tWavEx, "FormatTag", 0x0003) + DllStructSetData($tWavEx, "Channels", 1) + DllStructSetData($tWavEx, "SamplesPerSec", 44100) + DllStructSetData($tWavEx, "AvgBytesPerSec", 176400) + DllStructSetData($tWavEx, "BlockAlign", 4) + DllStructSetData($tWavEx, "BitsPerSample", 32)

	Local $tDS_BufferDesc = DllStructCreate("struct; uint Size; uint Flags; uint BufferBytes; uint Reserved; ptr WaveFormatEX; byte p3DAlgorithm[16]; endstruct;")
	$vTmp = DllStructSetData($tDS_BufferDesc, "Size", DllStructGetSize($tDS_BufferDesc)) + DllStructSetData($tDS_BufferDesc, "Flags", BitOR(0x00008000, 0x00000080)) + DllStructSetData($tDS_BufferDesc, "BufferBytes", DllStructGetSize($tSamples)) + DllStructSetData($tDS_BufferDesc, "WaveFormatEX", DllStructGetPtr($tWavEx))

	DllStructSetData($tSND, "DSBuffer", DllCallAddress("uint", DllStructGetData($tDSoundM, 1, 4), "struct*", $tDSound, "struct*", $tDS_BufferDesc, "ptr*", 0, "struct*", Null)[3])
	Local $tDSBuffer = DllStructCreate("ptr VTBL;", DllStructGetData($tSND, "DSBuffer"))
	Local $tDSBufferM = DllStructCreate("ptr[21];", DllStructGetData($tDSBuffer, 1))

	Local $aResult = DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 12), "struct*", $tDSBuffer, "uint", 0, "uint", 0, "ptr*", 0, "uint*", 0, "ptr*", 0, "uint*", 0, "uint", 0x00000002)
	_MemMoveMemory($tSamples, $aResult[4], $aResult[5])
	DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 20), "struct*", $tDSBuffer, "struct*", $aResult[4], "uint", $aResult[5], "struct*", $aResult[6], "uint", $aResult[7])

	DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 16), "struct*", $tDSBuffer, "int", $iVolume * 100)
	DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 13), "struct*", $tDSBuffer, "uint", 0, "uint", 0, "uint", 0)

	Return $tSND
EndFunc   ;==>_Play


Func _Stop(ByRef $tSND)
	If Not IsDllStruct($tSND) Then Return

	Local $pDSBuffer = DllStructGetData($tSND, "DSBuffer")
	Local $tDSBuffer = DllStructCreate("ptr VTBL;", $pDSBuffer)
	Local $tDSBufferM = DllStructCreate("ptr[21];", DllStructGetData($tDSBuffer, 1))
	Local $aResult = DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 19), "struct*", $tDSBuffer)
	DllCallAddress("uint", DllStructGetData($tDSBufferM, 1, 3), "struct*", $tDSBuffer)

	Local $pDSound = DllStructGetData($tSND, "DSound")
	Local $tDSound = DllStructCreate("ptr VTBL;", $pDSound)
	Local $tDSoundM = DllStructCreate("ptr[12];", DllStructGetData($tDSound, 1))
	DllCallAddress("uint", DllStructGetData($tDSoundM, 1, 3), "struct*", $tDSound)

	DllClose(DllStructGetData($tSND, "Dll"))

	$tSND = 0
EndFunc   ;==>_Stop


Func _SaveWave($sPath, $tSamples)
	Local $iSize = DllStructGetSize($tSamples)

	Local $tWave = DllStructCreate("char RIFF [4];uint FileSize;char WAVE [4];char fmt [4];uint fmt_len;word Format;word Channels;uint Samplerate;uint Bytes;word Block;word BitsPerSample;char DATA [4];uint Len;byte WAVDATA[" & $iSize & "]")
	DllStructSetData($tWave, "RIFF", "RIFF")
	DllStructSetData($tWave, "FileSize", $iSize + 44 - 8)
	DllStructSetData($tWave, "WAVE", "WAVE")
	DllStructSetData($tWave, "fmt", "fmt ")
	DllStructSetData($tWave, "fmt_len", 16)
	DllStructSetData($tWave, "Format", 3)
	DllStructSetData($tWave, "Channels", 1)
	DllStructSetData($tWave, "Samplerate", 44100)
	DllStructSetData($tWave, "Bytes", 176400)
	DllStructSetData($tWave, "Block", 4)
	DllStructSetData($tWave, "BitsPerSample", 32)
	DllStructSetData($tWave, "DATA", "data")
	DllStructSetData($tWave, "Len", $iSize)

	Local $tData = DllStructCreate("byte[" & $iSize & "]", DllStructGetPtr($tSamples))
	DllStructSetData($tWave, "WAVDATA", DllStructGetData($tData, 1))

	Local $tData = DllStructCreate("byte[" & DllStructGetSize($tWave) & "]", DllStructGetPtr($tWave))
	Local $hFile = FileOpen($sPath, BitOR(2, 8, 16))
	FileWrite($hFile, DllStructGetData($tData, 1))
	FileClose($hFile)
EndFunc   ;==>_SaveWave


Func _StructCreateBCA16($bBinaryCode)
	Local $iSize = BinaryLen($bBinaryCode)
	Local $aResult = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", $iSize + 16, "dword", 0x00001000, "dword", 0x00000040)
	If @error Or Not $aResult[0] Then Return SetError(1, 0, False)
	Local $pStruct = Number($aResult[0])
	$pStruct = $pStruct + 16 - Mod($pStruct, 16)
	Local $tStruct = DllStructCreate("byte[" & $iSize & "];", $pStruct)
	DllStructSetData($tStruct, 1, $bBinaryCode)
	Return $pStruct
EndFunc   ;==>_StructCreateBCA16
