Click here to Skip to main content
Click here to Skip to main content
Articles » Multimedia » DirectX » General » Downloads
 
Add your own
alternative version
Go to top

Interactive 3D Spectrum Analyzer Visualization for Windows Media Player

, 17 May 2009
Interactive 3D Spectrum Analyzer for Windows Media Player using DirectX 9 and some light GPGPU.
///////////////////////////////////////////////////////////////////////////////
//  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 "GridRenderer.h"
#include <string>

CGridRenderer::CGridRenderer(void) :
	m_pVertPosDecl(NULL),
	m_pVertDecl(NULL),
	m_pVertBuff(NULL),
	m_pEffect(NULL),
	m_hRoseGardenTech(NULL),
	m_hCityLightsTech(NULL),
	m_hWVP(NULL),
	m_bRenderingAToB(true)
{
}

CGridRenderer::~CGridRenderer(void)
{
}

void CGridRenderer::Intitialize(LPRENDERCONTEXT context)
{
	//----------------------------------------
	// Create Vertex and Position Declarations
	//----------------------------------------
	D3DVERTEXELEMENT9 VertexPosElements[] = 
	{
		{0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		D3DDECL_END()
	};	
	CHECK(context->d3dDevice->CreateVertexDeclaration(VertexPosElements, &m_pVertPosDecl));

	D3DVERTEXELEMENT9 VertexColElements[] = 
	{
		{0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
		D3DDECL_END()
	};	
	CHECK(context->d3dDevice->CreateVertexDeclaration(VertexColElements, &m_pVertDecl));


	//-------------------------
	// Create Vertices for Grid
	//-------------------------
	std::vector<D3DXVECTOR3> verts;
	std::vector<DWORD> indices;
	MakePointGrid(1024, 1024, 0.5f, 0.5f, 
		D3DXVECTOR3(0.0f,0.0f,0.0f), verts, indices);

	m_iVertCount = 1024 * 1024;
	m_dwTriangleCount = 1023 * 1023 * 2;
	
	CHECK(context->d3dDevice->CreateVertexBuffer(m_iVertCount * sizeof(VertexPos), 
		D3DUSAGE_WRITEONLY,	0, D3DPOOL_MANAGED, &m_pVertBuff, 0));

	CHECK(context->d3dDevice->CreateIndexBuffer(m_dwTriangleCount * 3 * sizeof(DWORD), 
		D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_MANAGED, &m_pIndexBuff, 0)); 
	
	VertexPos* v = 0;
	CHECK(m_pVertBuff->Lock(0, 0, (void**)&v, 0));
	for(int i = 0; i < m_iVertCount; ++i)
		v[i] = verts[i];
	CHECK(m_pVertBuff->Unlock());

	DWORD* k = 0;
	CHECK(m_pIndexBuff->Lock(0, 0, (void**)&k, 0));
	for(DWORD i = 0; i < m_dwTriangleCount*3; ++i)
		k[i] = (DWORD)indices[i];
	CHECK(m_pIndexBuff->Unlock());


	//---------------------
	// Create data textures
	//---------------------
	CHECK(D3DXCreateTexture(context->d3dDevice, 
							1024, 
							1024, 
							1, 
							D3DUSAGE_RENDERTARGET, 
							D3DFMT_A32B32G32R32F, 
							D3DPOOL_DEFAULT, 
							&m_pDataTexA ));

	CHECK(D3DXCreateTexture(context->d3dDevice, 
							1024, 
							1024, 
							1, 
							D3DUSAGE_RENDERTARGET, 
							D3DFMT_A32B32G32R32F, 
							D3DPOOL_DEFAULT, 
							&m_pDataTexB ));

	CHECK(D3DXCreateTexture(context->d3dDevice,
							1024,
							1024,
							1,
							D3DUSAGE_DYNAMIC,
							D3DFMT_A32B32G32R32F,
							D3DPOOL_SYSTEMMEM,
							&m_pSwapTex));


	//--------------------------------
	// Create the "render to" surfaces
	//--------------------------------
	m_pDataTexA->GetSurfaceLevel( 0, &m_pDataTexASurface );
	m_pDataTexB->GetSurfaceLevel( 0, &m_pDataTexBSurface );
	m_pSwapTex->GetSurfaceLevel( 0, &m_pSwapSurface );

	D3DSURFACE_DESC desc;
	m_pDataTexASurface->GetDesc( &desc );

	CHECK(D3DXCreateRenderToSurface( context->d3dDevice, 
		                             desc.Width, 
		                             desc.Height, 
		                             desc.Format, 
		                             TRUE, 
		                             D3DFMT_D16, 
		                             &m_pRenderToSurface ));


	//--------------------------------------------
	// Create a sprite to assist with prerendering
	//--------------------------------------------
	CHECK(D3DXCreateSprite(context->d3dDevice, &m_pSprite));

	
	//--------------
	// Create effect
	//--------------
	ID3DXBuffer* errors = 0;
	CHECK(D3DXCreateEffect(context->d3dDevice, gEffect, (UINT)strlen(gEffect), 
		NULL, NULL, D3DXSHADER_DEBUG, NULL, &m_pEffect, &errors));
	if( errors )
		MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);

	m_sCurrentPreset = (short)context->preset;
	m_hRoseGardenTech = m_pEffect->GetTechniqueByName("ColorGridRoseGardenTech");
	m_hCityLightsTech = m_pEffect->GetTechniqueByName("ColorGridCityLightsTech");

	m_hTex  = m_pEffect->GetParameterByName(0, "gTex");
	m_hWVP  = m_pEffect->GetParameterByName(0, "gWVP");


	//-------------------------------
	// Create Font for text rendering
	//-------------------------------
	D3DXFONT_DESC fontdesc;
	fontdesc.Height          = 12;
	fontdesc.Width           = 8;
	fontdesc.Weight          = FW_BOLD;
	fontdesc.MipLevels       = 1;
	fontdesc.Italic          = false;
	fontdesc.CharSet         = DEFAULT_CHARSET;
	fontdesc.OutputPrecision = OUT_DEFAULT_PRECIS;
	fontdesc.Quality         = ANTIALIASED_QUALITY;
	fontdesc.PitchAndFamily  = DEFAULT_PITCH;
	strcpy_s(fontdesc.FaceName, 32, "Verdana");

	CHECK(D3DXCreateFontIndirect(context->d3dDevice, &fontdesc, &m_pFont));
}

void CGridRenderer::CreateDefaultPoolObjects(LPRENDERCONTEXT context)
{
	CHECK(D3DXCreateTexture(context->d3dDevice, 
							1024, 
							1024, 
							1, 
							D3DUSAGE_RENDERTARGET, 
							D3DFMT_A32B32G32R32F, 
							D3DPOOL_DEFAULT, 
							&m_pDataTexA ));

	CHECK(D3DXCreateTexture(context->d3dDevice, 
							1024, 
							1024, 
							1, 
							D3DUSAGE_RENDERTARGET, 
							D3DFMT_A32B32G32R32F, 
							D3DPOOL_DEFAULT, 
							&m_pDataTexB ));

	
	m_pDataTexA->GetSurfaceLevel( 0, &m_pDataTexASurface );
	m_pDataTexB->GetSurfaceLevel( 0, &m_pDataTexBSurface );
}

void CGridRenderer::Destroy(LPRENDERCONTEXT context)
{
	ReleaseCOM(m_pFont);
	ReleaseCOM(m_pEffect);
	ReleaseCOM(m_pSprite);
	ReleaseCOM(m_pSwapSurface);
	ReleaseCOM(m_pRenderToSurface);
	ReleaseCOM(m_pDataTexASurface);
	ReleaseCOM(m_pDataTexBSurface);
	ReleaseCOM(m_pSwapTex);
	ReleaseCOM(m_pDataTexA);
	ReleaseCOM(m_pDataTexB);
	ReleaseCOM(m_pVertBuff);
	ReleaseCOM(m_pVertPosDecl);
	ReleaseCOM(m_pVertDecl);
}

void CGridRenderer::Prerender(LPRENDERCONTEXT context)
{
	m_bRenderingAToB = m_bRenderingAToB == true ? false : true;

	if(m_bRenderingAToB)
		PrerenderAToB( context );
	else
		PrerenderBToA( context );
}

void CGridRenderer::Render(LPRENDERCONTEXT context)
{
	// Let Direct3D know the vertex buffer, index buffer and vertex 
	// declaration we are using.
	CHECK(context->d3dDevice->SetStreamSource(0, m_pVertBuff, 0, sizeof(VertexPos)));
	CHECK(context->d3dDevice->SetIndices(m_pIndexBuff));
	CHECK(context->d3dDevice->SetVertexDeclaration(m_pVertPosDecl));

	// Setup the rendering FX
	if(context->preset == PRESET_ROSE_GARDEN || 
	   context->preset == PRESET_ROSE_GARDEN_POINTS || 
	   context->preset == PRESET_ROSE_GARDEN_SMOOTH || 
	   context->preset == PRESET_ROSE_GARDEN_POINTS_SMOOTH)
	{
		CHECK(m_pEffect->SetTechnique(m_hRoseGardenTech));
	}
	else
	{
		CHECK(m_pEffect->SetTechnique(m_hCityLightsTech));
	}

	CHECK(m_pEffect->SetMatrix(m_hWVP, &(context->ViewMtx * context->ProjMtx)));
	if(m_bRenderingAToB)
	{
		CHECK(m_pEffect->SetTexture(m_hTex, m_pDataTexB));
	}
	else
	{
		CHECK(m_pEffect->SetTexture(m_hTex, m_pDataTexA));
	}

	// Begin passes.
	UINT numPasses = 0;
	CHECK(m_pEffect->Begin(&numPasses, 0));
	for(UINT i = 0; i < numPasses; ++i)
	{
		CHECK(m_pEffect->BeginPass(i));

		if( context->preset == PRESET_ROSE_GARDEN_POINTS || 
			context->preset == PRESET_CITY_LIGHTS_POINTS || 
			context->preset == PRESET_ROSE_GARDEN_POINTS_SMOOTH || 
			context->preset == PRESET_CITY_LIGHTS_POINTS_SMOOTH )
		{
			CHECK(context->d3dDevice->DrawPrimitive(D3DPT_POINTLIST, 0, m_iVertCount));
		}
		else
		{
			CHECK(context->d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, m_iVertCount, 0, m_dwTriangleCount));
		}

		CHECK(m_pEffect->EndPass());
	}
	CHECK(m_pEffect->End());

	// Uncomment the lines below to see a histogram
	//m_pSprite->Begin( NULL );
	//RECT rta = { 0, 0, 1024, 1024 };
	//if(m_bRenderingAToB)
	//	m_pSprite->Draw( m_pDataTexB, &rta, NULL, &D3DXVECTOR3(0.0f, -512.0f, 0.0f), 0xFFFFFFFF );
	//else
	//	m_pSprite->Draw( m_pDataTexA, &rta, NULL, &D3DXVECTOR3(0.0f, -512.0f, 0.0f), 0xFFFFFFFF );
	//m_pSprite->End();

	std::string company_name = "3D Spectrum Analyzer for Windows Media Player\nCopyright (c) 2009 Carlo McWhirter, Hyteq Systems\nDrag mouse up/down to zoom.\nDrag mouse left/right to rotate.";
	RECT rt = { 10, 10, 200, 200 };
	D3DCOLOR crTextColor = D3DCOLOR_RGBA(104, 140, 211, 255);
	m_pFont->DrawTextA( NULL, company_name.c_str(), (INT)company_name.size(), &rt, DT_NOCLIP, crTextColor);
}

