<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" media="screen" href="/styles/xslt/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:c9="http://channel9.msdn.com">
<channel>
	<title>Channel 9 - Entries tagged with Mash Up</title>
    <atom:link rel="self" type="application/rss+xml" href="http://channel9.msdn.com/Tags/mash+up/RSS"></atom:link>
    <itunes:summary></itunes:summary>
    <itunes:author>Microsoft</itunes:author>
    <itunes:subtitle></itunes:subtitle>
    <image>
      <url>http://mschnlnine.vo.llnwd.net/d1/Dev/App_Themes/C9/images/feedimage.png</url>
      <title>Channel 9 - Entries tagged with Mash Up</title>
      <link>http://channel9.msdn.com/Tags/mash+up</link>
    </image>
    <itunes:image href=""></itunes:image>
    <itunes:category text="Technology"></itunes:category>
    <description>Channel 9 keeps you up to date with the latest news and behind the scenes info from Microsoft that developers love to keep up with. From LINQ to SilverLight – Watch videos and hear about all the cool technologies coming and the people behind them.</description>
    <link>http://channel9.msdn.com/Tags/mash+up</link>
    <language>en</language>
    <pubDate>Sat, 25 May 2013 00:17:03 GMT</pubDate>
    <lastBuildDate>Sat, 25 May 2013 00:17:03 GMT</lastBuildDate>
    <generator>Rev9</generator>
    <c9:totalResults>12</c9:totalResults>
    <c9:pageCount>1</c9:pageCount>
    <c9:pageSize>25</c9:pageSize>
  <item>
      <title>TweeVo 1.1 is now available</title>
      <description><![CDATA[
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2335.image_5F00_68AA9D8C.png"><img title="image" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2335.image_5F00_68AA9D8C.png" align="right" border="0" height="94" width="94"></a>Users
 of <a href="http://www.brianpeek.com/" target="_blank">Brian Peek</a>’s <a href="http://www.tweevo.org/" target="_blank">
TweeVo</a> application have obviously noticed that the application hasn’t been working since early September.&nbsp; Twitter forced all applications to use the
<a href="http://en.wikipedia.org/wiki/Oauth" target="_blank">OAuth</a> authentication scheme, and now TweeVo has been updated to work with this new requirement.&nbsp; You can download the new version with source code over at
<a href="http://tweevo.codeplex.com/" target="_blank">CodePlex</a>.&nbsp; A big thanks to
<a href="http://anotherlab.rajapet.net/" target="_blank">Chris Miller</a> for getting things up and running again.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:d035bae87dcb46efbf1a9e7600c784ac">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/blog/TweeVo-11-is-now-available</comments>
      <itunes:summary>
Users
 of Brian Peek’s 
TweeVo application have obviously noticed that the application hasn’t been working since early September.&amp;nbsp; Twitter forced all applications to use the
OAuth authentication scheme, and now TweeVo has been updated to work with this new requirement.&amp;nbsp; You can download the new version with source code over at
CodePlex.&amp;nbsp; A big thanks to
Chris Miller for getting things up and running again. 
</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/blog/TweeVo-11-is-now-available</link>
      <pubDate>Sat, 16 Oct 2010 07:29:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/blog/TweeVo-11-is-now-available</guid>      
      <dc:creator>Clint Rutkas</dc:creator>
      <itunes:author>Clint Rutkas</itunes:author>
      <slash:comments>0</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/blog/TweeVo-11-is-now-available/RSS</wfw:commentRss>
      <category>utility</category>
      <category>Windows</category>
      <category>WPF</category>
      <category>Mash Up</category>
      <category>C4FNews</category>
    </item>
  <item>
      <title>XNA Effects – ASCII Art in 3D</title>
      <description><![CDATA[
<p>In this article, I'll demonstrate how to create a post-processing effect to turn 3D renders into ASCII Art.
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="157">
<p><strong>Louis Ingenthron <br>
FV Productions</strong></p>
</td>
<td valign="top" width="481">
<p><b>Code It:</b> <a href="http://xnaascii.codeplex.com/">http://xnaascii.codeplex.com/</a>
<br>
<b>Run It:</b> <a href="http://xnaascii.codeplex.com/">http://xnaascii.codeplex.com/</a></p>
<p><b>Difficulty:</b> Intermediate <br>
<b>Time Required:</b> 1-4 hours <br>
<b>Cost:</b> FREE <br>
<b>Software Needed: </b><a href="http://www.microsoft.com/express/download/">Visual C# Express</a>,
<a href="http://msdn.microsoft.com/en-us/netframework/default.aspx">.NET Framework 3.5</a>,
<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=80782277-D584-42D2-8024-893FCD9D3E82&amp;displaylang=en">
XNA Game Studio 3.1</a> <br>
<b>Hardware:</b> Windows PC</p>
</td>
</tr>
</tbody>
</table>
</p>
<h3>Introduction</h3>
<p>Back in November of 2009, I was met with an interesting challenge. Those of you who frequent the XNA Community Forums have probably heard of Nick Gravelyn. He was an XNA MVP, and now he works for the XNA team. He started a little contest called xna7day,
 which was designed to challenge developers to make a game in 7 days using a pre-defined theme.</p>
<p>The theme in November was “Text Based” and its rule was that you were only allowed to use text in the visuals of your game. I saw some of the cool stuff other developers were working on, including one that looked like it had text characters that could walk
 and talk, like people. But my mind immediately went to a different solution: I wanted to make a game that was fully 3D (effectively breaking the rule of rendering text), but then post-process the render into ASCII art, so it would look like it was text.</p>
<p>In this article, I'm going to show you how to do just that:</p>
<p align="center"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image002_2.jpg"><img title="clip_image002" border="0" alt="clip_image002" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image002_thumb.jpg" width="240" height="180"></a><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image004_2.jpg"><img title="clip_image004" border="0" alt="clip_image004" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image004_thumb.jpg" width="240" height="180"></a></p>
<h4>Setup</h4>
<p>Alright, let's dive right in. Open up the base project. It should compile and run as is. It's a simple, if ugly, first-person shooter. Play through the first level. Go ahead, I'll wait. Bonus points to anyone who recognizes the level design!</p>
<p>Okay, got a playthrough? Let's start! I separated this into three projects so the ASCII stuff can be extracted into other games. We'll be doing most of our work in the ASCII_Renderer project, so let's open that up and add a new class to the Source\ascii
 folder called “Renderer.” I use the namespace “ASCII3D” for all files in this project, so get rid of the extra folder sub-namespaces. Add these using statements, too:</p>
<p><b>C# </b></p>
<pre class="csharpcode"><span class="kwrd">using</span> Microsoft.Xna.Framework;
<span class="kwrd">using</span> Microsoft.Xna.Framework.Content;
<span class="kwrd">using</span> Microsoft.Xna.Framework.Graphics; </pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>This is going to be a post-processing effect, so we'll try to make it portable. Add the following empty function declarations:</p>
<p><b>C# </b></p>
<pre class="csharpcode"><span class="kwrd">public</span> Renderer(ContentManager content, <span class="kwrd">int</span> xRes, <span class="kwrd">int</span> yRes)
{ }

<span class="kwrd">public</span> <span class="kwrd">void</span> StartScene()
{ }

<span class="kwrd">public</span> <span class="kwrd">void</span> EndScene()
{ }</pre>
<p>The constructor takes three arguments: The ContentManager, which we will use to load in our font and post-processing shader, and the resolution variables, which will basically tell us how many lines of text to draw and how many characters in each line. Since
 we have these prototyped, we'll make our only edits to the ASCIIFPS project in the Source\Game1.cs file. But first, we need to define and initialize a new Renderer object. Add a new class variable:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="rem">/// &lt;summary&gt;</span>
<span class="rem">/// This is what handles the ASCII conversion</span>
<span class="rem">/// &lt;/summary&gt;</span>
<span class="kwrd">private</span> Renderer renderer; </pre>
<p>And at the end of the LoadContent function:</p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="rem">// and initialize our ASCII renderer</span>
<span class="rem">// the variables we pass make each character</span>
<span class="rem">// appx 10x14 which seems to be the smallest</span>
<span class="rem">// readable size</span>
renderer = <span class="kwrd">new</span> Renderer(Content, Global.ScreenWidth/10,Global.ScreenHeight/14);</pre>
<p></p>
<p>Finally, call the BeginScene and EndScene functions. Modify Game1's Draw function to look like this:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Draw(GameTime gameTime)
{
    <span class="rem">// notice we only wrap StartScene and</span>
    <span class="rem">// EndScene if we are UsingASCII</span>
    <span class="kwrd">if</span> (UsingASCII)
        renderer.StartScene();

    currentState.Draw(gameTime);

    <span class="kwrd">if</span> (UsingASCII)
        renderer.EndScene();

    <span class="kwrd">base</span>.Draw(gameTime);
}</pre>
<p>The Boolean variable UsingASCII is defined for us. It defaults to false and can be toggled any time in-game by pressing the [M] key. If you want to default it to true for testing purposes, just change its value in Game1's Initialize function. At this point,
 the Renderer is all hooked up, so changes will go right in.</p>
<p>I skimmed over the other classes earlier in the article, but I need to touch on a few of them. Almost everything in GlobalModules is fairly standard utility stuff. We'll be using it, so you might want to glance at the Global class. The ASCIIFPS has the FPS
 engine in it and is almost entirely irrelevant to this article, except that it offers a fun test-bed for our effect. Feel free to go through the code, but I'll warn you: I wrote it for a 7-day contest, so some of it looks like gibberish and is only incidentally
 functional. Finally, we have the ASCII_Renderer project. This already has two files in it: BitmapFontGenerator and Letter. The latter are for generating a bitmap font map that we'll use in a bit.</p>
<p>The functionality isn't specific to this article, since any artist could easily make this. But I'm no artist, which is why I had the computer do it for me. Basically, BitmapFontGenerator has a static function that will take a texture of letters (see the
 Content project in ASCII_Renderer), trim them, sort them by how much they fill their space, and print them into a new Texture for our functions. Feel free to read through the code for this process; I commented it pretty well.</p>
<h4>Post-Processing Basics</h4>
<p>We need to cover the basics of post-processing before we start coding the next part. When post-processing a scene, you first render the entire scene off-screen on a RenderTarget. Then, you can apply any effects you want to the resolved texture before putting
 it on the screen. That's it. Here's how in XNA:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> Renderer
{
    <span class="kwrd">private</span> RenderTarget2D SrcImage;

    <span class="kwrd">public</span> Renderer(ContentManager content, <span class="kwrd">int</span> xRes, <span class="kwrd">int</span> yRes)
    {
        SrcImage = <span class="kwrd">new</span> RenderTarget2D(Global.Graphics, 
            xRes, yRes, 1, SurfaceFormat.Color);
    }

    <span class="kwrd">public</span> <span class="kwrd">void</span> StartScene()
    {
        Global.Graphics.SetRenderTarget(0, SrcImage);
        Global.Graphics.Clear(Color.Black);
    }

    <span class="kwrd">public</span> <span class="kwrd">void</span> EndScene()
    {
        Global.Graphics.SetRenderTarget(0, <span class="kwrd">null</span>);
        Global.Graphics.Clear(Color.Black);

        <span class="rem">// TODO: draw the texture from the RenderTarget</span>
    }
}</pre>
<p>We create the RenderTarget2D we need in the constructor. Notice that the size of the RenderTarget is the same as the number of ASCII characters we plan to draw on screen. I'll explain that in the next section. In the StartScene function, we set the RenderTarget
 on the device so that all draw calls will actually draw to it. Then we clear the buffer, because RenderTargets' content from frame to frame is undefined. In EndScene, the RenderTarget is set to null, which is DirectX's way of saying “use the screen's framebuffer.”
 Now we need to actually draw the rendered image. XNA requires shaders for all draw calls, even if they're veiled by BasicEffect and SpriteBatch. We need to create a very simple shader to draw the texture to screen. Create a new Effect file in ASCII_Renderer's
 Content project and name it TextEffect. We'll start it out with this:<b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode">texture2D sourceTex;
sampler2D SourceTextureSampler = sampler_state
{
    Texture = &lt;sourceTex&gt;;
    MinFilter = point;
    MagFilter = point;
    MipFilter = point;
    AddressU = wrap;
    AddressV = wrap;
};

<span class="kwrd">struct</span> VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TexCoords : TEXCOORD0;
};

<span class="kwrd">struct</span> VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 TexCoords : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    
    output.Position = input.Position;
    output.TexCoords = input.TexCoords;

    <span class="kwrd">return</span> output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float4 sourceColor = tex2D(SourceTextureSampler,input.TexCoords);
    
    <span class="kwrd">return</span> sourceColor;
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}</pre>
<p>There are a couple things I want to note about the base shader. First, notice that we use point filtering. I'll explain why in the next section, but it's important to see where we did it. Next, notice that we took out all the default transformation matrices.
 We're going to be drawing a single full screen quad, so we can just pass in the coordinates in screen-space—no processing required. Now we need to make a few changes to the Renderer class to use this Effect and draw the rendered game. First, we need to add
 some new class variables:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">private</span> Effect effect;

<span class="kwrd">private</span> VertexPositionTexture[] verts;
<span class="kwrd">private</span> VertexDeclaration vd;</pre>
<p></p>
<p>The vertices are going to be for a simple quad that takes up the whole screen. They should be initialized in the LoadContent function like this:<b>
</b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode">effect = content.Load&lt;Effect&gt;(<span class="str">&quot;TextEffect&quot;</span>);

verts = <span class="kwrd">new</span> VertexPositionTexture[6];
verts[0] = <span class="kwrd">new</span> VertexPositionTexture(
    <span class="kwrd">new</span> Vector3(-1, -1, 0), <span class="kwrd">new</span> Vector2(0, 1));
verts[1] = <span class="kwrd">new</span> VertexPositionTexture(
    <span class="kwrd">new</span> Vector3(1, -1, 0), <span class="kwrd">new</span> Vector2(1, 1));
verts[2] = <span class="kwrd">new</span> VertexPositionTexture(
    <span class="kwrd">new</span> Vector3(-1, 1, 0), <span class="kwrd">new</span> Vector2(0, 0));
verts[3] = verts[1];
verts[4] = verts[2];
verts[5] = <span class="kwrd">new</span> VertexPositionTexture(
    <span class="kwrd">new</span> Vector3(1, 1, 0), <span class="kwrd">new</span> Vector2(1, 0));

vd = <span class="kwrd">new</span> VertexDeclaration(
    Global.Graphics, VertexPositionTexture.VertexElements);</pre>
<p></p>
<p>And finally, we add our draw code to the Draw function:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode">effect.Begin();
effect.CurrentTechnique.Passes[0].Begin();

effect.Parameters[<span class="str">&quot;sourceTex&quot;</span>].SetValue(SrcImage.GetTexture());
effect.CommitChanges();
Global.Graphics.RenderState.CullMode = CullMode.None;

Global.Graphics.VertexDeclaration = vd;
Global.Graphics.DrawUserPrimitives&lt;VertexPositionTexture&gt;
     (PrimitiveType.TriangleList, verts, 0, 2);

effect.CurrentTechnique.Passes[0].End();
effect.End();</pre>
<p></p>
<p>We set the texture we got from the RenderTarget, turn off culling (what would be culled in this geometry?), and draw it. Run the project. You should be able to toggle a difference in-game with the [M] key.<b></b></p>
<p><b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image006_2.jpg"><img title="clip_image006" border="0" alt="clip_image006" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image006_thumb.jpg" width="322" height="242"></a></b></p>
<h3>Drawing Some Text</h3>
<p>“Okay,” you say, “it runs at a lower resolution with the [M] key. So what?” Remember what we set the resolution to in the RenderTarget? Yes, every pixel on this low-resolution version will be a character in the final image. How do we turn these big pixels
 into characters? First we need to define a texture bitmap font. Let's add a Texture2D class variable to Renderer and a const int for the BitmapFontGenerator:
</p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">public</span> Texture2D text; 
<span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">int</span> NUM_SLOTS = 256;</pre>
<p></p>
<p>Then we'll use the BitmapFontGenerator to construct this texture:</p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode">text = BitmapFontGenerator.GenerateTextTexture(content, NUM_SLOTS);</pre>
<p></p>
<p>Okay, that's all for now. Let's go back to the TextEffect.fx. We need to add a few variables:<b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode">float2 dstLetterSize;

<span class="kwrd">int</span> numLetters;

texture2D textTex;
sampler2D TextTextureSampler = sampler_state
{
    Texture = &lt;textTex&gt;;
    MinFilter = point;
    MagFilter = point;
    MipFilter = point;
    AddressU = wrap;
    AddressV = wrap;
};</pre>
<p>The variable “dstLetterSize” will tell us how big the letters on the screen are. The “numLetters” variable will tell us how many letters are in the font texture. And then there's the font texture itself. Here's how we draw actual characters in the Pixel
 Shader:<b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode">float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
     float4 sourceColor = tex2D(SourceTextureSampler,input.TexCoords);
    
     float2 texCoords = float2(
          input.TexCoords.x-((<span class="kwrd">int</span>)
               (input.TexCoords.x/dstLetterSize.x)*dstLetterSize.x),
          input.TexCoords.y-((<span class="kwrd">int</span>)
               (input.TexCoords.y/dstLetterSize.y)*dstLetterSize.y));
     texCoords /= dstLetterSize;
     texCoords.x /= numLetters;
    
     float4 letterColor = tex2D(TextTextureSampler,texCoords);
    
     <span class="kwrd">return</span> letterColor;
}</pre>
<p></p>
<p>We've already seen the source color line. The next line will get the offset into the letter in letter size by subtracting the rounded-down (or integer-casted) texture coordinates scaled down. The line after that scales it back up to a 0-1 range. So now we
 have the new variable texCoords that is filled with the texture coordinates of the letter in 0-1 terms. We divide the x by numLetters because our texture is one long horizontal line of letters. Finally we get the letter color with these new texture coordinates.
 This letter color is grayscale, and when multiplied by the source color, we've assured that each letter is rendered in its own slot with the color from the original 3D render. The last thing we have to do is set those effect variables we defined from Renderer's
 EndScene function. Where we once just set the source texture, we now set these:<b></b></p>
<p><b>C# <br>
</b></p>
<pre class="csharpcode">effect.Parameters[<span class="str">&quot;dstLetterSize&quot;</span>].SetValue(
     <span class="kwrd">new</span> Vector2(1.0f/(<span class="kwrd">float</span>)SrcImage.Width, 1.0f/(<span class="kwrd">float</span>)SrcImage.Height));
effect.Parameters[<span class="str">&quot;numLetters&quot;</span>].SetValue(NUM_SLOTS);
effect.Parameters[<span class="str">&quot;sourceTex&quot;</span>].SetValue(SrcImage.GetTexture());
effect.Parameters[<span class="str">&quot;textTex&quot;</span>].SetValue(text);
effect.CommitChanges();</pre>
<p></p>
<p>Most of these are self explanatory, but the dstLetterSize is a bit more complicated. To get the size of the letter in the 0-1 texture space, you take the total size (0 to 1) divided by the number of letters in that axis which, in our case, is the RenderTarget
 size.<b></b></p>
<p><b></b></p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image008_2.jpg"><img title="clip_image008" border="0" alt="clip_image008" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image008_thumb.jpg" width="322" height="242"></a></p>
<h3>Wheel of Fortune</h3>
<p>‘M's aren't very interesting. In traditional ASCII art, the whole point is that you use characters that take more space or less space to handle shading. Let's add this code after the multiplication of the texCoords.x:<b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">float</span> lum = (sourceColor.r&#43;sourceColor.g&#43;sourceColor.b)/3.0; 
<span class="kwrd">float</span> val = max(fullValueColor.r,
     max(fullValueColor.g,fullValueColor.b));
    
<span class="kwrd">int</span> ind = ((numLetters-1)-
     max(0,min((numLetters-1),(<span class="kwrd">int</span>)(lum*numLetters))));

texCoords.x &#43;= ind*(1.0/numLetters);</pre>
<p></p>
<p><b></b></p>
<p>The first line gets the luminosity of the source color. The next gets the value. The next line gets the letter index from the luminosity. Subtracting it from (numLetters-1) reverses the index, because we want luminosity=1 to equate to index=0, because that
 has the letter that takes up the most space. Run the game. You'll see it now shows value, but it's still pretty hard to tell what's on the screen:</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image010_2.jpg"><img title="clip_image010" border="0" alt="clip_image010" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image010_thumb.jpg" width="322" height="242"></a></p>
<p>The last thing we can do is colorize it. You could just multiply it by the source color and get something like this…</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image012_2.jpg"><img title="clip_image012" border="0" alt="clip_image012" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image012_thumb.jpg" width="322" height="242"></a></p>
<p>…but everything looks too dark because the source color still has its value. Plus we're already practically multiplying it by choosing the letter. So we need to get the full value color first:</p>
<p><b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode">float4 fullValueColor = sourceColor * (1.0/val);</pre>
<p></p>
<p>We just multiply the original color by the inverse of the value. And then multiply that fullValueColor by the letter color in the return:<b></b></p>
<p><b>HLSL <br>
</b></p>
<pre class="csharpcode"><span class="kwrd">return</span> fullValueColor * letterColor;</pre>
<p></p>
<p>That should produce a nice result:</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image014_2.jpg"><img title="clip_image014" border="0" alt="clip_image014" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9994745/clip_image014_thumb.jpg" width="407" height="305"></a><b></b></p>
<h3>Conclusion</h3>
<p>And there it is! We have a real-time 3D render transformed into ASCII art. It's difficult to see and it's not very useful, but it's sure neat, eh? Why not add it as an unlockable easter egg in your next XBL Indie Game? I'm looking forward to seeing how people
 use this effect! If you use it, please <a href="mailto:louisingenthron@fvproductions.org">
send me an email</a> with a screenshot!</p>
<h3>About The Author</h3>
<p>Louis Ingenthron is a Game Developer in Orlando, FL. He works on commercial Console and PC titles, but runs his own Indie Games company, FV Productions, on the side. He is best known for his open source XNA rhythm game Unsigned and specializes in real-time
 Graphics programming. He has been working with XNA since the 2.0 Beta and with .NET C# for just as long. He is also familiar with several other development languages, such as C, C&#43;&#43;, and Java. Occasionally, he can even be found doing web development and Flash.
 Louis also writes for MSDN's Coding4Fun website, contributing articles on a monthly basis.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:bfa52c016c3640c7a8699e7600c99302">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/XNA-Effects--ASCII-Art-in-3D</comments>
      <itunes:summary>
In this article, I&#39;ll demonstrate how to create a post-processing effect to turn 3D renders into ASCII Art.




Louis Ingenthron 
FV Productions 


Code It: http://xnaascii.codeplex.com/

Run It: http://xnaascii.codeplex.com/ 
Difficulty: Intermediate 
Time Required: 1-4 hours 
Cost: FREE 
Software Needed: Visual C# Express,
.NET Framework 3.5,

XNA Game Studio 3.1 
Hardware: Windows PC 




 
Introduction
Back in November of 2009, I was met with an interesting challenge. Those of you who frequent the XNA Community Forums have probably heard of Nick Gravelyn. He was an XNA MVP, and now he works for the XNA team. He started a little contest called xna7day,
 which was designed to challenge developers to make a game in 7 days using a pre-defined theme. 
The theme in November was “Text Based” and its rule was that you were only allowed to use text in the visuals of your game. I saw some of the cool stuff other developers were working on, including one that looked like it had text characters that could walk
 and talk, like people. But my mind immediately went to a different solution: I wanted to make a game that was fully 3D (effectively breaking the rule of rendering text), but then post-process the render into ASCII art, so it would look like it was text. 
In this article, I&#39;m going to show you how to do just that: 
 
Setup
Alright, let&#39;s dive right in. Open up the base project. It should compile and run as is. It&#39;s a simple, if ugly, first-person shooter. Play through the first level. Go ahead, I&#39;ll wait. Bonus points to anyone who recognizes the level design! 
Okay, got a playthrough? Let&#39;s start! I separated this into three projects so the ASCII stuff can be extracted into other games. We&#39;ll be doing most of our work in the ASCII_Renderer project, so let&#39;s open that up and add a new class to the Source\ascii
 folder called “Renderer.” I use the namespace “ASCII3D” for all files in this project, so get rid of the extra folder sub-namespaces. Add these using statemen</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/XNA-Effects--ASCII-Art-in-3D</link>
      <pubDate>Wed, 14 Apr 2010 13:00:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/XNA-Effects--ASCII-Art-in-3D</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9994745_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9994745_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Louis Ingenthron </dc:creator>
      <itunes:author>Louis Ingenthron </itunes:author>
      <slash:comments>11</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/XNA-Effects--ASCII-Art-in-3D/RSS</wfw:commentRss>
      <category>Gaming</category>
      <category>Media</category>
      <category>XNA</category>
      <category>Mash Up</category>
      <category>XNA framework</category>
    </item>
  <item>
      <title>Popfly game engine – open source</title>
      <description><![CDATA[
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9887173/codeplex_2_5B1_5D_2.jpg"><img title="codeplex_2[1]" border="0" alt="codeplex_2[1]" align="right" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9887173/codeplex_2_5B1_5D_thumb.jpg" width="240" height="181"></a>
 As popfly is getting shut down August 31st, they released the game engine under the Ms-Pl.&nbsp; If you used the
<a href="http://blogs.msdn.com/coding4fun/archive/2009/07/22/9845135.aspx">Popfly Game Downloader</a> we released a few weeks back to grab your games, there's also instructions on how to crack them open and grab the data files.&nbsp; Which means you may be able
 to recreate the actual GUI for creating games!</p>
<p>Check it out over at <a title="Codeplex" href="http://popflygameengine.codeplex.com/">
Codeplex</a>.</p>
<p>[via <a href="http://blogs.msdn.com/ben_anderson/archive/2009/08/26/popfly-parting-present.aspx">
Ben Anderson</a>]</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:855670b55c914c2aacc79e7600cb7721">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/blog/Popfly-game-engine--open-source</comments>
      <itunes:summary>

 As popfly is getting shut down August 31st, they released the game engine under the Ms-Pl.&amp;nbsp; If you used the
Popfly Game Downloader we released a few weeks back to grab your games, there&#39;s also instructions on how to crack them open and grab the data files.&amp;nbsp; Which means you may be able
 to recreate the actual GUI for creating games! 
Check it out over at 
Codeplex. 
[via 
Ben Anderson] 
</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/blog/Popfly-game-engine--open-source</link>
      <pubDate>Thu, 27 Aug 2009 15:01:32 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/blog/Popfly-game-engine--open-source</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9887173_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9887173_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Clint Rutkas</dc:creator>
      <itunes:author>Clint Rutkas</itunes:author>
      <slash:comments>0</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/blog/Popfly-game-engine--open-source/RSS</wfw:commentRss>
      <category>Gaming</category>
      <category>Mash Up</category>
      <category>C4FNews</category>
    </item>
  <item>
      <title>TweetCraft - An in-game Twitter client for World of Warcraft</title>
      <description><![CDATA[<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/lfUDUAtG7Rs?fs=1&amp;hl=en_US&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/lfUDUAtG7Rs?fs=1&amp;hl=en_US&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>
<p><a href="http://ch9.ms/tweetcraft">TweetCraft</a> is a World of Warcraft add-on that enables you to send and receive tweets using
<a href="http://twitter.com/">Twitter</a> without leaving the game, automatically upload and post screenshots using
<a href="http://twitpic.com/">TwitPic</a> and automatically tweet certain in-game events such as achievements and moving around in the world of Azeroth. TweetCraft customizes your World of Warcraft user interface by adding its own frame similar to the built-in
 ones (such as the Quest Log) and provides easy access to it through a button near the minimap, slash commands or your favorite launcher (e.g. ChocolateBar).</p>

<h3>How TweetCraft works</h3>
<p>Let’s quickly walk through the different things that TweetCraft does to bring Twitter into World of Warcraft.</p>
<h4>Overview</h4>
<p>World of Warcraft’s user interface is highly customizable and enables add-on developers to do almost anything in the game they can dream of. Add-ons can interact with the world, listen to events that happen in the game, alter the look of the default user
 interface or create brand new ones, but add-ons are seriously limited when it comes to talking to the world outside World of Warcraft. They cannot read or write files, call web services or do anything that could be potentially harmful. This is why TweetCraft
 comes with a Windows application that sits in the Notification Area (also known as the tray) and does the bulk of the work, giving the TweetCraft add-on only the data that it needs to display, such as tweets, replies and user pictures. All the Twitter web
 service calls, uploading of screenshots and housekeeping is done by this application. Let’s see how!</p>
<h4>The TweetCraft Tray application</h4>
<p>The TweetCraft Tray application is a WPF application that periodically checks for new tweets, dowloads and converts the user pictures of the tweets’ authors and writes it out to a file that World of Warcraft picks up and the add-on can use. It also picks
 up all outgoing tweets queued up for sending and sends them using Twitter one-by-one. It watches for screenshots taken by World of Warcraft and uploads them using TwitPic immediately.</p>
<p>To do all this, it has a couple of settings that need to be configured before it can work. For starters, it needs your Twitter credentials to be able to send tweets and post pictures on your behalf. It also needs to know which World of Warcraft account you
 are going to use to play and send and receive tweets with. Finally, you can opt out of automatically uploading screenshots you take during game. Figure 1 shows the TweetCraft Settings window that shows up when you first start TweetCraft after installation
 and is also available by right clicking the tray icon (Figure 2).</p>
<p><img border="0" alt="Figure1_TweetCraftSettingsWindow" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure1_TweetCraftSettingsWindow_96f280c9-cef7-47db-a5a8-1b3ae3da5d13.png" width="415" height="544">
<br>
<strong>Figure 1. TweetCraft Settings Window</strong></p>
<p><strong></strong></p>
<p><img border="0" alt="Figure2_TweetCraftTrayIcon" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure2_TweetCraftTrayIcon_647e915d-5ae9-41ab-9f6a-85ffbf94c0cc.png" width="418" height="172">
<br>
<strong>Figure 2. TweetCraft tray icon in the notification area</strong></p>
<h4>Getting data from Twitter</h4>
<p>TweetCraft uses an open-source library called <strong>TwitterLib</strong> to send and receive tweets using Twitter’s API. TwitterLib is part of the open-source WPF Twitter Client
<a href="http://code.google.com/p/wittytwitter/">WittyTwitter</a> and wraps the web services provided by Twitter into an easy to use .NET class library. TweetCraft wraps this library into a simple class called
<strong>TwitterClient</strong> that all the services in TweetCraft rely on. One of those services is the
<strong>TwitterBackgroundService</strong> which polls Twitter periodically to check for new tweets. By default, this is set to five minutes. If new tweets or replies are available, an event is raised that other components of TweetCraft can handle and work with
 the new tweets, download user pictures, etc. Figure 3 shows the three classes that take part in downloading new tweets and replies and notifying other classes.</p>
<p><img border="0" alt="Figure3_TwitterBackgroundService" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure3_TwitterBackgroundService_2c37e43e-7c00-4a0b-8357-f821fc443b79.png" width="715" height="333">
<br>
<strong>Figure 3. TwitterBackgroundService, TwitterClient and TwitterNet (TwitterLib)</strong></p>
<h4>&nbsp;</h4>
<h4>Dynamically building TGA images</h4>
<p>When it comes to visualizing images (or textures, in WoW parlance), World of Warcraft poses quite a few restrictions on what files can be used and where does files should be. First of all, World of Warcraft only supports the TGA and BLP file formats. TGA
 stands for the Truevision Targa format, whereas BLP is Blizzard’s own image format. The images must also have dimensions of two (32×32, 64×64 pixels and so on…), otherwise they won’t show up. On the other hand, Twitter user pictures are available as 48×48
 or 75×75 pixels and in various well-known formats, such as JPG, PNG or even GIF. TweetCraft needs to convert these pictures to 64×64 TGA files if it wants to play nice with World of Warcraft. This is done for each tweet’s authors user picture that will show
 up in World of Warcraft. The converted pictures must be placed in the <em>Interface</em> folder of World of Warcraft where the add-ons themselves also reside. The
<strong>TwitterUserPictureService</strong> can retrieve the user picture and convert it to a 64×64 TGA file that contains the 48×48 user picture in the middle and puts that file into the same directory where the TweetCraft add-on is installed. The files are
 named after each user’s screen name and if <strong>TwitterUserPictureService</strong> sees that a file is already there, it won’t convert it again, saving some time and computing power.</p>
<p>Figure 4 shows <strong>TwitterUserPictureService</strong> and two helper classes that take care of downloading and the actual conversion. The
<code>GetUserPicturePath</code> method returns the path of the resulting World of Warcraft compatible TGA file for a single user.</p>
<p><img border="0" alt="Figure4_TwitterUserPictureService" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure4_TwitterUserPictureService_abf4a52c-8e97-4458-b620-dc62d0be314d.png" width="475" height="313">
<br>
<strong>Figure 4. TwitterUserPictureService and its helper classes</strong></p>
<h4>Serializing tweets into Lua</h4>
<p>Until now, we were quite secretive about how we can avoid World of Warcraft’s restrictions and let TweetCraft be able to read the data and show it. We’ve said that add-ons cannot read or write files and that still hasn’t changed during the last few minutes,
 but add-ons must have a way somehow to save their settings and other information when I log out of the game and load that when I log back in, you sincerely ask and you’re right! Although add-ons cannot read or write to files, they can ask World of Warcraft
 to save and load some of their global variables whenever the user reloads the UI (by entering an instance or hearthing), logs in or logs out. These files are called the SavedVariables files. When TweetCraft finishes downloading tweets, converting user pictures
 and everything else, it saves them using the very same format and to the very same file World of Warcraft uses on behalf of the TweetCraft add-on. Neat, huh? Just wait a little bit more… <img src='http://ecn.channel9.msdn.com/o9/content/images/emoticons/emotion-1.gif?v=c9' alt='Smiley' /></p>
<p>Those variables that an add-on asks to be saved between sessions are saved into a Lua file, which is the same programming language add-on developers can use to write their add-ons. So basically, World of Warcraft creates a file that contains code full of
 assignment statements (think <code>a = 5</code>) that it will just run whenever those values need to be set again. More complex data structures can also be expressed this way using Lua tables, the swiss army-knife of Lua.</p>
<p>It sounds like a great idea to put all the data we would like for TweetCraft to use, but all we have is .NET objects, arrays and dictionaries. Fortunately, all these data structures can be expressed with simple Lua types and tables and TweetCraft comes with
 a class called <strong>LuaSerializer</strong> that produces Lua code from any .NET object (with a few restrictions, of course), very similar to how
<strong>XmlSerializer</strong> works.</p>
<p>To make this even more simple, TweetCraft contains the concept of a <strong>SavedVariablesChannel</strong> that you can just drop objects in, flush it and those object show up on the other side. In our case, as Lua variables. What’s even better, it works
 the other way around as well. The TweetCraft add-on stores all the tweets queued up and ready to be sent in a variable and triggers World of Warcraft to save them to the SavedVariables file. The
<strong>SavedVariablesChannel</strong> detects this by watching for changes made to the SavedVariables file using
<strong>FileSystemWatcher</strong> and notifies other components of the TweetCraft application that can pull out the information and send the tweets.</p>
<p>Figure 5 shows <strong>SavedVariablesChannel</strong> and its base class, <strong>
ValueChannelBase</strong> which keeps track of all objects thrown into it until it is flushed.</p>
<p><img border="0" alt="Figure5_SavedVariablesChannel" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure5_SavedVariablesChannel_d3bf3853-d03e-43a6-b8e4-899708f431dd.png" width="475" height="325">
<br>
<strong>Figure 5. SavedVariablesChannel and ValueChannelBase</strong></p>
<h4>Refreshing the data in World of Warcraft</h4>
<p>There’s one last problem we face when trying to achieve this two-way communication between the TweetCraft add-on and the application. If World of Warcraft is running, we cannot just overwrite the SavedVariables file and expect World of Warcraft to pick it
 up as it does not watch for changes made to the file. If we were to overwrite the file and subsequently the user logs out, World of Warcraft will happily overwrite the information we placed there. There’s another trick we have to resort to: the TweetCraft
 add-on can trigger the reloading of the UI which will unload all add-ons, save their variables and then load them again. There’s a a little bit of time between the saving and loading of a particular add-ons SavedVariables file and that’s when TweetCraft can
 safely read the file for queued up tweets and overwrite it with the new data. This is what happens when the user clicks the Refresh button in TweetCraft’s frame.</p>
<h4>Sending tweets and uploading screenshots</h4>
<p>Whenever the user queues up tweets for sending, they are not immediately sent but kept in a data structure that will be saved out to the SavedVariables file by World of Warcraft. We’ve seen that his happens when either the user logs out or presses the Refresh
 button. The updated SavedVariables file gets detected by <strong>SavedVariablesChannel</strong> and the
<code>ChannelUpdated</code> event is raised. When the TweetCraft application handles this event, it cannot immediately send the tweets as that would take a couple seconds and if you remember from the last section, we only have a tiny little window of time to
 process the values and save back all the data we would like to send back. For this reason, the tweets that are picked up from the SavedVariables file using the
<strong>SavedVariablesChannel</strong> get quickly queued up in the <strong>TwitterDispatcherService</strong> that then goes ahead and starts sending them one by one in the background. To make sure that all the values we prepared to save into the file get flushed,
 the <strong>SavedVariablesChannel</strong> has a property called <code>AutoFlush</code> that is enabled by default.</p>
<p>One thing we have not mentioned is that <strong>TwitterDispatcherService</strong> can also upload and post pictures using TwitPic. It watches the
<em>Screenshots</em> folder of World of Warcraft for new files and if AutoTweeting of screenshots is enabled, immediately queues up the picture for uploading and posting it. This also means that you don’t need to press the Refresh button or wait until you log
 off for screenshots to be posted. They will show up immediately in your Twitter feed.</p>
<p>The <strong>TwitterDispatcherService</strong> uses <strong>TwitterClient</strong> (and
<strong>TwitterLib</strong>) to send the tweets but it does not interfere with the quick reading and writing of the SavedVariables file we discussed previously. It also uses
<strong>TwitPicClient</strong> to upload and post pictures. Figure 6 shows <strong>
TwitterDispatcherService</strong>, <strong>TwitterClient</strong> and <strong>TwitPicClient</strong>.</p>
<p><img border="0" alt="Figure6_TwitterDispatcherService" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure6_TwitterDispatcherService_575f8576-4afc-4191-b41e-e45084b19ae6.png" width="691" height="348">
<br>
<strong>Figure 6. TwitterDispatcherService, TwitterClient and TwitPicClient</strong></p>
<h4>Bringing it all together</h4>
<p>So far, we’ve looked at components of TweetCraft that implemented a particular part of the application. Retrieving and sending tweets, converting user pictures and making all this information available for the TweetCraft add-on, but there’s an important
 class that brings all this together and contains all the logic of how these components interact with each other. We also need to be able to detect World of Warcraft installations, figure out what accounts are available and where to look for files like the
 SavedVariables files and put the converted user pictures. The <strong>TweetCraft</strong> class implements the logic required and relies on the
<strong>WorldOfWarcraft</strong> class for everything World of Warcraft related.</p>
<p>Figure 7 shows the <strong>TweetCraft</strong> and <strong>WorldOfWarcraft</strong> side by side.</p>
<p><img border="0" alt="Figure7_TweetCraft" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/Figure7_TweetCraft_6bf95ca8-d5c2-4d54-8169-c02de469ecb3.png" width="523" height="463">
<br>
<strong>Figure 7. TweetCraft and WorldOfWarcraft</strong></p>
<h3>Summary</h3>
<p>While this is a high-level overview of TweetCraft only, it gives you a good idea of how it enables World of Warcraft players to use Twitter to send and receive tweets, upload the screenshots and share their achievements right inside the game. Make sure you
 try out TweetCraft if you have World of Warcraft installed and if you’re curious,
<a href="http://tweetcraft.codeplex.com/SourceControl/ListDownloadableCommits.aspx">
download the source code</a> and take a deeper look.</p>
<p>&nbsp;</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9813390/gaborratky_762a3823-615d-45f0-84f7-bb5c1d0a2707.png">
<br>
Gabor Ratky (<a href="http://rgabostyle.com/">Blog</a>, <a href="http://twitter.com/rgabostyle">
Twitter</a>), Senior Software Engineer and Coding4Fun Ninja at <a href="http://www.epam.com">
EPAM Systems</a>&nbsp;</p>
<p>Gabor is the developer behind TweetCraft and has been working on Coding4Fun projects for almost two years. He also leads the development of
<a href="http://addonstudio.codeplex.com/">AddOn Studio for World of Warcraft</a>, a Visual Studio-based IDE for building World of Warcraft add-ons and he co-authored the chapter in the
<a href="http://www.amazon.com/Coding4Fun-Programming-Projects-Wiimote-Warcraft/dp/0596520743/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1246522615&amp;sr=8-1">
Coding4Fun</a> book about it. He is a VSX Insider and was invited to speak at various events about Visual Studio Extensibility. When not working on Coding4Fun projects, Gabor works on large scale, enterprise solutions using exciting Microsoft technologies.
 He lives in Budapest, Hungary and loves traveling, good wine and Channel9 videos.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:ee1c5cc233474653be019e7600cc088c">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/TweetCraft-An-in-game-Twitter-client-for-World-of-Warcraft</comments>
      <itunes:summary>
TweetCraft is a World of Warcraft add-on that enables you to send and receive tweets using
Twitter without leaving the game, automatically upload and post screenshots using
TwitPic and automatically tweet certain in-game events such as achievements and moving around in the world of Azeroth. TweetCraft customizes your World of Warcraft user interface by adding its own frame similar to the built-in
 ones (such as the Quest Log) and provides easy access to it through a button near the minimap, slash commands or your favorite launcher (e.g. ChocolateBar). 

How TweetCraft works
Let’s quickly walk through the different things that TweetCraft does to bring Twitter into World of Warcraft. 
Overview
World of Warcraft’s user interface is highly customizable and enables add-on developers to do almost anything in the game they can dream of. Add-ons can interact with the world, listen to events that happen in the game, alter the look of the default user
 interface or create brand new ones, but add-ons are seriously limited when it comes to talking to the world outside World of Warcraft. They cannot read or write files, call web services or do anything that could be potentially harmful. This is why TweetCraft
 comes with a Windows application that sits in the Notification Area (also known as the tray) and does the bulk of the work, giving the TweetCraft add-on only the data that it needs to display, such as tweets, replies and user pictures. All the Twitter web
 service calls, uploading of screenshots and housekeeping is done by this application. Let’s see how! 
The TweetCraft Tray application
The TweetCraft Tray application is a WPF application that periodically checks for new tweets, dowloads and converts the user pictures of the tweets’ authors and writes it out to a file that World of Warcraft picks up and the add-on can use. It also picks
 up all outgoing tweets queued up for sending and sends them using Twitter one-by-one. It watches for screenshots taken by World of Warc</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/TweetCraft-An-in-game-Twitter-client-for-World-of-Warcraft</link>
      <pubDate>Thu, 02 Jul 2009 05:18:07 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/TweetCraft-An-in-game-Twitter-client-for-World-of-Warcraft</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9813390_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9813390_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Gabor Ratky</dc:creator>
      <itunes:author>Gabor Ratky</itunes:author>
      <slash:comments>17</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/TweetCraft-An-in-game-Twitter-client-for-World-of-Warcraft/RSS</wfw:commentRss>
      <category>Gaming</category>
      <category>Visual Studio</category>
      <category>Web Services</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>Skype Voice Changer</title>
      <description><![CDATA[
<p>In this article I demonstrate how you can create your own audio effects in .NET to manipulate digital audio at the sample level. These effects are used to process MP3 files while they are being played back, and to process the real-time microphone input allowing
 you to change your voice during a Skype conversation.</p>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="157">
<p>Mark Heath, <a href="http://mark-dot-net.blogspot.com">blog</a> <br>
</p>
</td>
<td valign="top" width="481">
<p><b>Source Code:</b> <a href="http://www.codeplex.com/skypefx">Download</a></p>
<p><b>Difficulty:</b> Intermediate <br>
<b>Time Required:</b> 8 hours <br>
<b>Cost:</b> Free <br>
<b>Software Needed:</b> <a href="http://www.microsoft.com/express/download/">Visual Basic or Visual C# Express 2008</a><b>
<br>
</b><b>Libraries:</b> <a href="http://www.codeplex.com/naudio">NAudio</a>, <a href="https://developer.skype.com/Docs/Skype4COM">
Skype4COM</a>, <a href="http://www.codeplex.com/MEF">MEF</a></p>
</td>
</tr>
</tbody>
</table>
<h3><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_15.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_12.png" width="500" height="204"></a>
</h3>
<h3>Audio and the .NET Framework</h3>
<p>Playing back audio in a .NET application is not quite as easy as you might hope it would be. The .NET 2.0 Framework introduced the
<a href="http://msdn.microsoft.com/en-us/library/system.media.soundplayer.aspx">SoundPlayer</a> component, which allows you to play back an existing WAV file. While this may be fine for many scenarios, as soon as you want to do things even slightly more advanced,
 such as changing the volume, or playing back from a different file-format, or pausing and repositioning, you must resort to writing P/Invoke wrappers for various Windows APIs.</p>
<p>Back in 2002, as I was getting started learning .NET, I create some audio-related classes of my own to compensate for the lack of audio support in the .NET Framework. I focused initially on reading and writing WAV and MIDI files, as well as playing back
 audio in a way that allowed real-time mixing and manipulation of the audio at a sample level. As time went by, I made this growing collection of audio classes available as an open source project, called
<a href="http://www.codeplex.com/naudio">NAudio</a>, now hosted at CodePlex.</p>
<h4>Audio Playback in NAudio</h4>
<p>NAudio works by constructing an audio playback graph. Audio comes in “streams” which can be connected together and modified before eventually they go to a renderer. This might be your soundcard if you are listening to the audio, or it might be to a file
 on the hard disk.</p>
<p>In NAudio, all streams derive from <b>WaveStream</b>. NAudio comes with a collection of useful
<b>WaveStream</b> derived classes such as <b>WaveFileReader</b> to read from WAV files or
<b>WaveStreamMixer</b> to sum together multiple audio streams. </p>
<p>Audio mixing and effects are almost always performed with floating point numbers (32 bit is most common), so one of the first steps after reading audio out of a WAV file is to convert it from 16 bit to 32 bit. NAudio includes the
<b>Wave16To32ConversionStream </b>class to do this. If the audio wasn't in PCM in the first place, for example it is MP3, then we make use of a combination of the
<b>Mp3FileReaderStream</b>, <b>WaveFormatConversionStream</b> and <b>BlockAlignmentReductionStream</b> to read the audio out and get it into just the format we want. Here's an example showing how to create a
<b>WaveStream</b> ready for playback.</p>
<pre class="csharpcode">WaveStream outStream;
<span class="kwrd">if</span> (fileName.EndsWith(<span class="str">&quot;.mp3&quot;</span>))
{
   outStream = <span class="kwrd">new</span> Mp3FileReader(fileName);
}
<span class="kwrd">else</span> <span class="kwrd">if</span>(fileName.EndsWith(<span class="str">&quot;.wav&quot;</span>))
{
   outStream = <span class="kwrd">new</span> WaveFileReader(fileName);
}
<span class="kwrd">else</span>
{
   <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">&quot;Can't open this type of file&quot;</span>);
}                
<span class="kwrd">if</span> (outStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
{
   outStream = WaveFormatConversionStream.CreatePcmStream(outStream);
   outStream = <span class="kwrd">new</span> BlockAlignReductionStream(outStream); <span class="rem">// reduces choppiness</span>
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>If we simply want to play back the audio without any extra processing, we would use one of the audio output classes provided by NAudio to create an object that implements
<b>IWavePlayer</b>. The options are <b>WaveOut</b>, <b>DirectSoundOut</b>, <b>AsioOut</b> and
<b>WasapiOut</b>, each representing a different technology for audio playback in Windows. We will use
<b>WaveOut</b>, which is the most universally supported. Here we are opening the default output device with a latency of 300ms, and instructing it to use windowed callbacks.</p>
<pre class="csharpcode">IWavePlayer player = <span class="kwrd">new</span> WaveOut(0, 300, <span class="kwrd">true</span>);
player.Init(outStream);
player.Play();</pre>
<p><b>WaveOut</b> will now repeatedly call the <b>Read</b> method of the output stream to get the next batch of audio samples to play. All we need to do now is to insert our audio effects into the playback chain.</p>
<h4>An Audio Effects Framework</h4>
<p>To allow us to process sample level audio more simply, I have create a new <b>
WaveStream</b> derived class called <b>EffectStream</b>. <b>EffectStream</b> will simply pass each audio sample to one or more audio effects before returning the modified audio in its
<b>Read</b> method. The reason I have chosen to host all the effects in a single <b>
EffectSteam</b> rather than creating one <b>WaveStream</b> derived class is that I want to avoid the performance penalty of converting between arrays of bytes and arrays of floating point numbers at every step. Some types of effect, particularly those involving
 Fourier transforms can be processor intensive, so anything we can do to speed up performance will help.</p>
<p>As well as the <b>EffectStream </b>class, we need a base <b>Effect</b> class, from which all of our effects can derive. Here's a simplified version of the base Effect class (minus a whole load of helper mathematical functions):</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> Effect
{
    <span class="kwrd">private</span> List&lt;Slider&gt; sliders;
    <span class="kwrd">public</span> <span class="kwrd">float</span> SampleRate { get; set; }
    <span class="kwrd">public</span> <span class="kwrd">float</span> Tempo { get; set; }
    <span class="kwrd">public</span> <span class="kwrd">bool</span> Enabled { get; set; }

    <span class="kwrd">public</span> Effect()
    {
        sliders = <span class="kwrd">new</span> List&lt;Slider&gt;();
        Enabled = <span class="kwrd">true</span>;
        Tempo = 120;
        SampleRate = 44100;
    }

    <span class="kwrd">public</span> IList&lt;Slider&gt; Sliders { get { <span class="kwrd">return</span> sliders; } }

    <span class="kwrd">public</span> Slider AddSlider(<span class="kwrd">float</span> defaultValue, <span class="kwrd">float</span> minimum, 
            <span class="kwrd">float</span> maximum, <span class="kwrd">float</span> increment, <span class="kwrd">string</span> description)
    {
        Slider slider = <span class="kwrd">new</span> Slider(defaultValue, minimum, 
            maximum, increment, description);
        sliders.Add(slider);
        <span class="kwrd">return</span> slider;
    }

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Should be called on effect load, </span>
    <span class="rem">/// sample rate changes, and start of playback</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> Init()
    {}

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// will be called when a slider value has been changed</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">void</span> Slider();

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// called before each block is processed</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> Block()
    { }

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// called for each sample</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">void</span> Sample(<span class="kwrd">ref</span> <span class="kwrd">float</span> spl0, <span class="kwrd">ref</span> <span class="kwrd">float</span> spl1);
}</pre>
<p>The bulk of the work of the effect should be done in the overridden <b>Sample</b> method. The
<b>spl0</b> and <b>spl1</b> parameters contain the current sample values to be modified for the left and right channels respectively. For example, to lower the volume we could halve the amplitude of every sample with the following code:</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Sample(<span class="kwrd">ref</span> <span class="kwrd">float</span> spl0, <span class="kwrd">ref</span> <span class="kwrd">float</span> spl1)
{
    spl0 *= 0.5f;
    spl1 *= 0.5f;
}</pre>
<p>The <b>Effect</b> class contains <b>Tempo</b> and <b>SampleRate</b> values which are useful for certain types of effect. It also contains a concept of ‘Sliders' for each effect. These are the effect parameters, which allow real-time modification of the effect.
 So if we wanted to control the volume using a slider, we could write the following code (although bear in mind that normally volume sliders should be logarithmic not linear – see the
<b>Volume</b> effect in the sample code for an example of how to do this):</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Sample(<span class="kwrd">ref</span> <span class="kwrd">float</span> spl0, <span class="kwrd">ref</span> <span class="kwrd">float</span> spl1)
{
    spl0 *= slider1;
    spl1 *= slider1;
}</pre>
<p>To simplify the task of adding, removing and re-ordering effects, I created an
<b>EffectChain</b> class, which is a simple wrapper around a <b>List&lt;Effect&gt;</b>. The
<b>EffectStream</b> class has an <b>EffectChain</b> that contains all the effects it needs to run. Here is the code for the
<b>EffectStream</b>:</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> EffectStream : WaveStream
{
    <span class="kwrd">private</span> EffectChain effects;
    <span class="kwrd">public</span> WaveStream source;
    <span class="kwrd">private</span> <span class="kwrd">object</span> effectLock = <span class="kwrd">new</span> <span class="kwrd">object</span>();
    <span class="kwrd">private</span> <span class="kwrd">object</span> sourceLock = <span class="kwrd">new</span> <span class="kwrd">object</span>();

    <span class="kwrd">public</span> EffectStream(EffectChain effects, WaveStream sourceStream)
    {
        <span class="kwrd">this</span>.effects = effects;
        <span class="kwrd">this</span>.source = sourceStream;
        <span class="kwrd">foreach</span> (Effect effect <span class="kwrd">in</span> effects)
        {
            InitialiseEffect(effect);
        }

    }

    <span class="kwrd">public</span> EffectStream(WaveStream sourceStream)
        : <span class="kwrd">this</span>(<span class="kwrd">new</span> EffectChain(), sourceStream)
    {        
    }

    <span class="kwrd">public</span> EffectStream(Effect effect, WaveStream sourceStream)
        : <span class="kwrd">this</span>(sourceStream)
    {
        AddEffect(effect);
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> WaveFormat WaveFormat
    {
        get { <span class="kwrd">return</span> source.WaveFormat; }
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">long</span> Length
    {
        get { <span class="kwrd">return</span> source.Length; }
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">long</span> Position
    {
        get { <span class="kwrd">return</span> source.Position; }
        set { <span class="kwrd">lock</span> (sourceLock) { source.Position = <span class="kwrd">value</span>; } }
    }        

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> Read(<span class="kwrd">byte</span>[] buffer, <span class="kwrd">int</span> offset, <span class="kwrd">int</span> count)
    {
        <span class="kwrd">int</span> read;
        <span class="kwrd">lock</span>(sourceLock)
        {
            read = source.Read(buffer, offset, count);
        }
        <span class="kwrd">if</span> (WaveFormat.BitsPerSample == 16)
        {
            <span class="kwrd">lock</span> (effectLock)
            {
                Process16Bit(buffer, offset, read);
            }
        }
        <span class="kwrd">return</span> read;
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> Process16Bit(<span class="kwrd">byte</span>[] buffer, <span class="kwrd">int</span> offset, <span class="kwrd">int</span> count)
    {
        <span class="kwrd">foreach</span> (Effect effect <span class="kwrd">in</span> effects)
        {
            <span class="kwrd">if</span> (effect.Enabled)
            {
                effect.Block();
            }
        }

        <span class="kwrd">for</span>(<span class="kwrd">int</span> sample = 0; sample &lt; count/2; sample&#43;&#43;)
        {
            <span class="rem">// get the sample(s)</span>
            <span class="kwrd">int</span> x = offset &#43; sample * 2;
            <span class="kwrd">short</span> sample16Left = BitConverter.ToInt16(buffer, x);
            <span class="kwrd">short</span> sample16Right = sample16Left;
            <span class="kwrd">if</span>(WaveFormat.Channels == 2)
            {                    
                sample16Right = BitConverter.ToInt16(buffer, x &#43; 2);
                sample&#43;&#43;;
            }
           
            <span class="rem">// run these samples through the effects</span>
            <span class="kwrd">float</span> sample64Left = sample16Left / 32768.0f;
            <span class="kwrd">float</span> sample64Right = sample16Right / 32768.0f;
            <span class="kwrd">foreach</span> (Effect effect <span class="kwrd">in</span> effects)
            {
                <span class="kwrd">if</span> (effect.Enabled)
                {
                    effect.Sample(<span class="kwrd">ref</span> sample64Left, <span class="kwrd">ref</span> sample64Right);
                }
            }

            sample16Left = (<span class="kwrd">short</span>)(sample64Left * 32768.0f);
            sample16Right = (<span class="kwrd">short</span>)(sample64Right * 32768.0f);

            <span class="rem">// put them back</span>
            buffer[x] = (<span class="kwrd">byte</span>)(sample16Left &amp; 0xFF);
            buffer[x &#43; 1] = (<span class="kwrd">byte</span>)((sample16Left &gt;&gt; 8) &amp; 0xFF); 

            <span class="kwrd">if</span>(WaveFormat.Channels == 2)    
            {
                buffer[x &#43; 2] = (<span class="kwrd">byte</span>)(sample16Right &amp; 0xFF);
                buffer[x &#43; 3] = (<span class="kwrd">byte</span>)((sample16Right &gt;&gt; 8) &amp; 0xFF);
            }
        }
    }


    <span class="kwrd">public</span> <span class="kwrd">bool</span> MoveUp(Effect effect)
    {
        <span class="kwrd">lock</span> (effectLock)
        {
            <span class="kwrd">return</span> effects.MoveUp(effect);
        }
    }

    <span class="kwrd">public</span> <span class="kwrd">bool</span> MoveDown(Effect effect)
    {
        <span class="kwrd">lock</span> (effectLock)
        {
            <span class="kwrd">return</span> effects.MoveDown(effect);
        }
    }

    <span class="kwrd">public</span> <span class="kwrd">void</span> AddEffect(Effect effect)
    {
        InitialiseEffect(effect);
        <span class="kwrd">lock</span> (effectLock)
        {
            <span class="kwrd">this</span>.effects.Add(effect);
        }
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> InitialiseEffect(Effect effect)
    {
        effect.SampleRate = WaveFormat.SampleRate;
        effect.Init();
        effect.Slider();
    }

    <span class="kwrd">public</span> <span class="kwrd">bool</span> RemoveEffect(Effect effect)
    {
        <span class="kwrd">lock</span> (effectLock)
        {
            <span class="kwrd">return</span> <span class="kwrd">this</span>.effects.Remove(effect);
        }
    }
}</pre>
<p>When the <b>Read</b> method on <b>EffectStream</b> is called, we first read the requested number of bytes from our source
<b>WaveStream</b>. This might be from a WAV or MP3 file, or from a microphone. Then, we convert it from 16 bit to 32 bit floating point audio. 16 bit audio is stored as integers going from -32,768 to 32,767, and 32 bit audio uses the range -1.0 to 1.0 to represent
 this range. This means we have plenty of headroom to mix together multiple signals without distorting. It is important though to remember that no samples should be greater than 1.0 before converting back to 16 bit.</p>
<h3>Porting Effects to .NET</h3>
<p>Now we have a basic effect framework, it is time to create some real effects to use. There are many sources of algorithms for digital signal processing (DSP) (try
<a href="http://www.musicdsp.org/">musicdsp.org</a> for a good starting point), but I have chosen to base my effects model on that provided by the
<a href="http://www.reaper.fm/">REAPER digital audio workstation</a> (DAW). This impressive application, masterminded by legendary software developer
<a href="http://en.wikipedia.org/wiki/Justin_Frankel">Justin Frankel</a>, includes a text-based effects framework. These effects, known as
<a href="http://www.reaper.fm/sdk/js/">JS effects</a>, allow the use of a C-like syntax to quickly write your own effects. I have modelled my
<b>Effect</b> class on the JS syntax, allowing me to quickly port effects across.</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> Tremolo : Effect
{
    <span class="kwrd">public</span> Tremolo()
    {
        AddSlider(4,0,100,1,<span class="str">&quot;frequency (Hz)&quot;</span>);
        AddSlider(-6,-60,0,1,<span class="str">&quot;amount (dB)&quot;</span>);
        AddSlider(0, 0, 1, 0.1f, <span class="str">&quot;stereo separation (0..1)&quot;</span>);
    }

    <span class="kwrd">float</span> adv, sep, amount, sc, pos;

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Slider()
    {
        adv=PI*2*slider1/SampleRate;
        sep=slider3*PI;
        amount=pow(2,slider2/6);
        sc=0.5f*amount; amount=1-amount;
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Sample(<span class="kwrd">ref</span> <span class="kwrd">float</span> spl0, <span class="kwrd">ref</span> <span class="kwrd">float</span> spl1)
    {
        spl0 = spl0 * ((cos(pos) &#43; 1) * sc &#43; amount);
        spl1 = spl1 * ((cos(pos &#43; sep) &#43; 1) * sc &#43; amount);
        pos &#43;= adv;
    }
}</pre>
<p>Some members in the <b>Effect</b> base class such as <b>cos</b> and <b>slider1</b> allow me to keep the ported syntax as similar to the original JS script as possible.</p>
<p>REAPER ships with well over 100 of these JS Effects, so I chose about 15 of them and ported them to .NET. They are available in the download that accompanies this article. With some of them, for example pitch shifting effects, you will immediately notice
 the effect on the sound, while others, such as compressors, require some knowledge of how to adjust the parameters to get good results.</p>
<h4>The Test Harness</h4>
<p>Obviously we need a way to pass audio through our effects, so our next task is to create a test harness that will allow us to load in audio files and listen to them with the effects applied. For this purpose I created a simple Windows Forms application that
 allows you to select a WAV or MP3 file to play back. After being converted into PCM using various classes from the NAudio library, the resulting
<b>WaveStream</b> is passed through an <b>EffectStream </b>before being passed to the soundcard for playback. The use of an
<b>EffectChain </b>allows us to modify the effects loaded, and their order during playback.</p>
<p>To make the loading of effects simpler, I used the <a></a><a href="http://www.codeplex.com/MEF">Managed Extensibility Framework</a> (MEF), to
<a href="#_msocom_1" name="_msoanchor_1">[DF1]</a> make each effect a “plugin” to the test harness. Each effect is decorated with an
<b>Export</b> attribute to indicate that it is a plugin:</p>
<pre class="csharpcode">[Export(<span class="kwrd">typeof</span>(Effect))]
<span class="kwrd">public</span> <span class="kwrd">class</span> SuperPitch : Effect</pre>
<p>Then I can request that MEF auto-populates a property with all the exported effects it can find:</p>
<pre class="csharpcode">[Import]
<span class="kwrd">public</span> ICollection&lt;Effect&gt; Effects { get; set; }</pre>
<p>When the user selects an effect from the list of available effects, we create a new instance of it. This is because you might want to put the same effect into the effect chain more than once:</p>
<pre class="csharpcode">EffectSelectorForm effectSelectorForm = <span class="kwrd">new</span> EffectSelectorForm(Effects);
<span class="kwrd">if</span> (effectSelectorForm.ShowDialog(<span class="kwrd">this</span>) == DialogResult.OK)
{
    <span class="rem">// create a new instance of the selected effect </span>
    <span class="rem">// as we may want multiple copies of one effect</span>
    Effect effect = (Effect)Activator.CreateInstance(
       effectSelectorForm.SelectedEffect.GetType());
    audioGraph.AddEffect(effect);
    checkedListBox1.Items.Add(effect, <span class="kwrd">true</span>);
}</pre>
<p>To allow real-time modification of the effect parameters, I created two user controls. The first,
<b>EffectSliderPanel</b> allows you to hook up a Windows Forms <b>TrackBar</b> to one of our effect's sliders and manages the minimum, maximum and granularity settings. The second user control,
<b>EffectPanel</b> takes an <b>Effect</b> and creates one <b>EffectSliderPanel</b> for each slider in that effect. It also is responsible for calling the
<b>Slider</b> method on the <b>Effect</b> whenever the user moves one of the sliders. Here's an example of what it looks like:</p>
<p><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image.png" width="488" height="164">
</p>
<p>Now we are able to test our effects by listening to WAV files and playing them with real-time control over their parameters.</p>
<h3>Intercepting Skype Audio</h3>
<p>There are many interesting uses for audio effects, but it was suggested to me that I create a “voice changer” for Skype as the example program for this article. At first I didn't think that this would be possible, as you would need access to the audio samples
 from the microphone <i>before</i> Skype transmitted them over the network.</p>
<p>However, it turns out that Skype has a full featured SDK to allow all kinds of third-party add-ons and enhancements. The Skype API can be used in .NET via a COM object, called
<a href="https://developer.skype.com/">Skype4Com</a>. Skype plugins are not loaded directly by the Skype application but attach to it via network sockets. The Skype4Com COM object hides much of this complexity from the user.</p>
<p>Having added Skype4Com as a reference to our application, we then need to connect to Skype. This is achieved by using the following code:</p>
<pre class="csharpcode"><span class="kwrd">const</span> <span class="kwrd">int</span> Protocol = 8;
skype = <span class="kwrd">new</span> Skype();
_ISkypeEvents_Event events = (_ISkypeEvents_Event)skype;
events.AttachmentStatus &#43;= OnSkypeAttachmentStatus;            
skype.CallStatus &#43;= OnSkypeCallStatus;
skype.Attach(Protocol, <span class="kwrd">false</span>);</pre>
<p>In the <b>CallStatus</b> event handler, we tell Skype that we wish to ‘capture' the microphone. This will cause it to send us the raw audio data from the microphone via a TCP socket. Then we tell it that we will send the audio to be transmitted using another
 TCP socket.</p>
<pre class="csharpcode"><span class="kwrd">void</span> OnSkypeCallStatus(Call call, TCallStatus status)
{
    log.Info(<span class="str">&quot;SkypeCallStatus: {0}&quot;</span>, status);
    <span class="kwrd">if</span> (status == TCallStatus.clsInProgress)
    {
        <span class="kwrd">this</span>.call = call;                  
        call.set_CaptureMicDevice(
        TCallIoDeviceType.callIoDeviceTypePort, MicPort.ToString());
        call.set_InputDevice(
        TCallIoDeviceType.callIoDeviceTypeSoundcard, <span class="str">&quot;&quot;</span>);
        call.set_InputDevice(
        TCallIoDeviceType.callIoDeviceTypePort, OutPort.ToString());
    }
    <span class="kwrd">else</span> <span class="kwrd">if</span> (status == TCallStatus.clsFinished)
    {
        call = <span class="kwrd">null</span>;
        packetSize = 0;
    }
}</pre>
<p>I found an example Delphi application on the Skype developer website which shows how to intercept the microphone signal to
<a href="https://developer.skype.com/Docs/Skype4COM/Example/MicBooster_pas">boost the signal level</a>. I used this sample as the starting point for creating my own application to intercept audio samples in Skype. The Delphi application made use of an object
 called <a href="http://www.indyproject.org/docsite/html/frames.html?frmname=topic&amp;frmfile=TIdTCPServer.html">
TIdTCPServer</a>, which is a multi-threaded socket server. I created a very simple .NET implementation of this class (without the multi-threading as we will only have one connection at a time):</p>
<pre class="csharpcode"><span class="kwrd">class</span> TcpServer : IDisposable
{
    TcpListener listener;
    <span class="kwrd">public</span> <span class="kwrd">event</span> EventHandler&lt;ConnectedEventArgs&gt; Connect;
    <span class="kwrd">public</span> <span class="kwrd">event</span> EventHandler Disconnect;
    <span class="kwrd">public</span> <span class="kwrd">event</span> EventHandler&lt;DataReceivedEventArgs&gt; DataReceived;
    
    <span class="kwrd">public</span> TcpServer(<span class="kwrd">int</span> port)
    {
        listener = <span class="kwrd">new</span> TcpListener(IPAddress.Loopback, port);
        listener.Start();
        ThreadPool.QueueUserWorkItem(Listen);
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> Listen(<span class="kwrd">object</span> state)
    {
        <span class="kwrd">while</span> (<span class="kwrd">true</span>)
        {
            <span class="kwrd">using</span> (TcpClient client = listener.AcceptTcpClient())
            {
                AcceptClient(client);
            }
        }
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> AcceptClient(TcpClient client)
    {
        <span class="kwrd">using</span> (NetworkStream inStream = client.GetStream())
        {
            OnConnect(inStream);
            <span class="kwrd">while</span> (client.Connected)
            {
                <span class="kwrd">int</span> available = client.Available;
                <span class="kwrd">if</span> (available &gt; 0)
                {
                    <span class="kwrd">byte</span>[] buffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[available];
                    <span class="kwrd">int</span> read = inStream.Read(buffer, 0, available);
                    Debug.Assert(read == available);
                    OnDataReceived(buffer);
                }
                <span class="kwrd">else</span>
                {
                    Thread.Sleep(50);
                }
            }
        }
        OnDisconnect();
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> OnConnect(NetworkStream stream)
    {
        var connect = Connect;
        <span class="kwrd">if</span> (connect != <span class="kwrd">null</span>)
        {
            connect(<span class="kwrd">this</span>, <span class="kwrd">new</span> ConnectedEventArgs() { Stream = stream });
        }
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> OnDisconnect()
    {
        var disconnect = Disconnect;
        <span class="kwrd">if</span> (disconnect != <span class="kwrd">null</span>)
        {
            disconnect(<span class="kwrd">this</span>, EventArgs.Empty);
        }
    }

    <span class="kwrd">private</span> <span class="kwrd">void</span> OnDataReceived(<span class="kwrd">byte</span>[] buffer)
    {
        var execute = DataReceived;
        <span class="kwrd">if</span> (execute != <span class="kwrd">null</span>)
        {
            execute(<span class="kwrd">this</span>, <span class="kwrd">new</span> DataReceivedEventArgs() { Buffer = buffer });
        }
    }

    <span class="preproc">#region</span> IDisposable Members

    <span class="kwrd">public</span> <span class="kwrd">void</span> Dispose()
    {
        listener.Stop();
    }

    <span class="preproc">#endregion</span>
}

<span class="kwrd">public</span> <span class="kwrd">class</span> DataReceivedEventArgs : EventArgs
{
    <span class="kwrd">public</span> <span class="kwrd">byte</span>[] Buffer { get; set; }
}

<span class="kwrd">public</span> <span class="kwrd">class</span> ConnectedEventArgs : EventArgs
{
    <span class="kwrd">public</span> NetworkStream Stream { get; set; }
}</pre>
<p>Once Skype has been told the port numbers on which to connect, it will attempt to open sockets to our
<b>TcpListener</b> classes (one for audio in, and one for audio out). We now simply need to pass the audio through our effect chain. But
<b>EffectStream </b>needs a <b>WaveStream</b> derived class for its input, so I created
<b>SkypeBufferStream</b> to which we pass the raw data received on the microphone in socket, and it returns it in its
<b>Read</b> method. One difficulty I encountered was that Skype offers no way of querying what the sample rate of the incoming data is. On my PC it seems to be 44.1kHz, but I do not know if this is guaranteed on all computers.</p>
<pre class="csharpcode"><span class="kwrd">class</span> SkypeBufferStream : WaveStream
{
    <span class="kwrd">byte</span>[] latestInBuffer;
    WaveFormat waveFormat;

    <span class="kwrd">public</span> SkypeBufferStream(<span class="kwrd">int</span> sampleRate)
    {
        waveFormat = <span class="kwrd">new</span> WaveFormat(sampleRate, 16, 1);
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> WaveFormat WaveFormat
    {
        get { <span class="kwrd">return</span> waveFormat; }
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">long</span> Length
    {
        get { <span class="kwrd">return</span> 0; }
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">long</span> Position
    {
        get
        {
            <span class="kwrd">return</span> 0;
        }
        set
        {
            <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException();
        }
    }

    <span class="kwrd">public</span> <span class="kwrd">void</span> SetLatestInBuffer(<span class="kwrd">byte</span>[] buffer)
    {
        latestInBuffer = buffer;
    }

    <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> Read(<span class="kwrd">byte</span>[] buffer, <span class="kwrd">int</span> offset, <span class="kwrd">int</span> count)
    {
        <span class="kwrd">if</span> (offset != 0)
            <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentOutOfRangeException(<span class="str">&quot;offset&quot;</span>);
        <span class="kwrd">if</span> (buffer != latestInBuffer)
            Array.Copy(latestInBuffer, buffer, count);
        <span class="kwrd">return</span> count;
    }
}</pre>
<p>Now when we receive any data from the microphone socket, we pass it through the
<b>SkypeBufferStream</b> which in turn passes it through the <b>EffectStream</b> and finally out on the output socket's data stream. Here's the relevant code (found in the
<b>MicInterceptor</b> class):</p>
<pre class="csharpcode">NetworkStream outStream;
SkypeBufferStream bufferStream;
WaveStream outputStream;

<span class="kwrd">void</span> OnOutServerConnect(<span class="kwrd">object</span> sender, ConnectedEventArgs e)
{
    log.Info(<span class="str">&quot;OutServer Connected&quot;</span>);
    outStream = e.Stream;
}

<span class="kwrd">void</span> OnMicServerExecute(<span class="kwrd">object</span> sender, DataReceivedEventArgs args)
{
    <span class="rem">// log.Info(&quot;Got {0} bytes&quot;, args.Buffer.Length);</span>
    <span class="kwrd">if</span> (outStream != <span class="kwrd">null</span>)
    {
        <span class="rem">// give the input audio to the beginning of our audio graph</span>
        bufferStream.SetLatestInBuffer(args.Buffer);
        <span class="rem">// process it out through the effects</span>
        outputStream.Read(args.Buffer, 0, args.Buffer.Length);
        <span class="rem">// play it back</span>
        outStream.Write(args.Buffer, 0, args.Buffer.Length);
    }
}</pre>
<p>When you run your application for the first time, you will need to grant it permission from within Skype:</p>
<p><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_3.png" width="203" height="133">
</p>
<p><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_4.png" width="500" height="371">
</p>
<p>To test that the effects are working in Skype is a little tricky as you will not hear the effected sound on your end of the conversation. One good way of checking the effects are working as expected is to use the Skype test call service. This is a number
 you can dial and it will record what you say and play it back to you. </p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_5.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb.png" width="240" height="20"></a>
</p>
<p>It is a good idea to test your effect first using a local audio file, as you will not easily be able to determine whether glitches and other audio artifacts were caused by your effect, or simply due to poor network conditions.</p>
<p>There are a few things you should be aware of when selecting effects for use with Skype. First, the audio is mono, so there is no point using any effects such as stereo delay. Second, the audio is almost certainly down-sampled to a much lower sample rate
 before being transmitted to save on network bandwidth. This means any high frequency components of your sound will be lost. Third, internet telephony applications often have built in echo-suppression, so using delay-based effects might not work quite as well
 as you were hoping. </p>
<p>For silly voice effects, the most effective is pitch shifting (try the <b>SuperPitch</b> effect and shift either up or down about five semitones).
<b>FlangeBaby</b> or <b>Chorus</b> can be used for more subtle voice changing effects. Or if you just want to be annoying, load up a
<b>Delay</b>. Feel free to experiment with the other included effects, but bear in mind that many of them are designed with more musical uses in mind, so may not be relevant for internet telephony.</p>
<h3>The Sample Code</h3>
<p>The source code for all the effects, the <b>EffectStream</b> and the Skype connection code described in this article is available in the provided download. It uses a recent unreleased build of NAudio, so you will also need to visit the NAudio CodePlex site
 if you want to get access to the full source code. The <b>EffectStream</b> and <b>
Effect</b> classes will eventually be made part of the NAudio framework, once I have refined their design a bit.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_6.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_3.png" width="500" height="227"></a>
</p>
<p><b></b></p>
<p>How to use Effect Tester sample app:
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_7.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_4.png" width="45" height="41"></a>
</td>
<td valign="top" width="559">
<p>Click this icon to attach to Skype and await a call. Click it again to disconnect, allowing you to test your effects using audio files instead. The textbox in the top right corner keeps you updated with the status of the connection to Skype</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_8.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_5.png" width="39" height="43"></a>
</td>
<td valign="top" width="559">
<p>Click this icon to load a WAV or MP3 file for playback. Files at 44.1kHz work best.</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_9.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_6.png" width="147" height="40"></a>
</td>
<td valign="top" width="559">
<p>Rewind, Play, Pause or Stop the current WAV or MP3 file.</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_10.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_7.png" width="38" height="42"></a>
</td>
<td valign="top" width="559">
<p>Brings up the effect selector dialog to add a new instance of an effect to the current Effect chain. Loaded effects appear in the CheckedListBox on the left.</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_11.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_8.png" width="42" height="41"></a>
</td>
<td valign="top" width="559">
<p>Removes the currently selected effect from the effect chain</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_12.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_9.png" width="74" height="40"></a>
</td>
<td valign="top" width="559">
<p>Move the currently selected effects up or down in the signal chain</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_13.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_10.png" width="96" height="42"></a>
</td>
<td valign="top" width="559">
<p>Use the checkboxes to enable or disable effects in the effect chain on the fly.</p>
</td>
</tr>
<tr>
<td valign="top" width="57"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_14.png"><img title="image" border="0" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9391048/image_thumb_11.png" width="228" height="44"></a>
</td>
<td valign="top" width="559">
<p>Use the sliders to adjust the effect parameters in real-time</p>
</td>
</tr>
</tbody>
</table>
</p>
<p><b></b></p>
<h3>About the Author</h3>
<p>Mark<b> </b>Heath is a .NET developer based in Southampton, UK. When he's not writing .NET audio applications for fun, he enjoys home studio recording, playing football, reading theology books and sword fighting with his four small children. His development
 blog can be found at <a href="http://mark-dot-net.blogspot.com">http://mark-dot-net.blogspot.com</a></p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:b7d8e2fa4703498d85ec9e7600cd487a">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Skype-Voice-Changer</comments>
      <itunes:summary>
In this article I demonstrate how you can create your own audio effects in .NET to manipulate digital audio at the sample level. These effects are used to process MP3 files while they are being played back, and to process the real-time microphone input allowing
 you to change your voice during a Skype conversation. 




Mark Heath, blog 
 


Source Code: Download 
Difficulty: Intermediate 
Time Required: 8 hours 
Cost: Free 
Software Needed: Visual Basic or Visual C# Express 2008

Libraries: NAudio, 
Skype4COM, MEF 






Audio and the .NET Framework
Playing back audio in a .NET application is not quite as easy as you might hope it would be. The .NET 2.0 Framework introduced the
SoundPlayer component, which allows you to play back an existing WAV file. While this may be fine for many scenarios, as soon as you want to do things even slightly more advanced,
 such as changing the volume, or playing back from a different file-format, or pausing and repositioning, you must resort to writing P/Invoke wrappers for various Windows APIs. 
Back in 2002, as I was getting started learning .NET, I create some audio-related classes of my own to compensate for the lack of audio support in the .NET Framework. I focused initially on reading and writing WAV and MIDI files, as well as playing back
 audio in a way that allowed real-time mixing and manipulation of the audio at a sample level. As time went by, I made this growing collection of audio classes available as an open source project, called
NAudio, now hosted at CodePlex. 
Audio Playback in NAudio
NAudio works by constructing an audio playback graph. Audio comes in “streams” which can be connected together and modified before eventually they go to a renderer. This might be your soundcard if you are listening to the audio, or it might be to a file
 on the hard disk. 
In NAudio, all streams derive from WaveStream. NAudio comes with a collection of useful
WaveStream derived classes such as WaveFileReader to read from WAV files or</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Skype-Voice-Changer</link>
      <pubDate>Mon, 02 Feb 2009 15:19:20 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Skype-Voice-Changer</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9391048_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9391048_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Mark Heath</dc:creator>
      <itunes:author>Mark Heath</itunes:author>
      <slash:comments>51</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Skype-Voice-Changer/RSS</wfw:commentRss>
      <category>Audio</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>WiiEarthVR – A Fully Immersive 3D Experience with Virtual Earth 3D</title>
      <description><![CDATA[
<table border="0" cellspacing="0" cellpadding="1" width="100%">
<tbody>
<tr class="entry_overview">
<td width="50"><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/globe4.png"><img title="globe4" border="0" alt="globe4" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/globe4_thumb.png" width="54" height="53"></a>
</td>
<td><span class="entry_description">In this article, Brian Peek will demonstrate how to use a Nintendo Wii Remote (Wiimote), a Wii Fit Balance Board, and Vuzix VR920 glasses as input devices for Microsoft Virtual Earth 3D, providing a fully immersive, 3D experience.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author"><a href="http://www.brianpeek.com/" target="_blank">Brian Peek</a></div>
<div class="entry_company"><a href="http://www.aspsoft.com/">ASPSOFT, Inc.</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Intermediate</span></div>
<div class="entry_details"><b>Time Required:</b> 2<span class="entry_details_input">-3 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">$60 for Wiimote and Nunchuk, $90 for Wii Fit (which includes Balance Board), $400 for
<a href="http://www.vr920.com/" target="_blank">Vuzix VR920 glasses</a></span></div>
<div class="entry_details"><b>Software: </b><a href="http://www.codeplex.com/WiimoteLib" target="_blank">Managed Library for Nintendo's Wiimote</a>,
<span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><a href="http://www.nintendo.com/wii/what/accessories">Nintendo Wii Remote (Wiimote) with Nunchuk</a>,
<a href="http://www.nintendo.com/games/detail/hoiNtus4JvIcPtP8LQPyud4Kyy393oep" target="_blank">
Wii Fit Balance Board</a>, <a href="http://www.vr920.com/iwear/products_vr920.html" target="_blank">
Vuzix VR920 glasses</a>, a compatible PC Bluetooth adapter and stack</div>
<div class="entry_details"><b>Download: </b><a href="http://www.brianpeek.com/files/folders/source_code/entry3066.aspx" target="_blank">Download</a></div>
<div class="entry_details"><strong>Discussion Forum: </strong><a href="http://www.brianpeek.com/forums/43.aspx" target="_blank">Forum</a></div>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h3>Introduction</h3>
<p>Virtual Earth is the 3D interface to <a href="http://maps.live.com" target="_blank">
Microsoft's Live Maps</a> service.&nbsp; Normally this control is loaded via the web browser and allows interaction with a keyboard, mouse, and Xbox 360 controller.&nbsp; In this article, we will take the Virtual Earth 3D control out of the web browser, use it in a WinForms
 application, and control it with a Nintendo Wii Remote (Wiimote) and a pair of Vuzix VR920 glasses, while also providing a stereoscopic 3D image to the glasses, creating the illusion of a fully three dimensional environment.&nbsp; Note that use of the Virtual Earth
 3D control in this way is undocumented and unsupported at the moment.&nbsp; Because of this, some of the descriptions in this article are educated guesses and may not be 100% accurate…</p>
<p>Originally, this project started as a simple Wiimote interface to Virtual Earth 3D as shown in the video below.&nbsp; Since I wrote that application, I learned of the VR920 glasses and the Wii Fit Balance Board was released, so I've decided to create a more immersive
 experience using all of these controls which was demonstrated at <a href="http://www.microsoftpdc.com/" target="_blank">
PDC2008</a>, shown here:</p>
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="320" height="240"> <param name="source" value="http://channel9.msdn.com/App_Themes/default/VideoPlayer.xap" /> <param name="initParams" value="m=mms://mschnlnine.wmod.llnwd.net/a1809/d1/ch9/3/1/2/1/4/4/WiiEarthVR_s_ch9.wmv,autostart=false,autohide=true,showembed=true, thumbnail=http://mschnlnine.vo.llnwd.net/d1/ch9/3/1/2/1/4/4/WiiEarthVR_large_ch9.jpg" /> <param name="background" value="#00FFFFFF" /> <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" /> </a> </object><h3>Setup</h3>
<p>Before we get started, you will need to install the Virtual Earth 3D control.&nbsp; If you haven't done this already, browse to
<a href="http://maps.live.com/">http://maps.live.com/</a> and click on the <b>3D</b> link to install the control and supporting software.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/captured_Image.png.png"><img title="captured_Image.png" border="0" alt="captured_Image.png" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/captured_Image.png_thumb.png" width="459" height="107"></a>
</p>
<p>Additionally, if you haven't already, please review my <a href="http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx" target="_blank">
Managed Library for Nintendo's Wiimote</a> article on this site.&nbsp; We will be using the library in this article, but I will not repeat the basic information that is located in the original article.&nbsp; You will also need to have the
<a href="http://www.vr920.com/" target="_blank">Vuzix VR920</a> glasses installed and setup according to its own user manual.&nbsp; That will also not be covered here.</p>
<h3>Implementation</h3>
<h4>The Virtual Earth 3D Control</h4>
<p>The Virtual Earth 3D (VE3D) control is intended to be used through a well documented JavaScript interface from a web page, however we would not be able to access the Wiimote or VR920 glasses from JavaScript.&nbsp; Therefore, we will be using the VE3D control
 through its native, but wholly undocumented interface.</p>
<p>Start by creating a new Windows Forms application named <strong>WiiEarthVR</strong> in C# or VB.&nbsp; As with all controls and 3rd party libraries, a reference needs to be set to the Virtual Earth 3D libraries.&nbsp; Add references to the following items:&nbsp;
</p>
<ul>
<li>Microsoft.MapPoint.Data </li><li>Microsoft.MapPoint.Data.VirtualEarthTileDataSource </li><li>Microsoft.MapPoint.Geometry </li><li>Microsoft.MapPoint.Rendering3D </li><li>Microsoft.MapPoint.Rendering3D.Utility </li></ul>
<p>If they do not show up in the .NET references, they can be found by selecting the Browse tab and navigating to C:\Program Files\Virtual Earth 3D\ or C:\Program Files (x86)\Virtual Earth 3D\ .&nbsp; With the references in place, the project file can now be opened
 and the references will be seen in the References folder in the Solution Explorer as usual.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/captured_Image.png11.png"><img title="captured_Image.png[11]" border="0" alt="captured_Image.png[11]" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9068804/captured_Image.png11_thumb.png" width="376" height="229"></a>
</p>
<p>Creating an instance of the control can be done in code just like any other control.&nbsp; Used in the constructor or load event of the form, the following code will create a VE3D control and add it to the form as fully docked:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> GlobeControl _globeControl;</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> MainForm()</pre>
<pre>{</pre>
<pre>    InitializeComponent();</pre>
<pre>&nbsp;</pre>
<pre>    _globeControl = <span>new</span> GlobeControl();</pre>
<pre>    SuspendLayout();</pre>
<pre>    _globeControl.Location = <span>new</span> System.Drawing.Point(0, 0);</pre>
<pre>    _globeControl.Name = <span>&quot;_globeControl&quot;</span>;</pre>
<pre>    _globeControl.Size = ClientSize;</pre>
<pre>    _globeControl.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;</pre>
<pre>    _globeControl.TabIndex = 0;</pre>
<pre>    _globeControl.SendToBack(); <span>// we want the button to be on top</span></pre>
<pre>&nbsp;</pre>
<pre>    pnlGlobe.Controls.Add(_globeControl);</pre>
<pre>    ResumeLayout(<span>false</span>);</pre>
<pre>}</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> _globeControl <span>As</span> GlobeControl</pre>
<pre>&nbsp;</pre>
<pre><span>Public</span> <span>Sub</span> <span>New</span>()</pre>
<pre>    InitializeComponent()</pre>
<pre>&nbsp;</pre>
<pre>    _globeControl = <span>New</span> GlobeControl()</pre>
<pre>    SuspendLayout()</pre>
<pre>    _globeControl.Location = <span>New</span> System.Drawing.Point(0, 0)</pre>
<pre>    _globeControl.Name = <span>&quot;_globeControl&quot;</span></pre>
<pre>    _globeControl.Size = ClientSize</pre>
<pre>    _globeControl.Anchor = AnchorStyles.Left <span>Or</span> AnchorStyles.Right <span>Or</span> AnchorStyles.Top <span>Or</span> AnchorStyles.Bottom</pre>
<pre>    _globeControl.TabIndex = 0</pre>
<pre>    _globeControl.SendToBack() <span>' we want the button to be on top</span></pre>
<pre>&nbsp;</pre>
<pre>    pnlGlobe.Controls.Add(_globeControl)</pre>
<pre>    ResumeLayout(<span>False</span>)</pre>
<pre><span>End</span> Sub</pre>
</div>
</div>
<p>This sets up the VE3D control in its default state.&nbsp; If you were to run an application with only this code, you would see nothing but the earth.&nbsp; The navigation controls and other extras would be missing.</p>
<p>We can start adding items to the VE3D control by listening for the <strong>FirstFrameRendered</strong> event of the
<strong>GlobeControl</strong> and then setting the appropriate properties.&nbsp; Setting these properties prior to this point can lead to some unexpected results.</p>
<p>In the <strong>FirstFrameRendered</strong> event handler, if you wish to add the default navigation controls to the screen, the
<b>PlugInLoader</b> object is used.&nbsp; The <b>PlugInLoader</b> is created by using the
<b>CreateLoader</b> static method, passing in an instance of the <b>GlobeControl</b>'s
<b>Host</b> object.&nbsp; Then, the <b>NavigationPlugIn</b> can be loaded and activated as shown:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// load all the spiffy UI navigation goodies</span></pre>
<pre>PlugInLoader loader = PlugInLoader.CreateLoader(<span>this</span>.globeControl.Host);</pre>
<pre>loader.LoadPlugIn(<span>typeof</span>(NavigationPlugIn));</pre>
<pre>loader.ActivatePlugIn(<span>typeof</span>(NavigationPlugIn).GUID, <span>null</span>);</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' load all the spiffy UI navigation goodies</span></pre>
<pre><span>Dim</span> loader <span>As</span> PlugInLoader = PlugInLoader.CreateLoader(<span>Me</span>.globeControl.Host)</pre>
<pre>loader.LoadPlugIn(<span>GetType</span>(NavigationPlugIn))</pre>
<pre>loader.ActivatePlugIn(<span>GetType</span>(NavigationPlugIn).GUID, <span>Nothing</span>)</pre>
</div>
</div>
<p>The last thing to be added for basic functionality is the data.&nbsp; As it stands, the only data that will appear on the globe is the image of the continents.&nbsp; Zooming in only produces a blurry representation of that base image.</p>
<p>Data layers are created from specially formatted data sources provided by maps.live.com known as content manifests.&nbsp; These are XML files which tell the VE3D control how to load the data required for any view.&nbsp; Content layers can be added by adding them to
 the <strong>DataSources</strong> object of the <strong>GlobeControl</strong>.&nbsp; We can add any of the following layers (note that there may be other content manifests provided by maps.live.com, but these are the only 5 that I am aware of):</p>
<table border="1" cellspacing="0" cellpadding="2" width="516">
<tbody>
<tr>
<td valign="top" width="244">
<p align="center"><b>URL</b></p>
</td>
<td valign="top" width="163">
<p align="center"><b>DataSourceUsage Type</b></p>
</td>
<td valign="top" width="107">
<p align="center"><b>Description</b></p>
</td>
</tr>
<tr>
<td valign="top" width="244"><a title="http://local.live.com/Manifests/HighDemContentManifest.xml" href="http://local.live.com/Manifests/HD.xml">http://local.live.com/Manifests/HD.xml</a></td>
<td valign="top" width="163">ElevationMap</td>
<td valign="top" width="107">Terrain data</td>
</tr>
<tr>
<td valign="top" width="244"><a title="http://local.live.com/Manifests/ModelContentManifest.xml" href="http://local.live.com/Manifests/MO.xml">http://local.live.com/Manifests/MO.xml</a></td>
<td valign="top" width="163">Model</td>
<td valign="top" width="107">3D buildings</td>
</tr>
<tr>
<td valign="top" width="244"><a title="http://local.live.com/Manifests/AerialContentManifest.xml" href="http://local.live.com/Manifests/AT.xml">http://local.live.com/Manifests/AT.xml</a></td>
<td valign="top" width="163">TextureMap</td>
<td valign="top" width="107">Unlabeled aerial</td>
</tr>
<tr>
<td valign="top" width="244"><a title="http://local.live.com/Manifests/HybridContentManifest.xml" href="http://local.live.com/Manifests/HT.xml">http://local.live.com/Manifests/HT.xml</a></td>
<td valign="top" width="163">TextureMap</td>
<td valign="top" width="107">Labeled aerial</td>
</tr>
<tr>
<td valign="top" width="244"><a title="http://local.live.com/Manifests/RoadContentManifest.xml" href="http://local.live.com/Manifests/RT.xml">http://local.live.com/Manifests/RT.xml</a></td>
<td valign="top" width="163">TextureMap</td>
<td valign="top" width="107">Roads only</td>
</tr>
</tbody>
</table>
<p>For the best display, add the <b>ElevationMap</b>, <b>Model</b> and <b>Aerial</b>
<b>TextureMap</b> layers as shown:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// set various data sources, here for elevation data, terrain data, and model data.</span></pre>
<pre>_globeControl.Host.DataSources.Add(<span>new</span> DataSourceLayerData(<span>&quot;Elevation&quot;</span>, <span>&quot;Elevation&quot;</span>, <span>@&quot;http://maps.live.com//Manifests/HD.xml&quot;</span>, DataSourceUsage.ElevationMap));</pre>
<pre>_globeControl.Host.DataSources.Add(<span>new</span> DataSourceLayerData(<span>&quot;Texture&quot;</span>, <span>&quot;Texture&quot;</span>, <span>@&quot;http://maps.live.com//Manifests/AT.xml&quot;</span>, DataSourceUsage.TextureMap));</pre>
<pre>_globeControl.Host.DataSources.Add(<span>new</span> DataSourceLayerData(<span>&quot;Models&quot;</span>, <span>&quot;Models&quot;</span>, <span>@&quot;http://maps.live.com//Manifests/MO.xml&quot;</span>, DataSourceUsage.Model));</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' set various data sources, here for elevation data, terrain data, and model data.</span></pre>
<pre>_globeControl.Host.DataSources.Add(<span>New</span> DataSourceLayerData(<span>&quot;Elevation&quot;</span>, <span>&quot;Elevation&quot;</span>, <span>&quot;http://maps.live.com//Manifests/HD.xml&quot;</span>, DataSourceUsage.ElevationMap))</pre>
<pre>_globeControl.Host.DataSources.Add(<span>New</span> DataSourceLayerData(<span>&quot;Texture&quot;</span>, <span>&quot;Texture&quot;</span>, <span>&quot;http://maps.live.com//Manifests/AT.xml&quot;</span>, DataSourceUsage.TextureMap))</pre>
<pre>_globeControl.Host.DataSources.Add(<span>New</span> DataSourceLayerData(<span>&quot;Models&quot;</span>, <span>&quot;Models&quot;</span>, <span>&quot;http://maps.live.com//Manifests/MO.xml&quot;</span>, DataSourceUsage.Model))</pre>
</div>
</div>
<p>By passing the URL of the content manifest, a name for the layer, and what the manifest represents, a new
<b>DataSource</b> is created, which is in turn used to create a <b>DataSourceLayerData</b> object which is then given to the VE3D control to consume.&nbsp; This should be done in the
<strong>FirstFrameRendered</strong> event handler as well.</p>
<p>We also need to setup VE3D to turn off any UI elements, turn on the atmosphere effects, and ensure we have a full unobstructed view. Again, in the
<strong>FirstFrameRendered</strong> event handler, we can use the following code to achieve this:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// turn on the nice atmosphere</span></pre>
<pre>_globeControl.Host.WorldEngine.Environment.AtmosphereDisplay = Microsoft.MapPoint.Rendering3D.Atmospherics.EnvironmentManager.AtmosphereStyle.Scattering;</pre>
<pre>&nbsp;</pre>
<pre><span>// default to all off</span></pre>
<pre>_globeControl.Host.WorldEngine.Display3DCursor = <span>false</span>;</pre>
<pre>_globeControl.Host.WorldEngine.SetWindowsCursor(<span>null</span>);</pre>
<pre>_globeControl.Host.WorldEngine.ShowNavigationControl = <span>false</span>;</pre>
<pre>_globeControl.Host.WorldEngine.ShowCursorLocationInformation = <span>false</span>;</pre>
<pre>_globeControl.Host.WorldEngine.ShowScale = <span>false</span>;</pre>
<pre>_globeControl.Host.WorldEngine.ShowUI = <span>false</span>;</pre>
<pre>_globeControl.Host.WorldEngine.Environment.SunPosition = <span>null</span>;</pre>
<pre>_globeControl.Host.WorldEngine.Environment.LocalWeatherEnabled = <span>true</span>;</pre>
<pre>_globeControl.Host.WorldEngine.BaseCopyrightText = <span>&quot; &quot;</span>; // workaround <span>for</span> a display issue</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' turn on the nice atmosphere</span></pre>
<pre>_globeControl.Host.WorldEngine.Environment.AtmosphereDisplay = Microsoft.MapPoint.Rendering3D.Atmospherics.EnvironmentManager.AtmosphereStyle.Scattering</pre>
<pre>&nbsp;</pre>
<pre><span>' default to all off</span></pre>
<pre>_globeControl.Host.WorldEngine.Display3DCursor = <span>False</span></pre>
<pre>_globeControl.Host.WorldEngine.SetWindowsCursor(<span>Nothing</span>)</pre>
<pre>_globeControl.Host.WorldEngine.ShowNavigationControl = <span>False</span></pre>
<pre>_globeControl.Host.WorldEngine.ShowCursorLocationInformation = <span>False</span></pre>
<pre>_globeControl.Host.WorldEngine.ShowScale = <span>False</span></pre>
<pre>_globeControl.Host.WorldEngine.ShowUI = <span>False</span></pre>
<pre>_globeControl.Host.WorldEngine.Environment.SunPosition = <span>Nothing</span></pre>
<pre>_globeControl.Host.WorldEngine.Environment.LocalWeatherEnabled = <span>True</span></pre>
<pre>_globeControl.Host.WorldEngine.BaseCopyrightText = <span>&quot; &quot;</span> ' workaround <span>for</span> a display issue</pre>
</div>
</div>
<p>If you were to run the application at this point, you would see a fully functioning Virtual Earth 3D control with proper data and navigation.</p>
<h4>Control Scheme and Bindings</h4>
<p>The user will control VE3D with the Wiimote by holding the nunchuk in the left hand, which will move the user forward/back/left/right using the joystick.&nbsp; The C and Z buttons on the front of the nunchuk will be used to raise and lower the altitude of the
 camera.&nbsp; The Wiimote, held in the right hand, will be used to toggle various things on or off and interact with menus.&nbsp; The user will also stand on the Balance Board which will use their center of gravity to turn them in the environment.</p>
<p>VE3D bindings allow you to change or create new control schemes for VE3D by placing an XML file in a specific directory as follows:</p>
<ul>
<li><strong>Vista:</strong> C:\Users\&lt;username&gt;\AppData\LocalLow\Microsoft\Virtual Earth 3D
</li><li><strong>XP:</strong> C:\Documents and Settings\&lt;username&gt;\Local Settings\Microsoft\Virtual Earth 3D
</li></ul>
<p>In this directory you will find a <b>Bindings.xml </b>file.&nbsp; This XML schema defines the default keyboard, mouse, Gamepad and other input device properties.&nbsp; Open the file to see the schema used to define events and parameters.</p>
<p>By default, VE3D will load any file named Bindings*.xml from this directory.&nbsp; For the Wiimote control scheme, create a new file named
<b>BindingsWiiEarthVR.xml</b> in this directory.&nbsp; Set the contents of the file to the following:</p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>&lt;?</span><span>xml</span> <span>version</span><span>=&quot;1.0&quot;</span> <span>encoding</span><span>=&quot;utf-8&quot;</span> ?<span>&gt;</span></pre>
<pre><span>&lt;</span><span>Bindings</span><span>&gt;</span></pre>
<pre>    <span>&lt;</span><span>BindingSet</span> <span>Name</span><span>=&quot;WiiEarthVRBindings&quot;</span> <span>AutoUse</span><span>=&quot;True&quot;</span> <span>Cursor</span><span>=&quot;Drag&quot;</span><span>&gt;</span></pre>
<pre>        <span>&lt;!-- Nunchuk joystick --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.NunchukX&quot;</span> <span>Factor</span><span>=&quot;0.5&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Strafe&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.NunchukY&quot;</span> <span>Factor</span><span>=&quot;1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Move&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;!-- Nunchuk buttons --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.NunchukC&quot;</span> <span>Factor</span><span>=&quot;0.20&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Ascend&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.NunchukZ&quot;</span> <span>Factor</span><span>=&quot;-0.20&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Ascend&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;!-- Balance Board --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.BalanceBoardX&quot;</span> <span>Factor</span><span>=&quot;-0.0009&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Turn&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.BalanceBoardY&quot;</span> <span>Factor</span><span>=&quot;0.0009&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Ascend&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;!-- Wiimote buttons --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Home&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ResetOnCenter&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.A&quot;</span> <span>Factor</span><span>=&quot;1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Locations, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Left&quot;</span> <span>Factor</span><span>=&quot;-1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Locations, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Up&quot;</span> <span>Factor</span><span>=&quot;-1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;LocationsMove, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Down&quot;</span> <span>Factor</span><span>=&quot;1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;LocationsMove, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;!-- FPS-style keyboard controls in case we don't have a nunchuk --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.W&quot;</span> <span>Factor</span><span>=&quot;22&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Move&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.S&quot;</span> <span>Factor</span><span>=&quot;-22&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Move&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.D&quot;</span> <span>Factor</span><span>=&quot;22&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Strafe&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.A&quot;</span> <span>Factor</span><span>=&quot;-22&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Strafe&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.Space&quot;</span> <span>Factor</span><span>=&quot;20&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Ascend&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.C&quot;</span> <span>Factor</span><span>=&quot;-20&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;Ascend&quot;</span> <span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;!-- Other keys --&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.F1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;VR920SetZero, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.F2&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;BalanceBoardSetZero, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.F3&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleVR920Stereo, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Minus&quot;</span> <span>Factor</span><span>=&quot;-0.1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;VR920SetEyeDistance, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Plus&quot;</span> <span>Factor</span><span>=&quot;0.1&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;VR920SetEyeDistance, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.One&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleBalanceBoard, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.Two&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleVR920, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.B&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleBalanceBoard, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.V&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleVR920, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>        <span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Key.F&quot;</span><span>&gt;&lt;</span><span>Action</span> <span>Name</span><span>=&quot;FullScreen, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<pre>    <span>&lt;/</span><span>BindingSet</span><span>&gt;</span></pre>
<pre><span>&lt;/</span><span>Bindings</span><span>&gt;</span></pre>
</div>
</div>
<p>The <b>&lt;BindingSet&gt;</b> tags wrap groups of control bindings.&nbsp; It requires a <b>
Name</b> and optionally a <b>Cursor</b>.&nbsp; If the binding set is to be used automatically, as it would be in most cases, set the
<b>AutoUse</b> parameter to <b>True</b>.&nbsp; Inside of that are <b>&lt;Bind&gt;</b> tags.&nbsp; The tag requires the
<strong>Event </strong>parameter and optionally the <b>Factor</b> parameter.&nbsp; The
<b>Event </b>parameter will be used to match the binding to its handler which will be written later.&nbsp; The syntax is &lt;Handler Name&gt;.&lt;Event Name&gt;.&nbsp; The
<b>Factor</b> parameter is optional and can be used to scale the data value up or down to increase or decrease sensitivity of the input method.&nbsp; The
<b>Action</b> tag inside the <strong>Bind</strong> tag is used to map the specific binding to a particular method.&nbsp; Once the handler is written, these will make more sense.</p>
<p>The bindings above create the control scheme described above:&nbsp; NunchukX/Y describe what happens when the analog joystick is moved, NunchukC/Z describe what happens with the C/Z buttons are pressed, and so on.</p>
<p>The bindings also allow for several variations.&nbsp; Bindings are defined for both the IR position (<b>IRX</b>,
<b>IRY</b>) and accelerometer values (<b>AX</b>, <b>AY</b>).&nbsp; If an IR sensor bar is not available, the accelerometer values of the Wiimote can be used instead.&nbsp; Additionally, keyboard bindings are created in the style of a first person shooter using WASD.&nbsp;
 These can be used if a Nunchuk is not available.</p>
<p>Note that some bindings append two <b>Event</b>s together with a &#43; sign.&nbsp; This allows for button combinations.&nbsp; In this case, for the accelerometer and/or IR sensor, we only want to register the action if a button is pressed down.&nbsp; So, those events which
 require the button to be held down contain <b>Wiimote.B&#43;</b> and the event it is combined with.</p>
<p>For those events which require a custom action that will be written separately and not part of the VE3D control, the
<b>Action</b> parameter must contain the action name, followed by a comma, and then the full assembly name:</p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>&lt;</span><span>Bind</span> <span>Event</span><span>=&quot;Wiimote.One&quot;</span><span>&gt;</span><br>    <span>&lt;</span><span>Action</span> <span>Name</span><span>=&quot;ToggleBalanceBoard, WiiEarthVR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span>/&gt;</span><br><span>&lt;/</span><span>Bind</span><span>&gt;</span></pre>
<br>
</div>
<div id="codeSnippetWrapper"><br>
You can change any of these button bindings simply by changing this XML file and deploying to the directory above.</div>
<h4>Event Source</h4>
<p>An <b>EventSource</b> is needed which will grab data from the Wiimote and pass it along to VE3D as defined by the bindings file above.&nbsp; Create a new class named
<b>WiimoteEventSource</b> which derives from <b>Microsoft.MapPoint.Binding.EventSource</b>&nbsp; as follows:</p>
<p>Next, add an enumeration named <b>WiimoteEvent</b> (the name isn't important) which contains all of the
<b>Name</b> items from the bindings XML file above.&nbsp; It should look like this:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>// all events handled by this event source from XML file</span><br><span>public</span> <span>enum</span> WiimoteEvent<br>{<br>    IRX,        <span>// IR X position</span><br>    IRY,        <span>// IR Y position</span><br>    NunchukX,    <span>// Nunchuk joystick X position</span><br>    NunchukY,    <span>// Nunchuk joystick Y position</span><br>    NunchukC,    <span>// Nunchuk C button</span><br>    NunchukZ,    <span>// Nunchuk Z button</span><br>    AX,            <span>// Wiimote accelerometer X</span><br>    AY,            <span>// Wiimote accelerometer Y</span><br>    Up,            <span>// Dpad up</span><br>    Down,        <span>// Dpad down</span><br>    Left,        <span>// Dpad left</span><br>    Right,        <span>// Dpad right</span><br>    A,            <span>// A button</span><br>    B,            <span>// B button</span><br>    Minus,        <span>// Minus button</span><br>    Home,        <span>// Wiimote Home button</span><br>    Plus,        <span>// Plus button</span><br>    One,        <span>// Wiimote One button</span><br>    Two,        <span>// Wiimote Two button</span><br>    BalanceBoardX,    <span>// Balance Board COG X</span><br>    BalanceBoardY    <span>// Balance Board COG Y</span><br>}</pre>
<br>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>' all events handled by this event source from XML file</span><br><span>Public</span> <span>Enum</span> WiimoteEvent<br>    IRX <span>' IR X position</span><br>    IRY <span>' IR Y position</span><br>    NunchukX <span>' Nunchuk joystick X position</span><br>    NunchukY <span>' Nunchuk joystick Y position</span><br>    NunchukC <span>' Nunchuk C button</span><br>    NunchukZ <span>' Nunchuk Z button</span><br>    AX <span>' Wiimote accelerometer X</span><br>    AY <span>' Wiimote accelerometer Y</span><br>    Up <span>' Dpad up</span><br>    Down <span>' Dpad down</span><br>    Left <span>' Dpad left</span><br>    Right <span>' Dpad right</span><br>    A <span>' A button</span><br>    B <span>' B button</span><br>    Minus <span>' Minus button</span><br>    Home <span>' Wiimote Home button</span><br>    Plus <span>' Plus button</span><br>    One <span>' Wiimote One button</span><br>    Two <span>' Wiimote Two button</span><br>    BalanceBoardX <span>' Balance Board COG X</span><br>    BalanceBoardY <span>' Balance Board COG Y</span><br><span>End</span> Enum</pre>
<br>
</div>
<p>Next, several methods from the <b>EventSource</b> object need to be overridden:&nbsp;
<b>GetEventData</b>, <b>IsModifier</b>, <b>CanModify</b>, <b>TryGetEventId</b>, <b>
TryGetEventName</b>, <b>Name</b>.&nbsp; The methods do the following:</p>
<table border="1" cellspacing="0" cellpadding="2" width="400">
<tbody>
<tr>
<td valign="top" width="200">
<p align="center"><b>Method/Property</b></p>
</td>
<td valign="top" width="200">
<p align="center"><b>Description</b></p>
</td>
</tr>
<tr>
<td valign="top" width="200">GetEventData</td>
<td valign="top" width="200">Unsure at the moment...does not need to be implemented?</td>
</tr>
<tr>
<td valign="top" width="200">IsModifier</td>
<td valign="top" width="200">Returns a boolean stating whether the passed in event ID is a modifier (such as the Wiimote.B event above)</td>
</tr>
<tr>
<td valign="top" width="200">CanModify</td>
<td valign="top" width="200">Returns a boolean stating whether the current event is allowed as a modifier</td>
</tr>
<tr>
<td valign="top" width="200">TryGetEventID</td>
<td valign="top" width="200">Maps a string event name from the bindings file to the integer value in the enumeration above</td>
</tr>
<tr>
<td valign="top" width="200">TryGetEventName</td>
<td valign="top" width="200">Maps an integer event ID to the string name in the enumeration above</td>
</tr>
<tr>
<td valign="top" width="200">Name (property)</td>
<td valign="top" width="200">Returns the name of the handler which must match the name in the XML file above (Wiimote in this case)</td>
</tr>
</tbody>
</table>
<p>The code for these methods is presented below:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// return out value of the passed enum</span></pre>
<pre><span>public</span> <span>override</span> <span>bool</span> TryGetEventId(<span>string</span> eventName, <span>out</span> <span>int</span> eventId)</pre>
<pre>{</pre>
<pre>    eventId = (<span>int</span>)Enum.Parse(<span>typeof</span>(WiimoteEvent), eventName);</pre>
<pre>    <span>return</span> <span>true</span>;</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// return out the string name of the passed in enum value</span></pre>
<pre><span>public</span> <span>override</span> <span>bool</span> TryGetEventName(<span>int</span> eventId, <span>out</span> <span>string</span> eventName)</pre>
<pre>{</pre>
<pre>    eventName = ((WiimoteEvent)eventId).ToString();</pre>
<pre>    <span>return</span> <span>true</span>;</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// unknown</span></pre>
<pre><span>public</span> <span>override</span> EventData GetEventData(<span>int</span> eventId, EventActivateState state)</pre>
<pre>{</pre>
<pre>    <span>throw</span> <span>new</span> NotImplementedException();</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// can the event be used as a modifier?</span></pre>
<pre><span>public</span> <span>override</span> <span>bool</span> IsModifier(<span>int</span> eventId)</pre>
<pre>{</pre>
<pre>    <span>// yes to all for now</span></pre>
<pre>    <span>return</span> <span>true</span>;</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// can the supplied event be used as a modifier?</span></pre>
<pre><span>public</span> <span>override</span> <span>bool</span> CanModify(<span>int</span> eventId, EventKey other)</pre>
<pre>{</pre>
<pre>    <span>// only if it's from us</span></pre>
<pre>    <span>return</span> (other.Source == <span>this</span>);</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// this must match the Source name in the bindings XML file</span></pre>
<pre><span>public</span> <span>override</span> <span>string</span> Name</pre>
<pre>{</pre>
<pre>    get { <span>return</span> <span>&quot;Wiimote&quot;</span>; }</pre>
<pre>}</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' return out value of the passed enum</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>Function</span> TryGetEventId(<span>ByVal</span> eventName <span>As</span> <span>String</span>, &lt;System.Runtime.InteropServices.Out()&gt; <span>ByRef</span> eventId <span>As</span> <span>Integer</span>) <span>As</span> <span>Boolean</span></pre>
<pre>    eventId = <span>CInt</span>(Fix(System.<span>Enum</span>.Parse(<span>GetType</span>(WiimoteEvent), eventName)))</pre>
<pre>    <span>Return</span> <span>True</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>' return out the string name of the passed in enum value</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>Function</span> TryGetEventName(<span>ByVal</span> eventId <span>As</span> <span>Integer</span>, &lt;System.Runtime.InteropServices.Out()&gt; <span>ByRef</span> eventName <span>As</span> <span>String</span>) <span>As</span> <span>Boolean</span></pre>
<pre>    eventName = (<span>CType</span>(eventId, WiimoteEvent)).ToString()</pre>
<pre>    <span>Return</span> <span>True</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>' unknown</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>Function</span> GetEventData(<span>ByVal</span> eventId <span>As</span> <span>Integer</span>, <span>ByVal</span> state <span>As</span> EventActivateState) <span>As</span> EventData</pre>
<pre>    <span>Throw</span> <span>New</span> NotImplementedException()</pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>' can the event be used as a modifier?</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>Function</span> IsModifier(<span>ByVal</span> eventId <span>As</span> <span>Integer</span>) <span>As</span> <span>Boolean</span></pre>
<pre>    <span>' yes to all for now</span></pre>
<pre>    <span>Return</span> <span>True</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>' can the supplied event be used as a modifier?</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>Function</span> CanModify(<span>ByVal</span> eventId <span>As</span> <span>Integer</span>, <span>ByVal</span> other <span>As</span> EventKey) <span>As</span> <span>Boolean</span></pre>
<pre>    <span>' only if it's from us</span></pre>
<pre>    <span>Return</span> (other.Source <span>Is</span> <span>Me</span>)</pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>' this must match the Source name in the bindings XML file</span></pre>
<pre><span>Public</span> <span>Overrides</span> <span>ReadOnly</span> <span>Property</span> Name() <span>As</span> <span>String</span></pre>
<pre>    <span>Get</span></pre>
<pre>        <span>Return</span> <span>&quot;Wiimote&quot;</span></pre>
<pre>    <span>End</span> <span>Get</span></pre>
<pre><span>End</span> <span>Property</span></pre>
</div>
</div>
<p>With that in place, the constructor can be implemented which will call the base constructor and connect to the Wiimote.&nbsp; It is assumed you read the Wiimote article above and know how the library works.</p>
<p>The constructor must take one argument passed from the main from:&nbsp; an instance of the
<b>GlobeControl</b>'s <b>ActionSystem</b>.&nbsp; This just gets passed directly to the parent object's constructor untouched.&nbsp; The constructor code looks like the following:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>public</span> WiimoteEventSource(ActionSystem actionSystem) : <span>base</span>(actionSystem)<br>{<br>    <span>// get all connected Wiimotes</span><br>    WiimoteCollection wc = <span>new</span> WiimoteCollection();<br>    wc.FindAllWiimotes();<br><br>    <span>// setup wiimotes and event handlers</span><br>    <span>foreach</span>(Wiimote wm <span>in</span> wc)<br>    {<br>        wm.WiimoteChanged &#43;= <span>new</span> EventHandler&lt;WiimoteChangedEventArgs&gt;(OnWiimoteChanged);<br>        wm.WiimoteExtensionChanged &#43;= <span>new</span> EventHandler&lt;WiimoteExtensionChangedEventArgs&gt;(OnWiimoteExtensionChanged);<br>        wm.Connect();<br><br>        <span>// if we don't have an extension, set the report type to IR and accel's only</span><br>        <span>if</span>(!wm.WiimoteState.Extension &amp;&amp; wm.WiimoteState.ExtensionType != ExtensionType.BalanceBoard)<br>            wm.SetReportType(InputReport.IRAccel, <span>true</span>);<br><br>        <span>if</span>(wm.WiimoteState.ExtensionType == ExtensionType.BalanceBoard)<br>            _bb = wm;<br>        <span>else</span><br>            _wm = wm;<br><br>        <span>// turn off all LEDs</span><br>        wm.SetLEDs(0x00);<br>    }<br>}<br></pre>
<br>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>Public</span> <span>Sub</span> <span>New</span>(<span>ByVal</span> actionSystem <span>As</span> ActionSystem)<br>    <span>MyBase</span>.<span>New</span>(actionSystem)<br><br>    <span>' get all connected Wiimotes</span><br>    <span>Dim</span> wc <span>As</span> <span>New</span> WiimoteCollection()<br>    wc.FindAllWiimotes()<br><br>    <span>' setup wiimotes and event handlers</span><br>    <span>For</span> <span>Each</span> wm <span>As</span> Wiimote <span>In</span> wc<br>        <span>AddHandler</span> wm.WiimoteChanged, <span>AddressOf</span> OnWiimoteChanged<br>        <span>AddHandler</span> wm.WiimoteExtensionChanged, <span>AddressOf</span> OnWiimoteExtensionChanged<br>        wm.Connect()<br><br>        <span>' if we don't have an extension, set the report type to IR and accel's only</span><br>        <span>If</span> (<span>Not</span> wm.WiimoteState.Extension) <span>AndAlso</span> wm.WiimoteState.ExtensionType &lt;&gt; ExtensionType.BalanceBoard <span>Then</span><br>            wm.SetReportType(InputReport.IRAccel, <span>True</span>)<br>        <span>End</span> <span>If</span><br><br>        <span>If</span> wm.WiimoteState.ExtensionType = ExtensionType.BalanceBoard <span>Then</span><br>            _bb = wm<br>        <span>Else</span><br>            _wm = wm<br>        <span>End</span> <span>If</span><br><br>        <span>' turn off all LEDs</span><br>        wm.SetLEDs(&amp;H00)<br>    <span>Next</span> wm<br><span>End</span> Sub</pre>
<br>
</div>
<p>The <b>OnWiimoteExtensionChanged</b> method simply sets the report mode for the Wiimote based on whether or not a Nunchuk is inserted as shown:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>private</span> <span>void</span> OnWiimoteExtensionChanged(<span>object</span> sender, WiimoteExtensionChangedEventArgs args)<br>{<br>    <span>if</span>(_wm == <span>null</span>)<br>        <span>return</span>;<br><br>    <span>// if nunchuk inserted, set the report type to return extension data</span><br>    <span>if</span>(args.ExtensionType == ExtensionType.Nunchuk &amp;&amp; args.Inserted)<br>        _wm.SetReportType(InputReport.IRExtensionAccel, <span>true</span>);<br>    <span>else</span> <span>// in all other cases, set it to the default IR and accel's</span><br>        _wm.SetReportType(InputReport.IRAccel, <span>true</span>);<br>}</pre>
<br>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>Private</span> <span>Sub</span> OnWiimoteExtensionChanged(<span>ByVal</span> sender <span>As</span> <span>Object</span>, <span>ByVal</span> args <span>As</span> WiimoteExtensionChangedEventArgs)<br>    <span>If</span> _wm <span>Is</span> <span>Nothing</span> <span>Then</span><br>        <span>Return</span><br>    <span>End</span> <span>If</span><br><br>    <span>' if nunchuk inserted, set the report type to return extension data</span><br>    <span>If</span> args.ExtensionType = ExtensionType.Nunchuk <span>AndAlso</span> args.Inserted <span>Then</span><br>        _wm.SetReportType(InputReport.IRExtensionAccel, <span>True</span>)<br>    <span>Else</span> <span>' in all other cases, set it to the default IR and accel's</span><br>        _wm.SetReportType(InputReport.IRAccel, <span>True</span>)<br>    <span>End</span> <span>If</span><br><span>End</span> Sub</pre>
<br>
</div>
<p>The <b>OnWiimoteChanged</b> event handler is where the Wiimote data is handled and sent off to the VE3D control to reflect the changes.&nbsp; First, we handle the Balance Board.&nbsp; If the Balance Board is the controller reporting data, we take the center of gravity
 values and pass them to VE3D.&nbsp; This code looks at the appropriate values, determines if they are beyond the specified thresholds for the dead zones, and, if they are, activates the event for that value using the
<b>Execute </b>method.&nbsp; <b>Execute </b>is a method in the base <b>EventSource</b> class.&nbsp; This method will activate the event specified from the enumeration (which, remember, is contained in the bindings XML file) with the value associated with that event.&nbsp;
 An <b>EventData</b> object of some type must be created and passed to the <b>Execute</b> method.&nbsp; There are two
<b>EventData</b> types to know about:&nbsp; <b>AxisEventData</b> and <b>ButtonEventData</b>.&nbsp;
<b>AxisEventData</b> should be used when an event is activated that will modify the map position in some way.&nbsp; That is, if the map is being turned, elevation is changing, etc.&nbsp;
<b>ButtonEventData</b> should be used if the event is a simple toggle like pressing a button down and releasing it.</p>
<p>The Balance Board code can be seen below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>if</span>(ws.ExtensionType == ExtensionType.BalanceBoard &amp;&amp; <span>this</span>.BalanceBoardEnabled)</pre>
<pre>{</pre>
<pre>    <span>float</span> x1 = ws.BalanceBoardState.CenterOfGravity.X;</pre>
<pre>    <span>if</span>(x1 &gt; Properties.Settings.Default.BBDeadX || x1 &lt; -Properties.Settings.Default.BBDeadX)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.BalanceBoardX), x1 - _zero.X));</pre>
<pre>    <span>float</span> y1 = ws.BalanceBoardState.CenterOfGravity.Y;</pre>
<pre>    <span>if</span>(y1 &gt; Properties.Settings.Default.BBDeadY || y1 &lt; -Properties.Settings.Default.BBDeadY)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.BalanceBoardY), y1 - _zero.Y));</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>If</span> ws.ExtensionType = ExtensionType.BalanceBoard <span>AndAlso</span> <span>Me</span>.BalanceBoardEnabled <span>Then</span></pre>
<pre>    <span>Dim</span> x1 <span>As</span> <span>Single</span> = ws.BalanceBoardState.CenterOfGravity.X</pre>
<pre>    <span>If</span> x1 &gt; My.Settings.<span>Default</span>.BBDeadX <span>OrElse</span> x1 &lt; -My.Settings.<span>Default</span>.BBDeadX <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.BalanceBoardX))), x1 - _zero.X))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>    <span>Dim</span> y1 <span>As</span> <span>Single</span> = ws.BalanceBoardState.CenterOfGravity.Y</pre>
<pre>    <span>If</span> y1 &gt; My.Settings.<span>Default</span>.BBDeadY <span>OrElse</span> y1 &lt; -My.Settings.<span>Default</span>.BBDeadY <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.BalanceBoardY))), y1 - _zero.Y))</pre>
<pre>    <span>End</span> If</pre>
</div>
</div>
<p>Next, let's handle the IR and accelerometer data.&nbsp; The IR midpoint of the X and Y axes will be used from the
<b>WiimoteState</b> object to activate the <b>IRX</b> and <b>IRY</b> events we defined above in the bindings XML file.&nbsp; The accelerometer X and Y values will be used to activate the
<b>AX </b>and <b>AY</b> events.</p>
<p>This snippet assumes that there is a boolean property named <b>UseIR</b> created in the project to determine whether IR or motion values are used.&nbsp; Additionally, it assumes there are property settings created which contain values for the X/Y &quot;dead zones&quot;
 for the IR and accelerometers.&nbsp; These dead zones are used as a way to only activate the event when the values are pushed beyond the thresholds.&nbsp; This allows there to be a margin where the user's hand will not be read as movement, allowing the user to not have
 to worry about keeping a steady hand.</p>
<p>The application linked above uses the following default values for dead zones:</p>
<ul>
<li>NunchukDeadX/Y -&gt; 0.025 </li><li>WiimoteDeadX/Y -&gt; 0.15 </li><li>IRDeadX/Y -&gt; 0.1 </li><li>BBDeadX/Y –&gt; 3 </li></ul>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// if we're using the IR</span></pre>
<pre><span>if</span>(Properties.Settings.Default.UseIR)</pre>
<pre>{</pre>
<pre>    <span>// and both LEDs are found</span></pre>
<pre>    <span>if</span>(ws.IRState.Found1 &amp;&amp; ws.IRState.Found2)</pre>
<pre>    {</pre>
<pre>        <span>// normalize the midpoints to -0.5 to 0.5 (from 0 to 1.0)</span></pre>
<pre>        <span>float</span> x = ws.IRState.MidX - 0.5f;</pre>
<pre>        <span>float</span> y = ws.IRState.MidY - 0.5f;</pre>
<pre>&nbsp;</pre>
<pre>        <span>// if we're beyond the thresholds, activate the events</span></pre>
<pre>        <span>if</span>(x &gt; Properties.Settings.Default.IRDeadX || x &lt; -Properties.Settings.Default.IRDeadX)</pre>
<pre>            <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.IRX), x));</pre>
<pre>        <span>if</span>(y &gt; Properties.Settings.Default.IRDeadY || y &lt; -Properties.Settings.Default.IRDeadY)</pre>
<pre>            <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.IRY), y));</pre>
<pre>&nbsp;</pre>
<pre>        <span>// save the last IR settings...these get used if we go beyond the range of the IRs.</span></pre>
<pre>        <span>// in that case, the last used positions will be used until the Wiimote comes back in range</span></pre>
<pre>        <span>this</span>._lastIRX = x;</pre>
<pre>        <span>this</span>._lastIRY = y;</pre>
<pre>    }</pre>
<pre>    <span>else</span> <span>// one or both LEDs aren't seen</span></pre>
<pre>    {</pre>
<pre>        <span>// activate events based on the last known positions</span></pre>
<pre>        <span>if</span>(<span>this</span>._lastIRX &gt; Properties.Settings.Default.IRDeadX || <span>this</span>._lastIRX &lt; -Properties.Settings.Default.IRDeadX)</pre>
<pre>            <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.IRX), <span>this</span>._lastIRX));</pre>
<pre>        <span>if</span>(<span>this</span>._lastIRY &gt; Properties.Settings.Default.IRDeadY || <span>this</span>._lastIRY &lt; -Properties.Settings.Default.IRDeadY)</pre>
<pre>            <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.IRY), <span>this</span>._lastIRY));</pre>
<pre>    }</pre>
<pre>}</pre>
<pre><span>else</span> <span>// we're using motion controls</span></pre>
<pre>{</pre>
<pre>    <span>// activate the events based on the accelerometer values</span></pre>
<pre>    <span>if</span>(ws.AccelState.X &gt; Properties.Settings.Default.WiimoteDeadX || ws.AccelState.X &lt; -Properties.Settings.Default.WiimoteDeadX)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.AX), ws.AccelState.X));</pre>
<pre>    <span>if</span>(ws.AccelState.Y &gt; Properties.Settings.Default.WiimoteDeadY || ws.AccelState.Y &lt; -Properties.Settings.Default.WiimoteDeadY)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.AY), ws.AccelState.Y));</pre>
<pre>}</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' if we're using the IR</span></pre>
<pre><span>If</span> My.Settings.<span>Default</span>.UseIR <span>Then</span></pre>
<pre>    <span>' and both LEDs are found</span></pre>
<pre>    <span>If</span> ws.IRState.Found1 <span>AndAlso</span> ws.IRState.Found2 <span>Then</span></pre>
<pre>        <span>' normalize the midpoints to -0.5 to 0.5 (from 0 to 1.0)</span></pre>
<pre>        <span>Dim</span> x <span>As</span> <span>Single</span> = ws.IRState.MidX - 0.5f</pre>
<pre>        <span>Dim</span> y <span>As</span> <span>Single</span> = ws.IRState.MidY - 0.5f</pre>
<pre>&nbsp;</pre>
<pre>        <span>' if we're beyond the thresholds, activate the events</span></pre>
<pre>        <span>If</span> x &gt; My.Settings.<span>Default</span>.IRDeadX <span>OrElse</span> x &lt; -My.Settings.<span>Default</span>.IRDeadX <span>Then</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.IRX))), x))</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>        <span>If</span> y &gt; My.Settings.<span>Default</span>.IRDeadY <span>OrElse</span> y &lt; -My.Settings.<span>Default</span>.IRDeadY <span>Then</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.IRY))), y))</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>' save the last IR settings...these get used if we go beyond the range of the IRs.</span></pre>
<pre>        <span>' in that case, the last used positions will be used until the Wiimote comes back in range</span></pre>
<pre>        <span>Me</span>._lastIRX = x</pre>
<pre>        <span>Me</span>._lastIRY = y</pre>
<pre>    <span>Else</span> <span>' one or both LEDs aren't seen</span></pre>
<pre>        <span>' activate events based on the last known positions</span></pre>
<pre>        <span>If</span> <span>Me</span>._lastIRX &gt; My.Settings.<span>Default</span>.IRDeadX <span>OrElse</span> <span>Me</span>._lastIRX &lt; -My.Settings.<span>Default</span>.IRDeadX <span>Then</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.IRX))), <span>Me</span>._lastIRX))</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>        <span>If</span> <span>Me</span>._lastIRY &gt; My.Settings.<span>Default</span>.IRDeadY <span>OrElse</span> <span>Me</span>._lastIRY &lt; -My.Settings.<span>Default</span>.IRDeadY <span>Then</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.IRY))), <span>Me</span>._lastIRY))</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>Else</span> <span>' we're using motion controls</span></pre>
<pre>    <span>' activate the events based on the accelerometer values</span></pre>
<pre>    <span>If</span> ws.AccelState.X &gt; My.Settings.<span>Default</span>.WiimoteDeadX <span>OrElse</span> ws.AccelState.X &lt; -My.Settings.<span>Default</span>.WiimoteDeadX <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.AX))), ws.AccelState.X))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>    <span>If</span> ws.AccelState.Y &gt; My.Settings.<span>Default</span>.WiimoteDeadY <span>OrElse</span> ws.AccelState.Y &lt; -My.Settings.<span>Default</span>.WiimoteDeadY <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.AY))), ws.AccelState.Y))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>End</span> If</pre>
</div>
</div>
<p>Next, the nunchuk values need to be read and the associated events activated.&nbsp; This is done as follows:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// if the nunchuk is connected</span></pre>
<pre><span>if</span>(ws.Extension &amp;&amp; ws.ExtensionType == ExtensionType.Nunchuk)</pre>
<pre>{</pre>
<pre>    <span>// activate the nunchuk-based events</span></pre>
<pre>    <span>if</span>(ws.NunchukState.X &gt; Properties.Settings.Default.NunchukDeadX || ws.NunchukState.X &lt; -Properties.Settings.Default.NunchukDeadX)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.NunchukX), ws.NunchukState.X));</pre>
<pre>    <span>if</span>(ws.NunchukState.Y &gt; Properties.Settings.Default.NunchukDeadY || ws.NunchukState.Y &lt; -Properties.Settings.Default.NunchukDeadY)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.NunchukY), ws.NunchukState.Y));</pre>
<pre>    <span>if</span>(ws.NunchukState.C)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.NunchukC), 1.0f));</pre>
<pre>    <span>if</span>(ws.NunchukState.Z)</pre>
<pre>        <span>this</span>.Execute(<span>new</span> AxisEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)WiimoteEvent.NunchukZ), 1.0f));</pre>
<pre>}</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' if the nunchuk is connected</span></pre>
<pre><span>If</span> ws.Extension <span>AndAlso</span> ws.ExtensionType = ExtensionType.Nunchuk <span>Then</span></pre>
<pre>    <span>' activate the nunchuk-based events</span></pre>
<pre>    <span>If</span> ws.NunchukState.X &gt; My.Settings.<span>Default</span>.NunchukDeadX <span>OrElse</span> ws.NunchukState.X &lt; -My.Settings.<span>Default</span>.NunchukDeadX <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.NunchukX))), ws.NunchukState.X))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>    <span>If</span> ws.NunchukState.Y &gt; My.Settings.<span>Default</span>.NunchukDeadY <span>OrElse</span> ws.NunchukState.Y &lt; -My.Settings.<span>Default</span>.NunchukDeadY <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.NunchukY))), ws.NunchukState.Y))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>    <span>If</span> ws.NunchukState.C <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.NunchukC))), 1.0f))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>    <span>If</span> ws.NunchukState.Z <span>Then</span></pre>
<pre>        <span>Me</span>.Execute(<span>New</span> AxisEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(WiimoteEvent.NunchukZ))), 1.0f))</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>End</span> If</pre>
</div>
</div>
<p>Finally, the button events need to be activated.&nbsp; A helper method which will check the current button state will be used for determining which button of all the Wiimote buttons is pressed.&nbsp; For those that are, the appropriate event is activated with a call
 to <b>Execute</b>.</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> <span>void</span> HandleButton(WiimoteEvent we, <span>bool</span> buttonState, <span>bool</span> lastButtonState)</pre>
