MSTRHELP.C

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: mstream.c
* Content: Illustrates streaming data from a disk MIDI file to a
* midiStream buffer for playback.
*
***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <memory.h>
#include <mmreg.h>

#include "debug.h"
#include "resource.h"
#include "midstuff.h"
#include "mstream.h"

extern char szAppTitle[64];
extern char szAppCaption[64];
extern char szFileBuffer[64];
extern char szFileTitle[64];
extern char szTempo[64];
extern char szVolume[64];
extern char szProgress[64];
extern char szTemp[256];
extern char szDebug[256];

extern HWND hWndMain, hWndTempo, hWndVol, hWndProg, hWndPlay, hWndPause;
extern HWND hWndStop, hWndTempoText, hWndVolText, hWndLoopCheck, hWndProgText;
extern HINSTANCE hInst;
extern HMIDISTRMhStream;

extern BOOLbFileOpen, bPlaying, bPaused, bInsertTempo;
extern intnTextControlHeight;
extern DWORDdwBufferTickLength, dwTempoMultiplier, dwCurrentTempo;
extern DWORDdwProgressBytes, dwVolumePercent, dwVolCache[NUM_CHANNELS];

#ifdef DEBUG
extern HWNDhWndList;
#endif

/*****************************************************************************/
/* CreateChildren() */
/* */
/* This function creates a bunch of child controls for the main window. */
/* Most of them are used for controling various things about a playing sound */
/* file, like volume and panning. Returns FALSE if no errors, TRUE otherwise.*/
/* */
/*****************************************************************************/
int CreateChildren( RECT crect )
{
SIZE Size;
HDC hDC;
int x, y;
UINT uType;
char szTemplate[128], szType[32];
LPSTR lpszControl;

LoadString( hInst, IDS_ERROR_CHILDTEMPLATE, szTemplate, sizeof(szTemplate));

/* Don't handle failure for this one, because the app will still run fine */
CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
0, 0, crect.right, 2, hWndMain, (HMENU)0, hInst, NULL );

hDC = GetDC( hWndMain );
if( !GetTextExtentPoint32( hDC, szProgress, strlen(szProgress), &Size ))
{
ErrorMessageBox( IDS_ERROR_GETTEXTEXTENT, MB_ICONEXCLAMATION );
ReleaseDC( hWndMain, hDC );
return( TRUE );
}
ReleaseDC( hWndMain, hDC );
nTextControlHeight = Size.cy;

y = BORDER_SPACE_CY;

/* STATIC control -- text label for the TEMPO trackbar */
if(( hWndTempoText = CreateWindow( "static", szTempo,
WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
BORDER_SPACE_CX,
y,
TEMPO_TEXT_CX, nTextControlHeight,
hWndMain, (HMENU)0, hInst, NULL)) == NULL )
{
lpszControl = szTempo;
uType = IDS_ERROR_STATICTEXT;
goto DISPLAY_CREATE_FAILURE;
}

/* Create the TEMPO trackbar */
if(( hWndTempo = CreateWindow( TRACKBAR_CLASS, NULL,
WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
BORDER_SPACE_CX,
y + nTextControlHeight + TEXT_SPACE_CY,
TEMPO_TB_CX, TEMPO_TB_CY,
hWndMain, (HMENU)0, hInst, NULL)) == NULL )
{
lpszControl = szTempo;
uType = IDS_ERROR_TRACKBAR;
goto DISPLAY_CREATE_FAILURE;
}

dwTempoMultiplier = 100;
SendMessage( hWndTempo, TBM_SETRANGE, FALSE,
MAKELONG( TEMPO_MIN, TEMPO_MAX ));
SendMessage( hWndTempo, TBM_SETPOS, TRUE, dwTempoMultiplier );

/* STATIC control -- text label for the VOLUME trackbar */
if(( hWndVolText = CreateWindow( "static", szVolume,
WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
BORDER_SPACE_CX + TEMPO_TB_CX
+ CONTROL_SPACE_CX,
y,
VOL_TEXT_CX, nTextControlHeight,
hWndMain, (HMENU)0, hInst, NULL)) == NULL )
{
lpszControl = szVolume;
uType = IDS_ERROR_STATICTEXT;
goto DISPLAY_CREATE_FAILURE;
}
y += nTextControlHeight + TEXT_SPACE_CY;

