///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2009 Carlo McWhirter. All Rights Reserved.
// Copyright (c) 2009 Hyteq Systems. All Rights Reserved.
//
// http://www.hyteq.com
//
// Hyteq Systems Educational License
//
// This file is part of WM3DSpectrum, also known as 3D Spectrum Analyzer for
// Windows Media Player. This file, the project that this file is part of, and
// the resulting compiled program files are intended to be used for educational
// purposes only. Use of this file or this project for any non-educational or
// non-observatory purpose is strictly prohibited without the express written
// consent of all of the copyright holders listed above.
//
// This file may only be modified and later redistributed by one or more of the
// copyright holders listed above. Suggestions for bug fixes, enhancements,
// and other modifications must be sent directly to one of the copyright holders.
//
// This file may be modified without being redistributed by any recipient of
// this file provided the modifications are NOT intentionaly or unintentionaly
// directed toward malicious or illegal purposes, but, instead, toward educational
// purposes only.
//
// THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTY. IN NO EVENT WILL THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES
// ARISING FROM THE USE OF THIS SOFTWARE.
//
// This notice may not be removed from this file or altered in any manner.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "WM3DSpectrum.h"
#include "CPropertyDialog.h"
/////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::CWM3DSpectrum
// Constructor
CWM3DSpectrum::CWM3DSpectrum() :
m_hwndParent(NULL),
m_clrForeground(0x0000FF),
m_nPreset(0)
{
_tcsncpy_s(m_szPluginText, sizeof(m_szPluginText) / sizeof(m_szPluginText[0]), _T("WM3DSpectrum Plugin"), sizeof(m_szPluginText) / sizeof(m_szPluginText[0]));
m_dwAdviseCookie = 0;
memset(&m_RenderContext, 0, sizeof(RENDERCONTEXT));
m_vecWindowedRenderables.push_back( &m_GridRenerer );
}
/////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::~CWM3DSpectrum
// Destructor
CWM3DSpectrum::~CWM3DSpectrum()
{
if(m_WindowedRenderer.IsInitialized())
m_WindowedRenderer.Destroy( &m_RenderContext );
if(m_NonWindowedRenderer.IsInitialized())
m_NonWindowedRenderer.Destroy( &m_RenderContext );
}
/////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum:::FinalConstruct
// Called when an effect is first loaded. Use this function to do one-time
// intializations that could fail (i.e. creating offscreen buffers) instead
// of doing this in the constructor, which cannot return an error.
HRESULT CWM3DSpectrum::FinalConstruct()
{
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum:::FinalRelease
// Called when an effect is unloaded. Use this function to free any
// resources allocated in FinalConstruct.
void CWM3DSpectrum::FinalRelease()
{
ReleaseCore();
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::Render
// Called when an effect should render itself to the screen.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::Render(TimedLevel *pLevels, HDC hdc, RECT *prc)
{
m_RenderContext.bIsWindowed = false;
m_RenderContext.bHasNewData = true;
m_RenderContext.hWnd = m_hwndParent;
m_RenderContext.hDC = hdc;
m_RenderContext.lpRect = prc;
m_RenderContext.pLevel = pLevels;
m_RenderContext.preset = m_nPreset;
m_RenderContext.vecRenderables.assign(m_vecNonWindowedRenderables.begin(),
m_vecNonWindowedRenderables.end());
switch (m_nPreset)
{
case PRESET_ROSE_GARDEN:
case PRESET_CITY_LIGHTS:
case PRESET_ROSE_GARDEN_POINTS:
case PRESET_CITY_LIGHTS_POINTS:
m_RenderContext.Interpolator = &m_LinearInterpolator;
break;
case PRESET_ROSE_GARDEN_SMOOTH:
case PRESET_CITY_LIGHTS_SMOOTH:
case PRESET_ROSE_GARDEN_POINTS_SMOOTH:
case PRESET_CITY_LIGHTS_POINTS_SMOOTH:
default:
m_RenderContext.Interpolator = &m_SmoothInterpolator;
}
if(m_NonWindowedRenderer.IsInitialized() == false)
{
if(m_NonWindowedRenderer.InitializationFailed() == false)
{
m_NonWindowedRenderer.Intialize( &m_RenderContext );
m_NonWindowedRenderer.Render( &m_RenderContext );
}
}
else
{
m_NonWindowedRenderer.Render( &m_RenderContext );
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::MediaInfo
// Everytime new media is loaded, this method is called to pass the
// number of channels (mono/stereo), the sample rate of the media, and the
// title of the media
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::MediaInfo(LONG lChannelCount, LONG lSampleRate, BSTR bstrTitle )
{
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::GetCapabilities
// Returns the capabilities of this effect. Flags that can be returned are:
// EFFECT_CANGOFULLSCREEN -- effect supports full-screen rendering
// EFFECT_HASPROPERTYPAGE -- effect supports a property page
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::GetCapabilities(DWORD * pdwCapabilities)
{
if (NULL == pdwCapabilities)
{
return E_POINTER;
}
*pdwCapabilities = EFFECT_HASPROPERTYPAGE;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::DisplayPropertyPage
// Invoked when a host wants to display the property page for the effect
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::DisplayPropertyPage(HWND hwndOwner)
{
CPropertyDialog dialog(this);
dialog.DoModal(hwndOwner);
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::GetTitle
// Invoked when a host wants to obtain the title of the effect
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::GetTitle(BSTR* bstrTitle)
{
USES_CONVERSION;
if (NULL == bstrTitle)
{
return E_POINTER;
}
CComBSTR bstrTemp;
bstrTemp.LoadString(IDS_EFFECTNAME);
if ((!bstrTemp) || (0 == bstrTemp.Length()))
{
return E_FAIL;
}
*bstrTitle = bstrTemp.Detach();
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::GetPresetTitle
// Invoked when a host wants to obtain the title of the given preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::GetPresetTitle(LONG nPreset, BSTR *bstrPresetTitle)
{
USES_CONVERSION;
if (NULL == bstrPresetTitle)
{
return E_POINTER;
}
if ((nPreset < 0) || (nPreset >= PRESET_COUNT))
{
return E_INVALIDARG;
}
CComBSTR bstrTemp;
switch (nPreset)
{
case PRESET_ROSE_GARDEN:
bstrTemp.LoadString(IDS_ROSEGARDEN);
break;
case PRESET_CITY_LIGHTS:
bstrTemp.LoadString(IDS_CITYLIGHTS);
break;
case PRESET_ROSE_GARDEN_POINTS:
bstrTemp.LoadString(IDS_ROSEGARDENPOINTS);
break;
case PRESET_CITY_LIGHTS_POINTS:
bstrTemp.LoadString(IDS_CITYLIGHTSPOINTS);
break;
case PRESET_ROSE_GARDEN_SMOOTH:
bstrTemp.LoadString(IDS_ROSEGARDEN_SMOOTH);
break;
case PRESET_CITY_LIGHTS_SMOOTH:
bstrTemp.LoadString(IDS_CITYLIGHTS_SMOOTH);
break;
case PRESET_ROSE_GARDEN_POINTS_SMOOTH:
bstrTemp.LoadString(IDS_ROSEGARDENPOINTS_SMOOTH);
break;
case PRESET_CITY_LIGHTS_POINTS_SMOOTH:
bstrTemp.LoadString(IDS_CITYLIGHTSPOINTS_SMOOTH);
break;
}
if ((!bstrTemp) || (0 == bstrTemp.Length()))
{
return E_FAIL;
}
*bstrPresetTitle = bstrTemp.Detach();
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::GetPresetCount
// Invoked when a host wants to obtain the number of supported presets
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::GetPresetCount(LONG *pnPresetCount)
{
if (NULL == pnPresetCount)
{
return E_POINTER;
}
*pnPresetCount = PRESET_COUNT;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::SetCurrentPreset
// Invoked when a host wants to change the index of the current preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::SetCurrentPreset(LONG nPreset)
{
if ((nPreset < 0) || (nPreset >= PRESET_COUNT))
{
return E_INVALIDARG;
}
m_nPreset = nPreset;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::GetCurrentPreset
// Invoked when a host wants to obtain the index of the current preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::GetCurrentPreset(LONG *pnPreset)
{
if (NULL == pnPreset)
{
return E_POINTER;
}
*pnPreset = m_nPreset;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::SetCore
// Set WMP core interface
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::SetCore(IWMPCore * pCore)
{
HRESULT hr = S_OK;
// release any existing WMP core interfaces
ReleaseCore();
// If we get passed a NULL core, this means
// that the plugin is being shutdown.
if (pCore == NULL)
{
return S_OK;
}
m_spCore = pCore;
// connect up the event interface
CComPtr<IConnectionPointContainer> spConnectionContainer;
hr = m_spCore->QueryInterface( &spConnectionContainer );
if (SUCCEEDED(hr))
{
hr = spConnectionContainer->FindConnectionPoint( __uuidof(IWMPEvents), &m_spConnectionPoint );
}
if (SUCCEEDED(hr))
{
hr = m_spConnectionPoint->Advise( GetUnknown(), &m_dwAdviseCookie );
if ((FAILED(hr)) || (0 == m_dwAdviseCookie))
{
m_spConnectionPoint = NULL;
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::Create
// Invoked when the visualization should be initialized.
//
// If hwndParent != NULL, RenderWindowed() will be called and the visualization
// should draw into the window specified by hwndParent. This will be the
// behavior when the visualization is hosted in a window.
//
// If hwndParent == NULL, Render() will be called and the visualization
// should draw into the DC passed to Render(). This will be the behavior when
// the visualization is hosted windowless (like in a skin for example).
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::Create(HWND hwndParent)
{
m_hwndParent = hwndParent;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::Destroy
// Invoked when the visualization should be released.
//
// Any resources allocated for rendering should be released.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::Destroy()
{
m_hwndParent = NULL;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::NotifyNewMedia
// Invoked when a new media stream begins playing.
//
// The visualization can inspect this object for properties (like name or artist)
// that might be interesting for visualization.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::NotifyNewMedia(IWMPMedia *pMedia)
{
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::OnWindowMessage
// Window messages sent to the parent window.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::OnWindowMessage(UINT msg, WPARAM WParam, LPARAM LParam, LRESULT *plResultParam )
{
// return S_OK only if the plugin has handled the window message
// return S_FALSE to let the defWindowProc handle the message
if(m_hwndParent == NULL)
m_NonWindowedRenderer.OnWindowMessage( &m_RenderContext, msg, WParam, LParam, plResultParam );
else
m_WindowedRenderer.OnWindowMessage( &m_RenderContext, msg, WParam, LParam, plResultParam );
return S_FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::RenderWindowed
// Called when an effect should render itself to the screen.
//
// The fRequiredRender flag specifies if an update is required, otherwise the
// update is optional. This allows visualizations that are fairly static (for example,
// album art visualizations) to only render when the parent window requires it,
// instead of n times a second for dynamic visualizations.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::RenderWindowed(TimedLevel *pLevels, BOOL fRequiredRender )
{
// NULL parent window should not happen
if (NULL == m_hwndParent)
{
return E_UNEXPECTED;
}
RECT rParent = { 0 };
::GetClientRect(m_hwndParent, &rParent);
m_RenderContext.bIsWindowed = true;
m_RenderContext.bHasNewData = true;
m_RenderContext.hWnd = m_hwndParent;
m_RenderContext.lpRect = &rParent;
m_RenderContext.pLevel = pLevels;
m_RenderContext.preset = m_nPreset;
m_RenderContext.vecRenderables.assign(m_vecWindowedRenderables.begin(),
m_vecWindowedRenderables.end());
switch (m_nPreset)
{
case PRESET_ROSE_GARDEN:
case PRESET_CITY_LIGHTS:
case PRESET_ROSE_GARDEN_POINTS:
case PRESET_CITY_LIGHTS_POINTS:
m_RenderContext.Interpolator = &m_LinearInterpolator;
break;
case PRESET_ROSE_GARDEN_SMOOTH:
case PRESET_CITY_LIGHTS_SMOOTH:
case PRESET_ROSE_GARDEN_POINTS_SMOOTH:
case PRESET_CITY_LIGHTS_POINTS_SMOOTH:
default:
m_RenderContext.Interpolator = &m_SmoothInterpolator;
}
if(m_WindowedRenderer.IsInitialized() == false)
{
if(m_WindowedRenderer.InitializationFailed() == false)
{
m_WindowedRenderer.Intialize( &m_RenderContext );
m_WindowedRenderer.Render( &m_RenderContext );
}
}
else
{
m_WindowedRenderer.Render( &m_RenderContext );
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::ReleaseCore
// Release WMP core interfaces
//////////////////////////////////////////////////////////////////////////////
void CWM3DSpectrum::ReleaseCore()
{
if (m_spConnectionPoint)
{
if (0 != m_dwAdviseCookie)
{
m_spConnectionPoint->Unadvise(m_dwAdviseCookie);
m_dwAdviseCookie = 0;
}
m_spConnectionPoint = NULL;
}
if (m_spCore)
{
m_spCore = NULL;
}
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::get_foregroundColor
// Property get to retrieve the foregroundColor prop via the public interface.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::get_foregroundColor(BSTR *pVal)
{
return ColorToWz( pVal, m_clrForeground);
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::put_foregroundColor
// Property put to set the foregroundColor prop via the public interface.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CWM3DSpectrum::put_foregroundColor(BSTR newVal)
{
return WzToColor(newVal, &m_clrForeground);
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::WzToColor
// Helper function used to convert a string into a COLORREF.
//////////////////////////////////////////////////////////////////////////////
HRESULT CWM3DSpectrum::WzToColor(const WCHAR *pwszColor, COLORREF *pcrColor)
{
if (NULL == pwszColor)
{
//NULL color string passed in
return E_POINTER;
}
if (0 == lstrlenW(pwszColor))
{
//Empty color string passed in
return E_INVALIDARG;
}
if (NULL == pcrColor)
{
//NULL output color DWORD passed in
return E_POINTER;
}
if (lstrlenW(pwszColor) != 7)
{
//hex color string is not of the correct length
return E_INVALIDARG;
}
DWORD dwRet = 0;
for (int i = 1; i < 7; i++)
{
// shift dwRet by 4
dwRet <<= 4;
// and add in the value of this string
if ((pwszColor[i] >= L'0') && (pwszColor[i] <= L'9'))
{
dwRet += pwszColor[i] - '0';
}
else if ((pwszColor[i] >= L'A') && (pwszColor[i] <= L'F'))
{
dwRet += 10 + (pwszColor[i] - L'A');
}
else if ((pwszColor[i] >= L'a') && (pwszColor[i] <= L'f'))
{
dwRet += 10 + (pwszColor[i] - L'a');
}
else
{
//Invalid hex digit in color string
return E_INVALIDARG;
}
}
*pcrColor = SwapBytes(dwRet);
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::ColorToWz
// Helper function used to convert a COLORREF to a BSTR.
//////////////////////////////////////////////////////////////////////////////
HRESULT CWM3DSpectrum::ColorToWz( BSTR* pbstrColor, COLORREF crColor)
{
_ASSERT( NULL != pbstrColor );
_ASSERT( (crColor & 0x00FFFFFF) == crColor );
*pbstrColor = NULL;
WCHAR wsz[8];
HRESULT hr = S_OK;
swprintf_s( wsz, sizeof(wsz)/sizeof(wsz[0]), L"#%06X", SwapBytes(crColor) );
*pbstrColor = ::SysAllocString( wsz );
if (!pbstrColor)
{
hr = E_FAIL;
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////
// CWM3DSpectrum::SwapBytes
// Used to convert between a DWORD and COLORREF. Simply swaps the lowest
// and 3rd order bytes.
//////////////////////////////////////////////////////////////////////////////
inline DWORD CWM3DSpectrum::SwapBytes(DWORD dwRet)
{
return ((dwRet & 0x0000FF00) | ((dwRet & 0x00FF0000) >> 16) | ((dwRet & 0x000000FF) << 16));
}