LANGPLAY.C

/*-------------------------------------------------------------------- 
|
| LangPlay.c - Sample Win app to play AVI movies using MCIWnd. Handles
| multiple language track movies and lets the
| user select the track to listen to at playback.
|
|
+--------------------------------------------------------------------*/
/**************************************************************************
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
* Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commdlg.h>
#include <string.h>
#include <stdlib.h>
#include <direct.h>
#include <digitalv.h>
#include <vfw.h>
#include "langplay.h"


/**************************************************************
************************ GLOBALS ******************************
**************************************************************/
/* AVI stuff to keep around */
HWND hwndMovie; /* window handle of the movie */
BOOL fMovieOpen = FALSE; /* Open flag: TRUE == movie open, FALSE = none */
HMENU hMenuBar = NULL; /* menu bar handle */
char szAppName [] = "LangPlay";

// struct for handling multi-language support
typedef struct langs_tag {
WORD wLangTag; // language type tag
char achName[64]; // stream name (limited to 64 chars by AVIStreamInfo)
} LANGS, FAR *LPLANGS;

#define NOAUDIO 0 // no audio stream
int iCurLang; // current language selected (0 == NONE)


/* function declarations */
long FAR PASCAL WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void fileOpenMovie(HWND hWnd);
void menubarUpdate(HWND hWnd);
void titlebarUpdate(HWND hWnd, LPSTR lpstrMovie);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

/* language specific functions */
BOOL enumLangs(HWND hwnd, LPSTR lpstrMovie);
void buildLangMenu(HWND hwnd, LPLANGS lpLangs, DWORD dwLangs);
void switchLang(HWND hWnd, int iLangStream);


/********************************************************************
************************** FUNCTIONS ********************************
********************************************************************/


/*--------------------------------------------------------------+
| initApp - initialize the app overall. |
| |
| Returns the Window handle for the app on success, NULL if |
| there is a failure. |
| |
+--------------------------------------------------------------*/
HWND initApp(HINSTANCE hInstance, HINSTANCE hPrevInstance, int nCmdShow)
{
HWND hWnd; /* window handle to return */
int iWinHeight;
WORD wVer;

/* first let's make sure we are running on 1.1 */
wVer = HIWORD(VideoForWindowsVersion());
if (wVer < 0x010a){
/* oops, we are too old, blow out of here */
MessageBeep(MB_ICONHAND);
MessageBox(NULL, "Video for Windows version is too old",
"LangPlay Error", MB_OK|MB_ICONSTOP);
return FALSE;
}

if (!hPrevInstance){
WNDCLASS wndclass;

wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (hInstance, "AppIcon");
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;

if (!RegisterClass(&wndclass)){
MessageBox(NULL, "RegisterClass failure", szAppName, MB_OK);
return NULL;
}
}

iWinHeight = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYMENU) +
(GetSystemMetrics(SM_CYFRAME) * 2);

/* create the main window for the app */
hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 180, iWinHeight,
NULL, NULL, hInstance, NULL);

if (hWnd == NULL){
MessageBox(NULL, "CreateWindow failure", szAppName, MB_OK);
return NULL;
}

hMenuBar = GetMenu(hWnd); /* get the menu bar handle */
menubarUpdate(hWnd); /* update menu bar to disable Movie menu */

/* Show the main window */
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

/* create the movie window using MCIWnd that has no file open initially */
hwndMovie = MCIWndCreate(hWnd, hInstance, WS_CHILD |WS_VISIBLE | MCIWNDF_NOOPEN |
MCIWNDF_NOERRORDLG | MCIWNDF_NOTIFYSIZE, NULL);

if (!hwndMovie){
/* we didn't get the movie window, destroy the app's window and bail out */
DestroyWindow(hWnd);
return NULL;
}
return hWnd;
}




/*--------------------------------------------------------------+
| WinMain - main routine. |
| |
+--------------------------------------------------------------*/
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
HWND hWnd;
MSG msg;

if ((hWnd = initApp(hInstance, hPrevInstance,nCmdShow)) == NULL)
return 0; /* died initializing, bail out */

while (GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}


/*--------------------------------------------------------------+
| WndProc - window proc for the app |
| |
+--------------------------------------------------------------*/
long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
PAINTSTRUCT ps;
WORD w;
WORD wMenu;
RECT rc;