/* Create the VOLUME trackbar */
if(( hWndVol = CreateWindow( TRACKBAR_CLASS, NULL,
WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
BORDER_SPACE_CX + TEMPO_TB_CX
+ CONTROL_SPACE_CX,
y, VOL_TB_CX, VOL_TB_CY,
hWndMain, (HMENU)0, hInst, NULL)) == NULL )
{
lpszControl = szVolume;
uType = IDS_ERROR_TRACKBAR;
goto DISPLAY_CREATE_FAILURE;
}

SendMessage( hWndVol, TBM_SETRANGE, FALSE,
MAKELONG( VOL_TB_MIN, VOL_TB_MAX ));
SendMessage( hWndVol, TBM_SETPOS, TRUE, VOL_TB_MAX );
SendMessage( hWndVol, TBM_SETPAGESIZE, 0L, VOL_PAGESIZE );

x = BORDER_SPACE_CX + TEMPO_TB_CX + 2 * CONTROL_SPACE_CX + VOL_TB_CX;
y = BORDER_SPACE_CY;

/* Create the LOOPED CHECKBOX */
LoadString( hInst, IDS_CHECK_LOOPED, szTemp, sizeof(szTemp));
if(( hWndLoopCheck = CreateWindow( "button", szTemp,
WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
x, y, CHECK_CX, CHECK_CY, hWndMain,
(HMENU)IDC_LOOPCHECK, hInst, NULL )) == NULL )
{
lpszControl = szTemp;
uType = IDS_ERROR_CHECK;
goto DISPLAY_CREATE_FAILURE;
}

x = BORDER_SPACE_CX + TEMPO_TB_CX + VOL_TB_CX + 2 * CONTROL_SPACE_CX + CHECK_CX
- 3 * BUTTON_CX - 2 * BUTTON_SPACE_CX;
y = BORDER_SPACE_CY + nTextControlHeight + TEXT_SPACE_CY + TEMPO_TB_CY
+ CONTROL_SPACE_CY;

/* STATIC control -- text label for the progress trackbar. */
if(( hWndProgText = CreateWindow( "static", szProgress,
WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
BORDER_SPACE_CX,
y,
x - BORDER_SPACE_CX, nTextControlHeight,
hWndMain, (HMENU)0, hInst, NULL)) == NULL )
{
lpszControl = szProgress;
uType = IDS_ERROR_STATICTEXT;
goto DISPLAY_CREATE_FAILURE;
}

/* Create the PLAY BUTTON */
LoadString( hInst, IDS_BUTTON_PLAY, szTemp, sizeof(szTemp));
if(( hWndPlay = CreateWindow( "button", szTemp,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
x, y, BUTTON_CX, BUTTON_CY, hWndMain,
(HMENU)IDC_PLAY, hInst, NULL )) == NULL )
{
lpszControl = szTemp;
uType = IDS_ERROR_BUTTON;
goto DISPLAY_CREATE_FAILURE;
}
x += BUTTON_CX + BUTTON_SPACE_CX;

/* Create the PAUSE BUTTON */
LoadString( hInst, IDS_BUTTON_PAUSE, szTemp, sizeof(szTemp));
if(( hWndPause = CreateWindow( "button", szTemp,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
x, y, BUTTON_CX, BUTTON_CY, hWndMain,
(HMENU)IDC_PAUSE, hInst, NULL )) == NULL )
{
lpszControl = szTemp;
uType = IDS_ERROR_BUTTON;
goto DISPLAY_CREATE_FAILURE;
}
x += BUTTON_CX + BUTTON_SPACE_CX;

/* Create the STOP BUTTON */
LoadString( hInst, IDS_BUTTON_STOP, szTemp, sizeof(szTemp));
if(( hWndStop = CreateWindow( "button", szTemp,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
x, y, BUTTON_CX, BUTTON_CY, hWndMain,
(HMENU)IDC_STOP, hInst, NULL )) == NULL )
{
lpszControl = szTemp;
uType = IDS_ERROR_BUTTON;
goto DISPLAY_CREATE_FAILURE;
}

UpdateFromControls();
goto RETURN_NORMAL;