<pre>{</pre>
<pre>    <span>if</span>(buttonState == lastButtonState)</pre>
<pre>        <span>return</span>;</pre>
<pre>    <span>else</span></pre>
<pre>    {</pre>
<pre>        <span>if</span>(buttonState)</pre>
<pre>            <span>this</span>.Execute(<span>new</span> ButtonEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)we), EventActivateState.Activate));</pre>
<pre>        <span>else</span></pre>
<pre>            <span>this</span>.Execute(<span>new</span> ButtonEventData(<span>new</span> EventKey(<span>this</span>, (<span>int</span>)we), EventActivateState.Deactivate));</pre>
<pre>    }</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>// handle all the Wiimote buttons</span></pre>
<pre>HandleButton(WiimoteEvent.Up, ws.ButtonState.Up, _lastBS.Up);</pre>
<pre>HandleButton(WiimoteEvent.Down, ws.ButtonState.Down, _lastBS.Down);</pre>
<pre>HandleButton(WiimoteEvent.Left, ws.ButtonState.Left, _lastBS.Left);</pre>
<pre>HandleButton(WiimoteEvent.Right, ws.ButtonState.Right, _lastBS.Right);</pre>
<pre>HandleButton(WiimoteEvent.A, ws.ButtonState.A, _lastBS.A);</pre>
<pre>HandleButton(WiimoteEvent.B, ws.ButtonState.B, _lastBS.B);</pre>
<pre>HandleButton(WiimoteEvent.Minus, ws.ButtonState.Minus, _lastBS.Minus);</pre>
<pre>HandleButton(WiimoteEvent.Home, ws.ButtonState.Home, _lastBS.Home);</pre>
<pre>HandleButton(WiimoteEvent.Plus, ws.ButtonState.Plus, _lastBS.Plus);</pre>
<pre>HandleButton(WiimoteEvent.One, ws.ButtonState.One, _lastBS.One);</pre>
<pre>HandleButton(WiimoteEvent.Two, ws.ButtonState.Two, _lastBS.Two);</pre>
<pre>&nbsp;</pre>
<pre>...</pre>
<pre>&nbsp;</pre>
<pre><span>// save off the current button state for next time</span></pre>
<pre>_lastBS = ws.ButtonState;</pre>
<pre>_lastNunchuk = ws.NunchukState;</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> <span>Sub</span> HandleButton(<span>ByVal</span> we <span>As</span> WiimoteEvent, <span>ByVal</span> buttonState <span>As</span> <span>Boolean</span>, <span>ByVal</span> lastButtonState <span>As</span> <span>Boolean</span>)</pre>
<pre>    <span>If</span> buttonState = lastButtonState <span>Then</span></pre>
<pre>        <span>Return</span></pre>
<pre>    <span>Else</span></pre>
<pre>        <span>If</span> buttonState <span>Then</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> ButtonEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(we))), EventActivateState.Activate))</pre>
<pre>        <span>Else</span></pre>
<pre>            <span>Me</span>.Execute(<span>New</span> ButtonEventData(<span>New</span> EventKey(<span>Me</span>, <span>CInt</span>(Fix(we))), EventActivateState.Deactivate))</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>End</span> <span>Sub</span></pre>
<pre>&nbsp;</pre>
<pre>...</pre>
<pre>&nbsp;</pre>
<pre><span>' handle all the Wiimote buttons</span></pre>
<pre>HandleButton(WiimoteEvent.Up, ws.ButtonState.Up, _lastBS.Up)</pre>
<pre>HandleButton(WiimoteEvent.Down, ws.ButtonState.Down, _lastBS.Down)</pre>
<pre>HandleButton(WiimoteEvent.Left, ws.ButtonState.Left, _lastBS.Left)</pre>
<pre>HandleButton(WiimoteEvent.Right, ws.ButtonState.Right, _lastBS.Right)</pre>
<pre>HandleButton(WiimoteEvent.A, ws.ButtonState.A, _lastBS.A)</pre>
<pre>HandleButton(WiimoteEvent.B, ws.ButtonState.B, _lastBS.B)</pre>
<pre>HandleButton(WiimoteEvent.Minus, ws.ButtonState.Minus, _lastBS.Minus)</pre>
<pre>HandleButton(WiimoteEvent.Home, ws.ButtonState.Home, _lastBS.Home)</pre>
<pre>HandleButton(WiimoteEvent.Plus, ws.ButtonState.Plus, _lastBS.Plus)</pre>
<pre>HandleButton(WiimoteEvent.One, ws.ButtonState.One, _lastBS.One)</pre>
<pre>HandleButton(WiimoteEvent.Two, ws.ButtonState.Two, _lastBS.Two)</pre>
<pre>&nbsp;</pre>
<pre><span>' save off the current button state for next time</span></pre>
<pre>_lastBS = ws.ButtonState</pre>
<pre>_lastNunchuk = ws.NunchukState</pre>
</div>
</div>
<p>And, the current button values are stored away to check on the next event so button events are only fired once.</p>
<p>Now that the event source object is written, it needs to be hooked up to the <b>
GlobeControl</b> so it can be used.&nbsp; This can be done by creating an instance of the
<b>WiimoteEventSource</b> object, passing in the VE3D's <b>ActionSystem </b>from the
<b>BindingsManager</b> object.&nbsp; Then, the event source instance is passed to the <b>
ActionSystem</b>'s <b>EventSourceManager</b> and registered using the <b>RegisterEventSource</b> method.&nbsp; Event sources should re registered before the control is added to the form.</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>// wiimote events</span></pre>
<pre><span>private</span> WiimoteEventSource _wiimoteEventSource;</pre>
<pre>&nbsp;</pre>
<pre>...</pre>
<pre>&nbsp;</pre>
<pre><span>// create a new instance of the Wiimote event handler</span></pre>
<pre>_wiimoteEventSource = <span>new</span> WiimoteEventSource(<span>this</span>.globeControl.Host.BindingsManager.ActionSystem, <span>this</span>);</pre>
<pre>&nbsp;</pre>
<pre><span>// register it in the event source list</span></pre>
<pre>_globeControl.Host.BindingsManager.ActionSystem.EventSourceManager.RegisterEventSource(<span>this</span>._wiimoteEventSource);</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' wiimote events</span></pre>
<pre><span>Private</span> _wiimoteEventSource <span>As</span> WiimoteEventSource</pre>
<pre>&nbsp;</pre>
<pre>...</pre>
<pre>&nbsp;</pre>
<pre><span>' create a new instance of the Wiimote event handler</span></pre>
<pre>_wiimoteEventSource = <span>New</span> WiimoteEventSource(<span>Me</span>.globeControl.Host.BindingsManager.ActionSystem, <span>Me</span>)</pre>
<pre>&nbsp;</pre>
<pre><span>' register it in the event source list</span></pre>
<pre>_globeControl.Host.BindingsManager.ActionSystem.EventSourceManager.RegisterEventSource(<span>Me</span>._wiimoteEventSource)</pre>
</div>
</div>
<h4>Actions and the BindingManager</h4>
<p>Our binding list contains several action types that are not defined by the default VE3D actions.&nbsp; These actions and their handlers must be registered with the VE3D control.&nbsp; The actions can be registered as follows in the
<strong>FirstFrameRendered</strong> event handler:</p>
<p><b>C#</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>BindingsSource bs = <span>new</span> BindingsSource(<span>base</span>.GetType());</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;Locations&quot;</span>, LocationsHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;LocationsMove&quot;</span>, LocationsMoveHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleVR920&quot;</span>, ToggleVR920Handler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;VR920SetZero&quot;</span>, VR920SetZero);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;BalanceBoardSetZero&quot;</span>, BalanceBoardSetZero);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleBalanceBoard&quot;</span>, ToggleBalanceBoardHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;FullScreen&quot;</span>, FullScreenHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleVR920Stereo&quot;</span>, ToggleVR920Stereo);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;VR920SetEyeDistance&quot;</span>, VR920SetEyeDistance);</pre>
</div>
</div>
<p><b>VB</b></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>BindingsSource bs = <span>new</span> BindingsSource(base.<span>GetType</span>());</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;Locations&quot;</span>, LocationsHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;LocationsMove&quot;</span>, LocationsMoveHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleVR920&quot;</span>, ToggleVR920Handler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;VR920SetZero&quot;</span>, VR920SetZero);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;BalanceBoardSetZero&quot;</span>, BalanceBoardSetZero);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleBalanceBoard&quot;</span>, ToggleBalanceBoardHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;FullScreen&quot;</span>, FullScreenHandler);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;ToggleVR920Stereo&quot;</span>, ToggleVR920Stereo);</pre>
<pre>_globeControl.Host.BindingsManager.RegisterAction(bs, <span>&quot;VR920SetEyeDistance&quot;</span>, VR920SetEyeDistance);</pre>
</div>
</div>
<p>With the actions registered and handlers associated with them, the actual handlers need to be implemented.&nbsp; All event handler methods must be of the following signature:</p>
<p><b>C#</b></p>
<div>
<pre><span>public</span> <span>bool</span> EventHandler(EventData cause)</pre>
</div>
<p><b>VB</b></p>
<div>
<pre><span>Public</span> <span>Function</span> EventHandler(<span>ByVal</span> cause <span>As</span> EventData) <span>As</span> Boolean</pre>
</div>
<p>Let's take a look at the <strong>FullScreen </strong>binding which simply turns the status bar at the bottom of the window on and off:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>private</span> <span>bool</span> FullScreenHandler(EventData eventData)<br>{<br>    <span>if</span>(eventData.Activate)<br>        BeginInvoke(<span>new</span> UIEventHandlerDelegate(FullScreen), eventData);<br>    <span>return</span> <span>true</span>;<br>}</pre>
<br>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet"><span>Private</span> <span>Function</span> FullScreenHandler(<span>ByVal</span> eventData <span>As</span> EventData) <span>As</span> <span>Boolean</span><br>    <span>If</span> eventData.Activate <span>Then</span><br>        BeginInvoke(<span>New</span> UIEventHandlerDelegate(<span>AddressOf</span> FullScreen), eventData)<br>    <span>End</span> <span>If</span><br>    <span>Return</span> <span>True</span><br><span>End</span> <span>Function</span><br><br><span>Private</span> <span>Sub</span> FullScreen(<span>ByVal</span> eventData <span>As</span> EventData)<br>    statusStrip1.Visible = <span>Not</span> statusStrip1.Visible<br><span>End</span> <span>Sub</span><br></pre>
<br>
</div>
<p>Because we are in the VE3D thread when this handler is called, we need to use <strong>
BeginInvoke</strong> to call the real method on the UI thread.</p>
<p>Be sure to check the source code for the full demo linked above for the location handler methods.&nbsp; I omitted them here since it is just more of the same type of code above.</p>
<h3></h3>
<h3></h3>
<h3><b>VR920 Head Tracker</b></h3>
<p>Once the VR920 glasses and driver are installed on your PC, getting the data required from the glasses is quite easy.&nbsp; We are going to create an object named
<strong>VR920Tracker</strong> which will pull data from the device and send it to VE3D.</p>
<p>First we need to setup a few P/Invoke signatures to talk to the glasses as follows:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>[DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError = <span>true</span>, CharSet = CharSet.<span>Auto</span>)]</pre>
<pre><span>private</span> <span>static</span> extern int IWROpenTracker();</pre>
<pre>&nbsp;</pre>
<pre>[DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError = <span>true</span>, CharSet = CharSet.<span>Auto</span>)]</pre>
<pre><span>private</span> <span>static</span> extern void IWRZeroSet();</pre>
<pre>&nbsp;</pre>
<pre>[DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError = <span>true</span>, CharSet = CharSet.<span>Auto</span>)]</pre>
<pre><span>private</span> <span>static</span> extern int IWRGetTracking(out int yaw, out int pitch, out int roll);</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> <span>const</span> int ERROR_SUCCESS  = 0;</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>&lt;DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError := <span>True</span>, CharSet := CharSet.<span>Auto</span>)&gt; _</pre>
<pre><span>Private</span> <span>Shared</span> <span>Function</span> IWROpenTracker() <span>As</span> <span>Integer</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre>&lt;DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError := <span>True</span>, CharSet := CharSet.<span>Auto</span>)&gt; _</pre>
<pre><span>Private</span> <span>Shared</span> <span>Sub</span> IWRZeroSet()</pre>
<pre><span>End</span> <span>Sub</span></pre>
<pre>&nbsp;</pre>
<pre>&lt;DllImport(<span>&quot;IWEARDRV.dll&quot;</span>, SetLastError := <span>True</span>, CharSet := CharSet.<span>Auto</span>)&gt; _</pre>
<pre><span>Private</span> <span>Shared</span> <span>Function</span> IWRGetTracking(&lt;System.Runtime.InteropServices.Out()&gt; <span>ByRef</span> yaw <span>As</span> <span>Integer</span>, &lt;System.Runtime.InteropServices.Out()&gt; <span>ByRef</span> pitch <span>As</span> <span>Integer</span>, &lt;System.Runtime.InteropServices.Out()&gt; <span>ByRef</span> roll <span>As</span> <span>Integer</span>) <span>As</span> <span>Integer</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>private</span> <span>const</span> int ERROR_SUCCESS  = 0;</pre>
</div>
</div>
<p>These 3 methods will allows to open access to the glasses, set its zero position, and get the roll, pitch and yaw information from the sensors.</p>
<p>In the constructor for this object, we will open a handle to the glasses and startup a timer that will poll the glasses at a regular interval to get the sensor data as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> <span>const</span> int TimerPeriod = 1;</pre>
<pre><span>private</span> MainForm _mainForm;</pre>
<pre><span>private</span> Timer _timer;</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> VR920Tracker(MainForm mainForm)</pre>
<pre>{</pre>
<pre>    _mainForm = mainForm;</pre>
<pre>&nbsp;</pre>
<pre>    int openResult = IWROpenTracker();</pre>
<pre>    <span>if</span>(openResult != ERROR_SUCCESS)</pre>
<pre>        <span>throw</span> <span>new</span> ApplicationException(<span>&quot;Could not connect to VR920: &quot;</span> &#43; openResult);</pre>
<pre>&nbsp;</pre>
<pre>    _timer = <span>new</span> Timer(VR920Poller, null, Timeout.Infinite, TimerPeriod);</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> <span>Const</span> TimerPeriod <span>As</span> <span>Integer</span> = 1</pre>
<pre><span>Private</span> _mainForm <span>As</span> MainForm</pre>
<pre><span>Private</span> _timer <span>As</span> Timer</pre>
<pre>&nbsp;</pre>
<pre><span>Public</span> <span>Sub</span> <span>New</span>(<span>ByVal</span> mainForm <span>As</span> MainForm)</pre>
<pre>    _mainForm = mainForm</pre>
<pre>&nbsp;</pre>
<pre>    <span>Dim</span> openResult <span>As</span> <span>Integer</span> = IWROpenTracker()</pre>
<pre>    <span>If</span> openResult &lt;&gt; ERROR_SUCCESS <span>Then</span></pre>
<pre>        <span>Throw</span> <span>New</span> ApplicationException(<span>&quot;Could not connect to VR920: &quot;</span> &amp; openResult)</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>    _timer = <span>New</span> Timer(<span>AddressOf</span> VR920Poller, <span>Nothing</span>, Timeout.Infinite, TimerPeriod)</pre>
<pre><span>End</span> <span>Sub</span></pre>
</div>
</div>
<p>The <strong>VR920Poller</strong> method referenced above will be called at a specific interval and read the values from the glasses as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>// collection of values <span>for</span> averaging</pre>
<pre><span>private</span> List&lt;<span>double</span>&gt; _yawValues = <span>new</span> List&lt;<span>double</span>&gt;();</pre>
<pre><span>private</span> List&lt;<span>double</span>&gt; _rollValues = <span>new</span> List&lt;<span>double</span>&gt;();</pre>
<pre><span>private</span> List&lt;<span>double</span>&gt; _pitchValues = <span>new</span> List&lt;<span>double</span>&gt;();</pre>
<pre>&nbsp;</pre>
<pre>// last calculated values</pre>
<pre><span>private</span> <span>double</span> _lastYaw;</pre>
<pre><span>private</span> <span>double</span> _lastPitch;</pre>
<pre><span>private</span> <span>double</span> _lastRoll;</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> void VR920Poller(<span>object</span> state)</pre>
<pre>{</pre>
<pre>    lock(this)</pre>
<pre>    {</pre>
<pre>        int yaw, pitch, roll;</pre>
<pre>&nbsp;</pre>
<pre>        int result = IWRGetTracking(out yaw, out pitch, out roll);</pre>
<pre>&nbsp;</pre>
<pre>        <span>if</span>(result != ERROR_SUCCESS)</pre>
<pre>            <span>throw</span> <span>new</span> ApplicationException(<span>&quot;Could not get VR920 tracking information: &quot;</span> &#43; result);</pre>
<pre>&nbsp;</pre>
<pre>        _yawValues.Add(VR920ToRadians(yaw));</pre>
<pre>        _rollValues.Add(VR920ToRadians(roll));</pre>
<pre>        _pitchValues.Add(VR920ToRadians(pitch));</pre>
<pre>&nbsp;</pre>
<pre>        <span>if</span>(_yawValues.Count == 5)</pre>
<pre>        {</pre>
<pre>            <span>double</span> y = Average(_yawValues, _lastYaw);</pre>
<pre>            <span>if</span>(Math.Abs(y - _lastYaw) &gt; 0.026)</pre>
<pre>                _lastYaw = y;</pre>
<pre>&nbsp;</pre>
<pre>            <span>double</span> p = Average(_pitchValues, _lastPitch);</pre>
<pre>            <span>if</span>(Math.Abs(p - _lastPitch) &gt; 0.017)</pre>
<pre>                _lastPitch = p;</pre>
<pre>&nbsp;</pre>
<pre>            _lastRoll = Average(_rollValues, _lastRoll);</pre>
<pre>&nbsp;</pre>
<pre>            _yawValues.Clear();</pre>
<pre>            _pitchValues.Clear();</pre>
<pre>            _rollValues.Clear();</pre>
<pre>&nbsp;</pre>
<pre>            RollPitchYaw rollPitchYaw = <span>new</span> RollPitchYaw(_lastRoll, _lastPitch, _lastYaw);</pre>
<pre>            _mainForm.SetRollPitchYaw(rollPitchYaw);</pre>
<pre>&nbsp;</pre>
<pre>            _mainForm.lblAxes.Text = VR920ToDegrees(yaw) &#43; <span>&quot;, &quot;</span> &#43; VR920ToDegrees(pitch) &#43; <span>&quot;, &quot;</span> &#43; VR920ToDegrees(roll);</pre>
<pre>        }</pre>
<pre>    }</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> <span>double</span> Average(List&lt;<span>double</span>&gt; values, <span>double</span> last)</pre>
<pre>{</pre>
<pre>    <span>double</span> total = 0;</pre>
<pre>&nbsp;</pre>
<pre>    foreach(<span>double</span> value <span>in</span> values)</pre>
<pre>        total &#43;= value;</pre>
<pre>&nbsp;</pre>
<pre>    total &#43;= last;</pre>
<pre>&nbsp;</pre>
<pre>    <span>return</span> total / (values.Count&#43;1);</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> <span>double</span> VR920ToRadians(int vr920Value)</pre>
<pre>{</pre>
<pre>    <span>return</span> (vr920Value * .00549) * (Math.PI/180);</pre>
<pre>}</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> <span>double</span> VR920ToDegrees(int vr920Value)</pre>
<pre>{</pre>
<pre>    <span>return</span> (vr920Value * .00549);</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>' collection of values for averaging</span></pre>
<pre><span>Private</span> _yawValues <span>As</span> List(Of <span>Double</span>) = <span>New</span> List(Of <span>Double</span>)()</pre>
<pre><span>Private</span> _rollValues <span>As</span> List(Of <span>Double</span>) = <span>New</span> List(Of <span>Double</span>)()</pre>
<pre><span>Private</span> _pitchValues <span>As</span> List(Of <span>Double</span>) = <span>New</span> List(Of <span>Double</span>)()</pre>
<pre>&nbsp;</pre>
<pre><span>' last calculated values</span></pre>
<pre><span>Private</span> _lastYaw <span>As</span> <span>Double</span></pre>
<pre><span>Private</span> _lastPitch <span>As</span> <span>Double</span></pre>
<pre><span>Private</span> _lastRoll <span>As</span> <span>Double</span></pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> <span>Sub</span> VR920Poller(<span>ByVal</span> state <span>As</span> <span>Object</span>)</pre>
<pre>    <span>SyncLock</span> <span>Me</span></pre>
<pre>        <span>Dim</span> yaw, pitch, roll <span>As</span> <span>Integer</span></pre>
<pre>&nbsp;</pre>
<pre>        <span>Dim</span> result <span>As</span> <span>Integer</span> = IWRGetTracking(yaw, pitch, roll)</pre>
<pre>&nbsp;</pre>
<pre>        <span>If</span> result &lt;&gt; ERROR_SUCCESS <span>Then</span></pre>
<pre>            <span>Throw</span> <span>New</span> ApplicationException(<span>&quot;Could not get VR920 tracking information: &quot;</span> &amp; result)</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>        _yawValues.Add(VR920ToRadians(yaw))</pre>
<pre>        _rollValues.Add(VR920ToRadians(roll))</pre>
<pre>        _pitchValues.Add(VR920ToRadians(pitch))</pre>
<pre>&nbsp;</pre>
<pre>        <span>If</span> _yawValues.Count = 5 <span>Then</span></pre>
<pre>            <span>Dim</span> y <span>As</span> <span>Double</span> = Average(_yawValues, _lastYaw)</pre>
<pre>            <span>If</span> Math.Abs(y - _lastYaw) &gt; 0.026 <span>Then</span></pre>
<pre>                _lastYaw = y</pre>
<pre>            <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>            <span>Dim</span> p <span>As</span> <span>Double</span> = Average(_pitchValues, _lastPitch)</pre>
<pre>            <span>If</span> Math.Abs(p - _lastPitch) &gt; 0.017 <span>Then</span></pre>
<pre>                _lastPitch = p</pre>
<pre>            <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>            _lastRoll = Average(_rollValues, _lastRoll)</pre>
<pre>&nbsp;</pre>
<pre>            _yawValues.Clear()</pre>
<pre>            _pitchValues.Clear()</pre>
<pre>            _rollValues.Clear()</pre>
<pre>&nbsp;</pre>
<pre>            <span>Dim</span> rollPitchYaw <span>As</span> <span>New</span> RollPitchYaw(_lastRoll, _lastPitch, _lastYaw)</pre>
<pre>            _mainForm.SetRollPitchYaw(rollPitchYaw)</pre>
<pre>&nbsp;</pre>
<pre>            _mainForm.lblAxes.Text = VR920ToDegrees(yaw) &amp; <span>&quot;, &quot;</span> &amp; VR920ToDegrees(pitch) &amp; <span>&quot;, &quot;</span> &amp; VR920ToDegrees(roll)</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>    <span>End</span> <span>SyncLock</span></pre>
<pre><span>End</span> <span>Sub</span></pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> <span>Function</span> Average(<span>ByVal</span> values <span>As</span> List(Of <span>Double</span>), <span>ByVal</span> last <span>As</span> <span>Double</span>) <span>As</span> <span>Double</span></pre>
<pre>    <span>Dim</span> total <span>As</span> <span>Double</span> = 0</pre>
<pre>&nbsp;</pre>
<pre>    <span>For</span> <span>Each</span> value <span>As</span> <span>Double</span> <span>In</span> values</pre>
<pre>        total &#43;= value</pre>
<pre>    <span>Next</span> value</pre>
<pre>&nbsp;</pre>
<pre>    total &#43;= last</pre>
<pre>&nbsp;</pre>
<pre>    <span>Return</span> total / (values.Count&#43;1)</pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> <span>Function</span> VR920ToRadians(<span>ByVal</span> vr920Value <span>As</span> <span>Integer</span>) <span>As</span> <span>Double</span></pre>
<pre>    <span>Return</span> (vr920Value *.00549) * (Math.PI/180)</pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> <span>Function</span> VR920ToDegrees(<span>ByVal</span> vr920Value <span>As</span> <span>Integer</span>) <span>As</span> <span>Double</span></pre>
<pre>    <span>Return</span> (vr920Value *.00549)</pre>
<pre><span>End</span> Function</pre>
</div>
</div>
<p>This chunk calls the <strong>IWRGetTracking</strong> method and tosses the results in 3 lists for roll, pitch and yaw.&nbsp; then, when the list has 5 members in it, the values are averaged and, if that resulting values is past a certain threshold, the values
 are passed back to the <strong>MainForm</strong>'s <strong>SetRollPitchYaw</strong> method as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> <span>double</span> _lastYaw;</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> <span>void</span> SetRollPitchYaw(RollPitchYaw rollPitchYaw)</pre>
<pre>{</pre>
<pre>    <span>if</span>(!_initialized || _globeControl.IsDisposed || (_globeControl.Host) == <span>null</span> || _globeControl.Host.CameraControllers.Current == <span>null</span>)</pre>
<pre>        <span>return</span>;</pre>
<pre>&nbsp;</pre>
<pre>    <span>double</span> y = ((_globeControl.Host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.LocalOrientation.Yaw - _lastYaw) &#43; rollPitchYaw.Yaw;</pre>
<pre>&nbsp;</pre>
<pre>    RollPitchYaw rpw = <span>new</span> RollPitchYaw(rollPitchYaw.Roll, rollPitchYaw.Pitch, y);</pre>
<pre>    (_globeControl.Host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.LocalOrientation.RollPitchYaw = rpw;</pre>
<pre>&nbsp;</pre>
<pre>    _lastYaw = rollPitchYaw.Yaw;</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> _lastYaw <span>As</span> <span>Double</span></pre>
<pre>&nbsp;</pre>
<pre><span>Public</span> <span>Sub</span> SetRollPitchYaw(<span>ByVal</span> rollPitchYaw <span>As</span> RollPitchYaw)</pre>
<pre>    <span>If</span> (<span>Not</span> _initialized) <span>OrElse</span> _globeControl.IsDisposed <span>OrElse</span> (_globeControl.Host) <span>Is</span> <span>Nothing</span> <span>OrElse</span> _globeControl.Host.CameraControllers.Current <span>Is</span> <span>Nothing</span> <span>Then</span></pre>
<pre>        <span>Return</span></pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>    <span>Dim</span> y <span>As</span> <span>Double</span> = ((<span>TryCast</span>(_globeControl.Host.CameraControllers.Current, ActionCameraController)).LastReportedViewpoint.LocalOrientation.Yaw - _lastYaw) &#43; rollPitchYaw.Yaw</pre>
<pre>&nbsp;</pre>
<pre>    <span>Dim</span> rpw <span>As</span> <span>New</span> RollPitchYaw(rollPitchYaw.Roll, rollPitchYaw.Pitch, y)</pre>
<pre>    <span>TryCast</span>(_globeControl.Host.CameraControllers.Current, ActionCameraController).LastReportedViewpoint.LocalOrientation.RollPitchYaw = rpw</pre>
<pre>&nbsp;</pre>
<pre>    _lastYaw = rollPitchYaw.Yaw</pre>
<pre><span>End</span> <span>Sub</span></pre>
</div>
</div>
<p>This method takes the roll, pitch and yaw values provided and applies them to the current
<strong>CameraController</strong> used by VE3D (which, by default, is an <strong>
ActionCameraController</strong>).</p>
<p>So, this code ultimately takes the roll, yaw and pitch of the user's head and translates that directly to the roll, yaw and pitch of the camera in VE3D providing an accurate, real-time view into the VE3D world.</p>
<h3></h3>
<h1></h1>
<h3>Stereoscopic Images</h3>
<p>The final piece of the puzzle is drawing frames to the VR920 glasses in such a way that the final view to the user will be three dimensional.&nbsp; This works similarly to those
<a href="http://www.magiceye.com/3dfun/stwkdisp.shtml" target="_blank">Magic Eye puzzles</a> you may have seen.&nbsp; Essentially, for every frame, we want to take the current VE3D camera position and move it several units to the left, render that to the left eye
 of the glasses, and then move the camera several units to the right and render that to the right eye of the glasses.&nbsp; To handle this we,&nbsp; can create an object named
<strong>VR920StereoStep</strong> which derives from the <strong>Step</strong> class provided by VE3D and add this to the
<strong>StepManager</strong>.&nbsp; When this is done, the <strong>VR920StereoStep</strong> will be called at the end of every frame drawn by VE3D.</p>
<p>As with the <strong>VR920Tracker</strong> class, we will need to setup a few P/Invoke method signatures to talk to the stereo driver as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>[DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint = <span>&quot;IWRSTEREO_Open&quot;</span>, SetLastError=<span>true</span>)]</pre>
<pre><span>public</span> <span>static</span> <span>extern</span> IntPtr OpenStereo();</pre>
<pre>&nbsp;</pre>
<pre>[DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint = <span>&quot;IWRSTEREO_SetStereo&quot;</span>)]</pre>
<pre><span>public</span> <span>static</span> <span>extern</span> Boolean SetStereoEnabled(IntPtr handle, Boolean enabled);</pre>
<pre>&nbsp;</pre>
<pre>[DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint = <span>&quot;IWRSTEREO_SetLR&quot;</span>)]</pre>
<pre><span>public</span> <span>static</span> <span>extern</span> Boolean SetStereoLR(IntPtr handle, Boolean eye);</pre>
<pre>&nbsp;</pre>
<pre>[DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint = <span>&quot;IWRSTEREO_WaitForAck&quot;</span>)]</pre>
<pre><span>public</span> <span>static</span> <span>extern</span> Byte WaitForOpenFrame(IntPtr handle, Boolean eye);</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre>&lt;DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint := <span>&quot;IWRSTEREO_Open&quot;</span>, SetLastError:=<span>True</span>)&gt; _</pre>
<pre><span>Public</span> <span>Shared</span> <span>Function</span> OpenStereo() <span>As</span> IntPtr</pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre>&lt;DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint := <span>&quot;IWRSTEREO_SetLR&quot;</span>)&gt; _</pre>
<pre><span>Public</span> <span>Shared</span> <span>Function</span> SetStereoLR(<span>ByVal</span> handle <span>As</span> IntPtr, <span>ByVal</span> eye <span>As</span> <span>Boolean</span>) <span>As</span> <span>Boolean</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre>&lt;DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint := <span>&quot;IWRSTEREO_SetStereo&quot;</span>)&gt; _</pre>
<pre><span>Public</span> <span>Shared</span> <span>Function</span> SetStereoEnabled(<span>ByVal</span> handle <span>As</span> IntPtr, <span>ByVal</span> enabled <span>As</span> <span>Boolean</span>) <span>As</span> <span>Boolean</span></pre>
<pre><span>End</span> <span>Function</span></pre>
<pre>&nbsp;</pre>
<pre>&lt;DllImport(<span>&quot;iWrstDrv.dll&quot;</span>, EntryPoint := <span>&quot;IWRSTEREO_WaitForAck&quot;</span>)&gt; _</pre>
<pre><span>Public</span> <span>Shared</span> <span>Function</span> WaitForOpenFrame(<span>ByVal</span> handle <span>As</span> IntPtr, <span>ByVal</span> eye <span>As</span> <span>Boolean</span>) <span>As</span> <span>Byte</span></pre>
<pre><span>End</span> <span>Function</span></pre>
</div>
</div>
<p>In the constructor for this object, we will open a handle to the stereo driver and turn on the stereo functionality of the glasses as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> <span>static</span> <span>readonly</span> IntPtr INVALID_FILE_HANDLE = (IntPtr)(-1);</pre>
<pre>&nbsp;</pre>
<pre><span>private</span> IntPtr _hStereo = INVALID_FILE_HANDLE;</pre>
<pre><span>private</span> Host _host;</pre>
<pre><span>private</span> bool _stereoEnabled = <span>true</span>;</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> <span>double</span> EyeDistance { <span>get</span>; <span>set</span>; }</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> VR920StereoStep(StepManager manager, Host host) : base(manager)</pre>
<pre>{</pre>
<pre>    EyeDistance = 10;</pre>
<pre>    _host = host;</pre>
<pre>&nbsp;</pre>
<pre>    _hStereo = OpenStereo();</pre>
<pre>    <span>if</span>(_hStereo != INVALID_FILE_HANDLE)</pre>
<pre>        SetStereoEnabled(_hStereo, <span>true</span>);</pre>
<pre>    <span>else</span></pre>
<pre>        _stereoEnabled = <span>false</span>;</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> <span>Shared</span> <span>ReadOnly</span> INVALID_FILE_HANDLE <span>As</span> IntPtr = <span>CType</span>(-1, IntPtr)</pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> _hStereo <span>As</span> IntPtr = INVALID_FILE_HANDLE</pre>
<pre><span>Private</span> _host <span>As</span> Host</pre>
<pre><span>Private</span> _stereoEnabled <span>As</span> <span>Boolean</span> = <span>True</span></pre>
<pre>&nbsp;</pre>
<pre><span>Private</span> privateEyeDistance <span>As</span> <span>Double</span></pre>
<pre><span>Public</span> <span>Property</span> EyeDistance() <span>As</span> <span>Double</span></pre>
<pre>    <span>Get</span></pre>
<pre>        <span>Return</span> privateEyeDistance</pre>
<pre>    <span>End</span> <span>Get</span></pre>
<pre>    <span>Set</span>(<span>ByVal</span> value <span>As</span> <span>Double</span>)</pre>
<pre>        privateEyeDistance = value</pre>
<pre>    <span>End</span> <span>Set</span></pre>
<pre><span>End</span> <span>Property</span></pre>
<pre>&nbsp;</pre>
<pre><span>Public</span> <span>Sub</span> <span>New</span>(<span>ByVal</span> manager <span>As</span> StepManager, <span>ByVal</span> host <span>As</span> Host)</pre>
<pre>    <span>MyBase</span>.<span>New</span>(manager)</pre>
<pre>    EyeDistance = 10</pre>
<pre>    _host = host</pre>
<pre>&nbsp;</pre>
<pre>    _hStereo = OpenStereo()</pre>
<pre>    <span>If</span> _hStereo &lt;&gt; INVALID_FILE_HANDLE <span>Then</span></pre>
<pre>        SetStereoEnabled(_hStereo, <span>True</span>)</pre>
<pre>    <span>Else</span></pre>
<pre>        _stereoEnabled = <span>False</span></pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>End</span> Sub</pre>
</div>
</div>
<p>Next, we need to override the <strong>OnExecute</strong> method provided by the base
<strong>Step </strong>class as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> <span>bool</span> _rightEye = <span>true</span>;</pre>
<pre><span>private</span> Vector3D _position;</pre>
<pre>&nbsp;</pre>
<pre><span>public</span> <span>override</span> <span>void</span> OnExecute(SceneState state)</pre>
<pre>{</pre>
<pre>    <span>if</span>(_hStereo != INVALID_FILE_HANDLE &amp;&amp; _stereoEnabled &amp;&amp; (_host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint != <span>null</span>)</pre>
<pre>    {</pre>
<pre>        <span>if</span>(!_rightEye)</pre>
<pre>        {</pre>
<pre>            _position = (_host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.Position.Vector;</pre>
<pre>            (_host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.Position.Vector = _position &#43; <span>new</span> Vector3D(-EyeDistance, 0, 0);</pre>
<pre>        }</pre>
<pre>        <span>else</span></pre>
<pre>        {</pre>
<pre>            _position = (_host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.Position.Vector;</pre>
<pre>            (_host.CameraControllers.Current <span>as</span> ActionCameraController).LastReportedViewpoint.Position.Vector = _position &#43; <span>new</span> Vector3D(EyeDistance, 0, 0);;</pre>
<pre>        }</pre>
<pre>&nbsp;</pre>
<pre>        SetStereoLR(_hStereo, _rightEye);</pre>
<pre>        WaitForOpenFrame(_hStereo, _rightEye);</pre>
<pre>        _rightEye = !_rightEye;</pre>
<pre>    }</pre>
<pre>}</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> _rightEye <span>As</span> <span>Boolean</span> = <span>True</span></pre>
<pre><span>Private</span> _position <span>As</span> Vector3D</pre>
<pre>&nbsp;</pre>
<pre><span>Public</span> <span>Overrides</span> <span>Sub</span> OnExecute(<span>ByVal</span> state <span>As</span> SceneState)</pre>
<pre>    <span>If</span> _hStereo &lt;&gt; INVALID_FILE_HANDLE <span>AndAlso</span> _stereoEnabled <span>AndAlso</span> (<span>TryCast</span>(_host.CameraControllers.Current, ActionCameraController)).LastReportedViewpoint IsNot <span>Nothing</span> <span>Then</span></pre>
<pre>        <span>If</span> (<span>Not</span> _rightEye) <span>Then</span></pre>
<pre>            _position = (<span>TryCast</span>(_host.CameraControllers.Current, ActionCameraController)).LastReportedViewpoint.Position.Vector</pre>
<pre>            <span>TryCast</span>(_host.CameraControllers.Current, ActionCameraController).LastReportedViewpoint.Position.Vector = _position &#43; <span>New</span> Vector3D(-EyeDistance, 0, 0)</pre>
<pre>        <span>Else</span></pre>
<pre>            _position = (<span>TryCast</span>(_host.CameraControllers.Current, ActionCameraController)).LastReportedViewpoint.Position.Vector</pre>
<pre>            <span>TryCast</span>(_host.CameraControllers.Current, ActionCameraController).LastReportedViewpoint.Position.Vector = _position &#43; <span>New</span> Vector3D(EyeDistance, 0, 0)</pre>
<pre>&nbsp;</pre>
<pre>        <span>End</span> <span>If</span></pre>
<pre>&nbsp;</pre>
<pre>        SetStereoLR(_hStereo, _rightEye)</pre>
<pre>        WaitForOpenFrame(_hStereo, _rightEye)</pre>
<pre>        _rightEye = <span>Not</span> _rightEye</pre>
<pre>    <span>End</span> <span>If</span></pre>
<pre><span>End</span> Sub</pre>
</div>
</div>
<p><strong>OnExecute</strong> will be called at the end of each VE3D frame rendered.&nbsp; In this method, we obtain the current position of the VE3D camera and then set its position several units to the left or right (- or &#43;
<strong>EyeDistance</strong>).&nbsp; <strong>SetStereoLR</strong> from the VR920 API is called, setting the appropriate eye to be active (left = false, right = true).&nbsp; Then
<strong>WaitForOpenFrame</strong> is called which will pause the action long enough for the glasses to draw the entire frame to eye to avoid image tearing.&nbsp; Essentially, this call “sits and spins” at this location until the glasses report back that the entire
 frame is drawn in the proper eye and that we can continue on drawing the opposite eye for the next frame.&nbsp; Finally, the
<strong>_rightEye</strong> member variable is togged to the opposite value, so the next time through this method the opposite eye is drawn.</p>
<p>With this class in place, back in our <strong>MainForm</strong> in the <strong>
FirstFrameRendered</strong> event handler, we can instantiate the object and add it to the
<strong>StepManager</strong> as shown below:</p>
<p><strong>C#</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>private</span> VR920StereoStep _vr920StereoStep;</pre>
<pre>&nbsp;</pre>
<pre>_vr920StereoStep = <span>new</span> VR920StereoStep(_globeControl.Host.RenderEngine.StepManager, _globeControl.Host);</pre>
<pre>_globeControl.Host.RenderEngine.StepManager.Add(_vr920StereoStep);</pre>
</div>
</div>
<p><strong>VB</strong></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet">
<pre><span>Private</span> _vr920StereoStep <span>As</span> VR920StereoStep</pre>
<pre>&nbsp;</pre>
<pre>_vr920StereoStep = <span>New</span> VR920StereoStep(_globeControl.Host.RenderEngine.StepManager, _globeControl.Host)</pre>
<pre>_globeControl.Host.RenderEngine.StepManager.Add(_vr920StereoStep)</pre>
</div>
</div>
<p>&nbsp;</p>
<h3><b>Running the Application</b></h3>
<p>To run the demo, do the following:</p>
<ol>
<li>Copy <b>BindingsWiimote.xml</b> to the appropriate directory listed above </li><li>Pair the Wiimote and Balance Board to the computer.&nbsp; See the <a href="http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx" target="_blank">
WiimoteLib article</a> for more information on how to do that </li><li>Run the executable </li><li>Stand on the Balance Board </li><li>Put on the glasses </li><li>Zero both the Balance Board and glasses </li><li>Toggle everything on or off at will </li></ol>
<p><strong>Controls</strong></p>
<ul>
<li>F1 – Set VR920 glasses to zero position </li><li>F2 – Set Balance Board to zero position </li><li>F3 – Toggle stereo mode on and off </li><li>Wiimote 1 or B – Toggle Balance Board on or off </li><li>Wiimote 2 or V – Toggle VR920 head tracking on or off </li><li>F – Toggle full-screen on or off </li><li>Nunchuk joystick X/Y – Strafe/Move </li><li>Nunchuk C/Z buttons – Raise/lower altitude </li><li>Wiimote A – Open location menu/Select location (note that if a new location is selected, the Balance Board is turned off and must be re-enabled after “landing”
</li><li>Wiimote Dpad Up/Down – Move through List </li><li>Balance Board – Lean your body left or right to turn in the VE3D environment </li></ul>
<h3><b>Conclusion</b></h3>
<p>With the above code, we have created a very interesting interface for Virtual Earth 3D.&nbsp; The demo and source code linked above contain a few more features and bindings which enhance the application a bit more.&nbsp; Be sure to give the full demo a try and check
 out the full source code for a few more implementation details.</p>
<h3></h3>
<h3>Additional Information</h3>
<ul>
<li><a href="http://www.vr920.com/" target="_blank">Vuzix VR920 Glasses</a> </li><li><a href="http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx" target="_blank">Managed Library for Nintendo's Wiimote</a>
</li><li><a href="http://heptazane.spaces.live.com/default.aspx" target="_blank">Heptazane's Blog</a>
</li><li><a href="http://virtualearth.spaces.live.com/" target="_blank">Virtual Earth Blog</a>
</li><li><a href="http://blogs.msdn.com/virtualearth/" target="_blank">Virtual Earth Developer's Blog</a>
</li></ul>
<h3>Bio</h3>
<p>Brian is a <a href="https://mvp.support.microsoft.com/profile/Brian.Peek">Microsoft C# MVP</a> who has been actively developing in .NET since its early betas in 2000, and who has been developing solutions using Microsoft technologies and platforms for even
 longer. Along with .NET, Brian is particularly skilled in the languages of C, C&#43;&#43; and assembly language for a variety of CPUs. He is also well-versed in a wide variety of technologies including web development, document imaging, GIS, graphics, game development,
 and hardware interfacing. Brian has a strong background in developing applications for the health-care industry, as well as developing solutions for portable devices, such as tablet PCs and PDAs. Additionally, Brian has co-authored the book &quot;<a href="http://www.amazon.com/dp/0735711410/">Debugging
 ASP.NET</a>&quot; published by New Riders, and is currently co-authoring a book titled &quot;<a href="http://www.amazon.com/exec/obidos/ASIN/0596520743/brianpcom-20">Coding4Fun: 10 .NET Programming Projects for Wiimote, YouTube, World of Warcraft, and More</a>&quot; to be
 published by <a href="http://www.oreilly.com/">O'Reilly</a> in December 2008. Brian is also an author for MSDN's
<a href="http://blogs.msdn.com/coding4fun/">Coding4Fun</a> website.&nbsp; You can reach Brian via vis website at
<a href="http://www.brianpeek.com/">http://www.brianpeek.com/</a> .</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:b6e01f70d39f46a58b729e7600cdc83f">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/WiiEarthVR--A-Fully-Immersive-3D-Experience-with-Virtual-Earth-3D</comments>
      <itunes:summary>





In this article, Brian Peek will demonstrate how to use a Nintendo Wii Remote (Wiimote), a Wii Fit Balance Board, and Vuzix VR920 glasses as input devices for Microsoft Virtual Earth 3D, providing a fully immersive, 3D experience.



Brian Peek
ASPSOFT, Inc.

Difficulty: Intermediate
Time Required: 2-3 hours
Cost: $60 for Wiimote and Nunchuk, $90 for Wii Fit (which includes Balance Board), $400 for
Vuzix VR920 glasses
Software: Managed Library for Nintendo&#39;s Wiimote,
Visual Basic or Visual C# Express Editions
Hardware: Nintendo Wii Remote (Wiimote) with Nunchuk,

Wii Fit Balance Board, 
Vuzix VR920 glasses, a compatible PC Bluetooth adapter and stack
Download: Download
Discussion Forum: Forum




&amp;nbsp; 
Introduction
Virtual Earth is the 3D interface to 
Microsoft&#39;s Live Maps service.&amp;nbsp; Normally this control is loaded via the web browser and allows interaction with a keyboard, mouse, and Xbox 360 controller.&amp;nbsp; In this article, we will take the Virtual Earth 3D control out of the web browser, use it in a WinForms
 application, and control it with a Nintendo Wii Remote (Wiimote) and a pair of Vuzix VR920 glasses, while also providing a stereoscopic 3D image to the glasses, creating the illusion of a fully three dimensional environment.&amp;nbsp; Note that use of the Virtual Earth
 3D control in this way is undocumented and unsupported at the moment.&amp;nbsp; Because of this, some of the descriptions in this article are educated guesses and may not be 100% accurate… 
Originally, this project started as a simple Wiimote interface to Virtual Earth 3D as shown in the video below.&amp;nbsp; Since I wrote that application, I learned of the VR920 glasses and the Wii Fit Balance Board was released, so I&#39;ve decided to create a more immersive
 experience using all of these controls which was demonstrated at 
PDC2008, shown here: 
       Setup
Before we get started, you will need to install the Virtual Earth 3D control.&amp;nbsp; If you haven&#39;t done this already, browse to
http:/</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/WiiEarthVR--A-Fully-Immersive-3D-Experience-with-Virtual-Earth-3D</link>
      <pubDate>Fri, 14 Nov 2008 05:57:58 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/WiiEarthVR--A-Fully-Immersive-3D-Experience-with-Virtual-Earth-3D</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9068804_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/9068804_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Brian Peek</dc:creator>
      <itunes:author>Brian Peek</itunes:author>
      <slash:comments>5</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/WiiEarthVR--A-Fully-Immersive-3D-Experience-with-Virtual-Earth-3D/RSS</wfw:commentRss>
      <category>Hardware</category>
      <category>Windows</category>
      <category>Mash Up</category>
      <category>hardwarehacks</category>
      <category>hardware miscellaneous</category>
    </item>
  <item>
      <title>Live Maps and Virtual Earth 3D Keep Getting Better</title>
      <description><![CDATA[The Live Maps / Virtual Earth team must have gotten the last shipment of <a href="http://www.xbox360fanboy.com/2007/08/03/halo-3-dew-game-fuel-available-august-13th/">Mountain Dew Gamer Fuel </a>and are putting it to good use pumping out tons of great new features. Jump over to the <a href="http://virtualearth.spaces.live.com/blog/cns!2BBC66E99FDCDB98!14129.entry">Virtual Earth Blog </a>for a full recap, or over to <a href="http://arstechnica.com/news.ars/post/20080411-hands-on-new-microsoft-live-maps-improvements-impressive.html">ars technica </a>for a hands on look, or just go to <a href="http://maps.live.com">http://maps.live.com</a> to use it. <br><br>Some of the cool new features include improved 3D views with higher resolution textures, the ability to export to your GPS, the ability to make your own 3D models with 3DVIA (just right click where you want a building), street/other labels in birds eye view, and 1-click directions. <br><br>One of my favorite new features is the mash-up features, check out&nbsp;<a href="http://maps.live.com/default.aspx?v=2&amp;FORM=LMLTCC&amp;cp=40.812238~-74.076935&amp;style=h&amp;lvl=18&amp;tilt=-90&amp;dir=0&amp;alt=-1000&amp;scene=2692423&amp;phx=0&amp;phy=0&amp;phscl=1&amp;encType=1&amp;cid=2BBC66E99FDCDB98!12691">this view of Giants Stadium</a> with&nbsp;a seating data overlay. Also check out this view of the <a href="http://maps.live.com/default.aspx?v=2&amp;FORM=LMLTCC&amp;cp=40.812238~-74.076935&amp;style=h&amp;lvl=18&amp;tilt=-90&amp;dir=0&amp;alt=-1000&amp;scene=2692423&amp;phx=0&amp;phy=0&amp;phscl=1&amp;encType=1&amp;cid=2BBC66E99FDCDB98!12691">Seattle Transit System</a>. <br><br>Those alone make for a significant update, but there's much more. You can now share your custom map content with other people, you can browse to a location and subscribe to a geo-location via RSS, there are tour enhancements, high-def movie output, and direction/traffic enhancements. Great stuff! <br><br>Update: Max on Channel 8 has a video, <a href="http://channel8.msdn.com/Posts/New-Windows-Live-Maps-brings-unique-3-D-rendering-and-real-images-to-life/">check it out</a>.  <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:0385f9faeb51422aa1c59e1000f7e039">]]></description>
      <comments>http://channel9.msdn.com/Blogs/LarryLarsen/Live-Maps-and-Virtual-Earth-3D-Keep-Getting-Better</comments>
      <itunes:summary>The Live Maps / Virtual Earth team must have gotten the last shipment of Mountain Dew Gamer Fuel and are putting it to good use pumping out tons of great new features. Jump over to the Virtual Earth Blog for a full recap, or over to ars technica for a hands on look, or just go to http://maps.live.com to use it. Some of the cool new features include improved 3D views with higher resolution textures, the ability to export to your GPS, the ability to make your own 3D models with 3DVIA (just right click where you want a building), street/other labels in birds eye view, and 1-click directions. One of my favorite new features is the mash-up features, check out&amp;nbsp;this view of Giants Stadium with&amp;nbsp;a seating data overlay. Also check out this view of the Seattle Transit System. Those alone make for a significant update, but there&#39;s much more. You can now share your custom map content with other people, you can browse to a location and subscribe to a geo-location via RSS, there are tour enhancements, high-def movie output, and direction/traffic enhancements. Great stuff! Update: Max on Channel 8 has a video, check it out. </itunes:summary>
      <link>http://channel9.msdn.com/Blogs/LarryLarsen/Live-Maps-and-Virtual-Earth-3D-Keep-Getting-Better</link>
      <pubDate>Fri, 11 Apr 2008 17:18:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/Blogs/LarryLarsen/Live-Maps-and-Virtual-Earth-3D-Keep-Getting-Better</guid>      
      <dc:creator>Larry Larsen</dc:creator>
      <itunes:author>Larry Larsen</itunes:author>
      <slash:comments>0</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/Blogs/LarryLarsen/Live-Maps-and-Virtual-Earth-3D-Keep-Getting-Better/RSS</wfw:commentRss>
      <category>Live</category>
      <category>RSS</category>
      <category>Virtual Earth</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>Silverlight Dynamic Video Puzzle</title>
      <description><![CDATA[
<p>My blog title says it all, <a href="http://www.betterthaneveryone.com/">Monkey see, Monkey Build</a>.&nbsp; I saw Microsoft Surface's video puzzle and I needed to build it.&nbsp; I took this opportunity to play with Microsoft's new technology,
<a href="http://silverlight.net/">Silverlight</a> and build my own puzzle game.&nbsp; I couldn't find anything out on the Internet regarding dynamic video creation with Silverlight so I took it upon myself to do this.&nbsp; The screen shots in this demo will be in c#,
 however, everything should hold true for VB.</p>
<p>Clint Rutkas - Academic Developer Evangelist - Microsoft <br>
<a href="http://www.betterthaneveryone.com/">Monkey see, Monkey Build</a></p>
<p><b>Difficulty: </b>Intermediate <br>
<b>Time Required:</b> 6-10 hours <br>
<b>Cost: </b>Free <br>
<b>Software: </b><b><a href="http://www.microsoft.com/express/">Visual Studio Express</a>,
<a href="http://silverlight.net/GetStarted/">Silverlight SDK and Runtime for <strike>
1.1</strike> 2.0 beta2</a></b> <br>
<b>Download: </b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/surfacePuzzleDemo.zip"><strike>Download c#</strike></a><strike> -
</strike><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/surfacePuzzleDemoVB.zip"><strike>Download VB</strike></a> Updated to Silverlight 2.0 Beta -
<a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/VideoRealTimeBreakUp.zip">
Download c#</a></p>
<p><strong>Updates: </strong></p>
<ul>
<li>Fixed the Silverlight.Js, used the new version that is included with the 1.1 Refresh SDK so the proper installer will prompt now.&nbsp; Downloads are corrected also too.
</li><li>6/20/2008&nbsp;- Verified the solution works with Silverlight 2.0beta2.</li></ul>
<h2>Spec's, Requirements and headaches</h2>
<p>So I had a few mental requirements for the application to &quot;entertain&quot; myself.&nbsp;
</p>
<ol>
<li>Be able to rotate the video blocks </li><li>Be able to translate </li><li>Be able to increase the difficulty of the puzzle on the fly. </li><li>Zero interaction with the keyboard</li></ol>
<p>Simple, right?&nbsp; You want to see a demo too see some awesomeness?&nbsp; No problem, <a href="http://www.betterthaneveryone.com/archive/2008/01/06/silverlight-ms-surface-puzzle-like-demo.aspx">
head over to my site</a> and see it in person.</p>
<p><strong>Microsoft Surface Video Puzzle</strong></p>
<p><img height="180" alt="sl_9[1]" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/sl_91.jpg" width="240" border="0"></p>
<p><strong>Clint's Silverlight Video Puzzle</strong></p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image1.png"><img height="219" alt="image[1]" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image1_thumb.png" width="240" border="0"></a></p>
<h2>To the Internet Batman!</h2>
<p>To help save time building this, I first when out and attempting to see if anything has been done anything close to this before.&nbsp; I found</p>
<p>Also I need 2 small controls which I found out are in the Silverlight SDK.&nbsp; I needed horizontal slider controls for this app.&nbsp; While I know a text box control would have worked just as well, see requirement 4.</p>
<p>On the Silverlight community site, there is also a very nice demo showing the <a href="http://silverlight.net/samples/1.1/SilverlightSurface/Run/default.html">
photo application in Surface with source code</a>.&nbsp; This is where I learned the majority of what I needed.&nbsp; This application was very close to where I needed to go with mine.&nbsp; However, while it was close, it wasn't perfect.&nbsp; Parts of the photo application code
 are pretty much copied.&nbsp; This isn't the best way of doing this, but it is one way of doing it.</p>
<h2>Toolbelt: check, Keyboard: check, Band-aids: check</h2>
<p>So on to building it.&nbsp; I first started off with a new project and some XAML.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_6.png"><img height="269" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image.png" width="400" border="0"></a></p>
<p>After clicking &quot;OK&quot;, I get a very nice blank project.</p>
<p>&nbsp;<a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_7.png"><img height="367" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_3.png" width="400" border="0"></a>
</p>
<p>We'll want to add an additional XAML object for the video blocks.&nbsp; Go to &quot;<strong>Project -&gt; Add New Item</strong>&quot;, select Silverlight Page, name it &quot;VideoBlock.xaml&quot; and click &quot;OK&quot;.</p>
<p>So now we have everything we need for the most part, grab any video you have, if you don't have one, go to a site like
<a href="http://teamxbox.com/">TeamXbox</a> and grab one from there.&nbsp; I used Windows Movie Maker and grabbed a snippet from The Office (best show ever).</p>
<p>In addition to all these files, we need to add in a reference to the Silverlight SDK to get a hold of the Slider controls. Go to &quot;<strong>Project -&gt; Add Reference</strong>&quot;, and add in the
<strong>Silverlight.Samples.Controls.dll</strong>.</p>
<p>While we have a reference in the application, we still need the XAML to know about this also.&nbsp; So we'll add a line to link this in.&nbsp; It is the xmlns line that I bolded, XML name space, real clever, eh?&nbsp; We'll also&nbsp; change the background color to a gray so
 we can see it more easy.</p>
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">Canvas</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;parentCanvas&quot;</span>
        <span class="attr">xmlns</span><span class="kwrd">=&quot;http://schemas.microsoft.com/client/2007&quot;</span> 
        <span class="attr">xmlns:x</span><span class="kwrd">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span> 
        <span class="attr">Loaded</span><span class="kwrd">=&quot;Page_Loaded&quot;</span> 
        <span class="attr">x:Class</span><span class="kwrd">=&quot;c4f_SilverlightVideoDemo.VideoBlock;assembly=ClientBin/c4f_SilverlightVideoDemo.dll&quot;</span>
        <strong><span class="attr">xmlns:uicontrol</span><span class="kwrd">=&quot;clr-namespace:Silverlight.Samples.Controls;assembly=ClientBin/Silverlight.Samples.Controls.dll&quot;</span></strong>
        <span class="attr">Background</span><span class="kwrd">=&quot;#CCCCCC&quot; <span class="attr">Width</span><span class="kwrd">=&quot;640&quot;</span> <span class="attr">Height</span><span class="kwrd">=&quot;480&quot;</span></span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">Canvas</span><span class="kwrd">&gt;</span></pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff;
	overflow:auto}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h2>Silverlight 101</h2>
<p>So in Silverlight, you have brushes which can &quot;painted&quot; onto objects.&nbsp; One of these brushes is called the VideoBrush which I'll talk about later on.&nbsp; For playing videos, one would use the
<strong>MediaElement</strong>.&nbsp; Lets add this to our canvas.</p>
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">MediaElement</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;media&quot;</span> <span class="attr">Source</span><span class="kwrd">=&quot;theOffice.wmv&quot;</span> <span class="kwrd">/&gt;</span></pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>Now if we run the application at this point, we'll get the video playing.&nbsp; Lets sit back with some popcorn and enjoy this victory for a second or two.&nbsp; Now that we have this, lets hide it since this isn't what we want.&nbsp; We do that by adding in an XML attribute
<strong>Opacity</strong> and setting it to 0.</p>
<p>On top of that element, lets add on some TextBlock elements and the sliders.</p>
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">Canvas</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;parentCanvas&quot;</span>
        <span class="attr">xmlns</span><span class="kwrd">=&quot;http://schemas.microsoft.com/client/2007&quot;</span> 
        <span class="attr">xmlns:x</span><span class="kwrd">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span> 
        <span class="attr">Loaded</span><span class="kwrd">=&quot;Page_Loaded&quot;</span> 
        <span class="attr">x:Class</span><span class="kwrd">=&quot;VideoCreatedObjectTest.Page;assembly=ClientBin/VideoCreatedObjectTest.dll&quot;</span>
        <span class="attr">xmlns:uicontrol</span><span class="kwrd">=&quot;clr-namespace:Silverlight.Samples.Controls;assembly=ClientBin/Silverlight.Samples.Controls.dll&quot;</span> 
        <span class="attr">Background</span><span class="kwrd">=&quot;#CCCCCC&quot;</span> <span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">MediaElement</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;media&quot;</span> <span class="attr">Source</span><span class="kwrd">=&quot;theOffice.wmv&quot;</span> <span class="attr">Opacity</span><span class="kwrd">=&quot;0&quot;</span> <span class="kwrd">/&gt;</span>
    
    <span class="kwrd">&lt;</span><span class="html">TextBlock</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;sliderX&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Top</span><span class="kwrd">=&quot;5&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Left</span><span class="kwrd">=&quot;5&quot;</span> <span class="attr">Text</span><span class="kwrd">=&quot;X (01):&quot;</span> <span class="kwrd">/&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">TextBlock</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;sliderY&quot;</span>  <span class="attr">Canvas</span>.<span class="attr">Top</span><span class="kwrd">=&quot;21&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Left</span><span class="kwrd">=&quot;5&quot;</span> <span class="attr">Text</span><span class="kwrd">=&quot;Y (01):&quot;</span> <span class="kwrd">/&gt;</span>
    
    <span class="kwrd">&lt;</span><span class="html">uicontrol:Slider</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;hSliderX&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Top</span><span class="kwrd">=&quot;8&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Left</span><span class="kwrd">=&quot;55&quot;</span> <span class="kwrd">/&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">uicontrol:Slider</span> <span class="attr">x:Name</span><span class="kwrd">=&quot;hSliderY&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Top</span><span class="kwrd">=&quot;25&quot;</span> <span class="attr">Canvas</span>.<span class="attr">Left</span><span class="kwrd">=&quot;55&quot;</span> <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">Canvas</span><span class="kwrd">&gt;</span></pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>So now when we run the application, we get a blank screen but we can hear the audio from the video.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_8.png"><img height="345" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_4.png" width="400" border="0"></a>
</p>
<p>Super exciting I know.&nbsp; Now if you resize the browser, you'll notice also the canvas isn't resizing automatically.&nbsp; Lets fix that.</p>
<p>So in the code behind lets add in a new event on the BrowserHost object with the Resize event.&nbsp; We'll attach the Browser_Resize event to it.</p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">void</span> BrowserHost_Resize(<span class="kwrd">object</span> sender, EventArgs e)
{
    <span class="rem">// Set size to host size</span>
    Width = BrowserHost.ActualWidth;
    Height = BrowserHost.ActualHeight;
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.Net</strong></p>
<pre class="csharpcode"><span class="kwrd">Private</span> <span class="kwrd">Sub</span> BrowserHost_Resize(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> EventArgs)
    <span class="rem">' Set size to host size</span>
    Width = BrowserHost.ActualWidth
    Height = BrowserHost.ActualHeight
<span class="kwrd">End</span> Sub</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h2>Video as Paint?</h2>
<p>So now that we have that small part done, lets get into more of the nitty gritty with video.&nbsp; As I said earlier, you'll use a
<strong>VideoBrush</strong> to paint on the video.</p>
<p>We'll use the brush as the Fill for the rectangle we'll be using to show off the clip of the video we want.</p>
<h3>Still not moving?&nbsp; Try explosives?</h3>
<p>While doing this project, I discovered an interesting thing when using the VideoBrush and dynamically appending items on.&nbsp; Having items in the XAML rather than programmatically creating everything caused it not to work properly.&nbsp; Nothing would appear.</p>
<h3>Throwback to how UI's use to be done</h3>
<p>The XAML for this is a simplified version of the Surface demo Photo.XAML.&nbsp; You have a 3 Rectangles, 1 for the video, 1 for translation and 1 for rotation.&nbsp; Simple, no?&nbsp; Doing all this by hand gets a bit annoying since you need to nest everything properly.</p>
<p>Here is the more interesting <strong>VideoBrush</strong> code.&nbsp; You need the name from the
<strong>MediaElement </strong>along with the how much of an offset you want the video.&nbsp; Since your moving the &quot;camera&quot; in, this value needs to be negative.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_10.png"><img height="395" alt="image" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/image_5.png" width="654" border="0"></a>
</p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">delegate</span> <span class="kwrd">void</span> SetActiveVideo(VideoBlock videoBlock, ActionType actionType, Point photoCenter, Point lastPosition);
<span class="kwrd">public</span> VideoBlock(<span class="kwrd">string</span> mediaName, <span class="kwrd">int</span> offsetX, <span class="kwrd">int</span> offsetY, <span class="kwrd">int</span> rectWidth, <span class="kwrd">int</span> rectHeight, SetActiveVideo functionPointer)
{
    <span class="rem">// additional code here</span>
    Rectangle video = <span class="kwrd">new</span> Rectangle();
    VideoBrush vb = <span class="kwrd">new</span> VideoBrush();
    TranslateTransform videoTransform = <span class="kwrd">new</span> TranslateTransform();
    videoTransform.X = -1 * offsetX;
    videoTransform.Y = -1 * offsetY;

    vb.SourceName = mediaName;
    vb.Transform = videoTransform;
    vb.Stretch = Stretch.None;
    vb.AlignmentX = AlignmentX.Left;
    vb.AlignmentY = AlignmentY.Top;

    video.Width = Width;
    video.Height = Height;
    video.Fill = vb;
    <span class="rem">// additional code here</span>
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.Net</strong></p>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Delegate</span> <span class="kwrd">Sub</span> SetActiveVideo(<span class="kwrd">ByVal</span> videoBlock <span class="kwrd">As</span> VideoBlock, <span class="kwrd">ByVal</span> actionType <span class="kwrd">As</span> ActionType, <span class="kwrd">ByVal</span> photoCenter <span class="kwrd">As</span> Point, <span class="kwrd">ByVal</span> lastPosition <span class="kwrd">As</span> Point)
<span class="kwrd">Public</span> <span class="kwrd">Sub</span> VideoBlock(<span class="kwrd">ByVal</span> mediaName <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> offsetX <span class="kwrd">As</span> <span class="kwrd">Integer</span>, <span class="kwrd">ByVal</span> offsetY <span class="kwrd">As</span> <span class="kwrd">Integer</span>, <span class="kwrd">ByVal</span> rectWidth <span class="kwrd">As</span> <span class="kwrd">Integer</span>, <span class="kwrd">ByVal</span> rectHeight <span class="kwrd">As</span> <span class="kwrd">Integer</span>, <span class="kwrd">ByVal</span> functionPointer <span class="kwrd">As</span> SetActiveVideo)
      <span class="rem">' additional code here</span>
      <span class="kwrd">Dim</span> video <span class="kwrd">As</span> Rectangle = <span class="kwrd">New</span> Rectangle
      <span class="kwrd">Dim</span> vb <span class="kwrd">As</span> VideoBrush = <span class="kwrd">New</span> VideoBrush
      <span class="kwrd">Dim</span> videoTransform <span class="kwrd">As</span> TranslateTransform = <span class="kwrd">New</span> TranslateTransform
      videoTransform.X = ((1 * offsetX)  * -1)
      videoTransform.Y = ((1 * offsetY)  * -1)
      vb.SourceName = mediaName
      vb.Transform = videoTransform
      vb.Stretch = Stretch.None
      vb.AlignmentX = AlignmentX.Left
      vb.AlignmentY = AlignmentY.Top
      video.Width = Width
      video.Height = Height
      video.Fill = vb
      <span class="rem">' additional code here</span>
<span class="kwrd">End</span> Sub</pre>
<h3>There is a mouse on my object!</h3>
<p>So now we have our video block element prepped and ready.&nbsp; There are additional events added in, but they are fairly boiler plate.&nbsp; When looking at this code, you see I pass in a delegate, a function pointer if you will.&nbsp; Why do I do this?&nbsp; This is how the
 mouse position is calculated.&nbsp; I bet this could be redone better, but this is how I did it.&nbsp; The parent element, the canvas, has the mouse position I need to calculate proper translation and rotation, however, mouse events on the VideoBlock object will give
 me the mouse position only on that element.&nbsp; So I may be at point 150, 150 on the screen, on the VideoBlock object, I'm actually at 10,15.</p>
<p>Additionally, this approach allows me to move the VideoBlock to the top of the objects.&nbsp; Once again, this could be done other ways, this is how I chose to do it.</p>
<p>So on all my mouse related all boils down to doing the exact same function.&nbsp; Since events are bubbled, a mouse movement on a VideoBlock is also a mouse movement on the root canvas.</p>
<p>C#</p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">void</span> root_MouseLeftButtonDown(<span class="kwrd">object</span> sender, MouseEventArgs e)
{
    HandleMouseLeftButtonDown(ActionType.Selecting, e);
}

<span class="kwrd">private</span> <span class="kwrd">void</span> translateControls_MouseLeftButtonDown(<span class="kwrd">object</span> sender, MouseEventArgs e)
{
    HandleMouseLeftButtonDown(ActionType.Moving, e);
}

<span class="kwrd">private</span> <span class="kwrd">void</span> rotateScaleControls_MouseLeftButtonDown(<span class="kwrd">object</span> sender, MouseEventArgs e)
{
    HandleMouseLeftButtonDown(ActionType.RotatingScaling, e);
}

<span class="kwrd">private</span> <span class="kwrd">void</span> HandleMouseLeftButtonDown(ActionType actionType, MouseEventArgs e)
{
    <span class="kwrd">if</span> (functionPointer != <span class="kwrd">null</span>)
        functionPointer(<span class="kwrd">this</span>, actionType,
            <span class="kwrd">new</span> Point(translateTransform.X &#43; rotateTransform.CenterX,
                  translateTransform.Y &#43; rotateTransform.CenterY), e.GetPosition(<span class="kwrd">null</span>));
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>VB.Net</p>
<pre class="csharpcode">    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> root_MouseLeftButtonDown(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> MouseEventArgs)
        HandleMouseLeftButtonDown(ActionType.Selecting, e)
    <span class="kwrd">End</span> <span class="kwrd">Sub</span>
    
    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> translateControls_MouseLeftButtonDown(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> MouseEventArgs)
        HandleMouseLeftButtonDown(ActionType.Moving, e)
    <span class="kwrd">End</span> <span class="kwrd">Sub</span>
    
    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> rotateScaleControls_MouseLeftButtonDown(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> MouseEventArgs)
        HandleMouseLeftButtonDown(ActionType.RotatingScaling, e)
    <span class="kwrd">End</span> <span class="kwrd">Sub</span>
    
    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> HandleMouseLeftButtonDown(<span class="kwrd">ByVal</span> actionType <span class="kwrd">As</span> ActionType, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> MouseEventArgs)
        <span class="kwrd">If</span> (<span class="kwrd">Not</span> (functionPointer) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span>
            functionPointer(<span class="kwrd">Me</span>, actionType, <span class="kwrd">New</span> Point((translateTransform.X &#43; rotateTransform.CenterX), <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (translateTransform.Y &#43; rotateTransform.CenterY)), e.GetPosition(<span class="kwrd">Nothing</span>))
        <span class="kwrd">End</span> <span class="kwrd">If</span>
    <span class="kwrd">End</span> Sub</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h2>You built a car out of all these parts?</h2>
<p>So we move back from VideoBlock.xaml.cs to Page.xaml.cs</p>
<p>We've created our primary object for painting video, lets populate it.&nbsp; First, how do we know how big the media file is?&nbsp; With the
<strong>MediaOpened</strong> event!&nbsp; And we'll restart the video with the <strong>
MediaEnded</strong> event.</p>
<p>So here is the full Page_Load event from Page.xaml.cs</p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">void</span> Page_Loaded(<span class="kwrd">object</span> o, EventArgs e)
{
    <span class="rem">// Required to initialize variables</span>
    InitializeComponent();

    BrowserHost.Resize &#43;=<span class="kwrd">new</span> EventHandler(BrowserHost_Resize);
    media.MediaOpened &#43;= <span class="kwrd">new</span> EventHandler(media_MediaOpened);
    media.MediaEnded &#43;= <span class="kwrd">new</span> EventHandler(media_MediaEnded);
    MouseLeftButtonUp &#43;= Page_MouseLeftButtonUpOrLeave;
    MouseLeave &#43;= Page_MouseLeftButtonUpOrLeave;
    MouseMove &#43;= Page_MouseMove;

    hSliderX.Range = <span class="kwrd">new</span> ValueRange(1, 20);
    hSliderY.Range = <span class="kwrd">new</span> ValueRange(1, 20);
    hSliderX.ValueChanged &#43;= <span class="kwrd">new</span> EventHandler(slider_ValueChanged);
    hSliderY.ValueChanged &#43;= <span class="kwrd">new</span> EventHandler(slider_ValueChanged);

    hSliderX.SetValue(ZIndexProperty, 500);
    hSliderY.SetValue(ZIndexProperty, 500);
    sliderX.SetValue(ZIndexProperty, 500);
    sliderY.SetValue(ZIndexProperty, 500);
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.Net</strong></p>
<pre class="csharpcode">    <span class="kwrd">Public</span> <span class="kwrd">Sub</span> Page_Loaded(<span class="kwrd">ByVal</span> o <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> EventArgs)
        <span class="rem">' Required to initialize variables</span>
        InitializeComponent
        <span class="kwrd">AddHandler</span> BrowserHost.Resize, <span class="kwrd">AddressOf</span> <span class="kwrd">Me</span>.BrowserHost_Resize
        <span class="kwrd">AddHandler</span> media.MediaOpened, <span class="kwrd">AddressOf</span> <span class="kwrd">Me</span>.media_MediaOpened
        <span class="kwrd">AddHandler</span> media.MediaEnded, <span class="kwrd">AddressOf</span> <span class="kwrd">Me</span>.media_MediaEnded
        MouseLeftButtonUp = (MouseLeftButtonUp &#43; Page_MouseLeftButtonUpOrLeave)
        MouseLeave = (MouseLeave &#43; Page_MouseLeftButtonUpOrLeave)
        MouseMove = (MouseMove &#43; Page_MouseMove)
        hSliderX.Range = <span class="kwrd">New</span> ValueRange(1, 20)
        hSliderY.Range = <span class="kwrd">New</span> ValueRange(1, 20)
        <span class="kwrd">AddHandler</span> hSliderX.ValueChanged, <span class="kwrd">AddressOf</span> <span class="kwrd">Me</span>.slider_ValueChanged
        <span class="kwrd">AddHandler</span> hSliderY.ValueChanged, <span class="kwrd">AddressOf</span> <span class="kwrd">Me</span>.slider_ValueChanged
        hSliderX.SetValue(ZIndexProperty, 500)
        hSliderY.SetValue(ZIndexProperty, 500)
        sliderX.SetValue(ZIndexProperty, 500)
        sliderY.SetValue(ZIndexProperty, 500)
    <span class="kwrd">End</span> Sub</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>So now that we have the media loaded, we can use the media.NaturalVideoWidth and media.NaturalVideoHeight properties to find out exactly how big the video is!&nbsp; I have a function that is fired off when the page is first loaded or a slider value has changed.&nbsp;
 This is how we chop it up.</p>
<p><img height="180" alt="mince_garlic[1]" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/7163484/mince_garlic1.jpg" width="240" border="0">
</p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">void</span> prepVideoObjects()
{
    <span class="kwrd">int</span> totalPhotosX = (<span class="kwrd">int</span>)hSliderX.Value;
    <span class="kwrd">int</span> totalPhotosY = (<span class="kwrd">int</span>)hSliderY.Value;

    <span class="kwrd">if</span> (lastX != totalPhotosX || lastY != totalPhotosY)
    {
        lastX = totalPhotosX;
        lastY = totalPhotosY;

        sliderX.Text = <span class="kwrd">string</span>.Format(<span class="str">&quot;X ({0}):&quot;</span>, totalPhotosX.ToString(<span class="str">&quot;D2&quot;</span>));
        sliderY.Text = <span class="kwrd">string</span>.Format(<span class="str">&quot;Y ({0}):&quot;</span>, totalPhotosY.ToString(<span class="str">&quot;D2&quot;</span>));

        <span class="kwrd">int</span> rectWidth = (<span class="kwrd">int</span>)Math.Floor(media.NaturalVideoWidth / totalPhotosX);
        <span class="kwrd">int</span> rectHeight = (<span class="kwrd">int</span>)Math.Floor(media.NaturalVideoHeight / totalPhotosY);
        Random random = <span class="kwrd">new</span> Random();

        <span class="rem">// remove all old videoblocks</span>
        <span class="kwrd">for</span> (<span class="kwrd">int</span> i = Children.Count - 1; i &gt; 0; i--)
        {
            <span class="kwrd">if</span> (Children[i].GetType() == <span class="kwrd">typeof</span>(VideoBlock))
                Children.RemoveAt(i);
        }

        <span class="kwrd">for</span> (<span class="kwrd">int</span> x = 0; x &lt; totalPhotosX; x&#43;&#43;)
        {
            <span class="kwrd">for</span> (<span class="kwrd">int</span> y = 0; y &lt; totalPhotosY; y&#43;&#43;)
            {
                VideoBlock vb = <span class="kwrd">new</span> VideoBlock(media.Name, x * rectWidth, y * rectHeight, rectWidth, rectHeight, SetActivePhoto);
                shuffle(vb, random);
                Children.Add(vb);
            }
        }
    }
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.Net</strong></p>
<pre class="csharpcode"><span class="kwrd">Private</span> <span class="kwrd">Sub</span> prepVideoObjects()
        <span class="kwrd">Dim</span> totalPhotosX <span class="kwrd">As</span> <span class="kwrd">Integer</span> = <span class="kwrd">CType</span>(hSliderX.Value,<span class="kwrd">Integer</span>)
        <span class="kwrd">Dim</span> totalPhotosY <span class="kwrd">As</span> <span class="kwrd">Integer</span> = <span class="kwrd">CType</span>(hSliderY.Value,<span class="kwrd">Integer</span>)
        <span class="kwrd">If</span> ((lastX &lt;&gt; totalPhotosX)  _
                    <span class="kwrd">OrElse</span> (lastY &lt;&gt; totalPhotosY)) <span class="kwrd">Then</span>
            lastX = totalPhotosX
            lastY = totalPhotosY
            sliderX.Text = <span class="kwrd">String</span>.Format(<span class="str">&quot;X ({0}):&quot;</span>, totalPhotosX.ToString(<span class="str">&quot;D2&quot;</span>))
            sliderY.Text = <span class="kwrd">String</span>.Format(<span class="str">&quot;Y ({0}):&quot;</span>, totalPhotosY.ToString(<span class="str">&quot;D2&quot;</span>))
            <span class="kwrd">Dim</span> rectWidth <span class="kwrd">As</span> <span class="kwrd">Integer</span> = <span class="kwrd">CType</span>(Math.Floor((media.NaturalVideoWidth / totalPhotosX)),<span class="kwrd">Integer</span>)
            <span class="kwrd">Dim</span> rectHeight <span class="kwrd">As</span> <span class="kwrd">Integer</span> = <span class="kwrd">CType</span>(Math.Floor((media.NaturalVideoHeight / totalPhotosY)),<span class="kwrd">Integer</span>)
            <span class="kwrd">Dim</span> random <span class="kwrd">As</span> Random = <span class="kwrd">New</span> Random
            <span class="rem">' remove all old videoblocks</span>
            <span class="kwrd">Dim</span> i <span class="kwrd">As</span> <span class="kwrd">Integer</span> = (Children.Count - 1)
            <span class="kwrd">Do</span> <span class="kwrd">While</span> (i &gt; 0)
                <span class="kwrd">If</span> (Children(i).<span class="kwrd">GetType</span> = <span class="kwrd">GetType</span>(VideoBlock)) <span class="kwrd">Then</span>
                    Children.RemoveAt(i)
                <span class="kwrd">End</span> <span class="kwrd">If</span>
                i = (i - 1)
            <span class="kwrd">Loop</span>
            <span class="kwrd">Dim</span> x <span class="kwrd">As</span> <span class="kwrd">Integer</span> = 0
            <span class="kwrd">Do</span> <span class="kwrd">While</span> (x &lt; totalPhotosX)
                <span class="kwrd">Dim</span> y <span class="kwrd">As</span> <span class="kwrd">Integer</span> = 0
                <span class="kwrd">Do</span> <span class="kwrd">While</span> (y &lt; totalPhotosY)
                    <span class="kwrd">Dim</span> vb <span class="kwrd">As</span> VideoBlock = <span class="kwrd">New</span> VideoBlock(media.Name, (x * rectWidth), (y * rectHeight), rectWidth, rectHeight, SetActivePhoto)
                    shuffle(vb, random)
                    Children.Add(vb)
                    y = (y &#43; 1)
                <span class="kwrd">Loop</span>
                x = (x &#43; 1)
            <span class="kwrd">Loop</span>
        <span class="kwrd">End</span> <span class="kwrd">If</span>
    <span class="kwrd">End</span> <span class="kwrd">Sub</span></pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>Last but not least, lets see how the mouse movements are handled on the page.</p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">void</span> Page_MouseMove(<span class="kwrd">object</span> sender, MouseEventArgs e)
{
    <span class="kwrd">if</span> (<span class="kwrd">null</span> != activeVideoBlock)
    {
        <span class="rem">// Perform the appropriate transform on the active photo</span>
        Point position = e.GetPosition(<span class="kwrd">null</span>);
        <span class="kwrd">switch</span> (currentActionType)
        {
            <span class="kwrd">case</span> VideoBlock.ActionType.Moving:
                <span class="rem">// Move it by the amount of the mouse move</span>
                activeVideoBlock.Translate(position.X - currentLastPosition.X, position.Y - currentLastPosition.Y);
                <span class="kwrd">break</span>;
            <span class="kwrd">case</span> VideoBlock.ActionType.RotatingScaling:
                <span class="rem">// Rotate it according to the angle the mouse moved around the photo's center</span>
                <span class="kwrd">double</span> radiansToDegrees = 360 / (2 * Math.PI);
                <span class="kwrd">double</span> lastAngle = Math.Atan2(currentLastPosition.Y - currentVideoCenter.Y, currentLastPosition.X - currentVideoCenter.X) * radiansToDegrees;
                <span class="kwrd">double</span> currentAngle = Math.Atan2(position.Y - currentVideoCenter.Y, position.X - currentVideoCenter.X) * radiansToDegrees;
                activeVideoBlock.Rotate(currentAngle - lastAngle);

                <span class="kwrd">break</span>;
        }
        currentLastPosition = position;
    }
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.Net</strong></p>
<pre class="csharpcode">    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> Page_MouseMove(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> MouseEventArgs)
        <span class="kwrd">If</span> (<span class="kwrd">Not</span> (activeVideoBlock) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span>
            <span class="rem">' Perform the appropriate transform on the active photo</span>
            <span class="kwrd">Dim</span> position <span class="kwrd">As</span> Point = e.GetPosition(<span class="kwrd">Nothing</span>)
            <span class="kwrd">Select</span> <span class="kwrd">Case</span> (currentActionType)
                <span class="kwrd">Case</span> VideoBlock.ActionType.Moving
                    <span class="rem">' Move it by the amount of the mouse move</span>
                    activeVideoBlock.Translate((position.X - currentLastPosition.X), (position.Y - currentLastPosition.Y))
                <span class="kwrd">Case</span> VideoBlock.ActionType.RotatingScaling
                    <span class="rem">' Rotate it according to the angle the mouse moved around the photo's center</span>
                    <span class="kwrd">Dim</span> radiansToDegrees <span class="kwrd">As</span> <span class="kwrd">Double</span> = (360 / (2 * Math.PI))
                    <span class="kwrd">Dim</span> lastAngle <span class="kwrd">As</span> <span class="kwrd">Double</span> = (Math.Atan2((currentLastPosition.Y - currentVideoCenter.Y), (currentLastPosition.X - currentVideoCenter.X)) * radiansToDegrees)
                    <span class="kwrd">Dim</span> currentAngle <span class="kwrd">As</span> <span class="kwrd">Double</span> = (Math.Atan2((position.Y - currentVideoCenter.Y), (position.X - currentVideoCenter.X)) * radiansToDegrees)
                    activeVideoBlock.Rotate((currentAngle - lastAngle))
            <span class="kwrd">End</span> <span class="kwrd">Select</span>
            currentLastPosition = position
        <span class="kwrd">End</span> <span class="kwrd">If</span>
    <span class="kwrd">End</span> Sub</pre>
<h2>Wrapping it up</h2>
<p>So as you can see, Silverlight is pretty powerful once you figure out how to tame it.&nbsp; I used example base code and modified it to suit my own needs to dynamically alter a video in real time.&nbsp; There are a few code tweaks that could happen to improve this
 code and I'm betting with some additional effort, one could remove the delegate.&nbsp; In addition, you could add in edge detection code and a timer to make this into a true video puzzle game.&nbsp; This was a pet project I worked on while I was at the airport.&nbsp; This
 demo can be downgraded to Silverlight 1.0 also.&nbsp; I don't believe anything I did was really Silverlight 1.1 (now 2.0) only.</p>
<p><strong>Clint's Bio:</strong></p>
<p>Clint is an academic developer evangelist for Microsoft.&nbsp; His two primary development languages are C# and JavaScript. He has built a
<a href="http://msdn.microsoft.com/coding4fun/coolapplications/disco/default.aspx">
Disco Dance Floor</a> too! In his off time, he whips up other random weird projects and does twenty something activities with his friends.&nbsp; His next two big projects are an automated bartender and a skateboard segway.&nbsp; Clint's blog is
<a href="http://betterthaneveryone.com/">betterthaneveryone.com</a> and can be emailed at
<a href="mailto:crutkas@microsoft.com">crutkas@microsoft.com</a> if you have any question.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:082345779d1643298e649e7600d07b4d">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Silverlight-Dynamic-Video-Puzzle</comments>
      <itunes:summary>
My blog title says it all, Monkey see, Monkey Build.&amp;nbsp; I saw Microsoft Surface&#39;s video puzzle and I needed to build it.&amp;nbsp; I took this opportunity to play with Microsoft&#39;s new technology,
Silverlight and build my own puzzle game.&amp;nbsp; I couldn&#39;t find anything out on the Internet regarding dynamic video creation with Silverlight so I took it upon myself to do this.&amp;nbsp; The screen shots in this demo will be in c#,
 however, everything should hold true for VB. 
Clint Rutkas - Academic Developer Evangelist - Microsoft 
Monkey see, Monkey Build 
Difficulty: Intermediate 
Time Required: 6-10 hours 
Cost: Free 
Software: Visual Studio Express,
Silverlight SDK and Runtime for 
1.1 2.0 beta2 
Download: Download c# -
Download VB Updated to Silverlight 2.0 Beta -

Download c# 
Updates:  

Fixed the Silverlight.Js, used the new version that is included with the 1.1 Refresh SDK so the proper installer will prompt now.&amp;nbsp; Downloads are corrected also too.
6/20/2008&amp;nbsp;- Verified the solution works with Silverlight 2.0beta2.
Spec&#39;s, Requirements and headaches
So I had a few mental requirements for the application to &amp;quot;entertain&amp;quot; myself.&amp;nbsp;
 

Be able to rotate the video blocks Be able to translate Be able to increase the difficulty of the puzzle on the fly. Zero interaction with the keyboard
Simple, right?&amp;nbsp; You want to see a demo too see some awesomeness?&amp;nbsp; No problem, 
head over to my site and see it in person. 
Microsoft Surface Video Puzzle 
 
Clint&#39;s Silverlight Video Puzzle 
 
To the Internet Batman!
To help save time building this, I first when out and attempting to see if anything has been done anything close to this before.&amp;nbsp; I found 
Also I need 2 small controls which I found out are in the Silverlight SDK.&amp;nbsp; I needed horizontal slider controls for this app.&amp;nbsp; While I know a text box control would have worked just as well, see requirement 4. 
On the Silverlight community site, there is also a very nice demo showing the 
pho</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Silverlight-Dynamic-Video-Puzzle</link>
      <pubDate>Tue, 22 Jan 2008 16:02:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Silverlight-Dynamic-Video-Puzzle</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/7163484_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/7163484_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Clint Rutkas </dc:creator>
      <itunes:author>Clint Rutkas </itunes:author>
      <slash:comments>5</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Silverlight-Dynamic-Video-Puzzle/RSS</wfw:commentRss>
      <category>Media</category>
      <category>Silverlight</category>
      <category>Web</category>
      <category>Puzzle</category>
      <category>Mash Up</category>
      <category>web miscellaneous</category>
    </item>
  <item>
      <title>Facebook Developer Toolkit</title>
      <description><![CDATA[
<p>&nbsp;</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/wpF_5B2_5D.jpg"><img border="0" align="left" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/wpF_thumb.jpg" width="240" height="179"></a>
 Do you have a Facebook account and want to do more with it? Start creating cool, fun Windows applications and Web sites with the Facebook Developer Toolkit. This toolkit offers you a huge amount of easy-to-use components and controls, plus ready-to-run samples
 and detailed documentation to get you started. With Visual Studio Express Editions and the Facebook Developer Toolkit, you'll have the tools you need to get going today!
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/AddFacebook_5B2_5D.jpg"><img border="0" align="right" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/AddFacebook_thumb.jpg" width="240" height="228"></a>
 The Facebook Developer Toolkit is a full complement of components, controls and samples for developing applications using the Facebook Platform. The kit also includes Visual C# and Visual Basic wrappers for the Facebook API to make application development
 simple, fun and quick. You can use the Facebook Developer Toolkit to build applications using Windows Forms, ASP.NET or WPF. You can even use the toolkit to take advantage of LINQ.
</p>
<h5>See How Easy It Is</h5>
<p>Explore the <a href="http://msdn.microsoft.com/vstudio/express/showcase/quickstarts">
QuickStarts</a> to see just how easy it is to develop using the Facebook Developer Toolkit and Visual Studio Express Editions. Watch the
<a href="http://msdn.microsoft.com/vstudio/express/showcase/default.aspx#walkthrough">
walkthrough videos</a> to see how easy it is to build Facebook-enabled applications. You can have an
<a href="http://msdn.microsoft.com/vstudio/express/showcase/quickstarts/default.aspx#Win">
application</a> or <a href="http://msdn.microsoft.com/vstudio/express/showcase/quickstarts/default.aspx#web">
website</a> up and running in only a few minutes. </p>
<p>The Facebook Development Toolkit allows you to create applications that you can share with your Facebook friends.
<a href="http://facebooktoolkit.codeplex.com/Release/ProjectReleases.aspx">Download the toolkit today</a>!
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/FlitterImageS_5B2_5D.jpg"><img border="0" align="left" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2854939/FlitterImageS_thumb.jpg" width="205" height="240"></a>
 Want to see the Facebook Developer Toolkit in action? Download <a href="http://blogs.msdn.com/coding4fun/attachment/2854939.ashx" target="_blank">
this sample application</a> that uses the Facebook Developer Toolkit to build a dynamic screensaver with Visual C# Express Edition. Explore the
<a href="http://wiki.f8.facebook.com/">Facebook Developer Toolkit wiki</a> for more details on the toolkit.</p>
<br clear="all">
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:612172f98ed6467ab2039e7600d3cf05">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/blog/Facebook-Developer-Toolkit</comments>
      <itunes:summary>
&amp;nbsp; 

 Do you have a Facebook account and want to do more with it? Start creating cool, fun Windows applications and Web sites with the Facebook Developer Toolkit. This toolkit offers you a huge amount of easy-to-use components and controls, plus ready-to-run samples
 and detailed documentation to get you started. With Visual Studio Express Editions and the Facebook Developer Toolkit, you&#39;ll have the tools you need to get going today!
 

 The Facebook Developer Toolkit is a full complement of components, controls and samples for developing applications using the Facebook Platform. The kit also includes Visual C# and Visual Basic wrappers for the Facebook API to make application development
 simple, fun and quick. You can use the Facebook Developer Toolkit to build applications using Windows Forms, ASP.NET or WPF. You can even use the toolkit to take advantage of LINQ.
 
See How Easy It Is
Explore the 
QuickStarts to see just how easy it is to develop using the Facebook Developer Toolkit and Visual Studio Express Editions. Watch the

walkthrough videos to see how easy it is to build Facebook-enabled applications. You can have an

application or 
website up and running in only a few minutes.  
The Facebook Development Toolkit allows you to create applications that you can share with your Facebook friends.
Download the toolkit today!
 

 Want to see the Facebook Developer Toolkit in action? Download 
this sample application that uses the Facebook Developer Toolkit to build a dynamic screensaver with Visual C# Express Edition. Explore the
Facebook Developer Toolkit wiki for more details on the toolkit. 

</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/blog/Facebook-Developer-Toolkit</link>
      <pubDate>Thu, 24 May 2007 21:18:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/blog/Facebook-Developer-Toolkit</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/2854939_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/2854939_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Clint Rutkas</dc:creator>
      <itunes:author>Clint Rutkas</itunes:author>
      <slash:comments>24</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/blog/Facebook-Developer-Toolkit/RSS</wfw:commentRss>
      <category>Web</category>
      <category>Web Services</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>Creating a Vanity Search Page</title>
      <description><![CDATA[<span id="c4fmetadata">
<table cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td width="50">&nbsp;</td>
<td><span class="entry_description">It's all about you. Learn how to call the Windows Live Search service to create a vanity page that displays images, news articles, and websites about you.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author">Stephen Walther</div>
<div class="entry_company"><a href="http://www.superexpert.com">Superexpert Blog</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Easy</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
Less than 1 hour</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.microsoft.com/vstudio/express/vwd/">Visual Web Developer 2005 Express Edition</a></span></div>
<ul>
</ul>
<div></div>
</td>
</tr>
</tbody>
</table>
</span>
<p></p>
<p>This month, we create a vanity search page. The vanity search page displays any images, news or website entries about a particular person (for example, you). To create this page, we take advantage of the Windows Live Search API. This free API enables you
 to retrieve search results by calling a web service that Microsoft exposes on the web.
</p>
<p>I decided to write this article because I wanted an excuse to play with the Windows Live SDK (see
<a href="http://dev.live.com/">http://dev.live.com</a>). The SDK includes documentation and samples for accessing all of the Windows Live web services including the Windows Live Search Web Service, Virtual Earth, and Windows Live Alerts.
</p>
<p>I fully admit that I&nbsp;was&nbsp;actively avoiding writing this article because I dreaded the thought of needing to learn yet another Application Programming Interface. I was pleasantly surprised to discover that the API for Windows Live Search is both logical and
 painlessly simple to understand. I wrote the code for this article in less than an hour.
</p>
<p>Before you can access the Windows Live services, you need to get an Application ID. You pass this ID to the Windows Live services to identify yourself. You get an Application ID by signing an agreement at the following URL:
<a href="http://search.msn.com/developer/appids.aspx">http://search.msn.com/developer/appids.aspx</a>.
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image08.png"><img height="178" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image0_thumb4.png" width="240" border="0"></a>
</p>
<p><strong>Figure 1 - Adding a Web Reference for Windows&nbsp;Live Search</strong> </p>
<p>After you have an Application ID, you are ready to start calling Windows Live web services. Here are the steps for creating the vanity search page:
</p>
<p>1. Launch Visual Web Developer and create a new Web Site </p>
<p>2. Select the menu option Website, Add Web Reference to add a Web Reference to the Windows Live Search web service (see Figure 1). Enter the following address into the URL field:
<a href="http://soap.search.msn.com/webservices.asmx?wsdl">http://soap.search.msn.com/webservices.asmx?wsdl</a>. When you click OK, a new Web reference named com.msn.search.soap is created.
</p>
<p>3. Create a new Web Form Page by selecting the menu option Website, Add New Item. When you create the new Web Form Page, give it the name Vanity.aspx. Also, make sure that the checkbox labeled
<b>Place code in separate file</b> is not checked. </p>
<p>4. Replace the contents of the page with the following code: </p>
<p><b>VB.NET Code</b>&nbsp; </p>
<div>
<pre class="csharpcode">&lt;%@ Page Language=<span class="str">&quot;VB&quot;</span> %&gt;

&lt;%@ Import <span class="kwrd">Namespace</span>=<span class="str">&quot;com.msn.search.soap&quot;</span> %&gt;

&lt;%@ Import <span class="kwrd">Namespace</span>=<span class="str">&quot;System.Text.RegularExpressions&quot;</span> %&gt;

&lt;!DOCTYPE html <span class="kwrd">PUBLIC</span> <span class="str">&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span> <span class="str">&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;</span>&gt;

&lt;script runat=<span class="str">&quot;server&quot;</span>&gt;

<span class="kwrd">Const</span> appId <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;&quot;</span>

<span class="kwrd">Const</span> query <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;&quot;</span><span class="str">&quot;Bill Gates&quot;</span><span class="str">&quot;&quot;</span>

<span class="kwrd">Const</span> startMark <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;\uE000&quot;</span>

<span class="kwrd">Const</span> endMark <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;\uE001&quot;</span>

<span class="kwrd">Private</span> <span class="kwrd">Sub</span> Page_Load()

<span class="rem">' Create instance of MSN Search Services</span>

<span class="kwrd">Dim</span> search <span class="kwrd">As</span> <span class="kwrd">New</span> MSNSearchService()

<span class="rem">' Build Source Request</span>

<span class="kwrd">Dim</span> NewsSource <span class="kwrd">As</span> <span class="kwrd">New</span> SourceRequest()

NewsSource.Source = SourceType.News

<span class="kwrd">Dim</span> webSource <span class="kwrd">As</span> <span class="kwrd">New</span> SourceRequest()

webSource.Source = SourceType.Web

<span class="kwrd">Dim</span> imageSource <span class="kwrd">As</span> <span class="kwrd">New</span> SourceRequest()

imageSource.Source = SourceType.Image

imageSource.ResultFields = ResultFieldMask.Image <span class="kwrd">Or</span> ResultFieldMask.Url

<span class="rem">' Build the Search Request</span>

<span class="kwrd">Dim</span> request <span class="kwrd">As</span> <span class="kwrd">New</span> SearchRequest()

request.AppID = appId

request.Requests = <span class="kwrd">New</span> SourceRequest() {imageSource, NewsSource, webSource}

request.Query = query

request.CultureInfo = <span class="str">&quot;en-US&quot;</span>

request.Flags = SearchFlags.MarkQueryWords

<span class="rem">' Execute Search</span>

<span class="kwrd">Dim</span> response <span class="kwrd">As</span> SearchResponse = search.Search(request)

lstResponses.DataSource = response.Responses

lstResponses.DataBind()

<span class="kwrd">End</span> <span class="kwrd">Sub</span>

<span class="kwrd">Private</span> <span class="kwrd">Function</span> hiLite(<span class="kwrd">ByVal</span> item <span class="kwrd">As</span> <span class="kwrd">Object</span>) <span class="kwrd">As</span> <span class="kwrd">String</span>

<span class="kwrd">If</span> item <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>

<span class="kwrd">Return</span> <span class="kwrd">String</span>.Empty

<span class="kwrd">End</span> <span class="kwrd">If</span>

<span class="kwrd">Dim</span> text <span class="kwrd">As</span> <span class="kwrd">String</span> = item.ToString()

text = Regex.Replace(text, startMark, <span class="str">&quot;&lt;span class='hiLite'&gt;&quot;</span>)

text = Regex.Replace(text, endMark, <span class="str">&quot;&lt;/span&gt;&quot;</span>)

<span class="kwrd">Return</span> text

<span class="kwrd">End</span> <span class="kwrd">Function</span>

<span class="kwrd">Private</span> <span class="kwrd">Function</span> toImage(<span class="kwrd">ByVal</span> objImage <span class="kwrd">As</span> <span class="kwrd">Object</span>) <span class="kwrd">As</span> <span class="kwrd">String</span>

<span class="kwrd">If</span> <span class="kwrd">Not</span> objImage <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>

<span class="kwrd">Dim</span> image <span class="kwrd">As</span> com.msn.search.soap.Image = <span class="kwrd">CType</span>(objImage, com.msn.search.soap.Image)

<span class="kwrd">Return</span> image.ImageURL

<span class="kwrd">End</span> <span class="kwrd">If</span>

<span class="kwrd">Return</span> <span class="kwrd">String</span>.Empty

<span class="kwrd">End</span> <span class="kwrd">Function</span>

&lt;/script&gt;

&lt;html xmlns=<span class="str">&quot;http://www.w3.org/1999/xhtml&quot;</span> &gt;

&lt;head runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;title&gt;Untitled Page&lt;/title&gt;

&lt;style type=<span class="str">&quot;text/css&quot;</span>&gt;

html 

{

font:12px Verdana;

}

a {text-decoration:none}

.hiLite {background-color:yellow}

&lt;/style&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;form id=<span class="str">&quot;form1&quot;</span> runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;div&gt;

&lt;asp:DataList

id=<span class="str">&quot;lstResponses&quot;</span>

Runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;ItemTemplate&gt;

&lt;h1&gt;&lt;%# Eval(<span class="str">&quot;Source&quot;</span>) %&gt; (&lt;%# Eval(<span class="str">&quot;Total&quot;</span>) %&gt; Results)&lt;/h1&gt;

&lt;asp:Repeater

id=<span class="str">&quot;grdResults&quot;</span>

DataSource=<span class="rem">'&lt;%# Eval(&quot;Results&quot;) %&gt;'</span>

Runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;HeaderTemplate&gt;

&lt;ul&gt;

&lt;/HeaderTemplate&gt;

&lt;ItemTemplate&gt;

&lt;li&gt;

&lt;asp:HyperLink 

id=<span class="str">&quot;lnkResult&quot;</span>

ImageUrl=<span class="rem">'&lt;%# toImage( Eval(&quot;Image&quot;)) %&gt;'</span>

Text=<span class="rem">'&lt;%# hiLite( Eval(&quot;Title&quot;) ) %&gt;'</span>

NavigateUrl=<span class="rem">'&lt;%# Eval(&quot;Url&quot;) %&gt;'</span>

Runat=<span class="str">&quot;server&quot;</span> /&gt;

&lt;p&gt;

&lt;%# hiLite( Eval(<span class="str">&quot;Description&quot;</span>) ) %&gt;

&lt;/p&gt;

&lt;/li&gt;

&lt;/ItemTemplate&gt;

&lt;FooterTemplate&gt;

&lt;/ul&gt;

&lt;/FooterTemplate&gt;

&lt;/asp:Repeater&gt;

&lt;/ItemTemplate&gt;

&lt;/asp:DataList&gt;

&lt;/div&gt;

&lt;/form&gt;

&lt;/body&gt;

&lt;/html&gt;

</pre>
</div>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>C# Code</b> </p>
<p>&nbsp; </p>
<div>
<pre class="csharpcode">&lt;%@ Page Language=<span class="str">&quot;C#&quot;</span> %&gt;

&lt;%@ Import Namespace=<span class="str">&quot;com.msn.search.soap&quot;</span> %&gt;

&lt;%@ Import Namespace=<span class="str">&quot;System.Text.RegularExpressions&quot;</span> %&gt;

&lt;!DOCTYPE html PUBLIC <span class="str">&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span> <span class="str">&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;</span>&gt;

&lt;script runat=<span class="str">&quot;server&quot;</span>&gt;

<span class="kwrd">const</span> <span class="kwrd">string</span> appId = <span class="str">&quot;&quot;</span>;

<span class="kwrd">const</span> <span class="kwrd">string</span> query = <span class="str">&quot;\&quot;Bill Gates\&quot;&quot;</span>;

<span class="kwrd">const</span> <span class="kwrd">string</span> startMark = <span class="str">&quot;\uE000&quot;</span>;

<span class="kwrd">const</span> <span class="kwrd">string</span> endMark = <span class="str">&quot;\uE001&quot;</span>;

<span class="kwrd">void</span> Page_Load()

{

<span class="rem">// Create instance of MSN Search Services</span>

MSNSearchService search = <span class="kwrd">new</span> MSNSearchService();

<span class="rem">// Build Source Request</span>

SourceRequest newsSource = <span class="kwrd">new</span> SourceRequest();

newsSource.Source = SourceType.News;

SourceRequest webSource = <span class="kwrd">new</span> SourceRequest();

webSource.Source = SourceType.Web;

SourceRequest imageSource = <span class="kwrd">new</span> SourceRequest();

imageSource.Source = SourceType.Image;

imageSource.ResultFields = ResultFieldMask.Image | ResultFieldMask.Url;

<span class="rem">// Build the Search Request</span>

SearchRequest request = <span class="kwrd">new</span> SearchRequest();

request.AppID = appId;

request.Requests = <span class="kwrd">new</span> SourceRequest[] {imageSource, newsSource, webSource};

request.Query = query;

request.CultureInfo = <span class="str">&quot;en-US&quot;</span>;

request.Flags = SearchFlags.MarkQueryWords;

<span class="rem">// Execute Search</span>

SearchResponse response = search.Search(request);

lstResponses.DataSource = response.Responses;

lstResponses.DataBind();

}

<span class="kwrd">string</span> hiLite(<span class="kwrd">object</span> item)

{

<span class="kwrd">if</span> (item == <span class="kwrd">null</span>)

<span class="kwrd">return</span> String.Empty;

<span class="kwrd">string</span> text = item.ToString();

text = Regex.Replace(text, startMark, <span class="str">&quot;&lt;span class='hiLite'&gt;&quot;</span>);

text = Regex.Replace(text, endMark, <span class="str">&quot;&lt;/span&gt;&quot;</span>);

<span class="kwrd">return</span> text;

}

<span class="kwrd">string</span> toImage(<span class="kwrd">object</span> objImage)

{

<span class="kwrd">if</span> (objImage != <span class="kwrd">null</span>)

{

com.msn.search.soap.Image image = (com.msn.search.soap.Image)objImage;

<span class="kwrd">return</span> image.ImageURL;

}

<span class="kwrd">return</span> String.Empty;

}

&lt;/script&gt;

&lt;html xmlns=<span class="str">&quot;http://www.w3.org/1999/xhtml&quot;</span> &gt;

&lt;head runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;title&gt;Vanity Search&lt;/title&gt;

&lt;style type=<span class="str">&quot;text/css&quot;</span>&gt;

html 

{

font:12px Verdana;

}

a {text-decoration:none}

.hiLite {background-color:yellow}

&lt;/style&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;form id=<span class="str">&quot;form1&quot;</span> runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;div&gt;

&lt;asp:DataList

id=<span class="str">&quot;lstResponses&quot;</span>

Runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;ItemTemplate&gt;

&lt;h1&gt;&lt;%# Eval(<span class="str">&quot;Source&quot;</span>) %&gt; (&lt;%# Eval(<span class="str">&quot;Total&quot;</span>) %&gt; Results)&lt;/h1&gt;

&lt;asp:Repeater

id=<span class="str">&quot;grdResults&quot;</span>

DataSource=<span class="str">'&lt;%# Eval(&quot;Results&quot;) %&gt;'</span>

Runat=<span class="str">&quot;server&quot;</span>&gt;

&lt;HeaderTemplate&gt;

&lt;ul&gt;

&lt;/HeaderTemplate&gt;

&lt;ItemTemplate&gt;

&lt;li&gt;

&lt;asp:HyperLink 

id=<span class="str">&quot;lnkResult&quot;</span>

ImageUrl=<span class="str">'&lt;%# toImage( Eval(&quot;Image&quot;)) %&gt;'</span>

Text=<span class="str">'&lt;%# hiLite( Eval(&quot;Title&quot;) ) %&gt;'</span>

NavigateUrl=<span class="str">'&lt;%# Eval(&quot;Url&quot;) %&gt;'</span>

Runat=<span class="str">&quot;server&quot;</span> /&gt;

&lt;p&gt;

&lt;%# hiLite( Eval(<span class="str">&quot;Description&quot;</span>) ) %&gt;

&lt;/p&gt;

&lt;/li&gt;

&lt;/ItemTemplate&gt;

&lt;FooterTemplate&gt;

&lt;/ul&gt;

&lt;/FooterTemplate&gt;

&lt;/asp:Repeater&gt;

&lt;/ItemTemplate&gt;

&lt;/asp:DataList&gt;

&lt;/div&gt;

&lt;/form&gt;

&lt;/body&gt;

&lt;/html&gt;

</pre>
</div>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>Before you use the vanity search page, you should modify two of the constants that appear at the top of the page: the appId and query constants. Assign the App ID that you get from visiting the following URL to the appId constant:
<a href="http://search.msn.com/developer/appids.aspx">http://search.msn.com/developer/appids.aspx</a>. Enter anything you want for the query constant. In the page above, the name Bill Gates (enclosed in quotes) is assigned to the query constant. When you open
 the page in a web browser, you should see a page similar to Figure 2. </p>
<p>&nbsp; </p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image07.png"><img height="240" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image06.png" width="228" border="0"></a>
</p>
<p><strong>Figure 2 - Image results for Bill Gates vanity search page</strong> </p>
<p>The page displays three different groups of results. First, the page displays a list of images that match the search query. Next, the page displays a list of news articles. Finally, the page displays a list of websites (see Figure 3).
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image010.png"><img height="240" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1855817/image09.png" width="228" border="0"></a>
</p>
<p><strong>Figure 3 - Web results&nbsp;for Bill Gates vanity search page</strong> </p>
<p>&nbsp; </p>
<p>The Windows Live Search web service currently supports the following types of searches:
</p>
<p>&nbsp; </p>
<ul>
<li>Image - Returns a list of images that match the search query. </li><li>Inline Answers - Only available for commercial use. This type of search returns information from Encarta, Finance, Weather, and Movie show times.
</li><li>News - Returns results from online news sources. </li><li>Phone Book - Returns results from white and yellow page phone books. </li><li>Query Location - Returns latitude and longitude information associated with a query.
</li><li>Spelling - Returns a list of spelling suggestions. </li><li>Web - Returns a list of websites that match the search query.</li></ul>
<p>&nbsp; </p>
<p>You perform a search by creating an instance of the SearchRequest class. You assign the text of your search query to the SearchRequest class's Query property. This class also includes a Requests collection. You add one or more SourceRequest objects to this
 collection that represent different types of searches (Image, Web, News, and so on). After you get all of the objects ready, you call the MSNSearchService.Search() method to actually perform the search.
</p>
<p>The search results are displayed with the help of a DataList and Repeater control. The DataList displays the source of the search and the number of results for each source returned. The Repeater control is nested in the DataList and it displays the actual
 search results.&nbsp;&nbsp; </p>
<h3>Conclusion</h3>
<p>As I mentioned before, I wrote this article as an excuse to play with the Windows Live SDK. I am very happy that I got the chance to experiment with this SDK since I discovered that it is very easy to work with. I'm looking forward to trying out some of
 the other Windows Live services such as the Virtual Earth and Alerts services over the coming months.
</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:03452b0c9a2e4ef49da49e7600d512db">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Creating-a-Vanity-Search-Page</comments>
      <itunes:summary>



&amp;nbsp;
It&#39;s all about you. Learn how to call the Windows Live Search service to create a vanity page that displays images, news articles, and websites about you.



Stephen Walther
Superexpert Blog

Difficulty: Easy
Time Required: 
Less than 1 hour
Cost: Free
Software: Visual Web Developer 2005 Express Edition








 
This month, we create a vanity search page. The vanity search page displays any images, news or website entries about a particular person (for example, you). To create this page, we take advantage of the Windows Live Search API. This free API enables you
 to retrieve search results by calling a web service that Microsoft exposes on the web.
 
I decided to write this article because I wanted an excuse to play with the Windows Live SDK (see
http://dev.live.com). The SDK includes documentation and samples for accessing all of the Windows Live web services including the Windows Live Search Web Service, Virtual Earth, and Windows Live Alerts.
 
I fully admit that I&amp;nbsp;was&amp;nbsp;actively avoiding writing this article because I dreaded the thought of needing to learn yet another Application Programming Interface. I was pleasantly surprised to discover that the API for Windows Live Search is both logical and
 painlessly simple to understand. I wrote the code for this article in less than an hour.
 
Before you can access the Windows Live services, you need to get an Application ID. You pass this ID to the Windows Live services to identify yourself. You get an Application ID by signing an agreement at the following URL:
http://search.msn.com/developer/appids.aspx.
 

 
Figure 1 - Adding a Web Reference for Windows&amp;nbsp;Live Search  
After you have an Application ID, you are ready to start calling Windows Live web services. Here are the steps for creating the vanity search page:
 
1. Launch Visual Web Developer and create a new Web Site  
2. Select the menu option Website, Add Web Reference to add a Web Reference to the Windows Live Search web service (see</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Creating-a-Vanity-Search-Page</link>
      <pubDate>Sat, 10 Mar 2007 20:09:12 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Creating-a-Vanity-Search-Page</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1855817_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1855817_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Stephen Walther</dc:creator>
      <itunes:author>Stephen Walther</itunes:author>
      <slash:comments>8</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Creating-a-Vanity-Search-Page/RSS</wfw:commentRss>
      <category>Web</category>
      <category>Web Services</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>Windows Clipboard Sharing Through Web Services</title>
      <description><![CDATA[<span id="c4fmetadata">
<table border="0" cellspacing="0" cellpadding="1" width="100%">
<tbody>
<tr class="entry_overview">
<td width="50">&nbsp;</td>
<td><span class="entry_description">This application transfers the contents of a machine's clipboard to another machine through the user of ASP .NET Web Services.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author">Brian Trautman</div>
<div class="entry_company"><a href=""></a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Easy</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
1-3 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input">None</span></div>
<div class="entry_details"><b>Download: </b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/clipboard.zip">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<h3>Background</h3>
<p>Have you ever worked on multiple machines and wished you could copy the clipboard contents from one machine to the other? I've often found myself wishing that there was a quick and easy way to move text snippets, screenshots, or even files to another machine
 with a simple copy and paste. If this sounds interesting to you then read on. </p>
<p>I wanted this to work regardless of whether both machines were online at the same time, and not be halted by firewalls, NAT's, etc. so I opted for a server based rather than a peer to peer architecture. The architecture involved a client application to transfer
 the clipboard contents to the server via a web services call, a web service to cache the clipboard contents, and another client component to retrieve the clipboard contents from the server and place them on the local machine's clipboard.
</p>
<p>To tackle this problem we need programmatic access to the clipboard on both the machine to copy from and also the machine we want to copy to. Luckily .NET provides a managed wrapper around the native Windows Clipboard API that gives us access to this. The
 relevant namespaces are Clipboard for C# and my.Computer.Clipboard. Since we're interested in moving clipboard objects from one machine to another we first need to determine what type of objects get placed onto the clipboard for our various actions (copy text,
 images, and files). Writing a quick code snippet with the Clipboard namespace allows us to iterate through all objects on the clipboard for various types of actions to see what we're dealing with.
</p>
<pre class="csharpcode">C#</pre>
<div class="csharpcode">
<pre class="alt">        IDataObject clipData = Clipboard.GetDataObject();</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//retrieve an array of strings for all the formats available on the clipboard.</span></pre>
<pre>        <span class="kwrd">string</span>[] formats = clipData.GetFormats();</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//iterate through the list of formats on the clipboard</span></pre>
<pre class="alt">        <span class="kwrd">foreach</span> (<span class="kwrd">string</span> format <span class="kwrd">in</span> formats)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//add each objeoct to an arraylist so we can inspect the object types</span></pre>
<pre>            <span class="kwrd">object</span> dataObject = clipData.GetData(format);</pre>
<pre class="alt">&nbsp;</pre>
<pre>        }</pre>
</div>
<style type="text/css">
<!--
.csharpcode
	{background-color:#ffffff;
	font-family:consolas,"Courier New",courier,monospace;
	color:black;
	font-size:small}
.csharpcode 
	{background-color:#ffffff;
	font-family:consolas,"Courier New",courier,monospace;
	color:black;
	font-size:small}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	margin:0em;
	width:100%}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    <span class="kwrd">Dim</span> clipData <span class="kwrd">As</span> IDataObject = Clipboard.GetDataObject</pre>
<pre>&nbsp;</pre>
<pre class="alt">    <span class="kwrd">Dim</span> formats() <span class="kwrd">As</span> <span class="kwrd">String</span> = clipData.GetFormats</pre>
<pre>&nbsp;</pre>
<pre class="alt">    <span class="rem">'iterate through the list of formats on the clipboard</span></pre>
<pre>    <span class="kwrd">For</span> <span class="kwrd">Each</span> format <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">In</span> formats</pre>
<pre class="alt">    <span class="rem">'add each objeoct to an arraylist so we can inspect the object types</span></pre>
<pre>          <span class="kwrd">Dim</span> dataObject <span class="kwrd">As</span> <span class="kwrd">Object</span> = clipData.GetData(Format)</pre>
<pre class="alt">    Next</pre>
</div>
<p>The screenshot below shows the results for having some text from Word copied onto the clipboard. Each format in the string array is a representation of the data on the clipboard. Since the target application (where you're going to paste to) is unknown the
 clipboard has multiple formats each containing the same data. The objective with this project is to replicate all these formats on the target machine's clipboard.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image02.png"><img border="0" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image0_thumb.png" width="717" height="212"></a>
</p>
<p>After inspection of the objects on the Clipboard for text, images, and file objects it was easy to determine that the primary object types we need to be concerned with. These are &quot;System.IO.MemoryStream&quot;, “System.IO.FileStream”, &quot;System.Drawing.Bitmap&quot;,
 and &quot;System.String&quot; . Since all this information would be transferred to the server via web services it's simple to serialize all objects to bytes for transmission. This is necessary for a number of reasons including the fact that complex objects such as MemoryStreams
 cannot be simply serialized and sent over the web service call as Strings can. In addition, some of the objects are larger than the web service call allows, and would need to be broken up into smaller pieces for transmission and then reassembled on the server
 side in the correct order. Again, when the client requests the clipboard items we'll need to disassemble each object, send it over the web service return result to the client, and then reassemble it.</p>
<p>The first item to build is a base function that handles the breaking up of these large streams into more manageable byte arrays for transmission to the web service. The function below performs this task by sending chunks of the MemoryStream limited in size
 by the “byteCount” constant. Once this limit is reached the buffer is sent over the web service call for storage and assembly on the server. Once we have either 0 bytes left to send, or a number less than “byteCount” we send the remaining elements of the buffer
 and signal the web service that this particular object is complete with the “isFinalTransaction” flag.
</p>
<p>C#</p>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">private</span> <span class="kwrd">void</span> UploadStreamBlock(<span class="kwrd">string</span> format, <span class="kwrd">string</span> objectType, MemoryStream memStream)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//each time we enter this function we have a new transaction beginning.  </span></pre>
<pre>            <span class="rem">//  A transaction represents a comlete</span></pre>
<pre class="alt">            <span class="rem">//object on the clipboard and we'll use this on the server side to know </span></pre>
<pre>            <span class="rem">//  how to put the stream back together</span></pre>
<pre class="alt">            <span class="kwrd">string</span> transactionGuid = System.Guid.NewGuid().ToString();</pre>
<pre>            memStream.Position = 0;</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">byte</span>[] buffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[byteCount];</pre>
<pre class="alt">            <span class="kwrd">bool</span> isFinalTransaction = <span class="kwrd">false</span>;</pre>
<pre>&nbsp;</pre>
<pre class="alt">            <span class="rem">//while the current stream position plus our byte count is less than the </span></pre>
<pre>            <span class="rem">//  length of the stream continue sending as much as we can.</span></pre>
<pre class="alt">            <span class="kwrd">while</span> ((memStream.Position &#43; byteCount) &lt;= memStream.Length)</pre>
<pre>            {</pre>
<pre class="alt">                <span class="rem">//if we happen to be on the last byte of the stream set the final </span></pre>
<pre>                <span class="rem">// transaction flag to true so the server will know that this is the </span></pre>
<pre class="alt">                <span class="rem">//  last bit of this transaction to expect.</span></pre>
<pre>                <span class="kwrd">if</span> (memStream.Position &#43; byteCount == memStream.Length)</pre>
<pre class="alt">                {</pre>
<pre>                    isFinalTransaction = <span class="kwrd">true</span>;</pre>
<pre class="alt">                }</pre>
<pre>                <span class="rem">//read the stream into our buffer for transmission over the web service.</span></pre>
<pre class="alt">                memStream.Read(buffer, 0, byteCount);</pre>
<pre>                ws.InsertMessageStream(buffer, format, objectType, transactionGuid, </pre>
<pre class="alt">                    isFinalTransaction, clipBoardGUID);</pre>
<pre>            }</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">long</span> remainingBytes = memStream.Length - memStream.Position;</pre>
<pre class="alt">            <span class="rem">//if we still have remaining bytes left figure out how many and transmit the </span></pre>
<pre>            <span class="rem">//  last bit of this ojbect over the web service.</span></pre>
<pre class="alt">            <span class="kwrd">if</span> ((<span class="kwrd">int</span>)remainingBytes &gt; 0)</pre>
<pre>            {</pre>
<pre class="alt">                <span class="kwrd">byte</span>[] remainingBuffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[(<span class="kwrd">int</span>)remainingBytes];</pre>
<pre>&nbsp;</pre>
<pre class="alt">                memStream.Read(remainingBuffer, 0, (<span class="kwrd">int</span>)remainingBytes);</pre>
<pre>                ws.InsertMessageStream(remainingBuffer, format, objectType, </pre>
<pre class="alt">                    transactionGuid, <span class="kwrd">true</span>, clipBoardGUID);</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
</div>
<p>VB</p>
<div class="csharpcode">
<pre class="alt">    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> UploadStreamBlock(<span class="kwrd">ByVal</span> format <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> objectType <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre>                                  <span class="kwrd">ByVal</span> memStream <span class="kwrd">As</span> MemoryStream)</pre>
<pre class="alt">        <span class="rem">'each time we enter this function we have a new transaction beginning. </span></pre>
<pre>        <span class="rem">' A transaction represents a comlete</span></pre>
<pre class="alt">        <span class="rem">'object on the clipboard and we'll use this on the server side to know how </span></pre>
<pre>        <span class="rem">' to put the stream back together</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> transactionGuid <span class="kwrd">As</span> <span class="kwrd">String</span> = System.Guid.NewGuid.ToString</pre>
<pre>        memStream.Position = 0</pre>
<pre class="alt">        <span class="kwrd">Dim</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((byteCount) - 1) {}</pre>
<pre>        <span class="kwrd">Dim</span> isFinalTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span> = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="rem">'while the current stream position plus our byte count is less than the </span></pre>
<pre>        <span class="rem">' length of the stream continue sending as much as we can.</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">While</span> ((memStream.Position &#43; byteCount) _</pre>
<pre class="alt">                    &lt;= memStream.Length)</pre>
<pre>            <span class="rem">'if we happen to be on the last byte of the stream set the final </span></pre>
<pre class="alt">            <span class="rem">' transaction flag to true so the server will know that this is the </span></pre>
<pre>            <span class="rem">' last bit of this transaction to expect.</span></pre>
<pre class="alt">            <span class="kwrd">If</span> ((memStream.Position &#43; byteCount) _</pre>
<pre>                        = memStream.Length) <span class="kwrd">Then</span></pre>
<pre class="alt">                isFinalTransaction = <span class="kwrd">True</span></pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">            <span class="rem">'ream the stream into our buffer for transmission over the web service.</span></pre>
<pre>            memStream.Read(buffer, 0, byteCount)</pre>
<pre class="alt">            clipService.InsertMessageStream(buffer, format, objectType, transactionGuid, _</pre>
<pre>                                            isFinalTransaction, clipBoardGUID)</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> remainingBytes <span class="kwrd">As</span> <span class="kwrd">Long</span> = (memStream.Length - memStream.Position)</pre>
<pre>        <span class="rem">'if we still have remaining bytes left figure out how many and </span></pre>
<pre class="alt">        <span class="rem">' transmit the last bit of this ojbect over the web service.</span></pre>
<pre>        <span class="kwrd">If</span> (<span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>) &gt; 0) <span class="kwrd">Then</span></pre>
<pre class="alt">            <span class="kwrd">Dim</span> remainingBuffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((<span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>)) - 1) {}</pre>
<pre>            memStream.Read(remainingBuffer, 0, <span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>))</pre>
<pre class="alt">            clipService.InsertMessageStream(remainingBuffer, format, objectType, _</pre>
<pre>                                            transactionGuid, <span class="kwrd">True</span>, clipBoardGUID)</pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
</div>
<p>The Server side of the web service needs to put the whole clipboard back together from a number of byte arrays so it's important that all the objects, their types, and formats be preserved for the clipboard to work properly on the target machine. We use
 the clipBoardGuid to determine if we are on a new clipboard posting or adding objects to an already existing instance. We use the isFinalTranaction flag to know if this byte array should be part of an existing transaction, or is the first in a new transaction.
 All clipboard items are saved to disk for later retrieval by any client requesting them. The code for this is below.
</p>
<p>C# </p>
<div class="csharpcode">
<pre class="alt">    [WebMethod]</pre>
<pre>    <span class="kwrd">public</span> <span class="kwrd">void</span> InsertMessageStream(<span class="kwrd">byte</span>[] buffer, <span class="kwrd">string</span> format, <span class="kwrd">string</span> objectType, </pre>
<pre class="alt">        <span class="kwrd">string</span> transactionGuid, <span class="kwrd">bool</span> isFinalTransaction, <span class="kwrd">string</span> clipBoardGUID)</pre>
<pre>    {</pre>
<pre class="alt">        <span class="rem">//always base the current directory on the clipboard that we're sending now.</span></pre>
<pre>        <span class="kwrd">string</span> clipBoardGUIDDirectory = </pre>
<pre class="alt">            System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; clipBoardGUID;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">try</span></pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//if the directory does not exist then delete all the other directories </span></pre>
<pre>            <span class="rem">// (clipboard instances) and create a new directory if the directory already </span></pre>
<pre class="alt">            <span class="rem">// exists then this particular transaction is part of the same clipboard so </span></pre>
<pre>            <span class="rem">// don't do anything. this works because othe clipboardDirectory is based </span></pre>
<pre class="alt">            <span class="rem">// off of the GUID sent from the client.</span></pre>
<pre>            <span class="kwrd">if</span> (!Directory.Exists(clipBoardGUIDDirectory))</pre>
<pre class="alt">            {</pre>
<pre>                <span class="kwrd">string</span>[] dirs = Directory.GetDirectories(</pre>
<pre class="alt">                    System.Web.HttpContext.Current.Request.PhysicalApplicationPath);</pre>
<pre>                <span class="kwrd">foreach</span> (<span class="kwrd">string</span> dir <span class="kwrd">in</span> dirs)</pre>
<pre class="alt">                {</pre>
<pre>                    Directory.Delete(dir, <span class="kwrd">true</span>);</pre>
<pre class="alt">                }</pre>
<pre>                Directory.CreateDirectory(clipBoardGUIDDirectory);</pre>
<pre class="alt">            }</pre>
<pre>        }</pre>
<pre class="alt">        <span class="kwrd">catch</span></pre>
<pre>        {</pre>
<pre class="alt">        }</pre>
<pre>        <span class="rem">//create the filename based on the current transaction, format, and object type.  </span></pre>
<pre class="alt">        <span class="rem">// We will parse this out later so we know how to add this back to the target clipboard.</span></pre>
<pre>        <span class="kwrd">string</span> fileName = clipBoardGUIDDirectory &#43; <span class="str">&quot;\\&quot; &#43; transactionGuid </pre>
<pre class="alt">            &#43; &quot;</span>_<span class="str">&quot; &#43; format &#43; &quot;</span>_&quot; &#43; objectType;</pre>
<pre>        FileStream fs = <span class="kwrd">new</span> FileStream(fileName, FileMode.Append, FileAccess.Write);</pre>
<pre class="alt">        fs.Position = fs.Length;</pre>
<pre>        fs.Write(buffer, 0, buffer.Length);</pre>
<pre class="alt">        fs.Close();</pre>
<pre>    }</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    &lt;WebMethod()&gt; <span class="kwrd">Public</span> <span class="kwrd">Sub</span> InsertMessageStream(<span class="kwrd">ByVal</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span>, <span class="kwrd">ByVal</span> _</pre>
<pre>            format <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> objectType <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> transactionGuid _</pre>
<pre class="alt">            <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> isFinalTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span>, <span class="kwrd">ByVal</span> clipBoardGUID <span class="kwrd">As</span> <span class="kwrd">String</span>)</pre>
<pre>        <span class="rem">'always base the current directory on the clipboard that we're sending now.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> clipBoardDataDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = _</pre>
<pre>        (System.Web.HttpContext.Current.Request.PhysicalApplicationPath _</pre>
<pre class="alt">         &#43; <span class="str">&quot;\\Clipboard_Data&quot;</span>)</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardGUIDDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = (clipBoardDataDirectory &#43; _</pre>
<pre class="alt">                                                (<span class="str">&quot;\\&quot;</span> &#43; clipBoardGUID))</pre>
<pre>        <span class="kwrd">Try</span></pre>
<pre class="alt">            <span class="rem">'if the directory does not exist then delete all the other directories (clipboard </span></pre>
<pre>            <span class="rem">' instances) and create a new directory if the directory already exists then this </span></pre>
<pre class="alt">            <span class="rem">' particular transaction is part of the same clipboard so don't do anything. this </span></pre>
<pre>            <span class="rem">' works because othe clipboardDirectory is based off of the GUID sent from the client.</span></pre>
<pre class="alt">            <span class="kwrd">If</span> <span class="kwrd">Not</span> Directory.Exists(clipBoardGUIDDirectory) <span class="kwrd">Then</span></pre>
<pre>                <span class="kwrd">Dim</span> dirs() <span class="kwrd">As</span> <span class="kwrd">String</span> = Directory.GetDirectories(clipBoardDataDirectory)</pre>
<pre class="alt">                <span class="kwrd">For</span> <span class="kwrd">Each</span> dir <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">In</span> dirs</pre>
<pre>                    Directory.Delete(dir, <span class="kwrd">True</span>)</pre>
<pre class="alt">                <span class="kwrd">Next</span></pre>
<pre>                Directory.CreateDirectory(clipBoardGUIDDirectory)</pre>
<pre class="alt">            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="kwrd">Catch</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">Try</span></pre>
<pre class="alt">        <span class="rem">'create the filename based on the current transaction, format, and object type.  We </span></pre>
<pre>        <span class="rem">' will parse this out later so we know how to add this back to the target clipboard.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> fileName <span class="kwrd">As</span> <span class="kwrd">String</span> = (clipBoardGUIDDirectory &#43; (<span class="str">&quot;\\&quot;</span> _</pre>
<pre>                    &#43; (transactionGuid &#43; (<span class="str">&quot;_&quot;</span> _</pre>
<pre class="alt">                    &#43; (format &#43; (<span class="str">&quot;_&quot;</span> &#43; objectType))))))</pre>
<pre>        <span class="kwrd">Dim</span> fs <span class="kwrd">As</span> FileStream = <span class="kwrd">New</span> FileStream(fileName, FileMode.Append, FileAccess.Write)</pre>
<pre class="alt">        fs.Position = fs.Length</pre>
<pre>        fs.Write(buffer, 0, buffer.Length)</pre>
<pre class="alt">        fs.Close()</pre>
<pre>    <span class="kwrd">End</span> Sub</pre>
</div>
<p>Each clipgoard format object is stored on disk for later retrievel by the client. Note in the screenshot below how the filename is used to store the unique transactionID for the object, the object type and also the clipboard format. All these pieces of information
 are necessary to reassemble the items correctly and place onto the target clipboard.
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image05.png"><img border="0" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image0_thumb1.png" width="728" height="198"></a>
</p>
<p>Now that we have a representation of each clipboard format object on the server we need a way to get each item back onto the target clipboard. The following web service method provides a return result of type “ClipboardStream”. The ClipboardStream object
 contains all relevant information necessary to reassemble each item onto the target clipboard. Since a web service is a request-response type relationship the web service expects the client to continue to call the web service until the all the clipboard items
 have been successfully received. In addition, further complexity is introduced because each individual clipboard item may be split into multiple items if they exceed the maximum length set by our contstant “byteCount”, therefore the target machine must keep
 track of each request and tell the server where the last transaction left off via the variable named “currentByte”. The web service code is shown below.
</p>
<p>C# </p>
<div class="csharpcode">
<pre class="alt">    [WebMethod]</pre>
<pre>    <span class="kwrd">public</span> ClipboardStream GetMessageStream(<span class="kwrd">string</span> transactionGUID, <span class="kwrd">string</span>[] </pre>
<pre class="alt">        previousTransactionGUIDs, <span class="kwrd">string</span> clipBoardGUID, <span class="kwrd">long</span> currentByte)</pre>
<pre>    {</pre>
<pre class="alt">        <span class="kwrd">string</span> clipBoardDataDirectory = </pre>
<pre>            System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; </pre>
<pre class="alt">            <span class="str">&quot;Clipboard_Data&quot;</span>;</pre>
<pre>        <span class="kwrd">string</span> clipBoardGUIDDirectory = clipBoardDataDirectory &#43; <span class="str">&quot;\\&quot; &#43; </pre>
<pre class="alt">            clipBoardGUID;</pre>
<pre>        string currentTransaction = &quot;</span><span class="str">&quot;;</pre>
<pre class="alt">        bool isLastTransaction = false;</pre>
<pre>&nbsp;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        //if the clipBoardGUID is not empty then we only need to make sure </pre>
<pre class="alt">        // that the directory still exists.</pre>
<pre>        if (clipBoardGUID != &quot;</span><span class="str">&quot;)</pre>
<pre class="alt">        {</pre>
<pre>            //if the directory does not exist throw an exception, it must </pre>
<pre class="alt">            // have already been deleted.</pre>
<pre>            if (!Directory.Exists(clipBoardGUIDDirectory))</pre>
<pre class="alt">            {</pre>
<pre>                throw new Exception(&quot;</span>Requested clipboard does not exist.<span class="str">&quot; &#43;</pre>
<pre class="alt">                        &quot;</span>It must have been deleted.&quot;);</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
<pre>        <span class="rem">//if the clipboardGUID is empty then this is the client's first contact </span></pre>
<pre class="alt">        <span class="rem">// with the server and we need</span></pre>
<pre>        <span class="rem">//to select the available clipboard GUID to return to the user.</span></pre>
<pre class="alt">        <span class="kwrd">else</span></pre>
<pre>        {</pre>
<pre class="alt">            <span class="kwrd">string</span>[] availableClipBoard = </pre>
<pre>                Directory.GetDirectories(clipBoardDataDirectory)[0].Split(<span class="str">'\\');</pre>
<pre class="alt">            clipBoardGUID = availableClipBoard[availableClipBoard.Length - 1];</pre>
<pre>            clipBoardGUIDDirectory &#43;= clipBoardGUID;</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        //we need to get the next transaction.  Each time we finish a transaction </pre>
<pre>        // we add it to previousTransactionGUIDs at the client end so we know not </pre>
<pre class="alt">        // to send it again.</pre>
<pre>        currentTransaction = GetCurrentTransaction(clipBoardGUIDDirectory, </pre>
<pre class="alt">            previousTransactionGUIDs);</pre>
<pre>&nbsp;</pre>
<pre class="alt">        //if the current transaction is null then we'</span>re done and there are no </pre>
<pre>        <span class="rem">// more to send to the client</span></pre>
<pre class="alt">        <span class="kwrd">if</span> (currentTransaction == <span class="kwrd">null</span>)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre>
<pre>        }</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//open the filestream and set it to the position requested by the client.</span></pre>
<pre class="alt">        FileStream fs = <span class="kwrd">new</span> FileStream(currentTransaction, FileMode.Open);</pre>
<pre>        fs.Position = currentByte;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//determind if this is the last transaction or not for this object </span></pre>
<pre class="alt">        <span class="rem">// so we can let the client know.</span></pre>
<pre>        <span class="kwrd">long</span> numBytesToRead = fs.Length - currentByte;</pre>
<pre class="alt">        <span class="kwrd">if</span> (numBytesToRead &gt; byteCount)</pre>
<pre>        {</pre>
<pre class="alt">            numBytesToRead = byteCount;</pre>
<pre>            isLastTransaction = <span class="kwrd">false</span>;</pre>
<pre class="alt">        }</pre>
<pre>        <span class="kwrd">else</span></pre>
<pre class="alt">        {</pre>
<pre>            isLastTransaction = <span class="kwrd">true</span>;</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//read the filestream bytes to the buffer and populate the object to </span></pre>
<pre>        <span class="rem">// return to the client.</span></pre>
<pre class="alt">        <span class="kwrd">byte</span>[] buffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[numBytesToRead];</pre>
<pre>        fs.Read(buffer, 0, (<span class="kwrd">int</span>)numBytesToRead);</pre>
<pre class="alt">        fs.Close();</pre>
<pre>&nbsp;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        FileInfo fi = <span class="kwrd">new</span> FileInfo(currentTransaction);</pre>
<pre class="alt">        ClipboardStream clipboardStream = <span class="kwrd">new</span> ClipboardStream();</pre>
<pre>        clipboardStream.Buffer = buffer;</pre>
<pre class="alt">        clipboardStream.ClipBoardID = clipBoardGUID;</pre>
<pre>        clipboardStream.Format = fi.Name.Split(<span class="str">'_'</span>)[1];</pre>
<pre class="alt">        clipboardStream.ObjectType = fi.Name.Split(<span class="str">'_'</span>)[2];</pre>
<pre>        clipboardStream.IsLastTransaction = isLastTransaction;</pre>
<pre class="alt">        clipboardStream.TransactionID = currentTransaction;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">return</span> clipboardStream;</pre>
<pre>    }</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    &lt;WebMethod()&gt; <span class="kwrd">Public</span> <span class="kwrd">Function</span> GetMessageStream(<span class="kwrd">ByVal</span> transactionGUID <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre>            <span class="kwrd">ByVal</span> previousTransactionGUIDs() <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> clipBoardGUID <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre class="alt">            <span class="kwrd">ByVal</span> currentByte <span class="kwrd">As</span> <span class="kwrd">Long</span>) <span class="kwrd">As</span> ClipboardStream</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardDataDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = _</pre>
<pre class="alt">            (System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; <span class="str">&quot;Clipboard_Data&quot;</span>)</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardGUIDDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = clipBoardDataDirectory</pre>
<pre class="alt">        <span class="kwrd">Dim</span> currentTransaction <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;&quot;</span></pre>
<pre>        <span class="kwrd">Dim</span> isLastTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span> = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="rem">'if the clipBoardGUID is not empty then we only need to make sure that </span></pre>
<pre>        <span class="rem">' the directory still exists.</span></pre>
<pre class="alt">        <span class="kwrd">If</span> (clipBoardGUID &lt;&gt; <span class="str">&quot;&quot;</span>) <span class="kwrd">Then</span></pre>
<pre>            <span class="rem">'if the directory does not exist throw an exception, it must have </span></pre>
<pre class="alt">            <span class="rem">' already been deleted.</span></pre>
<pre>            <span class="kwrd">If</span> <span class="kwrd">Not</span> Directory.Exists(clipBoardGUIDDirectory) <span class="kwrd">Then</span></pre>
<pre class="alt">                <span class="kwrd">Throw</span> <span class="kwrd">New</span> Exception(<span class="str">&quot;Requested clipboard does not exist.&quot;</span> &#43; _</pre>
<pre>                                    <span class="str">&quot;It must have been deleted.&quot;</span>)</pre>
<pre class="alt">            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">        <span class="rem">'if the clipboardGUID is empty then this is the client's first contact </span></pre>
<pre>        <span class="rem">' with the server and we need</span></pre>
<pre class="alt">        <span class="rem">'to select the available clipboard GUID to return to the user.</span></pre>
<pre>        <span class="kwrd">Dim</span> availableClipBoard() <span class="kwrd">As</span> <span class="kwrd">String</span> = Directory.GetDirectories _</pre>
<pre class="alt">            (clipBoardDataDirectory)(0).Split(Microsoft.VisualBasic.ChrW(92))</pre>
<pre>        clipBoardGUID = availableClipBoard((availableClipBoard.Length - 1))</pre>
<pre class="alt">        clipBoardGUIDDirectory = (clipBoardGUIDDirectory &#43; <span class="str">&quot;\&quot;</span> &#43; clipBoardGUID)</pre>
<pre>        <span class="rem">'we need to get the next transaction.  Each time we finish a transaction </span></pre>
<pre class="alt">        <span class="rem">' we add it to previousTransactionGUIDs</span></pre>
<pre>        <span class="rem">'at the client end so we know not to send it again.</span></pre>
<pre class="alt">        currentTransaction = GetCurrentTransaction(clipBoardGUIDDirectory, _</pre>
<pre>                                                   previousTransactionGUIDs)</pre>
<pre class="alt">        <span class="rem">'if the current transaction is null then we're done and there are no </span></pre>
<pre>        <span class="rem">' more to send to the client</span></pre>
<pre class="alt">        <span class="kwrd">If</span> (currentTransaction <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre>            <span class="kwrd">Return</span> <span class="kwrd">Nothing</span></pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="rem">'open the filestream and set it to the position requested by the client.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> fs <span class="kwrd">As</span> FileStream = <span class="kwrd">New</span> FileStream(currentTransaction, FileMode.Open)</pre>
<pre>        fs.Position = currentByte</pre>
<pre class="alt">        <span class="rem">'determind if this is the last transaction or not for this object so </span></pre>
<pre>        <span class="rem">' we can let the client know.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> numBytesToRead <span class="kwrd">As</span> <span class="kwrd">Long</span> = (fs.Length - currentByte)</pre>
<pre>        <span class="kwrd">If</span> (numBytesToRead &gt; byteCount) <span class="kwrd">Then</span></pre>
<pre class="alt">            numBytesToRead = byteCount</pre>
<pre>            isLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="kwrd">Else</span></pre>
<pre>            isLastTransaction = <span class="kwrd">True</span></pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="rem">'read the filestream bytes to the buffer and populate the object to </span></pre>
<pre class="alt">        <span class="rem">' return to the client.</span></pre>
<pre>        <span class="kwrd">Dim</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((numBytesToRead) - 1) {}</pre>
<pre class="alt">        fs.Read(buffer, 0, <span class="kwrd">CType</span>(numBytesToRead, <span class="kwrd">Integer</span>))</pre>
<pre>        fs.Close()</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">Dim</span> fi <span class="kwrd">As</span> FileInfo = <span class="kwrd">New</span> FileInfo(currentTransaction)</pre>
<pre class="alt">        <span class="kwrd">Dim</span> clipboardStream <span class="kwrd">As</span> ClipboardStream = <span class="kwrd">New</span> ClipboardStream</pre>
<pre>        clipboardStream.Buffer = buffer</pre>
<pre class="alt">        clipboardStream.ClipBoardID = clipBoardGUID</pre>
<pre>        clipboardStream.Format = fi.Name.Split(Microsoft.VisualBasic.ChrW(95))(1)</pre>
<pre class="alt">        clipboardStream.ObjectType = fi.Name.Split(Microsoft.VisualBasic.ChrW(95))(2)</pre>
<pre>        clipboardStream.IsLastTransaction = isLastTransaction</pre>
<pre class="alt">        clipboardStream.TransactionID = currentTransaction</pre>
<pre>        <span class="kwrd">Return</span> clipboardStream</pre>
<pre class="alt">    <span class="kwrd">End</span> Function</pre>
</div>
<p>The last remaining piece for our project to function correctly is the client code that receives the clipboard contents from the server and reassembles each item in the correct order, adding it to the clipboard with the correct format as each is completed.
 The client code for this is shown below.&nbsp;&nbsp;</p>
<pre class="csharpcode">C#</pre>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">string</span>[] transactionGuids = <span class="kwrd">null</span>;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        ClipboardService.ClipboardStream clipBoardStream = </pre>
<pre>            <span class="kwrd">new</span> WindowsApplication1.ClipboardService.ClipboardStream();</pre>
<pre class="alt">        DataObject dataObject = <span class="kwrd">new</span> DataObject();</pre>
<pre>        clipBoardStream.ClipBoardID = <span class="str">&quot;&quot;</span>;</pre>
<pre class="alt">        clipBoardStream.IsLastTransaction = <span class="kwrd">false</span>;</pre>
<pre>        clipBoardStream.TransactionID = <span class="str">&quot;&quot;</span>;</pre>
<pre class="alt">        <span class="kwrd">long</span> currentByte = 0;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        Clipboard.Clear();</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//while we don't get null back keep on contacting the web service </span></pre>
<pre>        <span class="rem">// to get the next ojbect.</span></pre>
<pre class="alt">        <span class="kwrd">while</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre>        {</pre>
<pre class="alt">            MemoryStream memStream = <span class="kwrd">new</span> MemoryStream();</pre>
<pre>            <span class="rem">//while this is not the last transaction keep on contacting </span></pre>
<pre class="alt">            <span class="rem">// the web service to get the rest of this particular object.</span></pre>
<pre>            <span class="kwrd">while</span> (clipBoardStream.IsLastTransaction == <span class="kwrd">false</span>)</pre>
<pre class="alt">            {</pre>
<pre>                <span class="rem">//contact the web service to get the next transaction</span></pre>
<pre class="alt">                clipBoardStream = clipService.GetMessageStream(</pre>
<pre>                    clipBoardStream.TransactionID, transactionGuids, </pre>
<pre class="alt">                    clipBoardStream.ClipBoardID, currentByte);</pre>
<pre>&nbsp;</pre>
<pre class="alt">                <span class="kwrd">if</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre>                {</pre>
<pre class="alt">                    <span class="rem">//write the results to the memory stream</span></pre>
<pre>                    memStream.Write(clipBoardStream.Buffer, 0, </pre>
<pre class="alt">                        clipBoardStream.Buffer.Length);</pre>
<pre>                    <span class="rem">//increment the current byte so next time we contact </span></pre>
<pre class="alt">                    <span class="rem">// the webservice we'll pick up where we left off</span></pre>
<pre>                    currentByte = memStream.Position;</pre>
<pre class="alt">&nbsp;</pre>
<pre>                    <span class="rem">//if it is the last transaction then we need to place </span></pre>
<pre class="alt">                    <span class="rem">// this item onto the clipboard.</span></pre>
<pre>                    <span class="kwrd">if</span> (clipBoardStream.IsLastTransaction)</pre>
<pre class="alt">                    {</pre>
<pre>                        <span class="rem">//handle the clipBoardStream appropriately and add it </span></pre>
<pre class="alt">                        <span class="rem">// to the dataObject for posting to the clipblard.</span></pre>
<pre>                        HandleFinalTransaction(clipBoardStream, memStream, <span class="kwrd">ref</span> dataObject);</pre>
<pre class="alt">&nbsp;</pre>
<pre>                        <span class="rem">//resize the transactionGuids array as necessary and </span></pre>
<pre class="alt">                        <span class="rem">// add the current transaction so next time we contact </span></pre>
<pre>                        <span class="rem">// the web service we won't get this one again.</span></pre>
<pre class="alt">                        <span class="kwrd">if</span> (transactionGuids == <span class="kwrd">null</span>)</pre>
<pre>                        {</pre>
<pre class="alt">                            Array.Resize(<span class="kwrd">ref</span> transactionGuids, 1);</pre>
<pre>                        }</pre>
<pre class="alt">                        <span class="kwrd">else</span></pre>
<pre>                        {</pre>
<pre class="alt">                            Array.Resize(<span class="kwrd">ref</span> transactionGuids, </pre>
<pre>                                transactionGuids.Length &#43; 1);</pre>
<pre class="alt">                        }</pre>
<pre>                        transactionGuids[transactionGuids.Length - 1] = </pre>
<pre class="alt">                            clipBoardStream.TransactionID;</pre>
<pre>                    }</pre>
<pre class="alt">                }</pre>
<pre>                <span class="kwrd">else</span></pre>
<pre class="alt">                {</pre>
<pre>                    <span class="kwrd">break</span>;</pre>
<pre class="alt">                }</pre>
<pre>            }</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">if</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre class="alt">            {</pre>
<pre>                clipBoardStream.IsLastTransaction = <span class="kwrd">false</span>;</pre>
<pre class="alt">                currentByte = 0;</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        Clipboard.SetDataObject(dataObject, <span class="kwrd">true</span>);</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">Dim</span> transactionGuids() <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="kwrd">Nothing</span></pre>
<pre>        <span class="kwrd">Dim</span> clipBoardStream <span class="kwrd">As</span> ClipboardService.ClipboardStream = _</pre>
<pre class="alt">            <span class="kwrd">New</span> ClipboardVB.ClipboardService.ClipboardStream</pre>
<pre>        <span class="kwrd">Dim</span> dataObject <span class="kwrd">As</span> DataObject = <span class="kwrd">New</span> DataObject</pre>
<pre class="alt">        clipBoardStream.ClipBoardID = <span class="str">&quot;&quot;</span></pre>
<pre>        clipBoardStream.IsLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">        clipBoardStream.TransactionID = <span class="str">&quot;&quot;</span></pre>
<pre>        <span class="kwrd">Dim</span> currentByte <span class="kwrd">As</span> <span class="kwrd">Long</span> = 0</pre>
<pre class="alt">        Clipboard.Clear()</pre>
<pre>        <span class="rem">'while we don't get null back keep on contacting the web service </span></pre>
<pre class="alt">        <span class="rem">' to get the next ojbect.</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">While</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>)</pre>
<pre>            <span class="kwrd">Dim</span> memStream <span class="kwrd">As</span> MemoryStream = <span class="kwrd">New</span> MemoryStream</pre>
<pre class="alt">            <span class="rem">'while this is not the last transaction keep on contacting the</span></pre>
<pre>            <span class="rem">' web service to get the rest of this particular object.</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">While</span> (clipBoardStream.IsLastTransaction = <span class="kwrd">False</span>)</pre>
<pre class="alt">                <span class="rem">'contact the web service to get the next transaction</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">                clipBoardStream = clipService.GetMessageStream( _</pre>
<pre>                    clipBoardStream.TransactionID, transactionGuids, _</pre>
<pre class="alt">                    clipBoardStream.ClipBoardID, currentByte)</pre>
<pre>                <span class="kwrd">If</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre class="alt">                    <span class="rem">'write the results to the memory stream</span></pre>
<pre>                    memStream.Write(clipBoardStream.Buffer, 0, _</pre>
<pre class="alt">                                    clipBoardStream.Buffer.Length)</pre>
<pre>                    <span class="rem">'increment the current byte so next time we contact </span></pre>
<pre class="alt">                    <span class="rem">' the webservice we'll pick up where we left off</span></pre>
<pre>                    currentByte = memStream.Position</pre>
<pre class="alt">                    <span class="rem">'if it is the last transaction then we need to place </span></pre>
<pre>                    <span class="rem">' this item onto the clipboard.</span></pre>
<pre class="alt">                    <span class="kwrd">If</span> clipBoardStream.IsLastTransaction <span class="kwrd">Then</span></pre>
<pre>                        <span class="rem">'handle the clipBoardStream appropriately and add </span></pre>
<pre class="alt">                        <span class="rem">' it to the dataObject for posting to the clipblard.</span></pre>
<pre>                        HandleFinalTransaction(clipBoardStream, memStream, dataObject)</pre>
<pre class="alt">                        <span class="rem">'resize the transactionGuids array as necessary and </span></pre>
<pre>                        <span class="rem">' add the current transaction so next time we contact</span></pre>
<pre class="alt">                        <span class="rem">' the web service we won't get this one again.</span></pre>
<pre>                        <span class="kwrd">If</span> (transactionGuids <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre class="alt">                            Array.Resize(transactionGuids, 1)</pre>
<pre>                        <span class="kwrd">Else</span></pre>
<pre class="alt">                            Array.Resize(transactionGuids, (transactionGuids.Length &#43; 1))</pre>
<pre>                        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">                        transactionGuids((transactionGuids.Length - 1)) = _</pre>
<pre>                            clipBoardStream.TransactionID</pre>
<pre class="alt">                    <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>                <span class="kwrd">Else</span></pre>
<pre class="alt">                    <span class="kwrd">Exit</span> <span class="kwrd">While</span></pre>
<pre>                <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">            <span class="kwrd">If</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre>                clipBoardStream.IsLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">                currentByte = 0</pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">        Clipboard.SetDataObject(dataObject, <span class="kwrd">True</span>)</pre>
</div>
<h3>Conclusion</h3>
<p>In this project we've shown how any object (string, bitmap, or file) from the Clipboard can be serialized to an array of bytes for transmission over a web service call. We've also show how to store these clipboard items on disk on the server, and then retrieve
 them later on for assembly onto the target clipboard. I've often thought that this would be an interesting idea for a hosted ASP (Application Service Provider) service, where a hosting company would host the web service of this project for customer use. Look
 out for an upcoming article on how to add ASP .NET membership class functionality providing authentication and allowing multiple users to use the same instance of the web service.
</p>
<p>For working samples of the client application and web service in both C# and VB check out the&nbsp;<a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/clipboard.zip">DOWNLOAD</a>.
</p>
<h3>Bio</h3>
<p>As President and principal founder of Personify Design Brian oversees the operations of the design and development businesses. Brian has more than 10 years experience in the technology industry. In his current role Brian's expertise lies in developing and
 architecting end to end customer solutions involving web application technologies such as SQL Server and ASP .NET. When not writing code Brian enjoys sailing in the Puget Sound on Far Niente, a 36 foot Catalina MKII cutter.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:4e78f4bb8a2d43d8a82a9e7600d523f7">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</comments>
      <itunes:summary>



&amp;nbsp;
This application transfers the contents of a machine&#39;s clipboard to another machine through the user of ASP .NET Web Services.



Brian Trautman


Difficulty: Easy
Time Required: 
1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware: None
Download: Download








Background
Have you ever worked on multiple machines and wished you could copy the clipboard contents from one machine to the other? I&#39;ve often found myself wishing that there was a quick and easy way to move text snippets, screenshots, or even files to another machine
 with a simple copy and paste. If this sounds interesting to you then read on.  
I wanted this to work regardless of whether both machines were online at the same time, and not be halted by firewalls, NAT&#39;s, etc. so I opted for a server based rather than a peer to peer architecture. The architecture involved a client application to transfer
 the clipboard contents to the server via a web services call, a web service to cache the clipboard contents, and another client component to retrieve the clipboard contents from the server and place them on the local machine&#39;s clipboard.
 
To tackle this problem we need programmatic access to the clipboard on both the machine to copy from and also the machine we want to copy to. Luckily .NET provides a managed wrapper around the native Windows Clipboard API that gives us access to this. The
 relevant namespaces are Clipboard for C# and my.Computer.Clipboard. Since we&#39;re interested in moving clipboard objects from one machine to another we first need to determine what type of objects get placed onto the clipboard for our various actions (copy text,
 images, and files). Writing a quick code snippet with the Clipboard namespace allows us to iterate through all objects on the clipboard for various types of actions to see what we&#39;re dealing with.
 
C#

        IDataObject clipData = Clipboard.GetDataObject();
&amp;nbsp;
        //retrieve an array of strings for all the f</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</link>
      <pubDate>Fri, 09 Mar 2007 21:16:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1848530_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1848530_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Brian Trautman</dc:creator>
      <itunes:author>Brian Trautman</itunes:author>
      <slash:comments>6</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services/RSS</wfw:commentRss>
      <category>Web Services</category>
      <category>Mash Up</category>
    </item>
  <item>
      <title>iAccelerate</title>
      <description><![CDATA[<span id="c4fmetadata">
<table cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td width="50"><img height="50" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1709215/rx8.PNG" width="50"></td>
<td><span class="entry_description">This article shows how to build a .NET application using an accelerometer to measure the performance of a vehicle. An accelerometer is an instrument that is used for measuring acceleration. By using basic physics equations,
 we can derive a vehicle's current speed, distance traveled, horsepower, and other performance metrics from the acceleration of the vehicle. iAccelerate is solely for use in a vehicle on a closed course or on private property while under the supervision of
 a certified emergency care provider. As a reminder, please always wear your safety belt.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author">Mike Harkabus</div>
<div class="entry_company"><a href="http://blogs.claritycon.com/blogs/mike_harkabus/default.aspx">Clarity Consulting</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Intermediate</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
6-10 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">$50-$100</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.microsoft.com/vstudio/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input">Analog Devices ADXL320</span></div>
<div class="entry_details"><b>Download: </b><a href="http://channel9.msdn.com/ShowPost.aspx?PostID=283953">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<h1></h1>
<h3>Introduction</h3>
<p>I have had a passion for computers and cars for as long as I can remember. Ever since I was little, I have been able to name the make and model of almost any car on the road. I have been around computers since I was young, and can still remember the day
 I wrote (read: “copied out of a book”) my first program in BASIC for IBM's PCjr. Twenty years later, these same interests coupled with an accelerometer played a role in the development of iAccelerate. iAccelerate displays accurate vehicle performance data
 from calculations performed 20-30 times per second. The data that is provided can be used to check if your car actually does benefit from a higher octane fuel, or to check if a new exhaust system has increased the horsepower as much as the manufacturer claims
 (or just makes a lot more noise). The article will first give you some background on what an accelerometer is, and show how you can use it to calculate performance data. An explanation of the development of iAccelerate will follow, describing the calculations
 and formulas used in code and the factors considered during development. Finally, I will demonstrate how iAccelerate works by walking you through a typical test scenario and comparing the results of the test to the results gathered from reputable sources to
 determine the accuracy of the data.</p>
<h3></h3>
<h3>Background</h3>
<p>In the abstract, I explained that an accelerometer is an instrument that is used to measure acceleration. Accelerometers are commonly used in many different types of applications. For example, they are used to determine if an airbag should be deployed during
 a crash, or to detect when your laptop has been dropped to prevent hard drive damage. Also, both the Nintendo Wii and PlayStation 3 controllers use accelerometers to calculate how you are holding or moving the controller. If you took physics class in high
 school or college, you may remember studying kinematics or dynamics. Together, these two areas of mechanics make up the foundation for iAccelerate. By using a few simple physics equations and an accelerometer, you can determine both the velocity (mph) and
 distance traveled (miles) of a car, assuming it is at a complete stop when the measurements are started. This assumption is important because acceleration is the rate of change of velocity with respect to time. For example, if we are sitting at 0 mph and we
 measure a change in acceleration with our accelerometer then we must be moving now since acceleration cannot occur without a change in velocity. We can determine the exact value of the car's velocity and distance traveled by using derivations of the following
 two equations: </p>
<blockquote>
<p></p>
<p>Velocity<sub>final</sub> = Velocity<sub>initial</sub> &#43; Acceleration × Time</p>
<p>Distance = 1/2 (Velocity<sub>final</sub> &#43; Velocity<sub>initial</sub> ) × Time</p>
</blockquote>
<p>Once we obtain these values, we can use a computer to calculate virtually any metric relating to a car's performance. The following must hold true for iAccelerate to provide accurate data: the accelerometer provides both the direction and magnitude of the
 acceleration; we are able to calculate the time spent accelerating; we were not moving when we started timing. Using iAccelerate, we can measure 0-60 mph (acceleration), 60-0 mph (braking distance), lateral acceleration (handling), ¼-mile time (acceleration),
 and much more. For example, if we provide the vehicle's weight, other measurements such as horsepower and torque can be calculated. The accuracy of the measurements for horsepower and torque can be improved significantly by using the vehicle's coefficient
 of drag to account for the air resistance incurred on the vehicle. The accuracy of the other measurements should theoretically be identical to a measurement taken with a radar gun, or from the use of a 5th wheel. In practice, however, factors such as the earth's
 gravity will introduce some margin of error. You will obtain the most accurate measurements by testing on a flat surface.
</p>
<h3>Development</h3>
<p>This application was developed using the 2005 Express Editions of VB.NET and Visual C#, which are available free of charge from the link provided at the beginning of this article. I created the project with each class being as loosely coupled from one another
 as possible. The project has a class called CarCalculator that is responsible for all of the necessary physics calculations and handles the conversion of the calculated results to U.S. customary units. This class is instantiated by calling the constructor
 with the gross weight (in pounds) of the car tested as its parameter. Most properties of this class return their value in U.S. customary units but are set internally using the metric system of units, as these are the native units the class uses in the calculations.
 The following code is an example of how the CarCalculator stores weight privately (in kilograms) and how the class exposes this value publically (in pounds).
</p>
<p>The constructor: </p>
<p><strong>C#</strong></p>
<pre class="csharpcode"><span class="kwrd">public</span> CarCalculator(<span class="kwrd">double</span> weight)
{
     <span class="kwrd">this</span>.Weight = weight;
     <span class="kwrd">this</span>.reset();
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.NET</strong></p>
<p>&nbsp;</p>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Sub</span> <span class="kwrd">New</span>(<span class="kwrd">ByVal</span> weight <span class="kwrd">As</span> <span class="kwrd">Double</span>)
    <span class="kwrd">Me</span>.Weight = weight
    <span class="kwrd">Me</span>.reset()
<span class="kwrd">End</span> <span class="kwrd">Sub</span> </pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>The Weight property of the class:</p>
<p><strong>C#</strong></p>
<p></p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">double</span> Weight
{
    get     
    {
         <span class="kwrd">return</span> _weight;
    }
    set
    {
         _weight = (<span class="kwrd">value</span> / POUNDS_PER_KG);
    }
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<div class="csharpcode"><strong>VB.NET</strong></div>
<div class="csharpcode">&nbsp;</div>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Property</span> Weight() <span class="kwrd">As</span> <span class="kwrd">Double</span>
    <span class="kwrd">Get</span>
        <span class="kwrd">Return</span> _weight
    <span class="kwrd">End</span> <span class="kwrd">Get</span>
    <span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> Value <span class="kwrd">As</span> <span class="kwrd">Double</span>)
        _weight = Value / POUNDS_PER_KG
    <span class="kwrd">End</span> <span class="kwrd">Set</span>
<span class="kwrd">End</span> <span class="kwrd">Property</span> </pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>&nbsp;</p>
<p>The CarCalculator uses an event model to notify consumers during a test. The SpeedReachedCheckpoint events are fired when the CurrentSpeed property reaches one of the speeds (e.g. 60 mph) added by the user in the Setup form. This functionality is shown in
 the following code snippets.</p>
<p>&nbsp;</p>
<p><strong>C#</strong></p>
<div class="csharpcode"></div>
<pre class="csharpcode"><span class="kwrd">if</span> (_checkPoints != <span class="kwrd">null</span>)
{
    <span class="kwrd">foreach</span> (<span class="kwrd">double</span> speed <span class="kwrd">in</span> _checkPoints)
    {
        <span class="kwrd">if</span> (_lastCheckpoint != Math.Floor(speed)
             &amp;&amp;  (Math.Floor(CurrentSpeed) == Math.Floor(speed)))
        {
            SpeedReachedCheckpoint(_totalTime, CurrentSpeed);
            _lastCheckpoint = Math.Floor(CurrentSpeed);
        }
     }
 }</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><strong>VB.NET</strong></p>
<p><strong></strong>&nbsp;</p>
<pre class="csharpcode"><span class="kwrd">If</span> <span class="kwrd">Not</span> _checkPoints <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>
    <span class="kwrd">For</span> <span class="kwrd">Each</span> speed <span class="kwrd">As</span> <span class="kwrd">Double</span> <span class="kwrd">In</span> _checkPoints
        <span class="kwrd">If</span> lastCheckpoint &lt;&gt; Math.Floor(speed) <span class="kwrd">AndAlso</span> Math.Floor(CurrentSpeed) = Math.Floor(speed) <span class="kwrd">Then</span>
            <span class="kwrd">RaiseEvent</span> SpeedReachedCheckpoint(_totalTime, CurrentSpeed)
            lastCheckpoint = Math.Floor(CurrentSpeed)
        <span class="kwrd">End</span> <span class="kwrd">If</span>
    <span class="kwrd">Next</span>
<span class="kwrd">End</span> <span class="kwrd">If</span>
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>The next class I will discuss, called Setup, is instantiated only when the application first loads. It is solely responsible for handling all of the data that is user configurable and validating input for any user-supplied settings, such as ”speed reached
 checkpoints”, vehicle and passenger weights, and the car's coefficient of drag (if horsepower is being measured).
</p>
<p>Figure 1 (below) shows the Setup form. </p>
<p>The next class, called Main, is responsible for handling all of the input and output to and from the CarCalculator class. The accelerometer raises an event providing the current acceleration value to the wired event handler 20-30 times per second. This value,
 along with the amount of time that has elapsed since the last time the event was raised, is passed into a public method in the CarCalculator class. This method uses these two values to perform all of the calculations, and sets the results of the calculations
 to properties in the CarCalculator class. Since this event is fired so frequently, the Main class/form only displays these results on the screen four times per second. The user can also configure the application to write the results to a CSV file for further
 analysis. In this case, a row is added to a file every time this event is fired. Consequently, the resulting file will contain the results of every calculation made by the CarCalculator. This file can be opened with Excel to create charts and graphs so the
 data can be easily interpreted. </p>
<p>This event handler is very hard to test or debug because all of the calculations performed are based on time. For instance, if you set a breakpoint in the event handler so you can evaluate an expression, the Stopwatch object responsible for measuring the
 elapsed time will continue to tick away when the debugger reaches the breakpoint – rendering it nearly impossible to evaluate the expression the next time the event is fired. The elapsed time value that the CarCalculator relies on will now also include the
 time that it took to debug, ultimately resulting in inaccurate output. </p>
<p>&nbsp; </p>
<div class="csharpcode">
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">void</span> accel_AccelerationChange(<span class="kwrd">object</span> sender, AccelerationChangeEventArgs e)</pre>
<pre>&nbsp;</pre>
<pre class="alt">{</pre>
<pre>    <span class="rem">// pseudo code</span></pre>
<pre class="alt">    time = SetElapsedTimeSinceLastCalculation;</pre>
<pre>    </pre>
<pre class="alt">    <span class="rem">// when a breakpoint is set to check the results of the following</span></pre>
<pre>    <span class="rem">// statement, the time value will be increasing, causing inaccurate</span></pre>
<pre class="alt">    <span class="rem">// data the next time this code is executed</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">    CalculateVehiclePerformance(time, e.Acceleration);</pre>
<pre>    <span class="rem">// example calculation (used in the above method): </span></pre>
<pre class="alt">    <span class="rem">// currentSpeed = lastSpeed &#43; (((e.Acceleration &#43; _lastAcceleration) * time) / 2);</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">    DisplayResults;</pre>
<pre>}</pre>
</div>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>&nbsp; </p>
<p>There is too much code to cover here, and I have already outlined the location and functionality of the methods that are pertinent to accurate data output. I highly recommend reading through the source code to fully understand the program. I will now demonstrate
 the application using the data from my RX-8's test run. </p>
<h3>Final Product</h3>
<p>To use this application, you will need a vehicle to test, a notebook computer, and an accelerometer. You can purchase a USB accelerometer from
<a href="http://www.trossenrobotics.com/">http://www.trossenrobotics.com/</a> for less than $70. A .NET assembly is also available for download from this site so you can start coding without having to write your own interface for the accelerometer. Most, if
 not all, RS232 accelerometers that I found would work just as well but would require much more development time. In addition, since you would be using the accelerometer from within the confines of a car, being able to quickly plug and unplug the device is
 essential. From information gathered from various forums, a tri-axis USB model is currently in development, which you could use to negate the error caused by the acceleration due to gravity that was mentioned earlier. I will now walk you through a scenario
 where the 0 to 60 mph time of my RX-8 is tested. </p>
<p>Setting the weight, output types, and checkpoints: </p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1709215/figure1.png"> </p>
<p>Figure 1 – Setting up the calculator<br>
</p>
<p>The frontal area of your car can be found by having a friend park behind you with their lights on (park your car's nose 5 feet or so from a wall or building) and then trace the shadow. Measure the shadow's area using a metric ruler. The current default setting
 is for an older RX-7 that I found data for. I am sure my car is a little bigger, so this probably has an effect on the resulting horsepower value. Since horsepower is the only calculation that uses this setting, all other data will be accurate – so in other
 words, you can leave this setting as the default if you do not know your car's frontal area. The other defaulted setting is the coefficient of drag, which is defaulted to the average value found for about ten sports cars. If you would like to use your car's
 specific value, you can find it by running your car's name through a search engine with the word “drag” next to it.
</p>
<p>&nbsp; </p>
<p>Main output screen (after start has been clicked): </p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1709215/figure2.png"> </p>
<p>Figure 2 – The application is waiting for the change of acceleration to exceed the value entered during the setup stage (.09g) before timing will begin.</p>
<p>&nbsp;</p>
<p>Main output screen results (10 seconds elapsed – timing over):</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1709215/figure3.png"> </p>
<p>Figure 3 – The results of a 0-60 test on a 2004 Mazda RX-8 (6-speed)<br>
</p>
<p>If you noticed, the 0-60 time arrived in 5.871x seconds. Two magazines quoted it right around 5.8 s while another quoted it at 6.5 s.
</p>
<p>&nbsp;</p>
<p><br>
Excel output (every calculation from the 10 second run is in this file – much more detailed than the screen's output):</p>
<h1></h1>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1709215/figure4.png"> </p>
<p>Figure 4 – A CSV file opened with Excel allowing for analysis of all of the calculated data (e.g. horsepower graph)</p>
<p>&nbsp;</p>
<h3>Conclusion</h3>
<p>I have tried to make this project as easy to modify as possible by keeping the data calculations separate from the display of calculated data to the user. This allows for greater flexibility when developing iAccelerate on other platforms. For example, rewriting
 this application for use on a Windows Mobile Smartphone would be cool, as you would not have to worry about breaking your laptop when you try to beat your cornering record. The processing power required to perform the needed calculations and output the results
 fast enough was unknown during design, so the decision to build a standard WinForm application was predisposed. If anyone redesigns this for use on a cell phone (or redesigns it at all), please provide some feedback on how it turns out. The use or modification
 of this software for educational or testing purposes is strongly encouraged. The developer prohibits the use of this source code and/or assembly for monetary and/or personal gain. If errors are discovered in any of the code or the calculations that were used
 within iAccelerate, please feel free to contact me at the following email address:&nbsp;<a title="" href="mailto:mharkabus@claritycon.com?subject=Accelerometer">mharkabus@claritycon.com</a></p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Tags/mash+up/RSS&WT.dl=0&WT.entryid=Entry:RSSView:6248e13ac4554cf684cc9e7600d5fed3">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/iAccelerate</comments>
      <itunes:summary>




This article shows how to build a .NET application using an accelerometer to measure the performance of a vehicle. An accelerometer is an instrument that is used for measuring acceleration. By using basic physics equations,
 we can derive a vehicle&#39;s current speed, distance traveled, horsepower, and other performance metrics from the acceleration of the vehicle. iAccelerate is solely for use in a vehicle on a closed course or on private property while under the supervision of
 a certified emergency care provider. As a reminder, please always wear your safety belt.



Mike Harkabus
Clarity Consulting

Difficulty: Intermediate
Time Required: 
6-10 hours
Cost: $50-$100
Software: Visual Basic or Visual C# Express Editions
Hardware: Analog Devices ADXL320
Download: Download









Introduction
I have had a passion for computers and cars for as long as I can remember. Ever since I was little, I have been able to name the make and model of almost any car on the road. I have been around computers since I was young, and can still remember the day
 I wrote (read: “copied out of a book”) my first program in BASIC for IBM&#39;s PCjr. Twenty years later, these same interests coupled with an accelerometer played a role in the development of iAccelerate. iAccelerate displays accurate vehicle performance data
 from calculations performed 20-30 times per second. The data that is provided can be used to check if your car actually does benefit from a higher octane fuel, or to check if a new exhaust system has increased the horsepower as much as the manufacturer claims
 (or just makes a lot more noise). The article will first give you some background on what an accelerometer is, and show how you can use it to calculate performance data. An explanation of the development of iAccelerate will follow, describing the calculations
 and formulas used in code and the factors considered during development. Finally, I will demonstrate how iAccelerate works by walking you through a typical tes</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/iAccelerate</link>
      <pubDate>Sun, 18 Feb 2007 21:13:29 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/iAccelerate</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1709215_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1709215_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Mike Harkabus</dc:creator>
      <itunes:author>Mike Harkabus</itunes:author>
      <slash:comments>9</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/iAccelerate/RSS</wfw:commentRss>
      <category>Hardware</category>
      <category>utility</category>
      <category>Mash Up</category>
    </item>    
</channel>
</rss>