Musik-Visualizer mit optimiertem ASM

  • 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 :D

    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 :P ^^

    Datei im Anhang!


    Viel Spaß, mfg
    CreativeName
    ehemals TheShadowAE


    EDIT:
    Für die, die den Sourcecode schon hier sehen wollen:

    Spoiler anzeigen
    [autoit]

    #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>

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $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

    [/autoit] [autoit][/autoit] [autoit]

    $w=800 ;must be dividable by 16
    $h=600 ;at least 10 :P
    $Form1 = GUICreate("Visualizer by CreativeName", $w, $h)
    GUISetOnEvent($GUI_EVENT_CLOSE,"_exit")

    [/autoit] [autoit][/autoit] [autoit]

    $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")

    [/autoit] [autoit][/autoit] [autoit]

    $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)

    [/autoit] [autoit][/autoit] [autoit]

    $fftstruct = DllStructCreate('float[128]')
    $fasm=0
    $code=""
    $codestruct=_init($code)

    [/autoit] [autoit][/autoit] [autoit]

    _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)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    _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

    [/autoit] [autoit][/autoit] [autoit]

    Func _init(ByRef $code)
    $fasm=FasmInit()
    ;Author: TheShadowAE (new nick: CreativeName)
    FasmReset($fasm)
    FasmAdd($fasm,"use32")
    FasmAdd($fasm,"org "&FasmGetBasePtr($fasm))

    [/autoit] [autoit][/autoit] [autoit]

    ;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 :)

    [/autoit] [autoit][/autoit] [autoit]

    ;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") ;

    [/autoit] [autoit][/autoit] [autoit]

    ;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]")

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit]

    ;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)

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit]

    ;scroll/copy downer pixel-lines speed times, in pixels, upside
    FasmAdd($fasm,"pop eax") ; restore level to eax
    FasmAdd($fasm,".loopy:") ;

    [/autoit] [autoit][/autoit] [autoit]

    FasmAdd($fasm,"mov ecx,edx") ;copy loopx cycles for loop to ecx
    FasmAdd($fasm,".loopx:")

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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 ^^ ;)

    [/autoit] [autoit][/autoit] [autoit]

    ;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:") ;

    [/autoit] [autoit][/autoit] [autoit]

    ;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?

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit]

    FasmAdd($fasm,"xor eax,eax") ;no error, return 0
    FasmAdd($fasm,"ret") ;return, the (simple) image calculation finished :D
    ;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

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit]
  • Hi,
    seeeeehr nice, vor allem die Musik ist gut :thumbup:

    Ggf. könnte man noch bissl mehr "Bewegung" in die Animation bringen, so linear wirds halt schnell langweilig 8o
    Wie wärs damit, den einzelnen Frequenzen bestimmte Positionen innerhalb des Fensters zuzuordnen und dort dann jeweils "Bass", Mittel- und Hochtöner zu visualisieren...


    Zitat

    dass Andy nichts großes mehr zum Optimieren findet


    Wie schon in der SB gesagt, da pfusch ich nicht mehr dran rum^^, wenns ausreichend schnell läuft, ist es genug optimiert :thumbup:

    //EDIT....und er kanns nicht lassen mit seinem Gemecker :D
    habe festgestellt, dass der Fensterinhalt "flackert", ich schiebe das jetzt einfach mal auf die GDI- und die "Lock"-funktion (die ich, wie bekannt, sowieso nicht leiden kann....)

    Spoiler anzeigen
    [autoit]

    While sleep(10)
    _BASS_ChannelGetData($stream, DllStructGetPtr($fftstruct), $BASS_DATA_FFT256)
    _Visualize($ptr, $w, $h, $fftstruct)
    _WinAPI_BitBlt($hdc_gui, 0, 0, $w, $h, $hdc_bitmap, 0, 0, $srccopy) ;Bitmap in die GUI blitten
    WEnd

    [/autoit]

    so flackerts nicht mehr....

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    2 Mal editiert, zuletzt von Andy (8. Januar 2013 um 20:08)