Stoppt die Vorratsdatenspeicherung! Jetzt klicken && handeln!Willst du auch bei der Aktion teilnehmen? Hier findest du alle relevanten Infos und Materialien:
 
Direct Draw und Direct Sound
von Plapperkatze am 11.März 2006 um 21:24
zurück zur Kategorie "Tutorials"

Hallo Freunde,
neulich stiess ich beim surfen auf http://www.deinmeister.de/w32asm3.htm
auf ein Tutorial von T$, das mich wirklich fasziniert hat (danke T$!). Es wird der aktuelle Eingangsdatenstrom der Soundkarte mit Direct Sound gelesen und mit Direct Draw visuell wiedergegeben, ein kleines Oszilloskop quasi.

Screenshot:


Den Quellcode gab es in Assembler, und ich habe ihn mal mit bcc32 in c++ umgesetzt. Das ganze ist schon ein bisschen komplex, überfliegen wird im Normalfall nicht genügen, wenn ihr wenig von DX wisst.

Man benötigt Header und Libraries für DirectX. ddraw.h, ddraw.lib, dsound.h, dsound.lib in unserem Fall. Wenn ihr das DX SDK habt, könnt ihr die Libs mit implib für bcc32 konvertieren, die Header findet man im Netz. Ich werde versuchen, Links zu passenden Seiten hier aufzulisten.

Die Libraries müssen gelinkt werden, also eingebunden. Das geht mit Renegade sehr einfach, unter Options->libs fügt ihr folgendes ein:
import32.lib cw32.lib ddraw.lib dsound.lib

Ohne Renegade kompiliert man mit:
bcc32 -c -tWM- -w -w-par -w-inl -W -a1 -Od quelldatei.cpp
link32 -aa -c -x -Gn quelldatei.obj c0w32.obj,quelldatei.exe,,import32.lib cw32.lib ddraw.lib dsound.lib,,

Ich habe den Quelltext in 4 Dateien aufgespalten.

In ddraw.cpp befindet sich alles, um Direct Draw in einem Fenster zu initialisieren. Es wird eine primary surface erzeugt, das ist unser Fenster, und eine secondary surface, der backbuffer, um flackerfrei zu zeichnen. Mit DDCleanUp() wird Direct Draw wieder "aufgeräumt". flip() zeichnet den backbuffer ins Fenster auf dem Schirm.
#include <ddraw.h>

LPDIRECTDRAW lpDD;                // DirectDraw object defined in DDRAW.H
LPDIRECTDRAWSURFACE lpDDSPrimary; // Zeichenfläche 1 (der screen)
LPDIRECTDRAWSURFACE lpDDSBack;    // Zeichenfläche 2 (backbuffer)
DDSURFACEDESC ddsd;               // Beschreibung einer Zeichenfläche
DDSCAPS ddscaps;                  // capabilities (fähigkeiten) der gfx
HRESULT ddrval;                   // rueckgabewert
LPDIRECTDRAWCLIPPER FAR lpClipper;// clipper, um nur im fenster zu zeichnen

// Direct Draw als Fenster (no Fullscreen) initialisieren
BOOL DDInitWindowed(HWND hwnd)
{
   // direct draw objekt erzeugen
   ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
   if( ddrval != DD_OK )
   {
       return FALSE;
   }
   // CooperativeLevel - wir wollen ein Fenster sein
   ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_NORMAL );
   if( ddrval != DD_OK )
   {
       lpDD->Release();
       return FALSE;
   }

   memset( &ddsd,0,sizeof(ddsd));
   ddsd.dwSize=sizeof(ddsd);
   ddsd.dwFlags=DDSD_CAPS;
   ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE;

   // Zeichenfläche 1 (der screen)
   if(lpDD->CreateSurface(&ddsd,&lpDDSPrimary,NULL)!=DD_OK)
   {
       lpDD->Release();
       return FALSE;
   }

   // clipper erzeugen, nur im Fenster zeichnen
   if(lpDD->CreateClipper(0,&lpClipper,NULL)!=DD_OK)
   {
       lpDDSPrimary->Release();
       lpDD->Release();
       return FALSE;
   }

   // setting it to our hwnd gives the clipper the coordinates from our window
   if(lpClipper->SetHWnd(0,hwnd)!=DD_OK)
   {
       lpClipper-> Release();
       lpDDSPrimary->Release();
       lpDD->Release();
       return FALSE;
   }

   // clipper gilt für Zeichenfläche 1
   if(lpDDSPrimary->SetClipper(lpClipper)!=DD_OK)
   {
       lpClipper-> Release();
       lpDDSPrimary->Release();
       lpDD->Release();
       return FALSE;
   }

   // Zeichenfläche 2 (backbuffer)
   memset( &ddsd, 0, sizeof(ddsd) );
   ddsd.dwSize = sizeof( ddsd );
   ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PITCH;
   ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
   ddsd.dwWidth = 256;
   ddsd.dwHeight = 256;
   ddsd.lPitch = 0;
   if(lpDD->CreateSurface(&ddsd,&lpDDSBack,NULL)!=DD_OK)
   {
       lpClipper-> Release();
       lpDDSPrimary->Release();
       lpDD->Release();
       return FALSE;
   }
   return TRUE;
}

