add: use Media Foundation to play sound effect

This commit is contained in:
Nomango 2018-10-11 20:00:21 +08:00
parent fdbece1663
commit 80d1a87955
13 changed files with 1277 additions and 747 deletions

View File

@ -25,38 +25,29 @@ namespace e2d
{
class Music;
// ÒôÔ´»Øµ÷
class VoiceCallback
: public IXAudio2VoiceCallback
class MediaAsyncCallback : public IMFAsyncCallback
{
public:
VoiceCallback();
MediaAsyncCallback(
HWND hwnd,
IMFMediaSession * pSession,
HANDLE hCloseEvent
);
~VoiceCallback();
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) {}
STDMETHOD_(void, OnStreamEnd) (THIS);
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) {}
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext);
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext);
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, SetCallbackOnStreamEnd) (THIS_ const Function& func);
STDMETHOD_(void, SetCallbackOnLoopEnd) (THIS_ const Function& func);
// IMFAsyncCallback methods
STDMETHODIMP GetParameters(DWORD*, DWORD*);
STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult);
protected:
Function loop_end_callback_;
Function stream_end_callback_;
long m_nRefCount;
IMFMediaSession * m_pSession;
HWND m_hwnd;
HANDLE m_hCloseEvent;
};

View File

@ -45,7 +45,9 @@
# define DIRECTINPUT_VERSION 0x0800
#endif
#define INITGUID
#ifndef INITGUID
# define INITGUID
#endif
// Windows Header Files
@ -55,8 +57,12 @@
#include <d2d1.h>
#include <dwrite.h>
#include <dinput.h>
#include <d2d1helper.h>
#include <xaudio2.h>
#include <shlwapi.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mferror.h>
#include <evr.h>
#include <mmdeviceapi.h>
// C++ RunTime Header Files
#include <map>
@ -76,8 +82,12 @@
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "xaudio2.lib")
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "mfplay.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib")
#ifndef HINST_THISCOMPONENT

View File

