2025-11-28 00:35:46 +09:00

377 lines
13 KiB
C++

// 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 (c) Microsoft Corporation. All rights reserved
//
#include "stdafx.h"
#include <commctrl.h>
#include <shlwapi.h>
#include "MediaPlayer.h"
#include "Resource.h"
#define progressTimer 0x1000
// Global Variables:
HINSTANCE g_hInstance;
CMediaPlayer *g_MediaPlayer;
// Forward declarations of functions included in this code module:
INT_PTR CALLBACK MediaPlayerDialogProc(HWND, UINT, WPARAM, LPARAM);
bool IsWin7OrLater()
{
bool bWin7OrLater = true;
OSVERSIONINFO ver = {};
ver.dwOSVersionInfoSize = sizeof(ver);
if (GetVersionEx(&ver))
{
bWin7OrLater = (ver.dwMajorVersion > 6) ||
((ver.dwMajorVersion == 6) && (ver.dwMinorVersion >= 1));
}
return bWin7OrLater;
}
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/,
LPWSTR /*lpCmdLine*/,
int /*nCmdShow*/)
{
if (!IsWin7OrLater())
{
MessageBox(NULL, L"This sample requires Windows 7 or later", L"Incompatible OS Version", MB_OK);
return 0;
}
CoInitialize(NULL);
static const INITCOMMONCONTROLSEX commonCtrls =
{
sizeof(INITCOMMONCONTROLSEX),
ICC_STANDARD_CLASSES | ICC_BAR_CLASSES
};
InitCommonControlsEx(&commonCtrls);
g_hInstance = hInstance; // Store instance handle in our global variable
INT_PTR id = DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MEDIAPLAYER), NULL, MediaPlayerDialogProc);
CoUninitialize();
return (int) id;
}
//
// FUNCTION: MediaPlayerDialogProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
INT_PTR CALLBACK MediaPlayerDialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
BOOL handled = FALSE;
switch (message)
{
case WM_INITDIALOG:
{
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_PLAYBACKPOS), TBM_SETRANGE, TRUE, MAKELONG(0, 1000));
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_PLAYBACKPOS), TBM_SETTICFREQ, 10, 0);
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_VOLUME), TBM_SETRANGE, TRUE, MAKELONG(0, 100));
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_VOLUME), TBM_SETTICFREQ, 10, 0);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
SHAutoComplete(GetDlgItem(hWnd, IDC_EDIT_FILENAME), SHACF_FILESYSTEM | SHACF_USETAB);
g_MediaPlayer = new (std::nothrow) CMediaPlayer(hWnd);
if (g_MediaPlayer == NULL)
{
MessageBox(hWnd, L"Unable to allocate media player", L"Initialization Failure", MB_OK);
EndDialog(hWnd, -1);
break;
}
HRESULT hr = g_MediaPlayer->Initialize();
if (FAILED(hr))
{
MessageBox(hWnd, L"Unable to initialize media player", L"Initialization Failure", MB_OK);
EndDialog(hWnd, -1);
break;
}
float volume = g_MediaPlayer->GetVolume();
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_VOLUME), TBM_SETPOS, TRUE, static_cast<int>(volume * 100.0f));
bool mute = g_MediaPlayer->GetMute();
CheckDlgButton(hWnd, IDC_CHECK_MUTE, mute ? BST_CHECKED : BST_UNCHECKED);
break;
}
case WM_DESTROY:
g_MediaPlayer->Shutdown();
g_MediaPlayer->Release();
g_MediaPlayer = NULL;
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDOK:
case IDCANCEL:
//
// Stop on Cancel/OK.
//
EndDialog(hWnd, TRUE);
handled = TRUE;
break;
//
// The user checked the "Pause on Duck" option - ask the media player to sync to that state.
//
case IDC_CHECK_PAUSE_ON_DUCK:
g_MediaPlayer->SyncPauseOnDuck(IsDlgButtonChecked(hWnd, IDC_CHECK_PAUSE_ON_DUCK) == BST_CHECKED);
break;
//
// The user checked the "Opt Out" option - ask the media player to sync to that state.
//
case IDC_CHECK_DUCKING_OPT_OUT:
g_MediaPlayer->SyncDuckingOptOut(IsDlgButtonChecked(hWnd, IDC_CHECK_DUCKING_OPT_OUT) == BST_CHECKED);
break;
//
// See if the user navigated away from the filename edit control - if so, load the file in the filename edit control.
//
case IDC_EDIT_FILENAME:
if (wmEvent == EN_KILLFOCUS)
{
wchar_t fileName[MAX_PATH];
//
// If we're playing (the stop button is enabled), stop playing.
//
if (IsWindowEnabled(GetDlgItem(hWnd, IDC_BUTTON_STOP)))
{
g_MediaPlayer->Stop();
KillTimer(hWnd, progressTimer);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
}
GetDlgItemText(hWnd, IDC_EDIT_FILENAME, fileName, ARRAYSIZE(fileName));
if (g_MediaPlayer->SetFileName(fileName))
{
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
}
}
break;
//
// If the user hit the "Browse" button, bring up the file common dialog box.
//
// If the user hit "OK" to the dialog then update the edit control to include the filename and load
// the file into the player.
//
case IDC_BUTTON_BROWSE:
{
wchar_t fileName[MAX_PATH];
OPENFILENAME openFileName = {0};
openFileName.lStructSize = sizeof(openFileName);
openFileName.lpstrFile = fileName;
openFileName.nMaxFile = ARRAYSIZE(fileName);
fileName[0] = L'\0';
openFileName.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST;
openFileName.hInstance = g_hInstance;
openFileName.hwndOwner = hWnd;
if (GetOpenFileName(&openFileName))
{
SetDlgItemText(hWnd, IDC_EDIT_FILENAME, openFileName.lpstrFile);
//
// If we're playing (the stop button is enabled), stop playing.
//
if (IsWindowEnabled(GetDlgItem(hWnd, IDC_BUTTON_STOP)))
{
g_MediaPlayer->Stop();
KillTimer(hWnd, progressTimer);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
}
g_MediaPlayer->SetFileName(fileName);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
}
break;
}
//
// The user hit the "Play" button.
//
// Sync the "Pause On Duck" and "Ducking Opt Out" buttons with the player and then start playback.
//
//
// Then disable the "Play" button and enable the "Pause" and "Stop" buttons.
//
case IDC_BUTTON_PLAY:
{
g_MediaPlayer->SyncPauseOnDuck(IsDlgButtonChecked(hWnd, IDC_CHECK_PAUSE_ON_DUCK) == BST_CHECKED);
g_MediaPlayer->SyncDuckingOptOut(IsDlgButtonChecked(hWnd, IDC_CHECK_DUCKING_OPT_OUT) == BST_CHECKED);
g_MediaPlayer->Play();
SetTimer(hWnd, progressTimer, 40, NULL);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), TRUE);
break;
}
//
// The user hit the "Pause/Continue" button.
//
// Toggle the "Pause" state in the player and update the button text as appropriate.
//
case IDC_BUTTON_PAUSE:
{
if (g_MediaPlayer->TogglePauseState())
{
SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), L"Continue");
KillTimer(hWnd, progressTimer);
}
else
{
SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), L"Pause");
SetTimer(hWnd, progressTimer, 40, NULL);
}
break;
}
//
// The user hit the "Stop" button.
//
// Stop the player and stop the progress timer and enable the "Play" button.
//
case IDC_BUTTON_STOP:
{
g_MediaPlayer->Stop();
KillTimer(hWnd, progressTimer);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
break;
}
case IDC_CHECK_MUTE:
{
g_MediaPlayer->SetMute(IsDlgButtonChecked(hWnd, IDC_CHECK_MUTE) == BST_CHECKED);
}
default:
break;
}
break;
}
case WM_HSCROLL:
{
//
// This horizontal scroll notification comes from the volume slider. Update the volume for the media player.
//
if (reinterpret_cast<HWND>(lParam) == GetDlgItem(hWnd, IDC_SLIDER_VOLUME))
{
int volumePosition = static_cast<int>(SendMessage(GetDlgItem(hWnd, IDC_SLIDER_VOLUME), TBM_GETPOS, 0, 0));
g_MediaPlayer->SetVolume(static_cast<float>(volumePosition) / 100.0f);
}
break;
}
case WM_TIMER:
{
if (wParam == progressTimer)
{
//
// Update the progress slider to match the current playback position.
//
long position = g_MediaPlayer->GetPosition();
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_PLAYBACKPOS), TBM_SETPOS, TRUE, position);
}
break;
}
//
// Let the media player know about the DShow graph event. If we come to the end of the track, reset the slider to the beginning of the track.
//
case WM_APP_GRAPHNOTIFY:
{
if (g_MediaPlayer->HandleGraphEvent())
{
// Reset the slider and timer, we're at the end of the track.
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_PLAYBACKPOS), TBM_SETPOS, TRUE, 0);
KillTimer(hWnd, progressTimer);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PLAY), TRUE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), FALSE);
EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_STOP), FALSE);
}
break;
}
//
// Called when the media player receives a ducking notification. Lets the media player know that the session has been ducked and pauses the player.
//
case WM_APP_SESSION_DUCKED:
if (g_MediaPlayer->Pause())
{
SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), L"Continue");
KillTimer(hWnd, progressTimer);
}
break;
//
// Called when the media player receives an unduck notification. Lets the media player know that the session has been unducked and continues the player.
//
case WM_APP_SESSION_UNDUCKED:
if (g_MediaPlayer->Continue())
{
SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_PAUSE), L"Pause");
SetTimer(hWnd, progressTimer, 40, NULL);
}
break;
//
// Process a session volume changed notification. Sync the UI elements with the values in the notification.
//
// The caller passes the new Mute state in wParam and the new volume value in lParam.
//
case WM_APP_SESSION_VOLUME_CHANGED:
{
BOOL newMute = (BOOL)wParam;
float newVolume = LPARAM2FLOAT(lParam);
int volumePos = static_cast<int>(newVolume * 100);
SendMessage(GetDlgItem(hWnd, IDC_SLIDER_VOLUME), TBM_SETPOS, TRUE, volumePos);
CheckDlgButton(hWnd, IDC_CHECK_MUTE, newMute ? BST_CHECKED : BST_UNCHECKED);
break;
}
default:
//
// If the current chat transport is going to handle this message, pass the message to the transport.
//
// Otherwise just let our caller know that they need to handle it.
//
break;
}
return handled;
}