BOOL flip(HWND hwnd)
{
   RECT rcRectSrc;
   RECT rcRectDest;
   POINT p;
   p.x = 0; p.y = 0;
   ClientToScreen(hwnd, &p);
   GetClientRect(hwnd, &rcRectDest);
   OffsetRect(&rcRectDest, p.x, p.y);
   SetRect(&rcRectSrc,0,0,256,256);
   if(lpDDSPrimary->Blt(&rcRectDest,lpDDSBack,&rcRectSrc,DDBLT_WAIT, NULL)!=DD_OK)
   return FALSE;
 return TRUE;
}

void DDCleanUp()
{
   if( lpDD != NULL )
   {
       if( lpDDSPrimary != NULL )
       {
           lpDDSPrimary->Release();
           lpDDSPrimary = NULL;
       }

       if( lpDDSBack != NULL )
       {
           lpDDSBack->Release();
           lpDDSBack = NULL;
       }
   }
   lpDD->Release();
   lpDD = NULL;
}

In dsound.cpp wird Direct Sound initialisiert, sehr nahe verwandt mit dem Init von Direct Draw. ReleaseDSound() gibt Direct Sound wieder frei:
#include <dsound.h>

LPDIRECTSOUND lpDirectSound;
LPDIRECTSOUNDBUFFER dsbSound;
WAVEFORMATEX wfx;
DSCBUFFERDESC dscbd;
LPDIRECTSOUNDCAPTURE lpdsc;
LPDIRECTSOUNDCAPTUREBUFFER lpdscbuf;

BOOL DSoundInit(HWND hwnd)
{
 if(DirectSoundCreate (NULL, &lpDirectSound, NULL) != DS_OK)
 {
   return (FALSE);
 }
 if(lpDirectSound->SetCooperativeLevel (hwnd, DSSCL_NORMAL) != DS_OK)
 {
   return (FALSE);
 }

 memset(&wfx,0,sizeof(wfx));
 wfx.wFormatTag=WAVE_FORMAT_PCM;
 wfx.nChannels=1;
 wfx.nSamplesPerSec=11025;
 wfx.nAvgBytesPerSec=11025;
 wfx.nBlockAlign=1;
 wfx.wBitsPerSample=8;
 wfx.cbSize=0;

 memset(&dscbd,0,20);
 dscbd.dwSize=20;
 dscbd.dwFlags=0;
 dscbd.dwBufferBytes=1024;
 dscbd.dwReserved=0;
 dscbd.lpwfxFormat=&wfx;

 if(DirectSoundCaptureCreate(NULL,&lpdsc,NULL)!= DS_OK)
 {
   return (FALSE);
 }

 if(lpdsc->CreateCaptureBuffer(&dscbd,&lpdscbuf,NULL)!= DS_OK)
 {
   return (FALSE);
 }

 if(lpdscbuf->Start(DSCBSTART_LOOPING)!= DS_OK)
 {
   return (FALSE);
 }

 return (TRUE);
}

void ReleaseDSound()
{
 if (dsbSound != NULL)
 {
   dsbSound->Release();
   dsbSound = NULL;
 }
 if (lpDirectSound != NULL)
 {
   lpDirectSound->Release();
   lpDirectSound = NULL;
 }
}

Nun die Zeichenroutine, die alles vollbringt, untergebracht in sigdraw.cpp. PutPixel ist dabei eine Hilfsfunktion, die den Zeichenpuffer sperrt, während wir reinzeichnen, drawSoundSignal() liest aus dem Soundpuffer:
unsigned char col1=0,col2=0,col3=0; // nur farben..

BOOL PutPixel( LPDIRECTDRAWSURFACE pDDS, int x, int y, int r, int g, int b )
{
 DDSURFACEDESC   ddsd;       // die zeichenfläche (surface)
 unsigned int    pixel;      // farbdaten des pixels aus r,b,g
 int             offset;     // pixeladresse aus x und y
 unsigned char * szSurface;  // zeiger auf surface
 int             pixelwidth=4; // true color, 4 bytes/pixel
 // init DDSURFACEDESC struct information
 ddsd.dwSize=sizeof(ddsd);

 // Calculate how the pixel is represented in memory
 pixel=(r<<16)|(g<<8)|(b);

 // Zeichenfläche (surface) sperren, wir schreiben
 if(pDDS->Lock( NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)!=DD_OK)
   return FALSE;

 // zeiger auf die zeichenfläche DDSURFACE
 szSurface = (unsigned char*)ddsd.lpSurface;

 // pixeladresse berechnen, lPitch weicht von lWidth evtl ab
 offset = (y*ddsd.lPitch + x*pixelwidth);

 // pixel in zeichenfläche schreiben
 memcpy( szSurface + offset, &pixel, pixelwidth );

 // surface freigeben
 if(pDDS->Unlock(NULL)!=DD_OK)
   return FALSE;

 return TRUE;
}