void CGridRenderer::LostDevice(LPRENDERCONTEXT context)
{
	ReleaseCOM(m_pDataTexASurface);
	ReleaseCOM(m_pDataTexBSurface);
	ReleaseCOM(m_pDataTexA);
	ReleaseCOM(m_pDataTexB);

	m_pFont->OnLostDevice();
	m_pRenderToSurface->OnLostDevice();
	m_pEffect->OnLostDevice();
	m_pSprite->OnLostDevice();
}

void CGridRenderer::ResetDevice(LPRENDERCONTEXT context)
{
	m_pFont->OnResetDevice();
	m_pRenderToSurface->OnResetDevice();
	m_pEffect->OnResetDevice();
	m_pSprite->OnResetDevice();

	CreateDefaultPoolObjects( context );
}

bool CGridRenderer::CanRenderWindowed()
{
	return true;
}

bool CGridRenderer::CanRenderNonWindowed()
{
	return false;
}

void CGridRenderer::MakePointGrid(int iRows, int iCols, float dx, float dz,
									const D3DXVECTOR3& center, 
									std::vector<D3DXVECTOR3>& verts,
									std::vector<DWORD>& indices)
{
	int iVertCount = iRows * iCols;
	int iCellRowCount = iRows - 1;
	int iCellColCount = iCols - 1;
	float width = (float)iCols * dx;
	float depth = (float)iRows * dz;

	int iTris = iCellRowCount * iCellColCount * 2;

	verts.resize( iVertCount );

	float xOffset = -width * 0.5f;
	float zOffset =  depth * 0.5f;

	// Build vertices
	int k = 0;
	for(float i = 0; i < iRows; ++i)
	{
		for(float j = 0; j < iCols; ++j)
		{
			// Negate the depth coordinate to put in quadrant four.  
			// Then offset to center about coordinate system.
			verts[k].x =  j * dx + xOffset;
			verts[k].z = -i * dz + zOffset;
			verts[k].y =  0.0f;

			// Translate so that the center of the grid is at the
			// specified 'center' parameter.
			D3DXMATRIX T;
			D3DXMatrixTranslation(&T, center.x, center.y, center.z);
			D3DXVec3TransformCoord(&verts[k], &verts[k], &T);
			
			++k; // Next vertex
		}
	}

	// Build indicies
	indices.resize(iTris * 3);
	k = 0;
	for(DWORD i = 0; i < (DWORD)iCellRowCount; ++i)
	{
		for(DWORD j = 0; j < (DWORD)iCellColCount; ++j)
		{
			indices[k]     =   i   * iCellColCount + j;
			indices[k + 1] =   i   * iCellColCount + j + 1;
			indices[k + 2] = (i+1) * iCellColCount + j;
					
			indices[k + 3] = (i+1) * iCellColCount + j;
			indices[k + 4] =   i   * iCellColCount + j + 1;
			indices[k + 5] = (i+1) * iCellColCount + j + 1;

			// next quad
			k += 6;
		}
	}
}