@ -149,22 +149,30 @@ namespace e2d
~Audio();
// 开启设备
void Open();
// 获取音量
float GetVolume();
// 关闭设备
void Close();
// 设置音量
void SetVolume(
float volume
);
// 创建音源
HRESULT CreateVoice(
IXAudio2SourceVoice ** voice,
WAVEFORMATEX * wfx,
VoiceCallback * callback
// 是否静音
bool GetMute();
// 设置静音
void SetMute(
bool mute
);
protected:
IXAudio2 * x_audio2_;
IXAudio2MasteringVoice* mastering_voice_;
LPWSTR device_id; // Device ID.
IMMDeviceEnumerator *enum_; // Audio device enumerator.
IMMDeviceCollection *devices_; // Audio device collection.
IMMDevice *device_; // An audio device.
IMFAttributes *attributes_; // Attribute store.
IMFMediaSink *sink_; // Streaming audio renderer (SAR)
IMFSimpleAudioVolume *audio_volume;
};
@ -301,9 +309,6 @@ namespace e2d
int height
);
// 进入下一场景
void EnterNextScene();
// Win32 ´°¿ÚÏûÏ¢»Øµ÷³ÌÐò
static LRESULT CALLBACK WndProc(
HWND hwnd,

View File

@ -71,6 +71,7 @@ namespace e2d
// 稜있
class Music
: public IMFAsyncCallback
{
public:
Music();
@ -97,15 +98,12 @@ namespace e2d
// 꺄렴
bool Play(
int loopCount = 0
int loop_count = 0
);
// 董界
void Pause();
// 继续
void Resume();
// 界岺
void Stop();
@ -117,51 +115,97 @@ namespace e2d
// <20>零稜좆
bool SetVolume(
float volume
float volume /* ·¶Î§: 0.0 ~ 1.0 */
);
// 设置播放结束时的执行函数
void SetCallbackOnEnd(
const Function& func
);
// »ñÈ¡ÒôÁ¿
float GetVolume() const;
// 设置循环播放中每一次播放结束时的执行函数
void SetCallbackOnLoopEnd(
const Function& func
);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// 获取 IXAudio2SourceVoice 对象
IXAudio2SourceVoice * GetSourceVoice() const;
// IMFAsyncCallback methods
STDMETHODIMP GetParameters(DWORD*, DWORD*) override;
STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) override;
protected:
enum class State : int
{
Closed = 0,
Loaded,
Started,
Paused,
Stopped,
Closing
};
E2D_DISABLE_COPY(Music);
bool ReadMMIO();
// Media event handlers
HRESULT OnNewPresentation(IMFMediaEvent *pEvent);
bool ResetFile();
HRESULT StartPlayback();
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource);
HRESULT HandleEvent(UINT_PTR pUnkPtr);
bool Read(
BYTE* buffer,
DWORD size_to_read
// Add a source node to a topology.
HRESULT AddSourceNode(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode // Receives the node pointer.
);
bool FindMediaFileCch(
wchar_t* dest_path,
int cch_dest,
const wchar_t * file_name
// Add an output node to a topology.
HRESULT AddOutputNode(
IMFTopology *pTopology, // Topology.
IMFActivate *pActivate, // Media sink activation object.
DWORD dwId, // Identifier of the stream sink.
IMFTopologyNode **ppNode // Receives the node pointer.
);
HRESULT AddBranchToPartialTopology(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
DWORD iStream // Stream index.
);
// Create a playback topology from a media source.
HRESULT CreatePlaybackTopology(
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFTopology **ppTopology // Receives a pointer to the topology.
);
// Create an activation object for a renderer, based on the stream media type.
HRESULT CreateMediaSinkActivate(
IMFStreamDescriptor *pSourceSD, // Pointer to the stream descriptor.
IMFActivate **ppActivate
);
HRESULT GetSimpleAudioVolume(
IMFSimpleAudioVolume** ppAudioVolume
) const;
static LRESULT CALLBACK MediaProc(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
protected:
bool opened_;
DWORD size_;
CHAR* buffer_;
BYTE* wave_data_;
HMMIO hmmio_;
MMCKINFO ck_;
MMCKINFO ck_riff_;
WAVEFORMATEX* wfx_;
VoiceCallback callback_;
IXAudio2SourceVoice* voice_;
long m_nRefCount;
int m_nTimes;
IMFMediaSession * m_pSession;
IMFMediaSource *m_pSource;
HWND m_hwndEvent;
State m_state;
HANDLE m_hCloseEvent;
};
@ -240,7 +284,7 @@ namespace e2d
// <20>零稜좆
void SetVolume(
float volume /* 音量范围为 -224 ~ 2240 是静音1 是正常音量 */
float volume /* ·¶Î§: 0.0 ~ 1.0 */
);
// 董界杰唐稜있

View File

@ -0,0 +1,116 @@
// Copyright (c) 2016-2018 Easy2D - Nomango
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "..\e2dimpl.h"
e2d::MediaAsyncCallback::MediaAsyncCallback(HWND hwnd, IMFMediaSession * pSession, HANDLE hCloseEvent)
: m_pSession(pSession)
, m_hCloseEvent(hCloseEvent)
, m_hwnd(hwnd)
{
}
HRESULT e2d::MediaAsyncCallback::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(MediaAsyncCallback, IMFAsyncCallback),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
ULONG e2d::MediaAsyncCallback::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG e2d::MediaAsyncCallback::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (uCount == 0)
{
delete this;
}
return uCount;
}
HRESULT e2d::MediaAsyncCallback::GetParameters(DWORD *, DWORD *)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
HRESULT e2d::MediaAsyncCallback::Invoke(IMFAsyncResult *pResult)
{
MediaEventType meType = MEUnknown; // Event type
IMFMediaEvent *pEvent = NULL;
// Get the event from the event queue.
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
if (FAILED(hr))
{
goto done;
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
if (meType == MESessionClosed)
{
// The session was closed.
// The application is waiting on the m_hCloseEvent event handle.
SetEvent(m_hCloseEvent);
}
else
{
// For all other events, get the next event in the queue.
hr = m_pSession->BeginGetEvent(this, NULL);
if (FAILED(hr))
{
goto done;
}
}
// Check the application state.
// If a call to IMFMediaSession::Close is pending, it means the
// application is waiting on the m_hCloseEvent event and
// the application's message loop is blocked.
// Otherwise, post a private window message to the application.
//if (m_state != Closing)
{
// Leave a reference count on the event.
pEvent->AddRef();
::PostMessage(m_hwnd, WM_APP + 1, (WPARAM)pEvent, (LPARAM)meType);
}
done:
SafeRelease(pEvent);
return S_OK;
}

View File

@ -1,64 +0,0 @@
// Copyright (c) 2016-2018 Easy2D - Nomango
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "..\e2dimpl.h"
#include "..\e2dutil.h"
e2d::VoiceCallback::VoiceCallback()
{
}
e2d::VoiceCallback::~VoiceCallback()
{
}
void e2d::VoiceCallback::OnLoopEnd(void * pBufferContext)
{
if (loop_end_callback_)
{
loop_end_callback_();
}
}
void e2d::VoiceCallback::OnStreamEnd()
{
if (stream_end_callback_)
{
stream_end_callback_();
}
}
void e2d::VoiceCallback::OnBufferEnd(void * pBufferContext)
{
if (loop_end_callback_)
{
loop_end_callback_();
}
}
void e2d::VoiceCallback::SetCallbackOnStreamEnd(const Function & func)
{
stream_end_callback_ = func;
}
void e2d::VoiceCallback::SetCallbackOnLoopEnd(const Function & func)
{
loop_end_callback_ = func;
}

View File

@ -22,40 +22,128 @@
e2d::Audio::Audio()
: x_audio2_(nullptr)
, mastering_voice_(nullptr)
: enum_(nullptr)
, devices_(nullptr)
, device_(nullptr)
, attributes_(nullptr)
, sink_(nullptr)
, device_id(nullptr)
, audio_volume(nullptr)
{
ThrowIfFailed(
XAudio2Create(&x_audio2_, 0)
MFStartup(MF_VERSION)
);
// Create the device enumerator.
ThrowIfFailed(
CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&enum_
)
);
// Enumerate the rendering devices.
ThrowIfFailed(
enum_->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices_)
);
// Get ID of the first device in the list.
ThrowIfFailed(
devices_->Item(0, &device_)
);
ThrowIfFailed(
x_audio2_->CreateMasteringVoice(&mastering_voice_)
device_->GetId(&device_id)
);
// Create an attribute store and set the device ID attribute.
ThrowIfFailed(
MFCreateAttributes(&attributes_, 2)
);
ThrowIfFailed(
attributes_->SetString(
MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID,
device_id
)
);
// Create the audio renderer.
ThrowIfFailed(
MFCreateAudioRenderer(attributes_, &sink_)
);
IMFGetService* service = NULL;
ThrowIfFailed(
sink_->QueryInterface(IID_IMFGetService, (void **)&service)
);
ThrowIfFailed(
service->GetService(MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&audio_volume))
);
SafeRelease(service);
}
e2d::Audio::~Audio()
{
if (mastering_voice_)
SafeRelease(enum_);
SafeRelease(devices_);
SafeRelease(device_);
SafeRelease(attributes_);
SafeRelease(audio_volume);
SafeRelease(sink_);
CoTaskMemFree(device_id);
MFShutdown();
}
float e2d::Audio::GetVolume()
{
float volume = 0.f;
if (audio_volume)
{
mastering_voice_->DestroyVoice();
mastering_voice_ = nullptr;
HRESULT hr = audio_volume->GetMasterVolume(&volume);
if (SUCCEEDED(hr))
{
return volume;
}
}
SafeRelease(x_audio2_);
return 0.f;
}
HRESULT e2d::Audio::CreateVoice(IXAudio2SourceVoice ** voice, WAVEFORMATEX * wfx, VoiceCallback * callback)
void e2d::Audio::SetVolume(float volume)
{
return x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
if (audio_volume)
{
volume = std::min(std::max(volume, 0.f), 1.f);
HRESULT hr = audio_volume->SetMasterVolume(volume);
printf("ºÇºÇ%#X\n", hr);
}
}
void e2d::Audio::Open()
bool e2d::Audio::GetMute()
{
x_audio2_->StartEngine();
BOOL mute = FALSE;
if (audio_volume)
{
HRESULT hr = audio_volume->GetMute(&mute);
if (SUCCEEDED(hr))
{
return mute ? true : false;
}
}
return FALSE;
}
void e2d::Audio::Close()
void e2d::Audio::SetMute(bool mute)
{
x_audio2_->StopEngine();
if (audio_volume)
{
audio_volume->SetMute(mute ? TRUE : FALSE);
}
}

