Playing MIDIs and WAVs using DirectX
I'll keep this short and simple. I assume you already know how to start a windows program and at least make a message box so the program doesn't just start and end, but I'll provide windows code as well as DirectSound. How This Tutorial is WrittenNot very detailed. I give you the code, where to put it, what you should care about, then let you write your own wrappers or use the DX documentation to figure out the rest. (or the comments in the source) Setting UpSet the linker to link to dsound.lib, #define INITGUID at the beginning and #include <dmusici.h> For later, add #define DXDELETE(p) if(p) p->Release(); or something similar to shorten coding. Also, add these as globals: IDirectMusicLoader8* g_pLoader = NULL; IDirectMusicPerformance8* g_pPerformance = NULL; IDirectMusicSegment8* g_pSegment = NULL; Each has their own reason, way to be wrapped, explanation, etc... but I know that you wanna know how to do it, and if you wanna learn why you'd read the documentation. Just make sure the above is there, what you are able to change comes later. The Basic SetupFirst thing in WinMain, put this: //DMusic and COM setup CoInitialize(NULL); CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, (void**)&g_pLoader); CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8, (void**)&g_pPerformance ); // end of COM and DMusic setup You need to CoInitialize now and CoUninitialize later. You can put this anywhere in WinMain, I'm guessing, as long as it's before all the other DirectSound code. I suggest writing a wrapper to do this. (I have not written any wrappers successfully yet, although I know how now, so don't ask me). Now, put this somewhere after the above: // init audio g_pPerformance->InitAudio( NULL, // IDirectMusic interface not needed. NULL, // IDirectSound interface not needed. hwnd, // Window handle. DMUS_APATH_SHARED_STEREOPLUSREVERB, // Default audiopath type. 64, // Number of performance channels. DMUS_AUDIOF_ALL, // Features on synthesizer. NULL // Audio parameters; use defaults. ); // end init audio Above, change hwnd to whatever you made your window handle's name to be. What You ChangeThe next set of code, I'll actually explain a bit: // Set the search directory. g_pLoader->SetSearchDirectory( GUID_DirectMusicAllTypes, // Types of files sought. /*L"C:\\Program Files\\KaZaA\\My Shared Folder"*/ NULL, // Where to look. Null for default FALSE // Don't clear object data. ); // This function has a lot of stuff I did to it, explained below WCHAR wstrFileName[MAX_PATH] = L"opening.mid"; //The above line's filename should be changed to whatever you wish to load //It can either be mid or wav files. Maybe others, look it up. if (FAILED(g_pLoader->LoadObjectFromFile( CLSID_DirectMusicSegment, // Class identifier. IID_IDirectMusicSegment8, // ID of desired interface. wstrFileName, // Filename. (LPVOID*) &g_pSegment))) // Pointer that receives interface. { MessageBox( NULL, "Media not found, sample will now quit.", "DMusic Tutorial", MB_OK ); return 0; } //make this message box say whatever you want to indicate the file //couldn't be found You alter the file name, as well as the location. within SetSearchDirectory(), if you uncomment the L"C:\\..." and delete the NULL, the program will search within anyone's My Shared Folder if that folder exists, for the file specified by the WCHAR wstrFileName[]"..." below it. I did not read the documentation, but took a guess that putting NULL instead of L"" within SetSearchDirectory() would search for the file within the game's directory, and it worked. If you're reading this tutorial, you're likely a beginner, so take the advice and don't be afraid to guess and check. I also suggest looking at the documentation to learn more about GUID_DirectMusicAllTypes. Playing the FileNow, add this: g_pSegment->Download( g_pPerformance ); g_pPerformance->PlaySegmentEx( g_pSegment, // Segment to play. NULL, // Used for songs; not implemented. NULL, // For transitions. 0, // Flags. 0, // Start time; 0 is immediate. NULL, // Pointer that receives segment state. NULL, // Object to stop. NULL // Audiopath, if not default. ); It plays the file. To keep it playing, put Sleep(5000) or a message box or basically anything you can to keep from reaching g_pPerformance->Stop( NULL, // Stop all segments. NULL, // Stop all segment states. 0, // Do it immediately. 0 // Flags. ); g_pPerformance->CloseDown(); Which stops the file. Most likely, you'll have an if( MUSIC_ISDONE) g_pPerformance->Stop(); //<- psuedo-code or similar. After it is done, put this line: CoUninitialize(); And now you stopped the music. Free the ResourcesWe created them, we must destroy them. Before WinMain ends, deInitialize the DMusic globals, like so: DXDELETE(g_pLoader); DXDELETE(g_pPerformance); DXDELETE(g_pSegment); I personally just put deInit(); at the end, which is where I uninitialize all DX variables. (Coincidentally, I also put the stop() function to stop my background music.) ConclusionOk, so you can't alter much, but you can change when the music stops and whether you want Midi files playing or not. I believe this becomes more complicated if you want multiple sound files playing. That is beyond the scope of this tutorial. Anyway, complete source code to my project that uses this tutorial is listed below. The project is small so you can still learn from it, and various things are commented out to give you ideas and experiment with. Have fun! //In VC++, under the C/C++ tab within the Project->Settings menu, within //the category of Code Generation, change the option under "Use run-time //library:" from Single-threaded* to Multithreaded. #define WIN32_LEAN_AND_MEAN #define INITGUID #include dmusici.h // need the arrow thingies, but they don't display in html #include windows.h #include windowsx.h #include fstream.h #include string.h #include stdio.h #include math.h #define DXDELETE(p) if(p != NULL) p->Release(); // a SAFEDELETE replacement // if arrays are deleted, be sure to use delete [] array; first. // defines #define MAINWINDOWNAME "Clone of Game" #define MAINWINDOWTITLE "Main Window" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 // Globals HWND mainwindowhandle = NULL; // for later, the main window handle. (needed for wrappers and stuff) // DX globals IDirectMusicLoader8* g_pLoader = NULL; IDirectMusicPerformance8* g_pPerformance = NULL; IDirectMusicSegment8* g_pSegment = NULL; // Game functions void deInit(void) { g_pPerformance->Stop( NULL, // Stop all segments. NULL, // Stop all segment states. 0, // Do it immediately. 0 // Flags. ); g_pPerformance->CloseDown(); DXDELETE(g_pLoader); DXDELETE(g_pPerformance); DXDELETE(g_pSegment); } int xx = 360, yy = 600; RECT washwall = {0,0, 800, 600}; HBRUSH brushblack= CreateSolidBrush(RGB(0,0,0)); // Functions LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // PAINTSTRUCT ps; // used in WM_PAINT HDC hdc; // handle to device context, used a lot switch(msg) { case WM_CREATE: { // called when window is created return(0); } break; case WM_PAINT: // called when repaintint is needed (refreshing) { // validate the window hdc= GetDC(hwnd); while(yy > -20) { FillRect(hdc, &washwall, brushblack); FrameRect(hdc, &washwall, brushblack); // here is where the game is drawn :) A wrapper or so would come in real handy. SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, RGB(0,0,255)); SetBkColor(hdc, RGB(0,0,0)); TextOut(hdc, xx,yy, "CREDITS", strlen("CREDITS")); Sleep(5); yy--; ReleaseDC(hwnd, hdc); } return(0); } break; case WM_DESTROY: // called when window is killed { // kill the app // do not release DX objects in here, causes errors. PostQuitMessage(0); return(0); } break; default: break; } // end switch // process any other messages... return (DefWindowProc(hwnd, msg, wParam, lParam)); } // end WindowProc, the message processing function, since Windows is a message-based OS int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lbCmdLine, int nShowCmd) { //DMusic and COM setup CoInitialize(NULL); CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, (void**)&g_pLoader); CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8, (void**)&g_pPerformance ); // end of COM and DMusic setup WNDCLASS mainwindow; HWND hwnd; // generic window handle MSG msg; // generic message mainwindow.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; mainwindow.lpfnWndProc = WindowProc; mainwindow.cbClsExtra = 0; mainwindow.cbWndExtra = 0; mainwindow.hInstance = hInstance; mainwindow.hIcon = LoadIcon(NULL, IDI_APPLICATION); // can use resources, IDB_BITMAP and MAKEINTRESOURCE() mainwindow.hCursor = LoadCursor(NULL, IDC_ARROW); mainwindow.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); mainwindow.lpszMenuName = NULL; // can add menu later mainwindow.lpszClassName = MAINWINDOWNAME; // can be changed using define above... // now that the window class has been styled/filled, we register it if(!RegisterClass(&mainwindow)) // check for errors return (0); // if an error. // now to make the window itself... if (!(hwnd = CreateWindow(MAINWINDOWNAME, MAINWINDOWTITLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,CW_USEDEFAULT, // x, y WINDOW_WIDTH, WINDOW_HEIGHT, NULL, // no parent window handle, since only one window NULL, // no handle to menu, currently hInstance, // instance NULL))) // creation parameters. (might be more useful in CreateWindowEx()?) return(0); // if no errors. // save window handle in a global handle variable mainwindowhandle = hwnd; // make DMusic initialization g_pPerformance->InitAudio( NULL, // IDirectMusic interface not needed. NULL, // IDirectSound interface not needed. hwnd, // Window handle. DMUS_APATH_SHARED_STEREOPLUSREVERB, // Default audiopath type. 64, // Number of performance channels. DMUS_AUDIOF_ALL, // Features on synthesizer. NULL // Audio parameters; use defaults. ); // end init audio // DMusic // Find the Windows media directory. /* CHAR strPath[MAX_PATH]; GetWindowsDirectory( strPath, MAX_PATH ); strcat( strPath, "c:\\Program Files\\KaZaA\\My Shared Folder" ); */ // Convert to Unicode. /* WCHAR wstrSearchPath[MAX_PATH]; MultiByteToWideChar( CP_ACP, 0, strPath, -1, wstrSearchPath, MAX_PATH ); */ // Set the search directory. g_pLoader->SetSearchDirectory( GUID_DirectMusicAllTypes, // Types of files sought. /*L"C:\\Program Files\\KaZaA\\My Shared Folder"*/ NULL, //Null for default // Where to look. was wstrSearchPath FALSE // Don't clear object data. ); WCHAR wstrFileName[MAX_PATH] = L"DBGT opening.mid"; if (FAILED(g_pLoader->LoadObjectFromFile( CLSID_DirectMusicSegment, // Class identifier. IID_IDirectMusicSegment8, // ID of desired interface. wstrFileName, // Filename. (LPVOID*) &g_pSegment // Pointer that receives interface. ))) { MessageBox( NULL, "Media not found, sample will now quit.", "DMusic Tutorial", MB_OK ); return 0; } g_pSegment->Download( g_pPerformance ); g_pPerformance->PlaySegmentEx( g_pSegment, // Segment to play. NULL, // Used for songs; not implemented. NULL, // For transitions. 0, // Flags. 0, // Start time; 0 is immediate. NULL, // Pointer that receives segment state. NULL, // Object to stop. NULL // Audiopath, if not default. ); // unneeded, but helps for splash screens. /*Sleep(6000);*/ // makes a segment of sound play for a bit. /* g_pPerformance->Stop( NULL, // Stop all segments. NULL, // Stop all segment states. 0, // Do it immediately. 0 // Flags. ); g_pPerformance->CloseDown(); g_pLoader->Release(); g_pPerformance->Release(); g_pSegment->Release(); */ // I put the above in WM_DESTROY, then got an error, put it in deInit() CoUninitialize(); // Entering main event loop // in winMain() the msg pump while(1) if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) { if(!GetMessage(&msg,NULL,0,0)) return msg.wParam; TranslateMessage(&msg); DispatchMessage(&msg); // main game goes here } deInit(); // releases stuff. } // end of winMain //else if(!appIsInactive)WaitMessate(); Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|