void CGridRenderer::PrerenderAToB(LPRENDERCONTEXT context)
{
	//-------------------------------------------------------
	// Render the bottom 1023 pixel rows of A to the top of B
	//-------------------------------------------------------
	m_pRenderToSurface->BeginScene( m_pDataTexBSurface, NULL );

	context->d3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
		                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0);

	m_pSprite->Begin( NULL );

	RECT rt = { 0, 0, 1024, 1024 };
	m_pSprite->Draw( m_pDataTexA, &rt, NULL, &D3DXVECTOR3(0.0f, -1.0f, 0.0f), 0xFFFFFFFF );

	m_pSprite->End();

	m_pRenderToSurface->EndScene( 0 );

	
	//------------------------
	// Update B with new audio
	//------------------------
	context->d3dDevice->GetRenderTargetData(m_pDataTexBSurface, m_pSwapSurface );

	RECT rtlocked = { 0, 1023, 1024, 1024 };
	D3DLOCKED_RECT lockedrect;
	m_pSwapSurface->LockRect( &lockedrect, &rtlocked, NULL );

	float* pfColor = (float*)lockedrect.pBits;
	context->Interpolator->PrepareInterpolation( context->pLevel );
	for(int x = 4; x < 508; x++)
	{
		context->Interpolator->GetAplitudesAtIndex(x, &pfColor[x * 4 + 1], &pfColor[x * 4 + 1 + 512 * 4]);
	}

	m_pSwapSurface->UnlockRect();

	POINT pt = { rtlocked.left, rtlocked.top };
	context->d3dDevice->UpdateSurface(m_pSwapSurface, &rtlocked, m_pDataTexBSurface, &pt);
}