BOOL drawSoundSignal(HWND hwnd)
{
 DDBLTFX BlitDescription;
 DWORD CaptureLen=0,CaptureLen2=0,CaptureReadPos=0;
 LPVOID CapturePos=NULL,CapturePos2=NULL;

 // backbuffer löschen mit dwFillColor
 BlitDescription.dwSize=sizeof(DDBLTFX);
 BlitDescription.dwFillColor=RGB(64,32,0);
 lpDDSBack->Blt(0,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&BlitDescription);

 // aktuelle position bestimmen
 if(lpdscbuf->GetCurrentPosition(0,&CaptureReadPos) != DS_OK)
 {
   return FALSE;
 }
 // sound capture buffer sperren
 if(lpdscbuf->Lock(CaptureReadPos,256,&CapturePos,&CaptureLen,
     &CapturePos2,&CaptureLen2,NULL)==DS_OK)
 {
   // der puffer ist evtl geteilt in 2 bereiche, deshalb beide lesen
   // erster puffer
   for(unsigned int i=0;i<CaptureLen;i++)
   {
     unsigned char *ad=((unsigned char *)((unsigned long)CapturePos+i));
     PutPixel(lpDDSBack,i,*ad*2-128,col1,col2,col3);
   }
   // zweiter puffer
   for(unsigned int i=0;i<CaptureLen2;i++)
   {
     unsigned char *ad=((unsigned char *)((unsigned long)CapturePos2+i));
     PutPixel(lpDDSBack,CaptureLen+i,*ad*2-128,col1,col2,col3);
   }

   // unlock - sound capture buffer freigeben
   if(lpdscbuf->Unlock(CapturePos,CaptureLen,CapturePos2,CaptureLen2))
     SetWindowText(hwnd,"unlock failed");
 }
 col1=col1+5;
 col2=col2+7;
 col3=col3+13;
 return TRUE;
}

Fehlt nur noch der Code, um ein Fenster zu erzeugen, ddraw und dsound zu starten und drawSoundSignal() aufzurufen (oszi.cpp):
#include <windows.h>

// dsound.cpp mit
//DSoundInit(HWND hwnd) und ReleaseDSound()
#include "dsound.cpp" // initialisation DSound

// ddraw.cpp mit
// DDInitWindowed(HWND hwnd), flip(HWND hwnd) und DDCleanUp()
#include "ddraw.cpp" // initialisation DDraw

// sigdraw.cpp mit drawSoundSignal(HWND hwnd)
#include "sigdraw.cpp" // zeichenroutine


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

const char szAppName[]    = "ddraw oszi";

int width=640;
int height=400;

BOOL done=false;

int WINAPI WinMain(  HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
  MSG        msg;
  HWND       hWnd;

  WNDCLASS   wc;
 
  wc.style               = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc         = WndProc;
  wc.cbClsExtra          = 0;
  wc.cbWndExtra          = 0;
  wc.hInstance           = hInstance;
  wc.hCursor             = LoadCursor(NULL, IDC_ARROW);
  wc.hIcon               = LoadIcon(NULL, MAKEINTRESOURCE("1"));
  wc.hbrBackground       = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszClassName       = szAppName;
  wc.lpszMenuName        = NULL;

  RegisterClass(&wc);
 
  hWnd = CreateWindow(   szAppName,
                         szAppName,
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         width,height,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);

  ShowWindow(hWnd, iCmdShow);
  UpdateWindow(hWnd);
 
 // Message-Pump-Loop
 while(!done)
 {
   if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
   {
     if(msg.message==WM_QUIT)
     {
       done=TRUE;
     }
     else
     {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
     }
   }
   else
   {
     if(!drawSoundSignal(hWnd))PostQuitMessage(0);
     flip(hWnd);

   }
 }

 return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 switch(message)
 {
   case WM_CREATE:

   {
     if(!DDInitWindowed(hWnd))
     {
       MessageBox(hWnd, "DDraw init failed!", "Error",
              MB_OK | MB_ICONINFORMATION);
       PostQuitMessage(0);
     }      
     if(!DSoundInit(hWnd))
     {
       MessageBox(hWnd, "DSound init failed!", "Error",
              MB_OK | MB_ICONINFORMATION);
       PostQuitMessage(0);      
     }

   }
   break;
   case WM_DESTROY:
   {
     DDCleanUp();
     ReleaseDSound();
     PostQuitMessage(0);
     return 0;
   }
 }
 return DefWindowProc(hWnd, message, wParam, lParam);
}

Hier der Download, Source + Exe, 27kB:
http://katze.dead-men.de/upload/33_oszi.rar

dieses tut ist noch nicht fertig

gruesse, die plapperkatze

zurück zur Kategorie "Tutorials"
[0 Kommentare]

Name


Kommentar




Bitte abtippen


 
(C) 2006-20012 Plapperkatze - 220706 Besucher seit dem 23.01.2012 Login