DISPLAY_CREATE_FAILURE:
LoadString( hInst, uType, szType, sizeof(szType));
wsprintf( szTemp, szTemplate, lpszControl, szType );
MessageBox( GetActiveWindow(), szTemp,
szAppTitle, MB_OK | MB_ICONEXCLAMATION );
return( TRUE );

RETURN_NORMAL:
return( FALSE );
}


/********************************************************************************/
/* HandleTempoScroll() */
/* */
/* Handles the tempo trackbar scroll when a WM_HSCROLL is received. */
/* */
/********************************************************************************/
void HandleTempoScroll( int nCode, int nPos )
{
long lTempo, lDelta;

lTempo = (LONG)SendMessage( hWndTempo, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );

switch( nCode )
{
case TB_LINEUP:
if( lTempo >= TEMPO_MIN-1 )
lDelta = -1;
break;
case TB_LINEDOWN:
if( lTempo <= TEMPO_MAX+1 )
lDelta = 1;
break;
case TB_PAGEUP:
if( lTempo >= TEMPO_MIN - TEMPO_PAGESIZE )
lDelta = -TEMPO_PAGESIZE;
break;
case TB_PAGEDOWN:
if( lTempo <= TEMPO_MAX + TEMPO_PAGESIZE )
lDelta = TEMPO_PAGESIZE;
break;
case TB_ENDTRACK:
return;
default:
lDelta = 0;
}

if( lDelta )
{
SendMessage( hWndTempo, TBM_SETPOS, TRUE, lTempo + lDelta );
dwTempoMultiplier = (DWORD)( lTempo + lDelta );
}
else
{
SendMessage( hWndTempo, TBM_SETPOS, TRUE, (long)nPos );
dwTempoMultiplier = (DWORD)nPos;
}

UpdateFromControls();
}


/********************************************************************************/
/* HandleVolScroll() */
/* */
/* Handles the volume trackbar scrolling when a WM_HSCROLL is received. */
/* */
/********************************************************************************/
void HandleVolScroll( int nCode, int nPos )
{
long lVol, lDelta;

lVol = (LONG)SendMessage( hWndVol, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );

switch( nCode )
{
case TB_LINEDOWN:
if( lVol <= VOL_TB_MAX - 1 )
lDelta = 1;
break;
case TB_LINEUP:
if( lVol >= VOL_TB_MIN + 1 )
lDelta = -1;
break;
case TB_PAGEDOWN:
if( lVol <= VOL_TB_MAX - VOL_PAGESIZE )
lDelta = VOL_PAGESIZE;
break;
case TB_PAGEUP:
if( lVol >= VOL_TB_MIN + VOL_PAGESIZE )
lDelta = -VOL_PAGESIZE;
break;
case TB_ENDTRACK:
return;
default:
lDelta = 0;
}

if( lDelta )
SendMessage( hWndVol, TBM_SETPOS, TRUE, (lVol + lDelta));
else
SendMessage( hWndVol, TBM_SETPOS, TRUE, (long)nPos );

UpdateFromControls();
}


