243 lines
6.5 KiB
C++
243 lines
6.5 KiB
C++
#include "stdafx.h"
|
|
/**
|
|
* @file TetrisMedia.cpp
|
|
* @brief 实现背景音乐开关和复活视频播放逻辑。
|
|
*/
|
|
|
|
#include "Tetris.h"
|
|
#include "TetrisAppInternal.h"
|
|
#include "TetrisAssets.h"
|
|
#include <shellapi.h>
|
|
#include <string>
|
|
|
|
static bool bgmPlaying = false;
|
|
static bool bgmUsingMci = false;
|
|
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
|
|
static constexpr const wchar_t* kReviveVideoAlias = L"TereisReviveVideo";
|
|
|
|
/**
|
|
* @brief 尝试通过 MCI 循环播放指定音乐文件。
|
|
* @param path 音频文件路径。
|
|
* @param forceMpegVideo 是否强制按 mpegvideo 类型打开。
|
|
* @return 播放成功返回 true,否则返回 false。
|
|
*/
|
|
static bool TryPlayMciLoop(const std::wstring& path, bool forceMpegVideo)
|
|
{
|
|
if (!FileExists(path))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
|
|
|
// MCI 对部分 OGG/视频容器识别不稳定,调用方会按不同类型尝试。
|
|
std::wstring openCommand = L"open \"" + path + L"\" ";
|
|
if (forceMpegVideo)
|
|
{
|
|
openCommand += L"type mpegvideo ";
|
|
}
|
|
openCommand += L"alias ";
|
|
openCommand += kBgmAlias;
|
|
|
|
if (mciSendStringW(openCommand.c_str(), nullptr, 0, nullptr) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::wstring playCommand = std::wstring(L"play ") + kBgmAlias + L" repeat";
|
|
if (mciSendStringW(playCommand.c_str(), nullptr, 0, nullptr) != 0)
|
|
{
|
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
|
return false;
|
|
}
|
|
|
|
bgmPlaying = true;
|
|
bgmUsingMci = true;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief 停止背景音乐并释放当前使用的播放设备。
|
|
*/
|
|
void StopBackgroundMusic()
|
|
{
|
|
if (bgmUsingMci)
|
|
{
|
|
mciSendStringW((std::wstring(L"stop ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
|
}
|
|
else
|
|
{
|
|
PlaySoundW(nullptr, nullptr, 0);
|
|
}
|
|
|
|
bgmPlaying = false;
|
|
bgmUsingMci = false;
|
|
}
|
|
|
|
/**
|
|
* @brief 按资源优先级查找并启动背景音乐。
|
|
*/
|
|
void StartBackgroundMusic()
|
|
{
|
|
if (!bgmEnabled || bgmPlaying)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const wchar_t* bgmWavRelativePath = L"assets\\audio\\bgm.wav";
|
|
const std::wstring bgmWavCandidates[] =
|
|
{
|
|
BuildAssetPath(bgmWavRelativePath),
|
|
BuildWorkingDirAssetPath(bgmWavRelativePath)
|
|
};
|
|
|
|
for (const std::wstring& candidate : bgmWavCandidates)
|
|
{
|
|
if (FileExists(candidate) &&
|
|
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
|
|
{
|
|
bgmPlaying = true;
|
|
bgmUsingMci = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
const wchar_t* oggRelativePath = L"assets\\audio\\bgm.ogg";
|
|
const std::wstring oggCandidates[] =
|
|
{
|
|
BuildAssetPath(oggRelativePath),
|
|
BuildWorkingDirAssetPath(oggRelativePath)
|
|
};
|
|
|
|
for (const std::wstring& candidate : oggCandidates)
|
|
{
|
|
if (TryPlayMciLoop(candidate, false) || TryPlayMciLoop(candidate, true))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const wchar_t* fallbackWavRelativePath = L"assets\\audio\\background.wav";
|
|
const std::wstring fallbackWavCandidates[] =
|
|
{
|
|
BuildAssetPath(fallbackWavRelativePath),
|
|
BuildWorkingDirAssetPath(fallbackWavRelativePath)
|
|
};
|
|
|
|
for (const std::wstring& candidate : fallbackWavCandidates)
|
|
{
|
|
if (FileExists(candidate) &&
|
|
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
|
|
{
|
|
bgmPlaying = true;
|
|
bgmUsingMci = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bgmEnabled = false;
|
|
}
|
|
|
|
/**
|
|
* @brief 切换背景音乐开关并刷新窗口。
|
|
* @param hWnd 当前窗口句柄。
|
|
*/
|
|
void ToggleBackgroundMusic(HWND hWnd)
|
|
{
|
|
bgmEnabled = !bgmEnabled;
|
|
if (bgmEnabled)
|
|
{
|
|
StartBackgroundMusic();
|
|
}
|
|
else
|
|
{
|
|
StopBackgroundMusic();
|
|
}
|
|
InvalidateRect(hWnd, nullptr, FALSE);
|
|
}
|
|
|
|
/**
|
|
* @brief 播放复活视频,先尝试 MCI,全屏播放失败时退回系统默认播放器。
|
|
* @param hWnd 当前窗口句柄,用作 MCI 父窗口和 ShellExecute 父窗口。
|
|
* @return 播放成功返回 true,否则返回 false。
|
|
*/
|
|
bool PlayReviveVideo(HWND hWnd)
|
|
{
|
|
std::wstring videoPath = BuildAssetPath(L"assets\\video\\video.avi");
|
|
if (!FileExists(videoPath))
|
|
{
|
|
videoPath = BuildWorkingDirAssetPath(L"assets\\video\\video.avi");
|
|
}
|
|
if (!FileExists(videoPath))
|
|
{
|
|
videoPath = BuildAssetPath(L"assets\\video\\video.mp4");
|
|
}
|
|
if (!FileExists(videoPath))
|
|
{
|
|
videoPath = BuildWorkingDirAssetPath(L"assets\\video\\video.mp4");
|
|
}
|
|
if (!FileExists(videoPath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool shouldResumeBgm = bgmEnabled;
|
|
if (bgmPlaying)
|
|
{
|
|
StopBackgroundMusic();
|
|
}
|
|
|
|
// 先用 MCI 全屏同步播放;失败时再交给系统默认播放器。
|
|
bool played = false;
|
|
for (int attempt = 0; attempt < 2 && !played; attempt++)
|
|
{
|
|
bool forceMpegVideo = attempt == 0;
|
|
mciSendStringW((std::wstring(L"close ") + kReviveVideoAlias).c_str(), nullptr, 0, nullptr);
|
|
|
|
std::wstring openCommand = L"open \"" + videoPath + L"\" ";
|
|
if (forceMpegVideo)
|
|
{
|
|
openCommand += L"type mpegvideo ";
|
|
}
|
|
openCommand += L"alias ";
|
|
openCommand += kReviveVideoAlias;
|
|
|
|
if (mciSendStringW(openCommand.c_str(), nullptr, 0, hWnd) == 0)
|
|
{
|
|
std::wstring playCommand = std::wstring(L"play ") + kReviveVideoAlias + L" fullscreen wait";
|
|
MCIERROR playResult = mciSendStringW(playCommand.c_str(), nullptr, 0, hWnd);
|
|
mciSendStringW((std::wstring(L"close ") + kReviveVideoAlias).c_str(), nullptr, 0, nullptr);
|
|
played = playResult == 0;
|
|
}
|
|
}
|
|
|
|
if (!played)
|
|
{
|
|
SHELLEXECUTEINFOW shellInfo = {};
|
|
shellInfo.cbSize = sizeof(shellInfo);
|
|
shellInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
shellInfo.hwnd = hWnd;
|
|
shellInfo.lpVerb = L"open";
|
|
shellInfo.lpFile = videoPath.c_str();
|
|
shellInfo.nShow = SW_SHOWNORMAL;
|
|
|
|
if (ShellExecuteExW(&shellInfo))
|
|
{
|
|
if (shellInfo.hProcess != nullptr)
|
|
{
|
|
WaitForSingleObject(shellInfo.hProcess, INFINITE);
|
|
CloseHandle(shellInfo.hProcess);
|
|
}
|
|
played = true;
|
|
}
|
|
}
|
|
|
|
if (shouldResumeBgm)
|
|
{
|
|
StartBackgroundMusic();
|
|
}
|
|
|
|
return played;
|
|
}
|