Let's compare Microsoft Visual C++ with Borland C++ Builder...
They are both "best of breed" technologies... but which one is better?
Using the TDx_Library, you can now do in 5-10 lines of Borland C++ Builder code
what you previously would do writing 50-100 lines of Visual C++ code... plus 1000's more lines to get DirectX working.
...
Let's start with Borland C++ Builder...
Let's write two applications to animate a bitmap full-screen, and play a sound effect out the speakers...
Here's the complete pseudo-code :-
#pragma link "TDx_Draw_Library_Install"
#pragma link "TDx_Sound_Library_Install"
void __fastcall TMainForm::Button1Click( TObject* Sender )
{
DirectDraw->Create(NULL);
DirectSound->Create(NULL);
DirectDraw->CreatePrimarySurface();
DirectSound->CreatePrimarySoundBuffer();
DirectDraw->Blt();
DirectSound->Play();
while (DirectSound->Playing)
{
DirectDraw->Flip();
Sleep(1);
}
}
And here is the actual code required to animate a bitmap full-screen, with lots of other stuff thrown in...
//---------------------------------------------------------------------------
// File: TMainForm.cpp, TDx_Draw_Library, Example9 - Fast Page Flipping
//
// Date: 7th January, 2004
// Author: Darren John Dwyer
// Copyright: (c) 2002-2004 Darren John Dwyer, All Rights Reserved.
//
// Description:
//
// This example shows how to partially optimize page flipping.
// It uses Application->OnIdle() instead of Timer1->OnTimer()
// It uses global TCanvas's, rather than creating and destroying
// each time a TCanvas is required.
//
// See later examples for increased frame rates.
//
//---------------------------------------------------------------------------
#include
#pragma hdrstop
//---------------------------------------------------------------------------
#include "TMainForm.h"
//---------------------------------------------------------------------------
#if (__BORLANDC__ >= 0x0530) // BCB Version 3 +
#pragma package(smart_init)
#endif
//---------------------------------------------------------------------------
#pragma link "tddscaps"
#pragma link "tddsurfacedesc"
#pragma link "tdx_draw"
#pragma link "tdx_drawsurface"
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
#pragma link "TDx_Draw"
#pragma link "TDx_DrawSurface"
#pragma link "TDDCaps"
#pragma link "TDDPixelFormat"
#pragma link "TDDSCaps"
#pragma link "TDDSurfaceDesc"
#pragma link "TDx_Draw_Library_Install"
//---------------------------------------------------------------------------
TMainForm *MainForm;
//---------------------------------------------------------------------------
AnsiString IntToHexStr( unsigned int hexvalue )
{
char* hexchars[16] = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
AnsiString string = "0x";
int value = 0x10000000;
for (int i=0;i<8;i++)
{
string += hexchars[hexvalue/value];
hexvalue -= value * (hexvalue/value);
value = value >> 4;
}
return string;
}
//---------------------------------------------------------------------------
#define Msg(A) {\
if (StatusMemo->Text.Length()>30000) StatusMemo->Text = "";\
StatusMemo->Lines->Add(A);\
}
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
__fastcall TMainForm::~TMainForm()
{
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
Top=0;
Left=0;
Width=800;
Height=600;
canvas = new TCanvas();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormDestroy(TObject *Sender)
{
Application->OnIdle = NULL;
if (Dx_Draw1->Created) Dx_Draw1->Destroy();
if (canvas) delete canvas;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ExitButtonClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClearButtonClick(TObject *Sender)
{
StatusMemo->Lines->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Draw1Create(TObject *Sender)
{
NumModes=0;
ModesListBox->Items->Clear();
Dx_Draw1->EnumDisplayModes(0,NULL,NULL);
Dx_Draw1->SetCooperativeLevel( Application->Handle, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE );
// create a complex primary surface
PrimarySurfaceDesc->Flags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
PrimarySurfaceDesc->BackBufferCount = 1;
PrimaryCaps->Caps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
PrimarySurfaceDesc->SCaps = PrimaryCaps;
PrimarySurface->Create( PrimarySurfaceDesc, Dx_Draw1 );
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Draw1Destroy(TObject *Sender)
{
PrimarySurface->Destroy();
Dx_Draw1->SetCooperativeLevel( Application->Handle, DDSCL_NORMAL );
if (Dx_Draw1->RestoreDisplayMode())
{
Top=0;
Left=0;
Width=800;
Height=600;
Invalidate();
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Draw1Error(TObject *Sender, AnsiString Function,
AnsiString Error, AnsiString ErrorMessage)
{
Msg("ERROR ["+Error+"]: "+Function+", "+ErrorMessage);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Draw1EnumDisplayModes(TObject *Sender,
TDDSurfaceDesc *SurfaceDesc, void *Context, bool &Finished)
{
int width = SurfaceDesc->Width;
int height = SurfaceDesc->Height;
int refresh_rate = SurfaceDesc->RefreshRate;
int bit_depth;
if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_PALETTEINDEXED1) bit_depth = 1;
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_PALETTEINDEXED2) bit_depth = 2;
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_PALETTEINDEXED4) bit_depth = 4;
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_PALETTEINDEXED8) bit_depth = 8;
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_PALETTEINDEXEDTO8) bit_depth = 8;
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_ALPHA) bit_depth = SurfaceDesc->PixelFormat->AlphaBitDepth[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_ALPHAPIXELS) bit_depth = SurfaceDesc->PixelFormat->AlphaBitDepth[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_ALPHAPREMULT) bit_depth = SurfaceDesc->PixelFormat->AlphaBitDepth[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_BUMPDUDV) bit_depth = SurfaceDesc->PixelFormat->BumpBitCount[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_BUMPLUMINANCE) bit_depth = SurfaceDesc->PixelFormat->BumpBitCount[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_RGB) bit_depth = SurfaceDesc->PixelFormat->RGBBitCount[0];
else if (SurfaceDesc->PixelFormat->Flags[0] & DDPF_YUV) bit_depth = SurfaceDesc->PixelFormat->YUVBitCount[0];
if (NumModesItems->Add( IntToStr(width)+"x"+IntToStr(height)+"x"+IntToStr(bit_depth)+" @ "+IntToStr(refresh_rate)+"Hz" );
}
else
Finished = true;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::NextModeButtonClick(TObject *Sender)
{
if (ModesListBox->Items->Count>0)
{
Msg("Selecting Next Available Mode");
if (ModesListBox->ItemIndex==ModesListBox->Items->Count-1) ModesListBox->ItemIndex=0;
else ModesListBox->ItemIndex = ModesListBox->ItemIndex + 1;
ModesListBox->OnClick(this);
}
else
ModesListBox->ItemIndex = -1;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ModesListBoxClick(TObject *Sender)
{
if (ModesListBox->ItemIndex!=-1)
{
int index = ModesListBox->ItemIndex;
AnsiString name = ModesListBox->Items->Strings[index];
Msg("Mode Selected: '"+name+"'");
CurrentModeLabel->Caption = name;
}
else
CurrentModeLabel->Caption = "-- default --";
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PrimarySurfaceCreate(TObject *Sender)
{
Msg(">>> Primary Surface Created");
// get it's attached backbuffer
if (PrimarySurface->GetAttachedSurface( BackBufferCaps, BackBuffer ))
// call the backbuffer::OnCreate() event manually.
BackBuffer->OnCreate(this);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PrimarySurfaceDestroy(TObject *Sender)
{
Msg(">>> Primary Surface Destroy");
if (BackBuffer->Created) BackBuffer->Destroy();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PrimarySurfaceSurfaceLost(TObject *Sender)
{
PrimarySurface->Restore();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ModesListBoxDblClick(TObject *Sender)
{
SelectModeButton->OnClick(this);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::SelectModeButtonClick(TObject *Sender)
{
if (ModesListBox->Items->Count>0)
{
ShowMessage("Please Note:\n\
You may experience difficulty restoring your display mode.\n\
To return to the original display mode at any time, press ALT-R (Restore) or ALT-X (eXit)" );
int index = ModesListBox->ItemIndex;
PrimarySurface->Destroy();
if (Dx_Draw1->SetDisplayMode( Widths[index], Heights[index], BitDepths[index], RefreshRates[index], 0 ))
{
Msg("SelectModeButton> Display mode has been set" );
}
PrimarySurface->Create( PrimarySurfaceDesc, Dx_Draw1 );
RepaintButton->OnClick(this);
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::RepaintButtonClick(TObject *Sender)
{
Application->OnIdle = NULL;
if (Dx_Draw1->Created) Dx_Draw1->FlipToGDISurface();
int old_top = Top;
Top=Screen->Height+1;
Invalidate();
Top=old_top;
Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::BackBufferCreate(TObject *Sender)
{
Msg(">>> BackBuffer Created");
Image2->AutoSize = false;
Image2->Stretch = true;
Image2->Width = Screen->Width;
Image2->Height = Screen->Height;
Image2->Canvas->StretchDraw( Image2->Canvas->ClipRect, Image1->Picture->Graphic );
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::BackBufferDestroy(TObject *Sender)
{
Msg(">>> BackBuffer Destroyed");
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::CreateButtonClick(TObject *Sender)
{
Dx_Draw1->Create(NULL);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FlipButtonClick(TObject *Sender)
{
ShowMessage("Please Note:\n\
After the primary surface has been painted the contents of the current form\n\
will no longer be visible though it is still active. To repaint the form\n\
Press ALT-R (Restore) or to exit the application, press ALT-X (eXit)" );
CurrentPageNumber = 0;
Application->OnIdle = OnIdle;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::OnIdle(TObject *Sender, bool& Done)
{
if (!BackBuffer->Created) return;
if (!PrimarySurface->Created) return;
// error-proof vcl code
HDC hdc;
if (BackBuffer->GetDC(&hdc))
{
try
{
// make sure to reassign the canvas so it paints in the right spot
canvas->Handle = hdc;
// paint the background star image
canvas->CopyRect( canvas->ClipRect, Image2->Canvas, Image2->Canvas->ClipRect );
// paint some text
canvas->Brush->Style = bsClear;
canvas->Pen->Style = psClear;
canvas->Font->Name = "Arial";
canvas->Font->Style = TFontStyles();
canvas->Font->Size = 20;
canvas->Font->Color = (TColor) RGB(0,0,0);
canvas->TextOut( 2, 2, "Paint onto the BackBuffer, then flip to the Primary Surface" );
canvas->Font->Color = (TColor) RGB(128,255,128);
canvas->TextOut( 0, 0, "Paint onto the BackBuffer, then flip to the Primary Surface" );
// and more text
canvas->Brush->Style = bsClear;
canvas->Font->Name = "Comic Sans MS";
canvas->Font->Style = TFontStyles() << fsBold << fsItalic << fsUnderline << fsStrikeOut;
canvas->Font->Size = 24;
canvas->Font->Color = (TColor) RGB(0,0,0);
canvas->TextOut( 102 + CurrentPageNumber, 42, "Frame# "+IntToStr(CurrentPageNumber) );
canvas->Font->Color = (TColor) RGB(128+CurrentPageNumber%128,255,128+CurrentPageNumber%128);
canvas->TextOut( 100 + CurrentPageNumber, 40, "Frame# "+IntToStr(CurrentPageNumber) );
// and remove our temporary canvas handle
canvas->Handle = NULL;
}
catch (Exception &e)
{
Application->OnIdle = NULL;
}
BackBuffer->ReleaseDC(hdc);
}
PrimarySurface->Flip( BackBuffer, DDFLIP_DONOTWAIT );
CurrentPageNumber++;
// continue immediately
Done = false;
}
//---------------------------------------------------------------------------
And here is the code to play a sound effect, with lots of other stuff thrown in...
//---------------------------------------------------------------------------
// File: TMainForm.cpp
//
// TDx_Sound_Library
// Example2 - Playing with Sounds
//
// Date: 9th January, 2004
// Author: Darren John Dwyer
// Copyright: (c) 2002-2004 Darren John Dwyer, All Rights Reserved.
//
// Description:
//
// This example shows how to manipulate sounds while they are playing,
// including adjusting the volume, pan and frequency.
//
//---------------------------------------------------------------------------
#include
#pragma hdrstop
#include "TMainForm.h"
//---------------------------------------------------------------------------
#pragma link "TDx_Sound"
#pragma link "TDx_SoundBuffer"
#pragma link "TDSCaps"
#pragma link "TDSBufferDesc"
#pragma link "TDx_Sound_Library_Install"
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
KnownDevices = new TStringList();
KnownModules = new TStringList();
KnownGuids = new TList();
DevicesListBox->Items->Clear();
if (Dx_Sound1->DSEnumerate( NULL ))
{
if (KnownDevices->Count>0)
{
DevicesListBox->ItemIndex = 0;
Dx_Sound1->Create( (GUID*) KnownGuids->Items[DevicesListBox->ItemIndex] );
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormDestroy(TObject *Sender)
{
if (Dx_Sound1->Created) Dx_Sound1->Destroy();
if (KnownGuids) delete KnownGuids;
if (KnownModules) delete KnownModules;
if (KnownDevices) delete KnownDevices;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::DevicesListBoxClick(TObject *Sender)
{
if (Dx_Sound1->Created) Dx_Sound1->Destroy();
NotCreatedLabel->Visible = !Dx_Sound1->Create( (GUID*) KnownGuids->Items[DevicesListBox->ItemIndex] );
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Sound1Destroy(TObject *Sender)
{
if (Dx_SoundBuffer1->Created) Dx_SoundBuffer1->Destroy();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_SoundBuffer1BufferLost(TObject *Sender)
{
Dx_SoundBuffer1->CreateFromFile( Edit1->Text, DSBufferDesc1, Dx_Sound1 );
Dx_SoundBuffer1->Play(0,0);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PlayButtonClick(TObject *Sender)
{
if (Paused)
{
Paused = false;
if (LoopingCheckBox->Checked) Dx_SoundBuffer1->Play(0,DSBPLAY_LOOPING);
else Dx_SoundBuffer1->Play(0,0);
return;
}
if (Dx_SoundBuffer1->Created) Dx_SoundBuffer1->Destroy();
if (Edit1->Text.Pos("*.WAV"))
{
// StatusMemo->Lines->Add( "Cannot select *.WAV" );
return;
}
if (Dx_SoundBuffer1->CreateFromFile( Edit1->Text, DSBufferDesc1, Dx_Sound1 ))
{
BufferSizeLabel->Caption = "Buffer Size: "+IntToStr(DSBufferDesc1->BufferBytes)+" bytes";
if (LoopingCheckBox->Checked) Dx_SoundBuffer1->Play(0,DSBPLAY_LOOPING);
else Dx_SoundBuffer1->Play(0,0);
}
else
ShowMessage("Couldn't Create From File");
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Timer1Timer(TObject *Sender)
{
if (!Dx_SoundBuffer1->Created) return;
if (Dx_SoundBuffer1->Playing)
PlayingLabel->Caption = "Buffer is Playing...";
else
PlayingLabel->Caption = "Buffer is Stopped.";
Dx_SoundBuffer1->GetCurrentPosition( &CurrentPlayPos, &CurrentWritePos );
BufferPosLabel->Caption = "Buffer Position: "+IntToStr(CurrentPlayPos);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_SoundBuffer1Create(TObject *Sender)
{
if (Dx_SoundBuffer1->Created)
{
Dx_SoundBuffer1->GetPan( &Pan );
Dx_SoundBuffer1->GetVolume( &Volume );
Dx_SoundBuffer1->GetFrequency( &Frequency );
PanScrollBar->Position = Pan;
VolumeScrollBar->Position = Volume;
FrequencyScrollBar->Position = Frequency/10; // see bugfix below
PanLabel->Caption = "Pan: "+IntToStr((int) Pan);
VolumeLabel->Caption = "Volume: "+IntToStr((int) Volume);
FrequencyLabel->Caption = "Frequency: "+IntToStr((int) Frequency);
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PanScrollBarChange(TObject *Sender)
{
Pan = PanScrollBar->Position;
if (Dx_SoundBuffer1->SetPan( Pan )) PanLabel->Caption = "Pan: "+IntToStr((int) Pan);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::VolumeScrollBarChange(TObject *Sender)
{
Volume = VolumeScrollBar->Position;
if (Dx_SoundBuffer1->SetVolume( Volume )) VolumeLabel->Caption = "Volume: "+IntToStr((int) Volume);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FrequencyScrollBarChange(TObject *Sender)
{
// There seems to be a bug in the TScrollBar, in which the TScrollBar does
// not seem to handle ranges 0..100,000 correctly and truncates values.
// So, we've fiddled with the FrequencyScrollBar's Position, Min, Max,
// SmallChange & LargeChange properties so they fit a smaller scale 0..10,000,
// which works appropriately.
Frequency = FrequencyScrollBar->Position * 10;
if (Dx_SoundBuffer1->SetFrequency( Frequency )) FrequencyLabel->Caption = "Frequency: "+IntToStr(Frequency);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_SoundBuffer1Destroy(TObject *Sender)
{
Dx_SoundBuffer1->Stop();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FileListBox1DblClick(TObject *Sender)
{
PlayButtonClick(this);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::StopButtonClick(TObject *Sender)
{
Dx_SoundBuffer1->Stop();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::RewindButtonClick(TObject *Sender)
{
Dx_SoundBuffer1->SetCurrentPosition(CurrentPlayPos-DSBufferDesc1->BufferBytes/10);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ForwardButtonClick(TObject *Sender)
{
Dx_SoundBuffer1->SetCurrentPosition(CurrentPlayPos+DSBufferDesc1->BufferBytes/10);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::PauseButtonClick(TObject *Sender)
{
if (Dx_SoundBuffer1->Playing)
{
Paused = Dx_SoundBuffer1->Stop();
PlayingLabel->Caption = "Buffer is Paused..";
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ExitButtonClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Sound1Create(TObject *Sender)
{
Dx_Sound1->SetCooperativeLevel(Application->Handle, DSSCL_PRIORITY);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Dx_Sound1DSEnumerate(TObject *Sender, GUID *Guid,
AnsiString DriverDescription, AnsiString DriverModule, void *Context,
bool &Finished)
{
if (KnownDevices && KnownModules && KnownGuids)
{
KnownDevices->Add( DriverDescription );
KnownModules->Add( DriverModule );
KnownGuids->Add( (void*) Guid );
DevicesListBox->Items->Add( DriverDescription + ", ("+DriverModule+")" );
}
else
Finished = true;
}
//---------------------------------------------------------------------------
Let's do the same thing, with much less fancy stuff thrown in, using Microsoft Visual C++...
Let's write two applications to animate a bitmap full-screen, and play a sound effect our the speakers...
Here's the complete code to animate a bitmap full-screen :-
//-----------------------------------------------------------------------------
// File: DDEx3.CPP
//
// Desc: Direct Draw example program 3. Adds functionality to
// example program 2. Creates two offscreen surfaces in
// addition to the primary surface and back buffer. Loads
// a bitmap file into each offscreen surface. Uses BltFast
// to copy the contents of an offscreen surface to the back
// buffer and then flips the buffers and copies the next
// offscreen surface to the back buffer. Press F12 to exit
// the program. This program requires at least 1.2 Megs of
// video ram.
//
// Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include
#include
#include
#include
#include "resource.h"
#include "ddutil.h"
//-----------------------------------------------------------------------------
// Local definitions
//-----------------------------------------------------------------------------
#define NAME "DDExample3"
#define TITLE "Direct Draw Example 3"
//-----------------------------------------------------------------------------
// Default settings
//-----------------------------------------------------------------------------
#define TIMER_ID 1
#define TIMER_RATE 500
//-----------------------------------------------------------------------------
// Global data
//-----------------------------------------------------------------------------
LPDIRECTDRAW7 g_pDD = NULL; // DirectDraw object
LPDIRECTDRAWSURFACE7 g_pDDSPrimary = NULL;// DirectDraw primary surface
LPDIRECTDRAWSURFACE7 g_pDDSBack = NULL; // DirectDraw back surface
LPDIRECTDRAWSURFACE7 g_pDDSOne = NULL; // Offscreen surface 1
LPDIRECTDRAWSURFACE7 g_pDDSTwo = NULL; // Offscreen surface 2
LPDIRECTDRAWPALETTE g_pDDPal = NULL; // The primary surface palette
BOOL g_bActive = FALSE; // Is application active?
//-----------------------------------------------------------------------------
// Local data
//-----------------------------------------------------------------------------
// Name of our bitmap resource.
static char szBitmap[] = "DDEX3";
//-----------------------------------------------------------------------------
// Name: ReleaseAllObjects()
// Desc: Finished with all objects we use; release them
//-----------------------------------------------------------------------------
static void
ReleaseAllObjects(void)
{
if (g_pDD != NULL)
{
if (g_pDDSPrimary != NULL)
{
g_pDDSPrimary->Release();
g_pDDSPrimary = NULL;
}
if (g_pDDSOne != NULL)
{
g_pDDSOne->Release();
g_pDDSOne = NULL;
}
if (g_pDDSTwo != NULL)
{
g_pDDSTwo->Release();
g_pDDSTwo = NULL;
}
if (g_pDDPal != NULL)
{
g_pDDPal->Release();
g_pDDPal = NULL;
}
g_pDD->Release();
g_pDD = NULL;
}
}
//-----------------------------------------------------------------------------
// Name: InitFail()
// Desc: This function is called if an initialization function fails
//-----------------------------------------------------------------------------
HRESULT
InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError,...)
{
char szBuff[128];
va_list vl;
va_start(vl, szError);
vsprintf(szBuff, szError, vl);
ReleaseAllObjects();
MessageBox(hWnd, szBuff, TITLE, MB_OK);
DestroyWindow(hWnd);
va_end(vl);
return hRet;
}
//-----------------------------------------------------------------------------
// Name: InitSurfaces()
// Desc: This function reads the bitmap file FRNTBACK.BMP and stores half of it
// in offscreen surface 1 and the other half in offscreen surface 2.
//-----------------------------------------------------------------------------
BOOL
InitSurfaces(void)
{
HBITMAP hbm;
// Load our bitmap resource.
hbm = (HBITMAP) LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, 0,
0, LR_CREATEDIBSECTION);
if (hbm == NULL)
return FALSE;
DDCopyBitmap(g_pDDSOne, hbm, 0, 0, 640, 480);
DDCopyBitmap(g_pDDSTwo, hbm, 0, 480, 640, 480);
DeleteObject(hbm);
return TRUE;
}
//-----------------------------------------------------------------------------
// Name: RestoreAll()
// Desc: Restore all lost objects
//-----------------------------------------------------------------------------
HRESULT
RestoreAll(void)
{
HRESULT hRet;
hRet = g_pDDSPrimary->Restore();
if (hRet == DD_OK)
{
hRet = g_pDDSOne->Restore();
if (hRet == DD_OK)
{
hRet = g_pDDSTwo->Restore();
if (hRet == DD_OK)
{
InitSurfaces();
}
}
}
return hRet;
}
//-----------------------------------------------------------------------------
// Name: UpdateFrame()
// Desc: Displays the proper image for the page
//-----------------------------------------------------------------------------
static void
UpdateFrame(HWND hWnd)
{
static BYTE phase = 0;
HRESULT hRet;
LPDIRECTDRAWSURFACE7 pdds;
RECT rcRect;
rcRect.left = 0;
rcRect.top = 0;
rcRect.right = 640;
rcRect.bottom = 480;
if (phase)
{
pdds = g_pDDSTwo;
phase = 0;
}
else
{
pdds = g_pDDSOne;
phase = 1;
}
while (TRUE)
{
hRet = g_pDDSBack->BltFast(0, 0, pdds, &rcRect, FALSE);
if (hRet == DD_OK)
break;
if (hRet == DDERR_SURFACELOST)
{
hRet = RestoreAll();
if (hRet != DD_OK)
break;
}
if (hRet != DDERR_WASSTILLDRAWING)
break;
}
}
//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The Main Window Procedure
//-----------------------------------------------------------------------------
long FAR PASCAL
WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HRESULT hRet;
switch (message)
{
case WM_ACTIVATE:
// Pause if minimized
g_bActive = !((BOOL)HIWORD(wParam));
return 0L;
case WM_DESTROY:
// Clean up and close the app
ReleaseAllObjects();
PostQuitMessage(0);
return 0L;
case WM_KEYDOWN:
// Handle any non-accelerated key commands
switch (wParam)
{
case VK_ESCAPE:
case VK_F12:
PostMessage(hWnd, WM_CLOSE, 0, 0);
return 0L;
}
break;
case WM_SETCURSOR:
// Turn off the cursor since this is a full-screen app
SetCursor(NULL);
return TRUE;
case WM_TIMER:
// Update and flip surfaces
if (g_bActive && TIMER_ID == wParam)
{
UpdateFrame(hWnd);
while (TRUE)
{
hRet = g_pDDSPrimary->Flip(NULL, 0);
if (hRet == DD_OK)
break;
if (hRet == DDERR_SURFACELOST)
{
hRet = RestoreAll();
if (hRet != DD_OK)
break;
}
if (hRet != DDERR_WASSTILLDRAWING)
break;
}
}
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Name: InitApp()
// Desc: Do work required for every instance of the application:
// Create the window, initialize data
//-----------------------------------------------------------------------------
static HRESULT
InitApp(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
WNDCLASS wc;
DDSURFACEDESC2 ddsd;
DDSCAPS2 ddscaps;
HRESULT hRet;
// Set up and register window class
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NAME;
wc.lpszClassName = NAME;
RegisterClass(&wc);
// Create a window
hWnd = CreateWindowEx(WS_EX_TOPMOST,
NAME,
TITLE,
WS_POPUP,
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
SetFocus(hWnd);
///////////////////////////////////////////////////////////////////////////
// Create the main DirectDraw object
///////////////////////////////////////////////////////////////////////////
hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "DirectDrawCreateEx FAILED");
// Get exclusive mode
hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");
// Set the video mode to 640x480x8
hRet = g_pDD->SetDisplayMode(640, 480, 8, 0, 0);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "SetDisplayMode FAILED");
// Create the primary surface with 1 back buffer
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "CreateSurface FAILED");
// Get a pointer to the back buffer
ZeroMemory(&ddscaps, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");
// Create a offscreen bitmap.
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwHeight = 480;
ddsd.dwWidth = 640;
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSOne, NULL);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "CreateSurface FAILED");
// Create another offscreen bitmap.
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSTwo, NULL);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "CreateSurface FAILED");
// Create a Direct Draw Palette and associate it with the front buffer
g_pDDPal = DDLoadPalette(g_pDD, szBitmap);
if (g_pDDPal)
g_pDDSPrimary->SetPalette(g_pDDPal);
if (!InitSurfaces())
return InitFail(hWnd, hRet, "InitSurfaces FAILED");
// Create a timer to flip the pages
if (TIMER_ID != SetTimer(hWnd, TIMER_ID, TIMER_RATE, NULL))
return InitFail(hWnd, hRet, "SetTimer FAILED");
return DD_OK;
}
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Initialization, message loop
//-----------------------------------------------------------------------------
int PASCAL
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
if (InitApp(hInstance, nCmdShow) != DD_OK)
return FALSE;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Plus you need this code as well, to actually load the bitmap
//-----------------------------------------------------------------------------
// File: ddutil.cpp
//
// Desc: Routines for loading bitmap and palettes from resources
//
//
// Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include
#include
#include
#include "ddutil.h"
//-----------------------------------------------------------------------------
// Name: DDLoadBitmap()
// Desc: Create a DirectDrawSurface from a bitmap resource.
//-----------------------------------------------------------------------------
extern "C" IDirectDrawSurface7*
DDLoadBitmap(IDirectDraw7 * pdd, LPCSTR szBitmap, int dx, int dy)
{
HBITMAP hbm;
BITMAP bm;
DDSURFACEDESC2 ddsd;
IDirectDrawSurface7 *pdds;
//
// Try to load the bitmap as a resource, if that fails, try it as a file
//
hbm = (HBITMAP) LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, dx,
dy, LR_CREATEDIBSECTION);
if (hbm == NULL)
hbm = (HBITMAP) LoadImage(NULL, szBitmap, IMAGE_BITMAP, dx, dy,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (hbm == NULL)
return NULL;
//
// Get size of the bitmap
//
GetObject(hbm, sizeof(bm), &bm);
//
// Create a DirectDrawSurface for this bitmap
//
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = bm.bmWidth;
ddsd.dwHeight = bm.bmHeight;
if (pdd->CreateSurface(&ddsd, &pdds, NULL) != DD_OK)
return NULL;
DDCopyBitmap(pdds, hbm, 0, 0, 0, 0);
DeleteObject(hbm);
return pdds;
}
//-----------------------------------------------------------------------------
// Name: DDReLoadBitmap()
// Desc: Load a bitmap from a file or resource into a directdraw surface.
// normaly used to re-load a surface after a restore.
//-----------------------------------------------------------------------------
HRESULT
DDReLoadBitmap(IDirectDrawSurface7 * pdds, LPCSTR szBitmap)
{
HBITMAP hbm;
HRESULT hr;
//
// Try to load the bitmap as a resource, if that fails, try it as a file
//
hbm = (HBITMAP) LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, 0,
0, LR_CREATEDIBSECTION);
if (hbm == NULL)
hbm = (HBITMAP) LoadImage(NULL, szBitmap, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (hbm == NULL)
{
OutputDebugString("handle is null\n");
return E_FAIL;
}
hr = DDCopyBitmap(pdds, hbm, 0, 0, 0, 0);
if (hr != DD_OK)
{
OutputDebugString("ddcopybitmap failed\n");
}
DeleteObject(hbm);
return hr;
}
//-----------------------------------------------------------------------------
// Name: DDCopyBitmap()
// Desc: Draw a bitmap into a DirectDrawSurface
//-----------------------------------------------------------------------------
extern "C" HRESULT
DDCopyBitmap(IDirectDrawSurface7 * pdds, HBITMAP hbm, int x, int y,
int dx, int dy)
{
HDC hdcImage;
HDC hdc;
BITMAP bm;
DDSURFACEDESC2 ddsd;
HRESULT hr;
if (hbm == NULL || pdds == NULL)
return E_FAIL;
//
// Make sure this surface is restored.
//
pdds->Restore();
//
// Select bitmap into a memoryDC so we can use it.
//
hdcImage = CreateCompatibleDC(NULL);
if (!hdcImage)
OutputDebugString("createcompatible dc failed\n");
SelectObject(hdcImage, hbm);
//
// Get size of the bitmap
//
GetObject(hbm, sizeof(bm), &bm);
dx = dx == 0 ? bm.bmWidth : dx; // Use the passed size, unless zero
dy = dy == 0 ? bm.bmHeight : dy;
//
// Get size of surface.
//
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
pdds->GetSurfaceDesc(&ddsd);
if ((hr = pdds->GetDC(&hdc)) == DD_OK)
{
StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y,
dx, dy, SRCCOPY);
pdds->ReleaseDC(hdc);
}
DeleteDC(hdcImage);
return hr;
}
//-----------------------------------------------------------------------------
// Name: DDLoadPalette()
// Desc: Create a DirectDraw palette object from a bitmap resource
// if the resource does not exist or NULL is passed create a
// default 332 palette.
//-----------------------------------------------------------------------------
extern "C" IDirectDrawPalette *
DDLoadPalette(IDirectDraw7 * pdd, LPCSTR szBitmap)
{
IDirectDrawPalette *ddpal;
int i;
int n;
int fh;
HRSRC h;
LPBITMAPINFOHEADER lpbi;
PALETTEENTRY ape[256];
RGBQUAD *prgb;
//
// Build a 332 palette as the default.
//
for (i = 0; i < 256; i++)
{
ape[i].peRed = (BYTE) (((i >> 5) & 0x07) * 255 / 7);
ape[i].peGreen = (BYTE) (((i >> 2) & 0x07) * 255 / 7);
ape[i].peBlue = (BYTE) (((i >> 0) & 0x03) * 255 / 3);
ape[i].peFlags = (BYTE) 0;
}
//
// Get a pointer to the bitmap resource.
//
if (szBitmap && (h = FindResource(NULL, szBitmap, RT_BITMAP)))
{
lpbi = (LPBITMAPINFOHEADER) LockResource(LoadResource(NULL, h));
if (!lpbi)
OutputDebugString("lock resource failed\n");
prgb = (RGBQUAD *) ((BYTE *) lpbi + lpbi->biSize);
if (lpbi == NULL || lpbi->biSize < sizeof(BITMAPINFOHEADER))
n = 0;
else if (lpbi->biBitCount > 8)
n = 0;
else if (lpbi->biClrUsed == 0)
n = 1 << lpbi->biBitCount;
else
n = lpbi->biClrUsed;
//
// A DIB color table has its colors stored BGR not RGB
// so flip them around.
//
for (i = 0; i < n; i++)
{
ape[i].peRed = prgb[i].rgbRed;
ape[i].peGreen = prgb[i].rgbGreen;
ape[i].peBlue = prgb[i].rgbBlue;
ape[i].peFlags = 0;
}
}
else if (szBitmap && (fh = _lopen(szBitmap, OF_READ)) != -1)
{
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
_lread(fh, &bf, sizeof(bf));
_lread(fh, &bi, sizeof(bi));
_lread(fh, ape, sizeof(ape));
_lclose(fh);
if (bi.biSize != sizeof(BITMAPINFOHEADER))
n = 0;
else if (bi.biBitCount > 8)
n = 0;
else if (bi.biClrUsed == 0)
n = 1 << bi.biBitCount;
else
n = bi.biClrUsed;
//
// A DIB color table has its colors stored BGR not RGB
// so flip them around.
//
for (i = 0; i < n; i++)
{
BYTE r = ape[i].peRed;
ape[i].peRed = ape[i].peBlue;
ape[i].peBlue = r;
}
}
pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
return ddpal;
}
//-----------------------------------------------------------------------------
// Name: DDColorMatch()
// Desc: Convert a RGB color to a pysical color.
// We do this by leting GDI SetPixel() do the color matching
// then we lock the memory and see what it got mapped to.
//-----------------------------------------------------------------------------
extern "C" DWORD
DDColorMatch(IDirectDrawSurface7 * pdds, COLORREF rgb)
{
COLORREF rgbT;
HDC hdc;
DWORD dw = CLR_INVALID;
DDSURFACEDESC2 ddsd;
HRESULT hres;
//
// Use GDI SetPixel to color match for us
//
if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
{
rgbT = GetPixel(hdc, 0, 0); // Save current pixel value
SetPixel(hdc, 0, 0, rgb); // Set our value
pdds->ReleaseDC(hdc);
}
//
// Now lock the surface so we can read back the converted color
//
ddsd.dwSize = sizeof(ddsd);
while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING)
;
if (hres == DD_OK)
{
dw = *(DWORD *) ddsd.lpSurface; // Get DWORD
if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // Mask it to bpp
pdds->Unlock(NULL);
}
//
// Now put the color that was there back.
//
if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
{
SetPixel(hdc, 0, 0, rgbT);
pdds->ReleaseDC(hdc);
}
return dw;
}
//-----------------------------------------------------------------------------
// Name: DDSetColorKey()
// Desc: Set a color key for a surface, given a RGB.
// If you pass CLR_INVALID as the color key, the pixel
// in the upper-left corner will be used.
//-----------------------------------------------------------------------------
extern "C" HRESULT
DDSetColorKey(IDirectDrawSurface7 * pdds, COLORREF rgb)
{
DDCOLORKEY ddck;
ddck.dwColorSpaceLowValue = DDColorMatch(pdds, rgb);
ddck.dwColorSpaceHighValue = ddck.dwColorSpaceLowValue;
return pdds->SetColorKey(DDCKEY_SRCBLT, &ddck);
}
...
And here's the code to play a sound effect...
//-----------------------------------------------------------------------------
// File: PlaySound.cpp
//
// Desc: DirectSound support for how to load a wave file and play it using a
// static DirectSound buffer.
//
// Copyright (c) 1999 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#include
#include
#include
#include
#include
#include "resource.h"
#include "WavRead.h"
//-----------------------------------------------------------------------------
// Function-prototypes
//-----------------------------------------------------------------------------
extern VOID OnEnablePlayUI( HWND hDlg, BOOL bEnable );
extern VOID SetFileUI( HWND hDlg, TCHAR* strFileName );
VOID LoadWaveFile( TCHAR* strFileName );
HRESULT CreateStaticBuffer( HWND hDlg, TCHAR* strFileName );
HRESULT FillBuffer();
HRESULT RestoreBuffers();
//-----------------------------------------------------------------------------
// Defines, constants, and global variables
//-----------------------------------------------------------------------------
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
LPDIRECTSOUND g_pDS = NULL;
LPDIRECTSOUNDBUFFER g_pDSBuffer = NULL;
LPDIRECTSOUNDNOTIFY g_pDSNotify = NULL;
CWaveSoundRead* g_pWaveSoundRead = NULL;
DWORD g_dwBufferBytes;
//-----------------------------------------------------------------------------
// Name: InitDirectSound()
// Desc: Initilizes DirectSound
//-----------------------------------------------------------------------------
HRESULT InitDirectSound( HWND hDlg )
{
HRESULT hr;
LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
// Initialize COM
if( hr = CoInitialize( NULL ) )
return hr;
// Create IDirectSound using the primary sound device
if( FAILED( hr = DirectSoundCreate( NULL, &g_pDS, NULL ) ) )
return hr;
// Set coop level to DSSCL_PRIORITY
if( FAILED( hr = g_pDS->SetCooperativeLevel( hDlg, DSSCL_PRIORITY ) ) )
return hr;
// Get the primary buffer
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;
if( FAILED( hr = g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
return hr;
// Set primary buffer format to 22kHz and 16-bit output.
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 22050;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )
return hr;
SAFE_RELEASE( pDSBPrimary );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: FreeDirectSound()
// Desc: Releases DirectSound
//-----------------------------------------------------------------------------
HRESULT FreeDirectSound()
{
SAFE_DELETE( g_pWaveSoundRead );
// Release DirectSound interfaces
SAFE_RELEASE( g_pDSBuffer );
SAFE_RELEASE( g_pDS );
// Release COM
CoUninitialize();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: LoadWaveFile()
// Desc: Loads the wave file into a secondary static DirectSound buffer
//-----------------------------------------------------------------------------
VOID LoadWaveFile( HWND hDlg, TCHAR* strFileName )
{
// Create the sound buffer object from the wave file data
if( FAILED( CreateStaticBuffer( hDlg, strFileName ) ) )
{
SetFileUI( hDlg, TEXT("Couldn't create sound buffer.") );
}
else // The sound buffer was successfully created
{
// Fill the buffer with wav data
FillBuffer();
// Update the UI controls to show the sound as the file is loaded
SetFileUI( hDlg, strFileName );
OnEnablePlayUI( hDlg, TRUE );
}
}
//-----------------------------------------------------------------------------
// Name: CreateStaticBuffer()
// Desc: Creates a wave file, sound buffer and notification events
//-----------------------------------------------------------------------------
HRESULT CreateStaticBuffer( HWND hDlg, TCHAR* strFileName )
{
HRESULT hr;
// Free any previous globals
SAFE_DELETE( g_pWaveSoundRead );
SAFE_RELEASE( g_pDSBuffer );
// Create a new wave file class
g_pWaveSoundRead = new CWaveSoundRead();
// Load the wave file
if( FAILED( g_pWaveSoundRead->Open( strFileName ) ) )
{
SetFileUI( hDlg, TEXT("Bad wave file.") );
}
// Set up the direct sound buffer, and only request the flags needed
// since each requires some overhead and limits if the buffer can
// be hardware accelerated
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_STATIC;
dsbd.dwBufferBytes = g_pWaveSoundRead->m_ckIn.cksize;
dsbd.lpwfxFormat = g_pWaveSoundRead->m_pwfx;
// Create the static DirectSound buffer
if( FAILED( hr = g_pDS->CreateSoundBuffer( &dsbd, &g_pDSBuffer, NULL ) ) )
return hr;
// Remember how big the buffer is
g_dwBufferBytes = dsbd.dwBufferBytes;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: FillBuffer()
// Desc: Fill the DirectSound buffer with data from the wav file
//-----------------------------------------------------------------------------
HRESULT FillBuffer()
{
HRESULT hr;
BYTE* pbWavData; // Pointer to actual wav data
UINT cbWavSize; // Size of data
VOID* pbData = NULL;
VOID* pbData2 = NULL;
DWORD dwLength;
DWORD dwLength2;
// The size of wave data is in pWaveFileSound->m_ckIn
INT nWaveFileSize = g_pWaveSoundRead->m_ckIn.cksize;
// Allocate that buffer.
pbWavData = new BYTE[ nWaveFileSize ];
if( NULL == pbWavData )
return E_OUTOFMEMORY;
if( FAILED( hr = g_pWaveSoundRead->Read( nWaveFileSize,
pbWavData,
&cbWavSize ) ) )
return hr;
// Reset the file to the beginning
g_pWaveSoundRead->Reset();
// Lock the buffer down
if( FAILED( hr = g_pDSBuffer->Lock( 0, g_dwBufferBytes, &pbData, &dwLength,
&pbData2, &dwLength2, 0L ) ) )
return hr;
// Copy the memory to it.
memcpy( pbData, pbWavData, g_dwBufferBytes );
// Unlock the buffer, we don't need it anymore.
g_pDSBuffer->Unlock( pbData, g_dwBufferBytes, NULL, 0 );
pbData = NULL;
// We dont need the wav file data buffer anymore, so delete it
SAFE_DELETE( pbWavData );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: PlayBuffer()
// Desc: User hit the "Play" button, so play the DirectSound buffer
//-----------------------------------------------------------------------------
BOOL IsBufferPlaying()
{
DWORD dwStatus = 0;
if( NULL == g_pDSBuffer )
return E_FAIL;
g_pDSBuffer->GetStatus( &dwStatus );
if( dwStatus & DSBSTATUS_PLAYING )
return TRUE;
else
return FALSE;
}
//-----------------------------------------------------------------------------
// Name: PlayBuffer()
// Desc: User hit the "Play" button, so play the DirectSound buffer
//-----------------------------------------------------------------------------
HRESULT PlayBuffer( BOOL bLooped )
{
HRESULT hr;
if( NULL == g_pDSBuffer )
return E_FAIL;
// Restore the buffers if they are lost
if( FAILED( hr = RestoreBuffers() ) )
return hr;
// Play buffer
DWORD dwLooped = bLooped ? DSBPLAY_LOOPING : 0L;
if( FAILED( hr = g_pDSBuffer->Play( 0, 0, dwLooped ) ) )
return hr;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: StopBuffer()
// Desc: Stop the DirectSound buffer from playing
//-----------------------------------------------------------------------------
VOID StopBuffer( BOOL bResetPosition )
{
if( NULL == g_pDSBuffer )
return;
g_pDSBuffer->Stop();
if( bResetPosition )
g_pDSBuffer->SetCurrentPosition( 0L );
}
//-----------------------------------------------------------------------------
// Name: IsSoundPlaying()
// Desc: Checks to see if a sound is playing and returns TRUE if it is.
//-----------------------------------------------------------------------------
BOOL IsSoundPlaying()
{
if( g_pDSBuffer )
{
DWORD dwStatus = 0;
g_pDSBuffer->GetStatus( &dwStatus );
return( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
}
else
{
return FALSE;
}
}
//-----------------------------------------------------------------------------
// Name: RestoreBuffers()
// Desc: Restore lost buffers and fill them up with sound if possible
//-----------------------------------------------------------------------------
HRESULT RestoreBuffers()
{
HRESULT hr;
if( NULL == g_pDSBuffer )
return S_OK;
DWORD dwStatus;
if( FAILED( hr = g_pDSBuffer->GetStatus( &dwStatus ) ) )
return hr;
if( dwStatus & DSBSTATUS_BUFFERLOST )
{
// Since the app could have just been activated, then
// DirectSound may not be giving us control yet, so
// the restoring the buffer may fail.
// If it does, sleep until DirectSound gives us control.
do
{
hr = g_pDSBuffer->Restore();
if( hr == DSERR_BUFFERLOST )
Sleep( 10 );
}
while( hr = g_pDSBuffer->Restore() );
if( FAILED( hr = FillBuffer() ) )
return hr;
}
return S_OK;
}
Plus you need this code as well, to load the sound data from a .wav file
//-----------------------------------------------------------------------------
// File: WavRead.cpp
//
// Desc: Wave file support for loading and playing Wave files using DirectSound
// buffers.
//
// Copyright (c) 1999 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#include
#include "WavRead.h"
//-----------------------------------------------------------------------------
// Defines, constants, and global variables
//-----------------------------------------------------------------------------
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
//-----------------------------------------------------------------------------
// Name: ReadMMIO()
// Desc: Support function for reading from a multimedia I/O stream
//-----------------------------------------------------------------------------
HRESULT ReadMMIO( HMMIO hmmioIn, MMCKINFO* pckInRIFF, WAVEFORMATEX** ppwfxInfo )
{
MMCKINFO ckIn; // chunk info. for general use.
PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in.
*ppwfxInfo = NULL;
if( ( 0 != mmioDescend( hmmioIn, pckInRIFF, NULL, 0 ) ) )
return E_FAIL;
if( (pckInRIFF->ckid != FOURCC_RIFF) ||
(pckInRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
return E_FAIL;
// Search the input file for for the 'fmt ' chunk.
ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
if( 0 != mmioDescend(hmmioIn, &ckIn, pckInRIFF, MMIO_FINDCHUNK) )
return E_FAIL;
// Expect the 'fmt' chunk to be at least as large as ;
// if there are extra parameters at the end, we'll ignore them
if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
return E_FAIL;
// Read the 'fmt ' chunk into .
if( mmioRead( hmmioIn, (HPSTR) &pcmWaveFormat,
sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
return E_FAIL;
// Allocate the waveformatex, but if its not pcm format, read the next
// word, and thats how many extra bytes to allocate.
if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
{
if( NULL == ( *ppwfxInfo = new WAVEFORMATEX ) )
return E_FAIL;
// Copy the bytes from the pcm structure to the waveformatex structure
memcpy( *ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat) );
(*ppwfxInfo)->cbSize = 0;
}
else
{
// Read in length of extra bytes.
WORD cbExtraBytes = 0L;
if( mmioRead( hmmioIn, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
return E_FAIL;
*ppwfxInfo = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
if( NULL == *ppwfxInfo )
return E_FAIL;
// Copy the bytes from the pcm structure to the waveformatex structure
memcpy( *ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat) );
(*ppwfxInfo)->cbSize = cbExtraBytes;
// Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
if( mmioRead( hmmioIn, (CHAR*)(((BYTE*)&((*ppwfxInfo)->cbSize))+sizeof(WORD)),
cbExtraBytes ) != cbExtraBytes )
{
delete *ppwfxInfo;
*ppwfxInfo = NULL;
return E_FAIL;
}
}
// Ascend the input file out of the 'fmt ' chunk.
if( 0 != mmioAscend( hmmioIn, &ckIn, 0 ) )
{
delete *ppwfxInfo;
*ppwfxInfo = NULL;
return E_FAIL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WaveOpenFile()
// Desc: This function will open a wave input file and prepare it for reading,
// so the data can be easily read with WaveReadFile. Returns 0 if
// successful, the error code if not.
//-----------------------------------------------------------------------------
HRESULT WaveOpenFile( CHAR* strFileName, HMMIO* phmmioIn, WAVEFORMATEX** ppwfxInfo,
MMCKINFO* pckInRIFF )
{
HRESULT hr;
HMMIO hmmioIn = NULL;
if( NULL == ( hmmioIn = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF|MMIO_READ ) ) )
return E_FAIL;
if( FAILED( hr = ReadMMIO( hmmioIn, pckInRIFF, ppwfxInfo ) ) )
{
mmioClose( hmmioIn, 0 );
return hr;
}
*phmmioIn = hmmioIn;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WaveStartDataRead()
// Desc: Routine has to be called before WaveReadFile as it searches for the
// chunk to descend into for reading, that is, the 'data' chunk. For
// simplicity, this used to be in the open routine, but was taken out and
// moved to a separate routine so there was more control on the chunks
// that are before the data chunk, such as 'fact', etc...
//-----------------------------------------------------------------------------
HRESULT WaveStartDataRead( HMMIO* phmmioIn, MMCKINFO* pckIn,
MMCKINFO* pckInRIFF )
{
// Seek to the data
if( -1 == mmioSeek( *phmmioIn, pckInRIFF->dwDataOffset + sizeof(FOURCC),
SEEK_SET ) )
return E_FAIL;
// Search the input file for for the 'data' chunk.
pckIn->ckid = mmioFOURCC('d', 'a', 't', 'a');
if( 0 != mmioDescend( *phmmioIn, pckIn, pckInRIFF, MMIO_FINDCHUNK ) )
return E_FAIL;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WaveReadFile()
// Desc: Reads wave data from the wave file. Make sure we're descended into
// the data chunk before calling this function.
// hmmioIn - Handle to mmio.
// cbRead - # of bytes to read.
// pbDest - Destination buffer to put bytes.
// cbActualRead - # of bytes actually read.
//-----------------------------------------------------------------------------
HRESULT WaveReadFile( HMMIO hmmioIn, UINT cbRead, BYTE* pbDest,
MMCKINFO* pckIn, UINT* cbActualRead )
{
MMIOINFO mmioinfoIn; // current status of
*cbActualRead = 0;
if( 0 != mmioGetInfo( hmmioIn, &mmioinfoIn, 0 ) )
return E_FAIL;
UINT cbDataIn = cbRead;
if( cbDataIn > pckIn->cksize )
cbDataIn = pckIn->cksize;
pckIn->cksize -= cbDataIn;
for( DWORD cT = 0; cT < cbDataIn; cT++ )
{
// Copy the bytes from the io to the buffer.
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
{
if( 0 != mmioAdvance( hmmioIn, &mmioinfoIn, MMIO_READ ) )
return E_FAIL;
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
return E_FAIL;
}
// Actual copy.
*((BYTE*)pbDest+cT) = *((BYTE*)mmioinfoIn.pchNext);
mmioinfoIn.pchNext++;
}
if( 0 != mmioSetInfo( hmmioIn, &mmioinfoIn, 0 ) )
return E_FAIL;
*cbActualRead = cbDataIn;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: CWaveSoundRead()
// Desc: Constructs the class
//-----------------------------------------------------------------------------
CWaveSoundRead::CWaveSoundRead()
{
m_pwfx = NULL;
}
//-----------------------------------------------------------------------------
// Name: ~CWaveSoundRead()
// Desc: Destructs the class
//-----------------------------------------------------------------------------
CWaveSoundRead::~CWaveSoundRead()
{
Close();
SAFE_DELETE( m_pwfx );
}
//-----------------------------------------------------------------------------
// Name: Open()
// Desc: Opens a wave file for reading
//-----------------------------------------------------------------------------
HRESULT CWaveSoundRead::Open( CHAR* strFilename )
{
SAFE_DELETE( m_pwfx );
HRESULT hr;
if( FAILED( hr = WaveOpenFile( strFilename, &m_hmmioIn, &m_pwfx, &m_ckInRiff ) ) )
return hr;
if( FAILED( hr = Reset() ) )
return hr;
return hr;
}
//-----------------------------------------------------------------------------
// Name: Reset()
// Desc: Resets the internal m_ckIn pointer so reading starts from the
// beginning of the file again
//-----------------------------------------------------------------------------
HRESULT CWaveSoundRead::Reset()
{
return WaveStartDataRead( &m_hmmioIn, &m_ckIn, &m_ckInRiff );
}
//-----------------------------------------------------------------------------
// Name: Read()
// Desc: Reads a wave file into a pointer and returns how much read
// using m_ckIn to determine where to start reading from
//-----------------------------------------------------------------------------
HRESULT CWaveSoundRead::Read( UINT nSizeToRead, BYTE* pbData, UINT* pnSizeRead )
{
return WaveReadFile( m_hmmioIn, nSizeToRead, pbData, &m_ckIn, pnSizeRead );
}
//-----------------------------------------------------------------------------
// Name: Close()
// Desc: Closes an open wave file
//-----------------------------------------------------------------------------
HRESULT CWaveSoundRead::Close()
{
mmioClose( m_hmmioIn, 0 );
return S_OK;
}
Want More Information? Click Below...