switch (message){
case WM_CREATE:
return 0;

case WM_INITMENUPOPUP:
/* be sure this isn't the system menu */
if (HIWORD(lParam))
return DefWindowProc(hWnd, WM_INITMENUPOPUP,
wParam, lParam);

wMenu = LOWORD(lParam);
switch (wMenu){
case 0: /* file menu */
/* turn on/off CLOSE & PLAY */
if (fMovieOpen) w = MF_ENABLED|MF_BYCOMMAND;
else w = MF_GRAYED|MF_BYCOMMAND;
EnableMenuItem((HMENU)wParam, IDM_CLOSE, w);
break;
} /* switch */
break;

case WM_COMMAND:
if (wParam >= IDM_STREAM){
// the command is to switch the audio stream
switchLang(hWnd, wParam - IDM_STREAM + 1);
return 0;
}
/* handle the menu commands */
switch (wParam) {
/* File Menu */
case IDM_OPEN:
fileOpenMovie(hWnd);
break;
case IDM_CLOSE:
fMovieOpen = FALSE;
MCIWndClose(hwndMovie); // close the movie
ShowWindow(hwndMovie, SW_HIDE); //hide the window
menubarUpdate(hWnd);
titlebarUpdate(hWnd, NULL); // title bar back to plain
break;
case IDM_EXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;

/* audio menu */
case IDM_NONE:
switchLang(hWnd, NOAUDIO);
break;

case IDM_ABOUT:
DialogBox((HANDLE)GetWindowInstance(hWnd),
MAKEINTRESOURCE(IDD_ABOUT),
hWnd,
AboutDlgProc);
break;

}
return 0;

case WM_PAINT:
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;

case WM_SIZE:
if (!IsIconic(hWnd) && hwndMovie && fMovieOpen)
MoveWindow(hwndMovie,0,0,LOWORD(lParam),HIWORD(lParam),TRUE);
break;

case WM_DESTROY:
if (fMovieOpen)
MCIWndClose(hwndMovie); // close an open movie
MCIWndDestroy(hwndMovie); // now destroy the MCIWnd window
PostQuitMessage(0);
return 0;

case MCIWNDM_NOTIFYSIZE:
if (!IsIconic(hWnd)) {
if (fMovieOpen){
/* adjust to size of the movie window */
GetWindowRect(hwndMovie, &rc);
AdjustWindowRect(&rc, GetWindowLong(hWnd, GWL_STYLE), TRUE);
SetWindowPos(hWnd, NULL, 0, 0, rc.right - rc.left,
rc.bottom - rc.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
} else {
/* movie closed, adjust to the default size */
int iWinHeight;

iWinHeight = GetSystemMetrics(SM_CYCAPTION) +
GetSystemMetrics(SM_CYMENU) +
(GetSystemMetrics(SM_CYFRAME) * 2);
SetWindowPos(hWnd, NULL, 0, 0, 180, iWinHeight,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
}
}
break;

case WM_ACTIVATE:
case WM_QUERYNEWPALETTE:
case WM_PALETTECHANGED:
//
// Forward palette-related messages through to the MCIWnd,
// so it can do the right thing.
//
if (hwndMovie)
return SendMessage(hwndMovie, message, wParam, lParam);
break;
} /* switch */
return DefWindowProc(hWnd, message, wParam, lParam);
}

/*--------------------------------------------------------------+
| menubarUpdate - update the menu bar based on the <fMovieOpen> |
| flag value. This will turn on/off the |
| Movie menu. |
| |
+--------------------------------------------------------------*/
void menubarUpdate(HWND hWnd)
{
WORD w;

if (fMovieOpen){
w = MF_ENABLED|MF_BYPOSITION;
} else {
w = MF_GRAYED|MF_BYPOSITION;
}
EnableMenuItem(hMenuBar, 1, w); /* change the Movie menu (#1) */
DrawMenuBar(hWnd); /* re-draw the menu bar */
}

/*--------------------------------------------------------------+
| titlebarUpdate - update the title bar to include the name |
| of the movie playing. |
| |
+--------------------------------------------------------------*/
void titlebarUpdate(HWND hWnd, LPSTR lpstrMovie)
{
char achNewTitle[BUFFER_LENGTH]; // space for the title

if (lpstrMovie != NULL)
wsprintf((LPSTR)achNewTitle,"%s - %s", (LPSTR)szAppName,lpstrMovie);
else
lstrcpy((LPSTR)achNewTitle, (LPSTR)szAppName);
SetWindowText(hWnd, (LPSTR)achNewTitle);
}

/*--------------------------------------------------------------+
| fileOpenMovie - open an AVI movie. Use CommDlg open box to |
| open and then handle the initialization to |
| show the movie and position it properly. Keep |
| the movie paused when opened. |
| |
| Sets <fMovieOpened> on success. |
+--------------------------------------------------------------*/
void fileOpenMovie(HWND hWnd)
{
OPENFILENAME ofn;

static char szFile [BUFFER_LENGTH];
static char szFileTitle [BUFFER_LENGTH];

/* use the OpenFile dialog to get the filename */
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = "Video for Windows\0*.avi\0\0";
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

/* use CommDlg to get our filename */
if (GetOpenFileName(&ofn)){

/* we got a filename, now close any old movie and open */
/* the new one. */
if (fMovieOpen)
MCIWndClose(hwndMovie);

enumLangs(hWnd, ofn.lpstrFile); // find out the languages used in the file.

/* try to open the file */
fMovieOpen = TRUE; // assume the best
if (MCIWndOpen(hwndMovie, ofn.lpstrFile, 0) == 0){
/* we opened the file o.k., now set up to */
/* play it. */
ShowWindow(hwndMovie, SW_SHOW);
} else {
/* generic error for open */
MessageBox(hWnd, "Unable to open Movie", NULL,
MB_ICONEXCLAMATION|MB_OK);
fMovieOpen = FALSE;
}
}
/* update menu bar */
menubarUpdate(hWnd);
if (fMovieOpen)
titlebarUpdate(hWnd, (LPSTR)ofn.lpstrFileTitle);
else
titlebarUpdate(hWnd, NULL);

/* cause an update to occur */
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
}



/*--------------------------------------------------------------+
| enumLangs - enumerate the audio streams in a file and set up |
| the global ghLangs as a handle to the array of |
| language streams. | |
| |
| To do this: |
| 1. Open the file using AVIFileOpen(). |
| 2. Open all Audio Streams, getting the pavi for it |
| 3. Get the stream info on all audio streams to get names|
| 4. If names don't exist use "Audio Strean n" |
| 5. Close the file |
| 6. Build the audio stream menu |
| |
| Return TRUE if successfully set up menu, FALSE on any error |
| |
+--------------------------------------------------------------*/
BOOL enumLangs(HWND hWnd, LPSTR lpstrMovie)
{
PAVIFILE pFile;
PAVISTREAM pStream;
AVIFILEINFO aviInfo;
AVISTREAMINFO aviStream;
DWORD dwNumStreams;
DWORD dwNumAudioStreams = 0L;
LPLANGS lpLangs;
LPLANGS lpLang;
HANDLE hLangs;

DWORD dw;


AVIFileInit();

// go open the file
if (AVIFileOpen((PAVIFILE far *)&pFile, lpstrMovie, OF_READ, NULL) != 0)
return FALSE;

// get the file information
AVIFileInfo(pFile, (LPAVIFILEINFO)&aviInfo, sizeof(aviInfo));
dwNumStreams = aviInfo.dwStreams; // grab the number of streams

// loop through the streams and find the # of audio streams
for (dw = 0L; dw < dwNumStreams; dw++){
AVIFileGetStream(pFile, (PAVISTREAM far *)&pStream, 0, dw);
AVIStreamInfo(pStream, (LPAVISTREAMINFO)&aviStream, sizeof(aviStream));
if (aviStream.fccType == streamtypeAUDIO)
dwNumAudioStreams++;
AVIStreamClose(pStream);

}

// we now know how many audio streams we are dealing with, we need to allocate
// enough memory for the Langs array and then get all the audio stream information
// again.
hLangs = GlobalAlloc(GHND, (sizeof(LANGS) * dwNumAudioStreams));
if (hLangs == NULL)
return FALSE;

lpLangs = (LPLANGS)GlobalLock(hLangs); // get the memory

// loop through the audio streams and fill out the array
for (dw = 0L, lpLang = lpLangs; dw < dwNumAudioStreams; dw++, lpLang++) {
AVIFileGetStream(pFile, (PAVISTREAM far *)&pStream, streamtypeAUDIO, dw);
AVIStreamInfo(pStream, (LPAVISTREAMINFO)&aviStream, sizeof(aviStream));
if (aviStream.szName && *aviStream.szName != '\0')
lstrcpy((LPSTR)lpLang->achName, (LPSTR)aviStream.szName);
else
wsprintf((LPSTR)lpLang->achName, "Audio Stream %lu",dw);
AVIStreamClose(pStream);

}

// now build the menu for this
buildLangMenu(hWnd, lpLangs, dwNumAudioStreams);

// close up and deallocate resources
AVIFileClose(pFile);
AVIFileExit();

GlobalUnlock(hLangs);
GlobalFree(hLangs);

return TRUE;
}


/*--------------------------------------------------------------+
| buildLangMenu - build up the language menu for the audio |
| streams available. |
| |
| hwnd is the main application window handle |
| lang points to an array of LANGSTRUCT entries already filled |
| in by the caller. |
+--------------------------------------------------------------*/
void buildLangMenu(HWND hwnd, LPLANGS lpLangs, DWORD dwLangs)
{
UINT i;
HMENU hMenu;
LPLANGS lplang;
UINT uNumMenus;

// go through menu chain and get the Audio Stream pop-up menu
hMenu = GetMenu(hwnd); // get the menu bar
hMenu = GetSubMenu(hMenu, 1); // get the Audio Stream menu

uNumMenus = GetMenuItemCount(hMenu); // how many items are on this menu?
if (uNumMenus > 1){
// we've got a menu with items already, time to delete all of them
// except for the first one (NONE). NOTE: Item 0 == first item
// be sure to delete in reverse order so you get them all.
for ( --uNumMenus; uNumMenus; uNumMenus--) {
DeleteMenu(hMenu, uNumMenus, MF_BYPOSITION);
}
}

// loop through the languages and add menus to the existing menu
for (i=0, lplang = lpLangs; i<dwLangs; i++, lplang++){
AppendMenu(hMenu, MF_ENABLED | MF_STRING, IDM_STREAM+i, lplang->achName);
}

// get default set up
if (dwLangs)
iCurLang = 1; // use first audio stream
else
iCurLang = NOAUDIO; // else none

/* set up the checkmark initially */
CheckMenuItem(hMenu, (iCurLang), MF_BYPOSITION | MF_CHECKED);
}


/*------------------------------------------------------------------+
| switchLang - switch audio stream playback |
| |
| iLangStream == the audio stream to switch to (-1 == NONE) |
| Be sure to update iCurrLang global to be the current audio stream |
| selected. |
| |
+------------------------------------------------------------------*/
void switchLang(HWND hWnd, int iLangStream)
{
HMENU hMenu;
char achStrBuff[256];


// if user just picked the same stream then just get out of here
if (iCurLang == iLangStream)
return;

// go through menu chain and get the Audio Stream pop-up menu
hMenu = GetMenu(hWnd); // get the menu bar
hMenu = GetSubMenu(hMenu, 1); // get the Audio Stream menu

// turn off the checkmark from the old item
CheckMenuItem(hMenu, (iCurLang), MF_BYPOSITION | MF_UNCHECKED);

// turn on the checkmark on the new item
CheckMenuItem(hMenu, (iLangStream), MF_BYPOSITION | MF_CHECKED);

if (iLangStream == NOAUDIO){
// turn off all audio
MCIWndSendString(hwndMovie, "setaudio off");
} else {
// turn on audio & the specific stream
wsprintf(achStrBuff, "setaudio stream to %d", iLangStream);

// send the command
MCIWndSendString(hwndMovie, achStrBuff);


if (iCurLang == NOAUDIO){
// audio was off, turn it on
MCIWndSendString(hwndMovie, "setaudio on");
}
}

iCurLang = iLangStream; // set the current stream
}

/* AboutDlgProc()
*
* Dialog Procedure for the "about" dialog box.
*
*/

BOOL CALLBACK AboutDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_COMMAND:
EndDialog(hwnd, TRUE);
return TRUE;
case WM_INITDIALOG:
return TRUE;
}
return FALSE;
}