View File

@ -93,7 +93,12 @@ void e2d::Game::Run(const Options& options)
Start();
// 刷新场景
EnterNextScene();
if (next_scene_)
{
next_scene_->OnEnter();
curr_scene_ = next_scene_;
next_scene_ = nullptr;
}
::ShowWindow(hwnd_, SW_SHOWNORMAL);
::UpdateWindow(hwnd_);
@ -189,17 +194,21 @@ bool e2d::Game::IsTransitioning() const
void e2d::Game::UpdateScene(float dt)
{
if (curr_scene_)
auto update = [&](Scene * scene) -> void
{
curr_scene_->Update(dt);
curr_scene_->GetRoot()->UpdateChildren(dt);
}
if (scene)
{
scene->Update(dt);
Node * root = scene->GetRoot();
if (root)
{
root->UpdateChildren(dt);
}
}
};
if (next_scene_)
{
next_scene_->Update(dt);
next_scene_->GetRoot()->UpdateChildren(dt);
}
update(curr_scene_);
update(next_scene_);
if (transition_)
{
@ -216,7 +225,19 @@ void e2d::Game::UpdateScene(float dt)
}
}
EnterNextScene();
if (next_scene_)
{
if (curr_scene_)
{
curr_scene_->OnExit();
curr_scene_->Release();
}
next_scene_->OnEnter();
curr_scene_ = next_scene_;
next_scene_ = nullptr;
}
}
void e2d::Game::DrawScene()
@ -281,7 +302,7 @@ void e2d::Game::Init()
}
// 注册窗口类
RegisterClassEx(&wcex);
::RegisterClassEx(&wcex);
// 计算窗口大小
Rect client_rect = Locate(width_, height_);
@ -309,6 +330,12 @@ void e2d::Game::Init()
return;
}
::SetWindowLongPtrW(
hwnd_,
GWLP_USERDATA,
PtrToUlong(this)
);
// 初始化设备
Device::Init(hwnd_);
@ -379,23 +406,6 @@ e2d::Rect e2d::Game::Locate(int width, int height)
return std::move(client_rect);
}
void e2d::Game::EnterNextScene()
{
if (next_scene_)
{
if (curr_scene_)
{
curr_scene_->OnExit();
curr_scene_->Release();
}
next_scene_->OnEnter();
curr_scene_ = next_scene_;
next_scene_ = nullptr;
}
}
int e2d::Game::GetWidth() const
{
return width_;
@ -481,153 +491,134 @@ void e2d::Game::SetIcon(int resource_id)
LRESULT e2d::Game::WndProc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
LRESULT result = 0;
bool was_handled = false;
Game * game = reinterpret_cast<Game*>(
static_cast<LONG_PTR>(::GetWindowLongPtrW(hwnd, GWLP_USERDATA))
);
if (msg == WM_CREATE)
switch (msg)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)l_param;
Game * game = (Game *)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(game)
);
// 处理鼠标消息
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
{
if (game->IsTransitioning())
break;
result = 1;
auto curr_scene = game->GetCurrentScene();
if (curr_scene)
{
curr_scene->Dispatch(MouseEvent(msg, w_param, l_param));
}
}
else
result = 0;
was_handled = true;
break;
// 处理按键消息
case WM_KEYDOWN:
case WM_KEYUP:
{
bool was_handled = false;
Game * game = reinterpret_cast<Game*>(
static_cast<LONG_PTR>(
::GetWindowLongPtrW(hwnd, GWLP_USERDATA)
)
);
if (game->IsTransitioning())
break;
switch (msg)
auto curr_scene = game->GetCurrentScene();
if (curr_scene)
{
// 处理鼠标消息
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
{
if (game->IsTransitioning())
break;
auto curr_scene = game->GetCurrentScene();
if (curr_scene)
{
curr_scene->Dispatch(MouseEvent(msg, w_param, l_param));
}
curr_scene->Dispatch(KeyEvent(msg, w_param, l_param));
}
result = 0;
was_handled = true;
break;
}
result = 0;
was_handled = true;
break;
// 处理按键消息
case WM_KEYDOWN:
case WM_KEYUP:
// 处理窗口大小变化消息
case WM_SIZE:
{
UINT width = LOWORD(l_param);
UINT height = HIWORD(l_param);
if (w_param == SIZE_RESTORED)
{
if (game->IsTransitioning())
break;
auto curr_scene = game->GetCurrentScene();
if (curr_scene)
{
curr_scene->Dispatch(KeyEvent(msg, w_param, l_param));
}
}
result = 0;
was_handled = true;
break;
// 处理窗口大小变化消息
case WM_SIZE:
{
UINT width = LOWORD(l_param);
UINT height = HIWORD(l_param);
if (w_param == SIZE_RESTORED)
{
float dpi = Graphics::GetDpi();
game->width_ = static_cast<int>(width * 96.f / dpi);
game->height_ = static_cast<int>(height * 96.f / dpi);
}
// 如果程序接收到一个 WM_SIZE 消息,这个方法将调整渲染
// 目标的大小。它可能会调用失败,但是这里可以忽略有可能的
// 错误,因为这个错误将在下一次调用 EndDraw 时产生
auto render_target = Device::GetGraphics()->GetRenderTarget();
if (render_target)
{
render_target->Resize(D2D1::SizeU(width, height));
}
}
break;
// 处理窗口标题变化消息
case WM_SETTEXT:
{
game->title_ = (const wchar_t*)l_param;
}
break;
// 处理分辨率变化消息
case WM_DISPLAYCHANGE:
{
// 重绘客户区
::InvalidateRect(hwnd, nullptr, FALSE);
}
result = 0;
was_handled = true;
break;
// 重绘窗口
case WM_PAINT:
{
game->DrawScene();
::ValidateRect(hwnd, nullptr);
}
result = 0;
was_handled = true;
break;
// 窗口关闭消息
case WM_CLOSE:
{
if (game->OnExit())
{
game->Quit();
}
}
result = 0;
was_handled = true;
break;
// 窗口销毁消息
case WM_DESTROY:
{
::PostQuitMessage(0);
}
result = 1;
was_handled = true;
break;
float dpi = Graphics::GetDpi();
game->width_ = static_cast<int>(width * 96.f / dpi);
game->height_ = static_cast<int>(height * 96.f / dpi);
}
if (!was_handled)
// 如果程序接收到一个 WM_SIZE 消息,这个方法将调整渲染
// 目标的大小。它可能会调用失败,但是这里可以忽略有可能的
// 错误,因为这个错误将在下一次调用 EndDraw 时产生
auto render_target = Device::GetGraphics()->GetRenderTarget();
if (render_target)
{
result = ::DefWindowProc(hwnd, msg, w_param, l_param);
render_target->Resize(D2D1::SizeU(width, height));
}
}
break;
// 处理窗口标题变化消息
case WM_SETTEXT:
{
game->title_ = (const wchar_t*)l_param;
}
break;
// 处理分辨率变化消息
case WM_DISPLAYCHANGE:
{
// 重绘客户区
::InvalidateRect(hwnd, nullptr, FALSE);
}
result = 0;
was_handled = true;
break;
// 重绘窗口
case WM_PAINT:
{
game->DrawScene();
::ValidateRect(hwnd, nullptr);
}
result = 0;
was_handled = true;
break;
// 窗口关闭消息
case WM_CLOSE:
{
if (game->OnExit())
{
game->Quit();
}
}
result = 0;
was_handled = true;
break;
// 窗口销毁消息
case WM_DESTROY:
{
::PostQuitMessage(0);
}
result = 1;
was_handled = true;
break;
}
if (!was_handled)
{
result = ::DefWindowProc(hwnd, msg, w_param, l_param);
}
return result;
}