/********************************************************************************/
/* UpdateFromControls() */
/* */
/* This function gets all the required values from the DirectSoundBuffer and */
/* updates the screen interface controls. */
/* */
/********************************************************************************/
void UpdateFromControls( void )
{
longlTempo;

lTempo = (LONG)SendMessage( hWndTempo, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
dwVolumePercent = (WORD)SendMessage( hWndVol, TBM_GETPOS,
(WPARAM)0, (LPARAM)0 );

/* Set the Volume text */
wsprintf( szTemp, "%s: %lu%%", szVolume, dwVolumePercent / 10 );
Static_SetText( hWndVolText, szTemp );
if( hStream )
SetAllChannelVolumes( dwVolumePercent );

/* Set the Tempo text */
wsprintf( szTemp, "%s: %li%%", szTempo, lTempo );
Static_SetText( hWndTempoText, szTemp );
bInsertTempo = TRUE;

/* Set the Progress text */
wsprintf( szTemp, "%s: %lu bytes", szProgress, dwProgressBytes );
Static_SetText( hWndProgText, szTemp );

return;
}


/****************************************************************************/
/* ErrorMessageBox() */
/* */
/* A little routine to load error messages from the string resource table */
/* and pop them up in a MessageBox() for the world to see. The dwMBFlags */
/* parameter allows the caller to specify the type of icon to use. */
/* */
/****************************************************************************/
void ErrorMessageBox( UINT uID, DWORD dwMBFlags )
{
LoadString( hInst, uID, szTemp, sizeof(szTemp));
MessageBox( GetActiveWindow(), szTemp, szAppTitle, MB_OK | dwMBFlags );
#ifdef DEBUG
wsprintf( szDebug, "General error: %s", szTemp );
DebugPrint( szDebug );
#endif
}


/****************************************************************************/
/* MidiErrorMessageBox() */
/* */
/* Calls the midiOutGetErrorText() function and displays the text which */
/* corresponds to a midi subsystem error code. */
/* */
/****************************************************************************/
void MidiErrorMessageBox( MMRESULT mmr )
{
midiOutGetErrorText( mmr, szTemp, sizeof(szTemp));
MessageBox( GetActiveWindow(), szTemp, szAppTitle,
MB_OK | MB_ICONSTOP );
#ifdef DEBUG
wsprintf( szDebug, "Midi subsystem error: %s", szTemp );
DebugPrint( szDebug );
#endif
}


/*****************************************************************************/
/* HandleCommDlgError() */
/* */
/* The function translates extended common dialog error codes into a */
/* string resource ID, loads that string from our module, and displays it in */
/* a message box. This implementation only covers the general CD error codes.*/
/* */
/*****************************************************************************/
int HandleCommDlgError( DWORD dwError )
{
char szTitle[128];
UINT uMsgID;

if( dwError == CDERR_DIALOGFAILURE )
uMsgID = IDS_CDERR_DIALOGFAILURE;
else
uMsgID = (UINT)dwError + IDS_CDERR_GENERAL_BASE;

LoadString( hInst, uMsgID, szTemp, sizeof(szTemp));
LoadString( hInst, IDS_CDERR_TITLESTRING, szTitle, sizeof(szTitle));
MessageBox( GetActiveWindow(), szTemp, szTitle,
MB_OK | MB_ICONEXCLAMATION );

return( 0 );
}


/****************************************************************************/
/* BuildTitleBarText() */
/* */
/* Helper function designed to updated the title bar text of the main */
/* window to reflect the currently loaded file (if there is one). */
/* */
/****************************************************************************/
void BuildTitleBarText( void )
{
char szTitle[sizeof(szAppCaption) + MAX_PATH + sizeof( " - (Paused)")];

lstrcpy( szTitle, szAppCaption );
if( bFileOpen )
{
lstrcat( szTitle, " - " );
lstrcat( szTitle, szFileTitle );
}
if( bPaused )
lstrcat( szTitle, " (Paused)" );
SetWindowText( hWndMain, szTitle );
}


/****************************************************************************/
/* SetAllChannelVolumes() */
/* */
/* Given a percent in tenths of a percent, sets volume on all channels to */
/* reflect the new value. */
/****************************************************************************/
void SetAllChannelVolumes( DWORD dwVolumePercent )
{
DWORDdwEvent, dwStatus, dwVol, idx;
MMRESULTmmrRetVal;

if( !bPlaying )
return;


for( idx = 0, dwStatus = MIDI_CTRLCHANGE; idx < NUM_CHANNELS; idx++,
dwStatus++ )
{
dwVol = ( dwVolCache[idx] * dwVolumePercent ) / 1000;
dwEvent = dwStatus | ((DWORD)MIDICTRL_VOLUME << 8)
| ((DWORD)dwVol << 16);
if(( mmrRetVal = midiOutShortMsg( (HMIDIOUT)hStream, dwEvent ))
!= MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
return;
}
}
}


/****************************************************************************/
/* SetChannelVolume() */
/* */
/* Given a percent in tenths of a percent, sets volume on a specified */
/* channel to reflect the new value. */
/****************************************************************************/
void SetChannelVolume( DWORD dwChannel, DWORD dwVolumePercent )
{
DWORDdwEvent, dwVol;
MMRESULTmmrRetVal;

if( !bPlaying )
return;

dwVol = ( dwVolCache[dwChannel] * dwVolumePercent ) / 1000;
dwEvent = MIDI_CTRLCHANGE | dwChannel | ((DWORD)MIDICTRL_VOLUME << 8)
| ((DWORD)dwVol << 16);
if(( mmrRetVal = midiOutShortMsg( (HMIDIOUT)hStream, dwEvent ))
!= MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
return;
}
}