TweetCraft - An in-game Twitter client for World of Warcraft
02 July 09 01:18 AM | Coding4Fun | 0 Comments   

Get Microsoft Silverlight

 

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).

  • Compiled version: Download
  • Source Code: Download
  • Difficulty: Intermediate
  • Time Required: 3-5 hours
  • Cost: $19.99 for World of Warcraft; $14.99 for monthly fee
  • Software Needed: AddOn Studio for World of Warcraft, Visual Studio Standard (or higher), although possible to use Visual C# Express as well,
  • Hardware: none

    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 Warcraft and uploads them using TwitPic immediately.

    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).

    Figure1_TweetCraftSettingsWindow
    Figure 1. TweetCraft Settings Window

    Figure2_TweetCraftTrayIcon
    Figure 2. TweetCraft tray icon in the notification area

    Getting data from Twitter

    TweetCraft uses an open-source library called TwitterLib to send and receive tweets using Twitter’s API. TwitterLib is part of the open-source WPF Twitter Client WittyTwitter 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 TwitterClient that all the services in TweetCraft rely on. One of those services is the TwitterBackgroundService 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.

    Figure3_TwitterBackgroundService
    Figure 3. TwitterBackgroundService, TwitterClient and TwitterNet (TwitterLib)

     

    Dynamically building TGA images

    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 Interface folder of World of Warcraft where the add-ons themselves also reside. The TwitterUserPictureService 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 TwitterUserPictureService sees that a file is already there, it won’t convert it again, saving some time and computing power.

    Figure 4 shows TwitterUserPictureService and two helper classes that take care of downloading and the actual conversion. The GetUserPicturePath method returns the path of the resulting World of Warcraft compatible TGA file for a single user.

    Figure4_TwitterUserPictureService
    Figure 4. TwitterUserPictureService and its helper classes

    Serializing tweets into Lua

    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… :)

    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 a = 5) 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.

    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 LuaSerializer that produces Lua code from any .NET object (with a few restrictions, of course), very similar to how XmlSerializer works.

    To make this even more simple, TweetCraft contains the concept of a SavedVariablesChannel 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 SavedVariablesChannel detects this by watching for changes made to the SavedVariables file using FileSystemWatcher and notifies other components of the TweetCraft application that can pull out the information and send the tweets.

    Figure 5 shows SavedVariablesChannel and its base class, ValueChannelBase which keeps track of all objects thrown into it until it is flushed.

    Figure5_SavedVariablesChannel
    Figure 5. SavedVariablesChannel and ValueChannelBase

    Refreshing the data in World of Warcraft

    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.

    Sending tweets and uploading screenshots

    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 SavedVariablesChannel and the ChannelUpdated 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 SavedVariablesChannel get quickly queued up in the TwitterDispatcherService 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 SavedVariablesChannel has a property called AutoFlush that is enabled by default.

    One thing we have not mentioned is that TwitterDispatcherService can also upload and post pictures using TwitPic. It watches the Screenshots 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.

    The TwitterDispatcherService uses TwitterClient (and TwitterLib) 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 TwitPicClient to upload and post pictures. Figure 6 shows TwitterDispatcherService, TwitterClient and TwitPicClient.

    Figure6_TwitterDispatcherService
    Figure 6. TwitterDispatcherService, TwitterClient and TwitPicClient

    Bringing it all together

    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 TweetCraft class implements the logic required and relies on the WorldOfWarcraft class for everything World of Warcraft related.

    Figure 7 shows the TweetCraft and WorldOfWarcraft side by side.

    Figure7_TweetCraft
    Figure 7. TweetCraft and WorldOfWarcraft

    Summary

    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, download the source code and take a deeper look.

     


    Gabor Ratky (Blog, Twitter), Senior Software Engineer and Coding4Fun Ninja at EPAM Systems 

    Gabor is the developer behind TweetCraft and has been working on Coding4Fun projects for almost two years. He also leads the development of AddOn Studio for World of Warcraft, a Visual Studio-based IDE for building World of Warcraft add-ons and he co-authored the chapter in the Coding4Fun 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.

     

     

  • Coding4Fun on the move
    30 June 09 10:27 AM | Coding4Fun | 0 Comments   

    channel9 Coding4Fun will be moving to Channel9 on MSDN!  This has been in the planning for a bit but giving you guys the heads up.  Jeff Sandquist and Dan Fernandez talk about some of the reasons why on the latest episode of This Week on Channel 9 (This is a rare super long episode).

    What this will allow us to do is provide a far better experience and we even have a few suprises in store.

    In addition to just the move, we’ve started our own show on Channel9 called Coding4Fun TV!  Brian Peek, the author behind the WiiMote c# Library, is the mastermind behind this idea and his first show is on the Physics Helper for Silverlight.

    Creating audio signals in .Net
    28 June 09 08:19 AM | Coding4Fun | 0 Comments   

    whiteNoiseDan Waters has created a 4 part series on audio programming with .Net.   Some point in the future, he’ll be posting a full article with WPF on Coding4Fun as well!

    1. How Audio Data is Represented
    2. Demystifying the WAV Format
    3. Synthesizing Simple Wave Audio using C#
    4. Algorithms for Different Sound Waves in C#

    Dan goes into depth on how audio is represented, explains how the WAV format works then creates different audio waves like the sawtooth, square and sine wave.  He gives the equation then an implementation with c#.  With part 3 and 4, Dan provides full source code so you too can rock out with some audio dial tones.

    Filed under: ,
    Shutdown/Restart/Logoff your PC using TweetMyPC
    23 June 09 12:01 PM | Coding4Fun | 2 Comments   

    In this article I will show you how you can use Twitter API to Shutdown/Restart/Logoff your PC remotely using VB.net.

    Introduction

    I have a very slow internet connection at home and most of the time Downloads takes hours to complete. I decided to write an application which will help me Shutdown my PC from a remote location. I wanted to use it to shutdown when I go out when some downloading is going on in my laptop. Instead of using a server and client architecture I decided to use Twitter API and use “My Timeline” to supply commands. One other reason to use Twitter is that I will be able to tweet from my mobile as well. I don’t need to look for a computer with an internet connection when I am on the move.

    Why Yedda Twitter framework?

    The Twitter REST API methods allow developers to access core Twitter data. This includes update timelines, status data, and user information. It’s very easy to connect to Twitter and get user time line using .net. But I dint wanted to reinvent wheel and decided to use an existing Twitter library. Yedda Twitter framework is the best open source Twitter library available in the internet. You can learn more and download the library from yedda’s home page.

    Designing the Interface

    I wanted the interface to be as simple as possible. Below is the screenshot of TweetMyPC’s interface which does not have more than 2 Text Boxes, 1 Check Box, Label and a Button.

    clip_image001

    The form also has a Notify Icon, Context Menu Strip and a Timer. Following are the names of all the controls in the form.

    • From : frmTweetMyPc
    • Text Boxes : txtUserName, txtPassword
    • Button : btnSave
    • Check Box : chkStartAutomatic
    • Timer : tmrTweet (Interval : 10000)
    • Label : lblSatus

    There are few My.settings properties to store user information. These properties are shown below.

    clip_image002

    The Code
    Add the following code which minimizes the Form on Load and Enable Timer to check for Tweets every 1 minute.

    Me.WindowState = FormWindowState.Minimized
    Me.ShowInTaskbar = False
    tmrTweet.Enabled = True

    The following code in Button’s Click event will validate Twitter username, Password and save it to My.Settings.

    If txtUserName.Text.Trim = "" Then
        lblSatus.Text = "Please enter Twitter Username"
        txtUserName.Focus()
        Exit Sub
    End If
    If txtPassword.Text.Trim = "" Then
        lblSatus.Text = "Please enter Twitter Password"
        txtPassword.Focus()
        Exit Sub
    End If
    
    lblSatus.Text = ""
    
    
    'Check for valid Username and Password and then Save Settings
    Dim objTwitter As New Yedda.Twitter
    Dim Updates As XmlDocument
    
    Try 'Try Logging in
        Updates = objTwitter.GetUserTimelineAsXML(txtUserName.Text.Trim, txtPassword.Text.Trim)
        My.Settings.UserName = txtUserName.Text.Trim
        My.Settings.Password = txtPassword.Text.Trim
        Me.WindowState = FormWindowState.Minimized
        Me.ShowInTaskbar = False
        tmrTweet.Enabled = True
    Catch ex As Exception
        MsgBox("Failed to Login to Twitter with the values supplied. Please check your login details.")
        txtUserName.Focus()
        Exit Sub
    End Try

    Yedda library is used to login to Twitter to check for valid username and password. The Label lblSatus is used to display any error messages.

    Add the following code to the Checkbox’s CheckChanged event which will add the required registry keys to start this app on Window’s startup

    If chkStartAutomatic.Checked = True Then       My.Settings.AutomaticStart = True
          Dim regKey As RegistryKey
          regKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", True)
          regKey.SetValue(Application.ProductName, Application.ExecutablePath)
          regKey.Close()
    Else
          My.Settings.AutomaticStart = False
          Dim regKey As RegistryKey
          regKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", True)
          regKey.DeleteValue(Application.ProductName)
          regKey.Close()
    End If

    The following code in Timer’s Tick event will do the job of checking Twitter Timeline every one minute and Shutdown/Restart/Log off the system based on the Tweet.

    If My.Settings.UserName.Trim = "" Then
        Exit Sub
    Else
        'Check for new Tweet 
        Dim objTwitter As New Yedda.Twitter
        Dim Updates As XmlDocument
        Dim node As XmlNode
        Try 'Try Logging in
            Updates = objTwitter.GetUserTimelineAsXML(My.Settings.UserName.Trim, My.Settings.Password.Trim)
            Catch ex As Exception
            MsgBox("Error : Failed to Login to Twitter with the values supplied. Please check your login details.")
            Exit Sub
        End Try
        Try
            node = Updates.SelectSingleNode("/statuses/status/id")
            If node.InnerText.Trim <> My.Settings.LastID.Trim Then 
                'Compare the Tweet ID to check for new tweets
                My.Settings.LastID = node.InnerText.Trim
                node = Updates.SelectSingleNode("/statuses/status/text")
                ProcessTweet(node.InnerText.Trim)
              End If
        Catch ex As Exception
            Exit Sub
        End Try
    End If

    If the Tweet is new then we process the tweet.

    Private Sub ProcessTweet(ByVal Tweet As String)
        If Tweet = "Shutdown" Then
            System.Diagnostics.Process.Start("shutdown", "-s -f -t 100") 'Shutdown
        ElseIf Tweet = "Logoff" Then
            System.Diagnostics.Process.Start("shutdown", "-l -f -t 100") 'Logoff
        ElseIf Tweet = "Restart" Then
            System.Diagnostics.Process.Start("shutdown", "-r -f -t 100") ' Restart
        End If
    End Sub

    The Context Menu strip has two menu items. They are Edit Setting and Exit.

    clip_image003

    Add the following code the “Edit Settings” Client event and Notify Icon’s Mouse Double Click Event.

    tmrTweet.Enabled = False
    txtUserName.Text = My.Settings.UserName.Trim
    txtPassword.Text = My.Settings.Password.Trim
    If My.Settings.AutomaticStart = True Then chkStartAutomatic.Checked = True
    Me.WindowState = FormWindowState.Normal
    Me.ShowInTaskbar = True

    TweetMyPC runs silently on startup. To Edit Settings double click/Right Click the notify icon.

    Working

    Conclusion

    Even though TweetMyPC is a simple app it has lot of interesting potential. Think about switching off your TV or washing machine using Twitter. It is possible with a little extra hardware and a simple .net program. TweetMyPC is free and open source. Feel free to download the sour code and add more functionality.

    About The Author

    Shoban Kumar is a Senior Software Engineer working for Allianz Cornhill India. Programming is his passion. He also writes about .net in http://www.dotnetcurry.com/ and an active participator in stackoverflow and speaker in Microsoft user group sessions. You also can follow him in Twitter.

    Box Selections and Multi-Edit in Visual Studio 2010
    21 June 09 11:12 PM | Coding4Fun | 6 Comments   

    I wasn’t aware of box selection which has been in Visual Studio for some time now until I viewed this video for what is coming up with Visual Studio 2010.  The video shows off some additional multiline editing functions added in 2010.

    All I have to say is holy gazooks Batman that is power!

    [via http://blog.filipfracz.net/]

    Filed under: ,
    XNA Game Studio 3.1
    11 June 09 12:14 PM | Coding4Fun | 0 Comments   

    cco_shell_default_header_xna_logo_print[1] XNA Game Studio 3.1 has just been released!  Download XNA GS 3.1 and check out the walkthrough for upgrading your old projects to v3.1!  For a more in-depth overview of what is new in 3.1, head to http://creators.xna.com/en-us/article/xnags3.1whatsnew.

    A quick overview of what is new:

    • Avatar Support: Render and animate Avatars to use in your game to represent gamers and other characters within your game.
    • Xbox LIVE Party Support: Enabling gamers to communicate, even when each gamer is not playing the same game in the same multiplayer session. LIVE Party supports up to an eight-way group voice chat for gamers and keeps gamers connected before, during, and after a gameplay session, persisting across title switches.
    • Video Playback: XNA Game Studio now supports the ability to play back video that can be used for such purposes as opening splash and logo scenes, cut scenes, or in-game video displays. This set of XNA Framework APIs supports the following features:
    • Full screen video playback
    • Video playback to simple textures in game
    • Control of playback such as pause/resume and stop
    • Retrieve properties of the video, such as playback time, size, and frame rate
    • Determine the type and usage of the audio track, such as if it has music, dialog, or music and dialog
    • Play back multiple video streams at the same time
    • Audio API: 3.1 has a new usage pattern of SoundEffect.Play. Sound instances created by Play calls are disposed automatically when playback ends, and SoundEffect.Play returns a Boolean to indicate success or failure.
    • Content Pipeline Enhancements: improvements making it much easier to add customer types (custom attributes for run-time of an object and run-time type version of an object, and the ability to determine if deserialization into an existing object is possible).
    • XACT3 Support: includes support for XACT3 with new features including the ability to enable a filter on every track, and support for the xWMA compression format.
    • Visual Studio Changes: XNA Game Studio 3.1 supports both 3.0 and 3.1 projects, and it includes support for upgrading projects from 3.0 to 3.1.
    Filed under: , ,
    Adding Sidetone to Skype
    09 June 09 02:44 PM | Coding4Fun | 0 Comments   

    clip_image001Ever use a headset with Skype – and were frustrated that it was too quiet? This article shows how to add the sound of your own voice to the headset and not feel exhausted from shouting to be heard.

    Skype: I find your lack of feedback disturbing.

    This article will discuss how to build a software tool that makes it easier to talk into Skype with a headset.

    Recently my family began experimenting with Skype, but we found that talking over Skype can be exhausting with headphones. After doing a little research, I learned that the problem was that we only heard the other party. You’d think that this is a good thing, but we have a social brain that works hard to gauge our behavior and adjust.

    The answer is to feed a little bit of the microphone back into the earpiece, so that our brain knows how loud we're talking. This is called side tone in the telecommunication industry. Without it, we talk louder and louder until we’re sure that we’ll be heard.

    I couldn’t find a Skype plug-in to do this… but I had just read a Coding4Fun article, by Mark Heath, about adding audio effects to Skype. I decided I was going to write a tool to add this feedback.

    How to use the tool

    First, let’s take a look at how to use the application. Once it is running, there will be a microphone icon in the lower right hand corner of the screen:

    Figure 1: Icon in the system notification tray

    clip_image004

    Clicking on it, will get the application control window:

    Figure 2: The application's controls

    clip_image006

    Let’s look at the side tone controls:

    • The “Side Tones” check box enables or disables the side tone. If you have headphones, you'll want this on; if you have loud speakers, you'll want it off.
    • The slider controls the volume of the microphone in the headset. You can change it while talking. The volume will vary with microphone and headset.

    You can do a “sound check” to see if the feedback is working by clicking on the Sound Check button, and adjusting the volume. I found that the volume setting that works best in a conversation is much, much lower than what works in a sound check.

    Next, let’s look at the AGC (“Automatic Gain Control”) section. When making a call, the software can automatically adjust how loud you sound to the other party:

    • When “Low Pass” is checked, the headphone feedback is at 44100 samples/sec. The microphone sound is filtered to keep only the sounds below 8 Khz, converted to 16000/samples per second and sent to Skype. When it is not checked, headphone feedback is at 16000 samples/sec, and sent to Skype without the low pass filter.
    • When "Skype AutoGain" is checked, it signals to Skype that Skype can use its own algorithm. When clear, Skype is told not to apply any adjustments.
    • When "AutoGain" is checked, the custom Automatic Gain Control algorithm is used.

    The Automatic Gain Control has three sliders:

    • The “Cutoff” slider controls the distinction between background noise and conversation. Sound below this level is cutoff and silence is sent to Skype. This will vary with microphone – more sensitive (expensive) microphones will pick more background noise and be better with a higher setting.
    • The “Normal” slider controls the volume when you are talking normally. The Gain Control tries to raise the volume to this level.
    • The “Loud” slider controls the volume when you talk exceptionally loud. This rarely happens, but when you do talk louder than the Normal level, the Gain Control tries to adjust the volume to this level.

    How the Software Works

    I originally started this project by creating an effect for the Skype Voice Changer. My plan was to open a WaveStream, begin playing it on the headphones and copy the microphone stream to it.

    I quickly found that this was not the way to add feedback. The underlying “WaveOut” system had a huge latency: Everything I heard in the headphones was at least a second or more behind what I was saying. This made it even harder to talk than before, and I had to abandon it.

    While researching the problem I found a DirectSound code sample that I could modify into doing what I wanted. (This initial prototype didn’t coordinate with Skype – it attached to the microphone and copied the sounds to the output, at a lower volume. But it was on always on.) The sound in the headphones is still slightly behind (the microphone) but is barely perceptible, and we’ll muffle it a bit more to make it less distinguishable.

    From here on I shall describe the major – or technically interesting – components of the program. We’ll look at:

    1. DirectSound and Circular buffers
    2. Sample Window and Sizing the buffers
    3. Using WaitHandle’s to Synchronize with DirectSound
    4. IIR Filters
    5. Automatic Gain Control
    6. Estimating loudness

    Note: I won’t be describing how to connect to Skype. Mark Heath’s description is very good.

    DirectSound and Circular Buffers

    The DirectSound code is in AudioLoop.cs. The module sets DirectSound to capture sound from the default recording device at 16bits / sample at either 16000 or 44100 samples per second. The capture and playback buffers are configured in “looping” mode to act as circular buffers. The capture buffer eventually overwrites samples, and we’ll lose them if we don’t act fast enough; if we don’t update the playback buffer, it will repeat the same sound over and over.

    The StartMicrophone() procedure sets up the capture and playback buffers. Then it creates a thread to do the work. The thread is at a high priority so that if the OS has a choice between (say) email or processing sound, it does the sound.

    The StopMicrophone() procedure stops the worker thread and cleans up the resources.

    The software processes a fixed number of samples at a time, called the “sample window.” The buffers are several times the size of sample window, so that the system can keep capturing and playing while the software is processing them. The sound processing loop is the heart of the application:

    1. Wait for the next sample window to be ready
    2. Copy the samples from the capture buffer
    3. Process the samples and send the results to the playback buffer
    4. Process the samples and sends the results to Skype

    C#

    while (Runnable)
    {
        for (int I = 0, offset = 0; Runnable && I < _bufferPositions; I++, offset += bufferSize)
        {
            // Wait for the sample areas to be ready
            notificationEvent[I].WaitOne(Timeout.Infinite, true);
    
            // Get the sound samples
            byte[] buffer = (byte[]) captureBuffer.Read(offset, typeof (byte), LockFlag.None, bufferSize);
    
            // Convert samples to 16bit PCM 
            for (int L = buffer.Length, J = 10, K = 0; K < L; K += 2)
                PCM16Buffer[J++] = (Int16) ((UInt16) buffer[K] | (((UInt16) buffer[K + 1]) << 8));
    
            // Play them out to the ear, if applicable
            if (null != playbackBuffer)
            {
                // Perform a low pass filter to "muffle" the sound
                Butterworth(PCM16Buffer, 10, LPSample, Coefs);
    
                // put the muffled sample into the output buffer
                // -- The lock flag seems to work, but others may work too
                playbackBuffer.Write(Idx, LPSample, LockFlag.None);
                Idx += buffer.Length;
                if (Idx >= 4*bufferSize)
                    Idx -= 4*bufferSize;
                if (!playing)
                {
                    playbackBuffer.Volume = _Volume;
                    playbackBuffer.Play(0, BufferPlayFlags.Looping);
                    playing = true;
                }
            }
    
            // Process the sound and deliver it to Skype
            if (null != outStream)
            {
                int L = AGC.Process(PCM16Buffer, 10, buffer);
                if (0 != L)
                    outStream.BeginSend(buffer, 0, L, SocketFlags.None, SendCallback, null);
                // Note: could send out pink noise if L == 0
            }
    
            // Move the sliding window of the previous 10 samples into the start
            // of the PCM16Buffer
            for (int K = 0, J = PCM16Buffer.Length - 10; K < 10; K++, J++)
                PCM16Buffer[K] = PCM16Buffer[J];
        }
    }

    Note the “for” loop at the bottom of the code. This preserves the last 10 incoming samples at the start of the buffer. This is needed to make the sound processing smooth, and will be discussed a bit later.

    Sample Window and Sizing the Buffers

    How big should the sample window be? This is bit of a trade off in responsiveness and design complexity.

    I chose a window big enough to hold 10 milliseconds of sound. Since the ear is sensitive sound to delays of even 30 milliseconds, I cut this done so that a delay wouldn’t be perceptible. (When I tried a 50 millisecond window, my voice came out the headphone sounding like an echo... and I found myself talking slower and slower.) The sample window could be made smaller, but I am sure that there is a point where the OS won’t schedule the audio loop to wake-n-run more frequently. And, as the sample window gets smaller, the processing may drop in quality, because it doesn’t have enough to work with.

    The capture buffer is 8 times the size of the sample window. This ratio is arbitrary, but I wanted the buffer to be about an order of magnitude larger. My rationale is that if the processing falls behind, the sound – for the Skype call – won’t be dropped. I feel that it is more important to preserve sound quality for the other party than to preserve the quality of feedback.

    The playback buffer is four times the sample window. I wanted it small, so that if the processing fell behind, the replaying of a sound will seem to be a continuation of a current sound.

    When writing the sound to the playback buffer, we have to track where in the buffer to put the samples. I tried to use GetCurrentPosition() to find where to write to next into the playback buffer; this created terrible sound. Instead, the software uses a local variable to track where to write next.

    DirectSound and Notifications

    How do we keep in sync with the sound capture – how do we know when a sample buffer is ready?

    The application gives a table of buffer indices and WaitHandle’s to DirectSound. When the capture buffer’s write index reaches one of those indices, it signals the corresponding WaitHandle. The worker thread cycles performs a WaitOne() one each of the WaitHandle’s, one at a time. As a convenience, we use a specific kind of WaitHandle called AutoResetEvent. This type of WaitHandle sets itself back to a “wait” state once WaitOne() returns.

    If the thread has gotten behind, the WaitOne will return immediately, the loop processes the sample, and begins to catch up with the work.

    We must use a separate AutoResetEvent for each of the 8 capture windows. The AutoResetEvent doesn’t tell us if it was signaled multiple times. If only one AutoResetEvent handle were used, it wouldn’t know that two (or more) sample windows were ready. Instead, it would process just one, falling further behind, adding latency. This would happen randomly overtime, and be hard to test consistently.

    IIR: Infinite Impulse Response Filters

    This project came together so quickly, so easy – once I found the right approach – that I couldn't resist getting fancy. I added a low-pass filter to muffle the feedback a little. And I added automatic gain control, as an experimental option.

    For both of these I used a filtering algorithm called “IIR” (this stands for Infinite Impulse Response – but that term is a confusing mouthful, so let’s just call it IIR). IIR is a special purpose virtual machine. Low-pass filters, high-pass filters, combinations of those filters, and even equalizers, can be specified, and use very specific techniques (like a compiler) to convert them into an IIR implementation.

    (You could, instead, “compile” the filters to be the resistor values to use in a hardware circuit. That’s programming in solder!)

    The machine code for these IIR virtual machines is just two list of coefficients, called A & B. The software emulator is code that looks like the following bit of code:

    C#

    Out[0] = Sample[0];
    Out[1] = Sample[1];
    for (int Idx = 2; Idx < N; Idx++)
    {
        Out[Idx] =
        B0 * Sample[Idx]
     + B1 * Sample[Idx - 1]
     + B2 * Sample[Idx - 2]
            // … more like this …
            // Next, the feed back
     - A1 * Out[Idx - 1]
     - A2 * Out[Idx - 2]
            // … more like this …
     ;
    }

    IIRs are easy to implement - and take less CPU power than other methods. But sometimes they sound poor; if they sound too bad, you’ll want to use a different technique. I found that the low-pass filters in this project work will for some microphones, and add a slight crackle to others.

    Example Low Pass Filter

    For the low pass filter to create the muffling, I used a Butterworth filter, using the code below. It takes a buffer of signed, 16-bit samples, and then converts the 16-bit values into a byte array suitable for the sound buffer.

    The filter code is a bit different than the example code in the previous section. Most of the differences are for speed.

    • This code doesn’t use a buffer for the old values, instead uses separate variables for the elements of the buffer.  It uses I_0,I_1,I_2 instead of Sample[Idx], Sample[Idx-1], and Sample[Idx-2]. It also uses O_1, and O_2 instead of Out[Idx-1] and Out[Idx-2].
    • The A and B coefficients are put into a single array. It also adds two of the sample input values, and is missing a coefficient; this is because the B0 and B2 coefficients are always the same for this kind of filter.

    There is one difference that is not for speed. These are tricks done to make the filter smooth, and needed because the sample window is so small. They preserve the state of filter. If we didn’t preserve them, the filter would be starting and stopping so frequently that it would add distracting clicks to the output sound. The filters performance would be weakened, because the sample window isn’t big enough to hold sounds lower than (about) 200 Hz. Preserving these values, the filter isn’t starting and stopping, and doesn’t really know about the sample window. All of the IIR filters in this program use similar techniques.

    • O_1, O_2 are explicitly preserved across calls by being stored in class variables
    • I_1 and I_2 are preserved by the audio loop (remember the warning about preserving 10 samples at the bottom of the loop?) The audio loop preserves the last 10 samples at the start of InBuffer. When this procedure is called, it retrieves the last two samples.

    C#

    static double O_1 = 0.0, O_2=0.0;
    
    static void Butterworth(Int16[] InBuffer, int Ofs, byte[] OutBuffer, double[] Coefs)
    {
      double C0=Coefs[0], C1=Coefs[1], C2 = Coefs[2], C3=Coefs[3];
      double I_1=InBuffer[Ofs-1], I_2= InBuffer[Ofs-2];
      for (int L = InBuffer . Length, J=0, I = Ofs; I < L; I++)
      {
         double I_0 = InBuffer[I];
    
         // Filter the samples
         double A = (I_0 + I_2) * C0 + I_1 * C1;
         I_2 = I_1;
         I_1 = I_0;
    
         A = A - O_1 * C2 - O_2 * C3;
         O_2 = O_1;
         O_1 = A;
    
         // Convert it back to 16 bit
         Int16 S;
         if (A < -32767) S = -32767;
         if (A > 32767)  S = 32767;
         else S = (Int16) A;
    
         // Store it
         OutBuffer[J++] = (byte)(S & 0xFF);
         OutBuffer[J++] = (byte)(((UInt16)S >> 8) & 0xFF);
      }
    }

    Automatic Gain Control

    I decided next to tackle a problem where my wife's voice did not carry well on calls. This happens a lot to her with cell phones – and answering machines. I was pretty sure that the problem was poor automatic-gain-control (AGC). The typical amplifier in a headset (and in Skype) estimates how loud our voice is, then increases – or decreases – the volume to a reasonable level. It was deciding that my wife's voice was background noise, and cutting her off.

    I chose to write an alternate gain control that amplified the sound and passed it to Skype. That way we'd have four to choose from: The one built into the Microphone, the Soundcard’s, Mine, and Skype’s. (To be fair, these automatic gain controls work well in most cases).

    The main portion of the gain control is implemented in the file GainControl.cs. The control algorithm is:

    1. Calculate (or estimate) how loud we are currently talking (using the Analyze() procedure)
    2. If the loudness is very low, no one is talking… so set the output to zero. (Without this step, the volume of noise and hum would be cranked up)
    3. Otherwise, compute the guess-gain by dividing how loud the sound of our voice should be by how loud it currently is
    4. Compute the gain (called MaxGain) at which the sound will start clipping. If the guess-gain is louder than this, reduce it to MaxGain.
    5. The software adjusts the gain for a gentle transition – especially in the case when we go from absolute quiet, to the start of talking. It does this by tracking the gain (called PrevGain) used in previous sample and the current one.
    6. Multiple all the samples by this gain value.
    7. If the sample rate is greater than 16000 samples/sec
      1. Do a low pass filter at 8 khz (again in IIR form). This helps prevent artifacts from down sampling
      2. Resample the sound to 16000

    The portion of code that calculates the gain looks like (CutOff_dB, LowGain_dB, and TgtGain_dB are the three slider values):

    C#

    if (!AutoGain)
        Gain = 1.0;
    else
    {
        double MaxGain;
        double dB = Analyze(InBuffer, Ofs, out MaxGain);
    
        if (dB < CutOff_dB)
            Gain = 0.0;
        else if (dB < LowGain_dB + 4.0)
        {
            Gain = Math.Exp((LowGain_dB - dB) * db2log);
        }
        else
        {
            Gain = Math.Exp((TgtGain_dB - dB) * db2log);
        }
        Gain = (0.4 * Gain + 0.6 * PrevGain);
        if (Gain > MaxGain)
            Gain = MaxGain;
        PrevGain = Gain;
    }
    
    // Skip further process if there is silence
    if (0.0 == Gain)
    {
        return 0;
    }

    If you look at the code, you’ll see that we don’t compare directly with LowGain_db; rather we compare the estimate volume with LowGain_db+4. This gives a little “hysteresis” – if we raise our voice momentarily, the software won’t suddenly make it the highest possible volume. Instead, the software lowers the volume a little bit.

    When the software changes the sample rate, it basically needs to know how many input samples to skip. At the start of a call, the software computes this, calling it InInc:

    C#

    // Calculate how we resample to 16Khz
    InInc = (int)(1024.0 * SampleRate / 16000.0);

    The process of applying the gain adjustment, performing a low pass filter and re-sampling is below:

    C#

    int NumSamples = InBuffer.Length;
    int End = OutBuffer.Length;
    int NextIdxForOut = -InInc;
    int OutIdx = 0;
    for (int I = Ofs; OutIdx < End && I < NumSamples; I++)
    {
        // Retrieve the sample
        Int16 S = InBuffer[I];
        double I_0 = S;
    
        // Apply Gain
        I_0 *= Gain;
    
        // 8khz low pass filter 
        if (DoLP)
        {
            // Simple Butterworth 8KHz low-pass filter
            double A = (I_0 + DS_I_2) * LP[0] + DS_I_1 * LP[1];
            DS_I_2 = DS_I_1;
            DS_I_1 = I_0;
            I_0 = A - DS_O_1 * LP[2] - DS_O_2 * LP[3];
            DS_O_2 = DS_O_1;
            DS_O_1 = I_0;
    
            // Change sample rate
            int Tmp = NextIdxForOut / 1024;
            if (I < Tmp)
                continue;
            NextIdxForOut += InInc;
        }
    
        // Convert it back to 16 bit
        S = (Int16)I_0;
    
        // Store it
        OutBuffer[OutIdx++] = (byte)(S & 0xFF);
        OutBuffer[OutIdx++] = (byte)(((UInt16)S >> 8) & 0xFF);
    }

    Estimating Volume

    How loud “it should be” is controlled by a slider on the screen. The software estimates how loud the sound is by using an algorithm devised by David Robinson that takes into account how it sounds to a person. This way we can increase the gain on hard to hear sounds, and reduce the gain on sounds that a person is very sensitive to.

    The loudness estimator, implemented in the Analysis procedure in GainAnalysis.cs, uses the following algorithm:

    1. Uses a combination of two (IIR) filters to make the sound to reflect how our ears hear it.
    2. Compute the Mean-Square value of the filtered sound (called MS)
    3. Track the last 750 ms of these values.
    4. Make a sorted copy of these.
    5. Retrieve the first non-zero value at least 95% of the way into the buffer. This is so that we don’t take the loudest sample and assume that is how the person is talking.
    6. If MS (the value computed in step 2) is much quieter, use that one instead
    7. Convert this value into dB scale by performing a logarithm on it.

    The code to “normalize” the sound into how a person hears it is below. Along the way it computes the square of the samples (used in step 2). Like the earlier IIR filters, these preserves their variables across the calls. The first IIR is a yulewalk filter, but it preserves it old intermediary values in an array. Like the trick in AudioLoop, where we copy the last 10 samples into the start of the current buffer, the analysis procedure copies the last 10 immediate values into the start of YuleTmp array.

    The output of the yulewalk filter is feed into a 150Hz high pass filter. It is essentially the same as the low-pass filter described earlier.

    C#

    for (int L = Samples.Length, N = Ofs; N < L; N++)
    {
        int _V = Samples[N];
        double V = _V;
        if (_V > MaxSample) MaxSample = _V;
        if (-_V > MaxSample) MaxSample = -_V;
    
        // Perform yulewalk filter
        double S = V * YuleCoefs[0];
        for (int J = N - 1, I = 1; I < 11; I++, J--)
            S += Samples[J] * YuleCoefs[I];
        for (int J = N - 1, I = 11; I < 21; I++, J--)
            S -= YuleTmp[J] * YuleCoefs[I];
    
        // Store for the feedback into the next stage of the yule walk
        YuleTmp[N] = S;
    
    
        // Perform butterworth high-pass filter stage, using S as an input
        double Accum =
           (S + GA_I_2) * HPCoefs[0]
           + GA_I_1 * HPCoefs[1];
        GA_I_2 = GA_I_1;
        GA_I_1 = S;
        Accum = Accum - GA_O_1 * HPCoefs[2] - GA_O_2 * HPCoefs[3];
    
        GA_O_2 = GA_O_1;
        GA_O_1 = Accum;
    
        // The square of the filtered results
        Sum += Accum * Accum;
    }
    
    // Copy the intermediate yulewalk state for the next
    // (this is needed since we are looking at a fairly small time window)
    for (int I = 0, J = YuleTmp.Length - 10; I < 10; I++)
        YuleTmp[I] = YuleTmp[J];

    The mean-squared is computed:

    C#

    // The mean square of the filtered results
    double MS = Sum / NumSamples;

    Tracking the last 750ms of samples is a simple matter of putting it into a circular queue:

    C#

    MSQueue[QIdx++] = MS;
    if (QIdx >= MSQueue . Length)
       QIdx = 0;

    Next, is the code to finding the first non-zero value 95% of the way into the buffer. It is a straightforward copy-the-array, sort it, and fetch:

    C#

    Array.Copy(MSQueue, SortedQ, MSQueue.Length);
    Array.Sort(SortedQ);
    
    // Return the 95% 
    double X = SortedQ[Q95Idx]; 
    for (int I = Q95Idx +1; X < 400.0 && I < SortedQ . Length; I++)
          X = SortedQ[I];

    Next, override this value if the current sample window is very, very quiet – that is, the user stopped talking. (If we don’t do this, we’ll amplify background noise between words)

    C#

    if (MS < X * 0.40 && MS < 12800.0)
       X = MS;

    Finally, convert the result into decibels (or a reasonable approximation of a decibel)

    C#

    return 10.0 * Math . Log10 (X * 0.5 + double . Epsilon);

    Note: The logarithm function takes a positive, non-zero floating point number. However, the value we pass to it can be zero; if we pass a zero, though, the Logarithm function would return a bad value. The simplest thing to do is check to see if the value we are passing is “zero” and not call Logarithm. However, I learned a long time ago to just add “epsilon” to the value to whatever we pass. This can really improve performance on number crunching.

    Conclusion

    This concludes how to add a little bit feedback and fancy amplification to you Skype phone calls.

    If you want to try this out, the download link for the source code is at the top of the article!

    If you’d like to experiment further, here are ideas of what can be done:

    1. DirectSound has echo cancellation and noise suppression. These seem desirable, esp. if you wanted to try making your own speakerphone. I was not able to get them to work and I would love to learn how.
    2. I’m sure it is possible to trim even more latency off of the side-tone playback, and I would be interested in learning better techniques to do so.
    3. Another would be to create the ideal equalizer from Robinson’s Equal Loudness model, and use that filter and amplify the sound.
    4. It might be useful for the other party to control the settings, so that they decide when your voice has the right volume.

    Resources and References

    About The Author

    Randall Maas writes firmware for medical devices, and consults in embedded software. Before that he did a lot of other things… like everyone else in the software industry. You can contact him at randym@acm.org.

    Computer, Hello Computer, I need some transparent aluminum
    08 June 09 10:53 AM | Coding4Fun | 1 Comments   

    Kim Major is working on a rather neat item for Visual Studio.  He is working on using speech recognition to program with Visual Studio!

    They are looking for some feedback and if you want to, please post some over here.

    Robozzle
    02 June 09 06:02 PM | Coding4Fun | 0 Comments   

    image Robozzle is a Silverlight game that helps teach players the art of programming logic through a series of fun and challenging community-created puzzles.  What's more, the game is "community sourced" meaning not only can you contribute new levels - but actually help expand the game and shape it's future. (http://robozzle.codeplex.com/).

    [via Tim Farley]

    Filed under: , ,
    WPF NotifyIcon control!
    26 May 09 09:32 AM | Coding4Fun | 1 Comments   

    wpfNotifyIcon If you’ve ever had a WPF application and wanted a notify icon in your system tray, you’ve had to import the System.Windows bits.  This always seemed to me as a hack to get it but at least I could do.  Fear not anymore, Philipp Sumi has created a WPF NotifyIcon component!

    Features at a glance
    • Custom Popups (interactive controls) on mouse clicks.
    • Customized ToolTips (Vista and above) with fallback mechanism for xp/2003.
    • Rich event model including attached events to trigger animations in Popups, ToolTips, and balloon messages. I just love that.
    • Full support for standard Windows balloons, including custom icons.
    • Custom balloons that pop up in the tray area that can be styled with effects
    • WPF context menus.
    • You can define whether to show Popups on left-, right-, double-clicks etc. The same goes for context menus.
    • Simple data binding for Popups, ToolTips and custom balloons through attached properties and derived data context.
    • Command support for single / double clicks on the tray icon.
    Filed under:
    Cropping with ASP.Net
    22 May 09 11:54 AM | Coding4Fun | 2 Comments   

    image

    Cem Sisman created a ASP.Net image cropping control for the JQuery plugin, Jcrop.

    Demos:

    Matthew Paulson has a quick and dirty write up on how to implement this on your page too.

    The codeplex project needs to be updated to correct some IE8 issues but I’m hoping all you need to do is download the new version of JCrop and everything should be fixed.

    Filed under:
    Rook to bishop, pawn to queen, checkmate
    21 May 09 10:25 AM | Coding4Fun | 0 Comments   

    ChessBoard[1] Ever wonder how to build a chess game?  Jacques Fournier has built a c# chess game with a built in game import engine.

    Jacques’s version has the following features:

    • Visual interface
    • Multiple difficulty levels
    • Database of book openings
    • Loading / saving of game
    • Undo / redo functions
    • Reversing the board
    • Player against computer
    • Computer against computer
    • Player against player
    • Creating your own chess board (manually or from PGN)
    • Hints for the player

    If you want a chess program in Visual Basic, Kaushal Golwala has a version of a chess game which can be found at vbdotnetheaven.com!

    Filed under:
    Classic JukeBox
    19 May 09 12:09 PM | Coding4Fun | 0 Comments   

    CJBLogo Remember the classic arcade style jukeboxes? Today we will be creating our own jukebox using WPF, WMP, a little bit of 3D, some very basic electronics (costing less than $10 USD) and a splash of M-V-VM

    Rudi Grobler

    • Difficulty: Intermediate
    • Time Required: 2-4 hours
    • Cost: ~$10 for the 5 buttons and the LPT connector
    • Software: Visual C# Express Edition 2008
    • Source Code Download: Available on CodePlex

    Introduction

    A couple of weeks ago I took my son to the game arcade to play a little… while playing, I noticed in the corner a old jukebox… I started wondering how difficult it would be to recreate one of these using “real” hardware and WPF? This article explorers how to make real hardware control software applications in a practical manner

    M-V-VM

    “Once a developer becomes comfortable with WPF and MVVM, it can be difficult to differentiate the two. MVVM is the lingua franca of WPF developers because it is well suited to the WPF platform, and WPF was designed to make it easy to build applications using the MVVM pattern (amongst others). In fact, Microsoft was using MVVM internally to develop WPF applications, such as Microsoft Expression Blend, while the core WPF platform was under construction. Many aspects of WPF, such as the look-less control model and data templates, utilize the strong separation of display from state and behavior promoted by MVVM.”

    WPF Apps With The Model-View-ViewModel Design Pattern by Josh Smith

    The M-V-VM pattern helps to separate the logic from the UI which is VERY important when you actually want to manipulate the logic from external sources (Like real push buttons). Our MediaViewModel is extremely simple…

    ViewModel

    The MediaViewModel has a collection of albums (Which gets fetched when Initialize() is called) and also exposes some commands

    Setting up Windows Media Player media library

    WMPMediaLibrary

    Ensure that your Windows Media Player (WMP) media library has some media. I added a few albums to my library by clicking on the Add to library… menu option.

    TIP - I also ensured that all my CD’s that I do add has full ID3 tags and album art

    Fetching WMP media library

    To fetch the media library from WMP, we have to use a little bit of COM

    “Microsoft COM (Component Object Model) technology in the Microsoft Windows-family of Operating Systems enables software components to communicate. COM is used by developers to create re-usable software components, link components together to build applications, and take advantage of Windows services. The family of COM technologies includes COM+, Distributed COM (DCOM) and ActiveX® Controls.”

    Adding a reference to a COM component is just as easy as adding a reference to a .NET assembly. Click the Project menu and select Add Reference, then click the COM tab on the Add Reference dialog.

    AddWmpComReference

    The WMP library is a flat structure of media items, so the Albums -> Tracks hierarchy must be created manually. Here is our Model

    Model

    We will be abstracting the fetching of the data by using a IMediaLibraryRepository. By using a interface we have the added advantage of supporting other media sources (like iTunes, etc) in the future

    C#:

    public interface IMediaLibraryRepository 
    { 
        IList<Album> GetAlbums(); 
    }

    And here is our WMP-specific implementation

    C#:

    public class WMPMediaLibraryRepository : IMediaLibraryRepository 
    { 
        public IList<Album> GetAlbums() 
        { 
            List<Album> Albums = new List(); 
            WindowsMediaPlayer wmp = new WindowsMediaPlayer(); 
            IWMPPlaylist playlist = wmp.mediaCollection.getAll(); 
            for (int i = 0; i < playlist.count; i++) 
            { 
                IWMPMedia media = (IWMPMedia)playlist.get_Item(i); 
                
                Track track = new Track(); 
                track.Title = media.getItemInfo("Title"); 
                track.Location = media.getItemInfo("SourceUrl"); 
                track.Number = media.getItemInfo("OriginalIndex"); 
                string albumName = media.getItemInfo("Album"); 
                
                var album = (from a in Albums 
                                where a.Name == albumName 
                                select a).FirstOrDefault(); 
                
                if (album != null) 
                { 
                    album.Tracks.Add(track); 
                } 
                else 
                { 
                    Album a = new Album(); 
                    a.Name = albumName; 
                    string dir = System.IO.Path.GetDirectoryName(track.Location); 
                    FileInfo file = new FileInfo(System.IO.Path.Combine(dir, "Folder.jpg"));                 
                    if (file.Exists) 
                    { 
                        a.Cover = file.FullName; 
                    } 
                    a.Artist = media.getItemInfo("AlbumArtist"); 
                    a.Tracks.Add(track); 
                    if (albumName != string.Empty) 
                    { 
                        Albums.Add(a); 
                    } 
                } 
            } 
            return Albums; 
        } 
    }

    Commands

    All the interaction between the View and the ViewModel happens by using ICommand. We will be using the RelayCommand

    RelayCommand - An ICommand whose delegates can be attached for Execute and CanExecute

    The MediaViewModel exposes the following commands

    • NextAlbum
    • PreviousAlbum
    • SongUp
    • SongDown
    • PlaySong

    To create a RelayCommand, we first need the CanExecute and Execute delegates

    C#:

    private bool SongUpCanExecute(object parameter) 
    { 
        return (TracksCollectionView.CurrentPosition > 0); 
    } 
    
    private void SongUpExecute(object parameter) 
    { 
        if (TracksCollectionView != null) 
        { 
            TracksCollectionView.MoveCurrentToPrevious(); 
        } 
    }

    And then create the command

    C#:

    private ICommand songUp; 
    public ICommand SongUp 
    { 
        get 
        { 
            if (songUp == null) 
            { 
                songUp = new RelayCommand(SongUpExecute, SongUpCanExecute); 
            } 
            return songUp; 
        }
    }

    The RelayCommand is very well suited to the M-V-VM pattern because it allows me to encapsulate the whole command and its executing logic very cleanly in my ViewModel. The View can then just bind to these commands!

    For more information about custom ICommand implementations, read The Power of ICommand

    TurnThePage 3D WPF Book Control

    “The Great Library of Alexandria was founded in 300 B.C.E. with the grand objective of collecting the world's knowledge in one place; at its height, the library contained nearly 750,000 scrolls. In the modern world, the British Library contains one of the foremost collections; among its twenty million books and manuscripts are some of the rarest works in existence. It holds the Diamond Sutra, the oldest printed book; Mercator's first atlas of Europe; the Lindisfarne Gospels; Leonardo da Vinci's personal notebook; the Magna Carta; and the Codex Sinaiticus, one of the two earliest Christian Bibles. Such unique items must, of course, be treated with the utmost care. If they are on public display at all, they are well protected behind glass, and direct interaction is limited to a handful of individuals.

    Happily, these works are now being digitized for the first time in order to reach a broad audience. Even better, the digitized versions are being turned into a rich interactive experience that adds curatorial content and brings the books to life. In collaboration with a UK-based software developer, the British Library developed a Windows®-based application called Turning the Pages that offers a virtual facsimile in three-dimensional space of a growing number of the library's most precious items”

    Turning the Pages with WPF by Tim Sneath

    By using the free book control (released by Mitsuru Furuta on CodePlex), we will be transforming our WMP media library into a book browsing experience!

    To use the controls, we need to reference the WPMMitsuControls.dll and add the following namespace

    xmlns:controls="clr-namespace:WPFMitsuControls;assembly=WPFMitsuControls"

    Because Book derive from ItemsControl, we can bind it to any collection using ItemsSource and change the ItemTemplate (Like we would using ListBox, ListView, etc)

    <controls:Book ItemsSource="...">
        <controls:Book.ItemTemplate>
            ...
        </controls:Book.ItemTemplate>
    </controls:Book>

    Accessing the parallel port

    parallelPort1

    parallelPortReg

    We will be using the status register (Base + 1) for feedback from the keypad. To access the keypad we need a hardware IO driver (I will be using inpout32.dll). inpout32.dll is a win32 dll so we need to p/invoke

    C#:

    public class PortAccess 
    { 
        [DllImport("inpout32.dll", EntryPoint = "Out32")] 
        public static extern void Output(int adress, int value); 
        
        [DllImport("inpout32.dll", EntryPoint = "Inp32")] 
        public static extern int Input(int adress); 
    }

    For more information on how to access the LPT port using managed code, first read the following CodeProject article:

    Electronics 101

    Schematic

    This is a very basic schematic of how my keypad is wired up...

    Cable

    I had a keypad laying round which I used but you can also use normal push buttons available at your local electronics shop (Here is a list of push buttons available from RadioShack)

    WindowWithKeypadSupport

    The last part we need to cover is how to react to a key being pressed on the keypad. I sub-classed the WPF Window and created my own WindowWithKeypadSupport. WindowWithKeypadSupport has a background thread running which monitors the LPT port. If a button is pressed, it raises a PreviewKeypadDown routed event.

    C#:

    public class WindowWithKeypadSupport : Window 
    { 
        public static readonly RoutedEvent PreviewKeypadDownEvent; 
        
        static WindowWithKeypadSupport() 
        {
            WindowWithKeypadSupport.PreviewKeypadDownEvent = 
                EventManager.RegisterRoutedEvent( "PreviewKeypadDown", 
                RoutingStrategy.Bubble, typeof(KeypadEventHandler), 
                typeof(WindowWithKeypadSupport)); 
        } 
        
        public event RoutedEventHandler PreviewKeypadDown 
        { 
            add 
            { 
                base.AddHandler(WindowWithKeypadSupport.PreviewKeypadDownEvent, value); 
            }         
            remove 
            { 
                base.RemoveHandler(WindowWithKeypadSupport.PreviewKeypadDownEvent, value); 
            } 
        } 
        // The rest of the class is omited for brevity 
    } 

    To raise the event, we need to invoke back to the UI thread

    C#:

    Dispatcher.BeginInvoke(DispatcherPriority.Background,
        (SendOrPostCallback)delegate 
        { 
            KeypadEventArgs e = new KeypadEventArgs(
                WindowWithKeypadSupport.PreviewKeypadDownEvent, this); 
            e.Key = CreateKeyFromIOPortValue(keyValue); 
            base.RaiseEvent(e); 
        }, null);

    Here is a example of how to instantiate the WindowWithKeyadSupport

    <controls:WindowWithKeypadSupport x:Class="ClassicJukebox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:ClassicJukebox"
        PreviewKeypadDown="WindowWithKeypadSupport_PreviewKeypadDown" />

    TIP - I also react to keyboard buttons being pressed... Use the left, right, up, down and enter keys to control the jukebox

    And thats it...

    ClassicJukebox

    Conclusion

    WPF provides a very powerful infrastructure for creating applications that can mix 3D & hardware effortless! The binding architecture and command support allows for great separation of concerns!

    And probably the most important advantage of WPF? It makes writing applications fun…

    About the author

    Rudi Grobler's main area of interest is development in the embedded space (his day-to-day job). In the last 10 years Rudi interfaced to various devices in the embedded space ranging from Graphic LCDs using parallel port, various bill acceptors, RoboHum, smart card readers, Wii remote, data acquisition devices, petrol pumps and much, much more!About 2 years ago, he received a copy of Charles Petzold's WPF book and fell in love…

    His ramblings can be found at http://dotnet.org.za/rudi

    Who made my text circular?
    14 May 09 10:36 AM | Coding4Fun | 0 Comments   

    Step5_SettingUpAnimation Ever see text following a path and wonder how they did that?  Silverzine shows you how to make text that will follow a circular path and rotate.

    SilverZine uses Expression Blend to accomplish this tutorial.  They talk about how to keyframe it, space items out, and use the render transform origin.

    Filed under:
    Hopping in Silverlight
    12 May 09 10:33 AM | Coding4Fun | 0 Comments   

    frogger_original Michael Ceranski has an article over at Code Project about recreating the classic game Frogger in Silverlight.

    Michael goes in about creating sprites, collision detection, and animation.  He created lanes for the traffic in design time then made them transparent when the application loaded.  Smart!

    Nice job Michael.

    Filed under:
    More Posts Next page »
    Page view tracker