View File

@ -78,6 +78,12 @@ void e2d::Scene::Draw()
void e2d::Scene::Dispatch(const MouseEvent & e)
{
auto handler = dynamic_cast<MouseEventHandler*>(this);
if (handler)
{
handler->Handle(e);
}
if (root_)
{
root_->Dispatch(e, false);
@ -86,6 +92,12 @@ void e2d::Scene::Dispatch(const MouseEvent & e)
void e2d::Scene::Dispatch(const KeyEvent & e)
{
auto handler = dynamic_cast<KeyEventHandler*>(this);
if (handler)
{
handler->Handle(e);
}
if (root_)
{
root_->Dispatch(e, false);

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ void e2d::Player::Resume(const String & file_path)
size_t hash = file_path.GetHash();
if (musics_.end() != musics_.find(hash))
musics_[hash]->Resume();
musics_[hash]->Play();
}
void e2d::Player::Stop(const String & file_path)
@ -137,7 +137,7 @@ void e2d::Player::Pause(const Resource& res)
void e2d::Player::Resume(const Resource& res)
{
if (musics_.end() != musics_.find(res.id))
musics_[res.id]->Resume();
musics_[res.id]->Play();
}
void e2d::Player::Stop(const Resource& res)
@ -160,7 +160,7 @@ float e2d::Player::GetVolume()
void e2d::Player::SetVolume(float volume)
{
volume_ = std::min(std::max(volume, -224.f), 224.f);
volume_ = std::min(std::max(volume, 0.f), 1.f);
for (const auto& pair : musics_)
{
pair.second->SetVolume(volume_);
@ -179,7 +179,7 @@ void e2d::Player::ResumeAll()
{
for (const auto& pair : musics_)
{
pair.second->Resume();
pair.second->Play();
}
}

View File

@ -222,7 +222,7 @@
<ClCompile Include="..\..\core\events\KeyEvent.cpp" />
<ClCompile Include="..\..\core\events\MouseEvent.cpp" />
<ClCompile Include="..\..\core\impl\TextRenderer.cpp" />
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp" />
<ClCompile Include="..\..\core\impl\MediaAsyncCallback.cpp" />
<ClCompile Include="..\..\core\modules\Audio.cpp" />
<ClCompile Include="..\..\core\modules\Device.cpp" />
<ClCompile Include="..\..\core\modules\Game.cpp" />

View File

@ -141,9 +141,6 @@
<ClCompile Include="..\..\core\impl\TextRenderer.cpp">
<Filter>impl</Filter>
</ClCompile>
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp">
<Filter>impl</Filter>
</ClCompile>
<ClCompile Include="..\..\core\events\KeyEvent.cpp">
<Filter>events</Filter>
</ClCompile>
@ -219,6 +216,9 @@
<ClCompile Include="..\..\core\modules\Device.cpp">
<Filter>modules</Filter>
</ClCompile>
<ClCompile Include="..\..\core\impl\MediaAsyncCallback.cpp">
<Filter>impl</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\core\easy2d.h" />