Hallo liebe Freunde,
ich stell euch mal meinen Stream-Visualizer vor. Den Autoit Teil hab ich schon vor einiger Zeit, der ist nur da, um Musikdaten zu kriegen und das Bild anzuzeigen, nichts großes. Ich hoffe es stört nicht, dass der Autoitteil recht unordentlich ist. (Tut mir Leid :P)
Der Visualizer zeigt je nach Intensitätslevel die Farben von Blau über Grün nach Rot an. Die Farben laufen je nach Tieftonstärke schneller oder langsamer nach oben. Man kann recht gut erkennen, wie Intensiv und Bassstark die Musik ist.
Man könnte höchstens noch probieren ein bisschen Blur mitreinzubringen, aber ansonsten finde ich das Ganze schon ganz nett. (Wie schön das aussieht, könnt ihr selebr entscheiden.
Das Bild wird komplett vom ASM-Teil berechnet. Ich habe diesmal möglichst viel optimiert, obwohl es recht einfacher Code ist und sowieso nicht allzu lange braucht Unter anderem wird Caching verwendet. der DLLCall mit der ASM-Funktion braucht bei mir 0.3 bis 0.5 ms.
Ich hoffe es gefällt euch und natürlich hoffe ich, dass Andy nichts großes mehr zum Optimieren findet
Da es mir mehr um den ASM-Code und ums visualizen geht, muss die Stream-URL und Streamvolume (Lautstärke) von Hand in der stream.txt einstellen. Voreingestellt hab ich HouseTime.FM mit vol=15
Ich habe den ASM Code ordentlich und mit vielen (englischen) Kommentaren versehen und geschrieben. Ich hoffe der Code ist verständlich und hilft ASM-Lernern ein bissen weiter
Datei im Anhang!
Viel Spaß, mfg
CreativeName
ehemals TheShadowAE
EDIT:
Für die, die den Sourcecode schon hier sehen wollen:
Spoiler anzeigen
#NoTrayIcon
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=Visualizer.ico
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include "Bass.au3"
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <FASM.au3>
;Author: TheShadowAE (new nick: CreativeName)
[/autoit] [autoit][/autoit] [autoit]Opt("GUIOnEventMode",1)
Local $txt="",$bmp=0,$hbmp=0,$gra=0,$backgra=0,$fasm=0,$stream=0 ;else may be error in _exit with error-exit
$vol=Int(IniRead("stream.txt","Stream","Volume",100))
$URL=IniRead("stream.txt","Stream","URL","")
If $cmdline[0]>0 Then $URL=$cmdline[1]
If $URL="" Then $URL="http://listen.housetime.fm/tunein-mp3-pls" ;HouseTime.FM als Standard
If $cmdline[0]>1 Then $vol=Int($cmdline[2])
If $vol<=0 Then $vol=1
If $vol>100 Then $vol=100
$w=800 ;must be dividable by 16
$h=600 ;at least 10
$Form1 = GUICreate("Visualizer by CreativeName", $w, $h)
GUISetOnEvent($GUI_EVENT_CLOSE,"_exit")
$user32=DllOpen("user32.dll")
[/autoit] [autoit][/autoit] [autoit]_BASS_Startup(@ScriptDir & "\bass" & _Iif(@OSVersion = "X64", 64, "") & ".dll")
If @error Then _exit("Could start bass.dll")
_BASS_Init(0, -1, 44100, 0, "")
If @error Then _exit("Could initialize bass.dll")
$stream = _BASS_StreamCreateURL($URL, 0, 0)
If @error Then
$stream = _BASS_StreamCreateURL($URL, 0, 0)
If @error Then _exit("Could not connect to stream")
EndIf
_BASS_ChannelSetVolume($stream,$vol)
If @error Then MsgBox(0,"Oops","Failed to set volume")
IniWrite("stream.txt","Stream","URL",$URL)
IniWrite("stream.txt","Stream","Volume",$vol)
_BASS_ChannelPlay($stream, 0)
$fftstruct = DllStructCreate('float[128]')
$fasm=0
$code=""
$codestruct=_init($code)
_GDIPlus_Startup()
$gra=_GDIPlus_GraphicsCreateFromHWND($Form1)
$hbmp=_GDIPlus_BitmapCreateFromGraphics($w,$h,$gra)
$backgra=_GDIPlus_ImageGetGraphicsContext($hbmp)
_GDIPlus_GraphicsClear($backgra)
$bmp=_GDIPlus_BitmapCreateFromGraphics($w,$h,$backgra)
GUISetState(@SW_SHOW)
[/autoit] [autoit][/autoit] [autoit]While 1
_BASS_ChannelGetData($stream, DllStructGetPtr($fftstruct), $BASS_DATA_FFT256)
;~ _GDIPlus_GraphicsClear($backgra) ;;;;;;;
$lock=_GDIPlus_BitmapLockBits($bmp,0,0,$w,$h,$GDIP_ILMWRITE+$GDIP_ILMREAD,$GDIP_PXF32ARGB)
If @error Then _exit("could not lock $bmp bytes")
$ptr=DllStructGetData($lock,"Scan0")
_Visualize($ptr,$w,$h,$fftstruct)
_GDIPlus_BitmapUnlockBits($bmp,$lock)
_GDIPlus_GraphicsDrawImageRect($backgra,$bmp,0,0,$w,$h)
;menü?
_GDIPlus_GraphicsDrawImageRect($gra,$hbmp,0,0,$w,$h)
Sleep(10)
WEnd
_exit() ;not used
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func __process($ptr,$wh,$level,$speed)
Local $ret=DllCall($user32, "int", "CallWindowProcW", "ptr", $code,"ptr", $ptr,"int",$wh, "int", $level,"int",$speed)
If $ret[0]<>0 Then _exit("The ASM code returned this Errorcode: "&$ret[0])
EndFunc
Func _init(ByRef $code)
$fasm=FasmInit()
;Author: TheShadowAE (new nick: CreativeName)
FasmReset($fasm)
FasmAdd($fasm,"use32")
FasmAdd($fasm,"org "&FasmGetBasePtr($fasm))
;CPUID check for SSE4.1 (needed for movntdqa)
;//movntdqa is not needed. montdq is the only working instruction
;~ FasmAdd($fasm,"mov eax,1")
;~ FasmAdd($fasm,"cpuid")
;~ FasmAdd($fasm,"test ecx,10000000000000000000b")
;~ FasmAdd($fasm,"jnz @f")
;~ FasmAdd($fasm,"mov eax,123456");give special error return!
;~ FasmAdd($fasm,"ret") ;return with error code 123456
;~ FasmAdd($fasm,"@@:") ;you have SSE4.1
;parameters
FasmAdd($fasm,"mov edi,[esp+4]") ;image ptr
FasmAdd($fasm,"mov edx,[esp+8]") ;WWWWHHHH
FasmAdd($fasm,"mov ebx,[esp+12]") ;level
FasmAdd($fasm,"mov ecx,[esp+16]") ;speed
FasmAdd($fasm,"mov esi,edi") ;
;cache image data for faster process
FasmAdd($fasm,"prefetchnta [esi]") ;assuming (older) CPUs only cache 32 byte
FasmAdd($fasm,"prefetchnta [esi+32]")
FasmAdd($fasm,"prefetchnta [esi+64]")
FasmAdd($fasm,"prefetchnta [esi+96]")
;set up image pointers
FasmAdd($fasm,"push ebx") ;store level
FasmAdd($fasm,"movzx ebx,dx") ;ebx=height
FasmAdd($fasm,"shr edx,16") ;WH->0W ;not ror, height isnt needed anymore
FasmAdd($fasm,"movzx eax,dx") ;eax=width
FasmAdd($fasm,"shl eax,2") ;*4 bytes/pixel
FasmAdd($fasm,"push eax") ;store stride
FasmAdd($fasm,"imul eax,ecx") ;*speed
FasmAdd($fasm,"add esi,eax") ; source+=speed*stride
;calculate end of image
FasmAdd($fasm,"mov eax,[esp]") ;eax=stride
FasmAdd($fasm,"imul ebx,eax") ;ebx=stride*height=bytes used by image
FasmAdd($fasm,"add ebx,edi") ;ebx+=start adress (image pointer), ebx=ptr to byte after image (end)
;calculate needed loopx cycles
FasmAdd($fasm,"pop edx") ;restore stride to edx
FasmAdd($fasm,"shr edx,6") ;stride/64 (64 bytes per cycle processed), edx=loopx cycles
;scroll/copy downer pixel-lines speed times, in pixels, upside
FasmAdd($fasm,"pop eax") ; restore level to eax
FasmAdd($fasm,".loopy:") ;
FasmAdd($fasm,"mov ecx,edx") ;copy loopx cycles for loop to ecx
FasmAdd($fasm,".loopx:")
FasmAdd($fasm,"prefetchnta [esi+128]") ;more caching
FasmAdd($fasm,"prefetchnta [esi+160]")
FasmAdd($fasm,"movdqa xmm0,[esi]") ;get image data
FasmAdd($fasm,"movdqa xmm1,[esi+16]") ;parallel processing, less loop cycles, memory group, more registers used -> faster
FasmAdd($fasm,"movdqa xmm2,[esi+32]") ;
FasmAdd($fasm,"movdqa xmm3,[esi+48]") ;
FasmAdd($fasm,"movntdq [edi],xmm0") ; move non-temporal (bypass cache -> faster), write image data
FasmAdd($fasm,"movntdq [edi+16],xmm1") ;
FasmAdd($fasm,"movntdq [edi+32],xmm2") ;
FasmAdd($fasm,"movntdq [edi+48],xmm3") ;
FasmAdd($fasm,"add esi,64") ;increase pointers
FasmAdd($fasm,"add edi,64") ;
FasmAdd($fasm,"loop .loopx") ;next loopx cycle
FasmAdd($fasm,"cmp esi,ebx") ;check everything got copied (source>=end)
FasmAdd($fasm,"jb .loopy") ;if not (if source<end) repeat loopy
;//|now: still needed: edi=current image ptr,eax=level,ebx=end of image , in stack: nothing anymore
;fill the last lines with color
;calculate color from level (blue->50->green->50->red)
FasmAdd($fasm,"mov ecx,0xFF0000FF") ;alpha=255, blue=255, red=0 and green=0
FasmAdd($fasm,"cmp eax,255") ;need to sub more than 255? (so to red)
FasmAdd($fasm,"jg .greater") ;goto red method for no problems and faster
FasmAdd($fasm,"mov ch,120") ;start points for better colors (brighter)
FasmAdd($fasm,"sub cl,al") ;move "color points" from blue
FasmAdd($fasm,"add ch,al") ;to green
FasmAdd($fasm,"jnc @f")
FasmAdd($fasm,"mov ch,0xFF") ;fix overflow (if there were an overflow)
FasmAdd($fasm,"@@:")
FasmAdd($fasm,"jmp .colorfinish") ;calculated color, start writing
FasmAdd($fasm,".greater:") ;
FasmAdd($fasm,"sub eax,255") ;do not use the points for blue to green, only use green to red points
FasmAdd($fasm,"xchg ch,cl") ;start from green points
FasmAdd($fasm,"ror ecx,8") ;rotate needed bytes to ch and cl
FasmAdd($fasm,"mov ch,120") ;start points for better colors (brighter)
FasmAdd($fasm,"sub cl,al") ;move points from green
FasmAdd($fasm,"add ch,al") ;to red
FasmAdd($fasm,"jnc @f")
FasmAdd($fasm,"mov ch,0xFF") ;fix overflow (if there were an overflow)
FasmAdd($fasm,"@@:")
FasmAdd($fasm,"rol ecx,8") ;rotate bytes back to get valid color
FasmAdd($fasm,".colorfinish:") ;
;prepare to write data from xmm registers
FasmAdd($fasm,"movd xmm0,ecx") ;copy color to lwoer dword of xmm0
FasmAdd($fasm,"pshufd xmm0,xmm0,0") ;copy color to other dwords in xmm0
FasmAdd($fasm,"movdqa xmm1,xmm0") ;copy to the other register
FasmAdd($fasm,"movdqa xmm2,xmm0") ;
FasmAdd($fasm,"movdqa xmm3,xmm1") ;maybe this line processed parallel?
;start writing and fill the remaining image bytes
FasmAdd($fasm,".loopfill:") ;loop to fill remaining lines
FasmAdd($fasm,"movntdq [edi],xmm0") ; write color from registers
FasmAdd($fasm,"movntdq [edi+16],xmm1") ;
FasmAdd($fasm,"movntdq [edi+32],xmm2") ;
FasmAdd($fasm,"movntdq [edi+48],xmm3") ;
FasmAdd($fasm,"add edi,64") ;increase pointer
FasmAdd($fasm,"cmp edi,ebx") ;end of image? (see loopy comments)
FasmAdd($fasm,"jb .loopfill") ;if not repeat loopfill
FasmAdd($fasm,"xor eax,eax") ;no error, return 0
FasmAdd($fasm,"ret") ;return, the (simple) image calculation finished
;hope it is a good example for optimized assembly code
$bin=FasmGetBinary($fasm)
If @error Then
Local $Error = FasmGetLastError()
_exit("FasmGetBinary-Error:"&@CRLF&@CRLF&"Error Code: "&$Error[$FASMERRINDEX_CODE]&@CRLF&"Error Message: "& _
$Error[$FASMERRINDEX_MESSAGE]&@CRLF&"Error LineNumber: "&$Error[$FASMERRINDEX_LINENUMBER]& _
@CRLF&"Error Line: "&$Error[$FASMERRINDEX_LINE]&@CRLF)
EndIf
$codestruct=DllStructCreate("byte["&(StringLen($bin)/2-1)&"]")
DllStructSetData($codestruct,1,$bin)
$code=DllStructGetPtr($codestruct)
Return $codestruct
EndFunc
Func _Visualize($ptr,$w,$h,$data)
Local $bass=DllStructGetData($data,1,1)
$bass+=DllStructGetData($data,1,2)
$bass+=DllStructGetData($data,1,3)
$bass=Int($bass*$h/20)
If $bass<5 Then $bass=5
Local $level=0
For $x=1 To 30 ;die ersten 30 sind die meistens benutzen, der rest fast nie. geht viel schneller als 128 durchläufe
$level+=DllStructGetData($data,1,$x)
Next
$level=Int($level*240)
;speed=lastbass, color=0xFF0000FF->blue>>>green->green>>>red (level)
__process($ptr,BitOR(BitShift($w,-16),$h),$level,$bass) ;0xWWWWHHHH
EndFunc
Func _exit($txt="")
_BASS_ChannelStop($stream)
FasmExit($fasm)
DllClose($user32)
_GDIPlus_BitmapDispose($bmp)
_GDIPlus_GraphicsDispose($backgra)
_GDIPlus_BitmapDispose($hbmp)
_GDIPlus_GraphicsDispose($gra)
_GDIPlus_Shutdown()
_BASS_StreamFree($stream)
_BASS_Free()
If $txt<>"" Then MsgBox(0,"Error",$txt)
Exit
EndFunc