void CGridRenderer::PrerenderBToA(LPRENDERCONTEXT context)
{
	//-------------------------------------------------------
	// Render the bottom 1023 pixel rows of B to the top of A
	//-------------------------------------------------------
	m_pRenderToSurface->BeginScene( m_pDataTexASurface, NULL );

	context->d3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
		                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0);

	m_pSprite->Begin( NULL );

	RECT rt = { 0, 0, 1024, 1024 };
	m_pSprite->Draw( m_pDataTexB, &rt, NULL, &D3DXVECTOR3(0.0f, -1.0f, 0.0f), 0xFFFFFFFF );

	m_pSprite->End();

	m_pRenderToSurface->EndScene( 0 );

	
	//------------------------
	// Update A with new audio
	//------------------------
	context->d3dDevice->GetRenderTargetData(m_pDataTexASurface, m_pSwapSurface );

	RECT rtlocked = { 0, 1023, 1024, 1024 };
	D3DLOCKED_RECT lockedrect;
	m_pSwapSurface->LockRect( &lockedrect, &rtlocked, NULL );

	float* pfColor = (float*)lockedrect.pBits;
	context->Interpolator->PrepareInterpolation( context->pLevel );
	for(int x = 4; x < 508; x++)
	{
		context->Interpolator->GetAplitudesAtIndex(x, &pfColor[x * 4 + 1], &pfColor[x * 4 + 1 + 512 * 4]);
	}

	m_pSwapSurface->UnlockRect();

	POINT pt = { rtlocked.left, rtlocked.top };
	context->d3dDevice->UpdateSurface(m_pSwapSurface, &rtlocked, m_pDataTexASurface, &pt);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Carlo McWhirter
Software Developer (Senior) Hyteq Systems
United States United States
I have been programming in C/C++ for over 10 years now, and I've programmed in C# since the .NET 1.0 beta was released. I know a lot of other languages, but I find very few languages to be as capable as the C family of languages.
 
I've worked for a few large corporations, but my favorite job was when I worked for ATS (Automation Tooling Systems) as a Control Systems Engineer. Programming PLCs taught me about the core of computing and the currently under-utilized power of concurrent state machines. Programming real-time computer systems for vision guided robotics and multi-axis motion control is just fun.
 
My personal interest are computation for finance, aritificial intelligence, physics, electronics engineering, embedded systems, motion control, and of course Music/Audio.

| Advertise | Privacy | Mobile
Web01 | 2.8.140922.1 | Last Updated 17 May 2009
Article Copyright 2009 by Carlo McWhirter
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid