Saturday, 15 September 2012

A XAML Loop Icon

I needed a XAML loop icon recently, so here’s what I came up with:

  <Path Stroke="Black" StrokeThickness="2" Data="M 5,3 h 20 a 5, 5, 180, 1, 1, 0, 10 h -20 a 5, 5, 180, 1, 1, 0, -10 Z" />
  <Path Stroke="Black" Fill="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 11,0 l 8,3 l -8, 3  Z" />

And this is what it looks like:


Friday, 14 September 2012

Using a WrapPanel with ItemsControl and ListView

I recently needed to put some items inside a WrapPanel in WPF, which I wanted to scroll vertically if there were too many items to fit into the available space. I was undecided on whether I wanted to use an ItemsControl or a ListView (which adds selected item capabilities), and discovered that when you switch between the two containers, the technique for getting the WrapPanel working is subtly different.

ItemsControl is the simplest. Just set the ItemsPanelTemplate to be a WrapPanel, and then put the whole thing inside a ScrollViewer (sadly you can’t put the ScrollViewer inside the ItemsPanelTemplate):

        <WrapPanel />
    <Rectangle Margin="5" Width="100" Height="100" Fill="Beige" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="PowderBlue" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF9ACD32" />    
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFF6347" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF6495ED" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFFA500" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFFD700" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFF4500" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF316915" />    
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF8E32A7" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFECBADC" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFE6D84F" />

this produces the following results:


But if you switch to an ItemsView instead, you’ll find that you get a single row of items with a horizontal scrollbar while the outer ScrollViewer has nothing to do.


The solution is to disable the horizontal scrollbar of the ListView itself:

<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled">

This allows our top-level ScrollViewer to work as with the ItemsControl and we have selection capabilities as well:


The full XAML for the ListView with vertically scrolling WrapPanel is:

  <ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <WrapPanel />
    <Rectangle Margin="5" Width="100" Height="100" Fill="Beige" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="PowderBlue" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF9ACD32" />    
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFF6347" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF6495ED" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFFA500" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFFD700" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFFF4500" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF316915" />    
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FF8E32A7" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFECBADC" />
    <Rectangle Margin="5" Width="100" Height="100" Fill="#FFE6D84F" />

Thursday, 13 September 2012

NAudio OffsetSampleProvider

I’ve added a new class to NAudio ready for the 1.6 release called the OffsetSampleProvider, which is another utility class implementing the ISampleProvider interface.

It simply passes through audio from a source ISampleProvider, but with the following options:

  1. You can delay the start of the source stream by using the DelayBySamples property. So if you want to insert a few seconds of silence, you can use this property.
  2. You can discard a certain number of samples from your source using the SkipOverSamples property
  3. You can limit the number of samples you read from the source using the TakeSamples property. If this is 0, it means take the whole thing. If it is any other value, it will only pass through the specified number of samples from the source.
  4. You can also add a period of silence to the end by using the LeadOutSamples property.

You can convert a TimeSpan to a number of samples using the following logic (Remember to multiply by channels). I may add a helper method to OffsetSampleProvider that can do this for you in future.

int sampleRate = offsetSampleProvider.WaveFormat.SampleRate;
int channels = offsetSampleProvider.WaveFormat.Channels;
TimeSpan delay = TimeSpan.FromSeconds(1.7); // set to whatever you like
int samplesToDelay = (int)(sampleRate * delay.TotalSeconds) * channels;
offsetSampleProvider.DelayBySamples = samplesToDelay;

It’s a fairly simple class, but it is quite powerful. You might use it for inputs to a mixer, where you want to delay each input by a certain amount to align the audio properly. Or you might use it to cut a piece out of a longer section of audio.

Note that the skipping over is implemented by reading from the source because ISampleProvider does not support repositioning. So if your source is (say) an AudioFileReader, it would perhaps be better to use the Position property to get to the right place before handing it to OffsetSampleReader.

Wednesday, 12 September 2012

ASIO Recording in NAudio

It’s been a long time coming, but I’ve finally checked in support for ASIO recording in NAudio. It’s not actually too difficult to implement, but the main problem has been finding the time for it, and how best to present the feature within the NAudio API. I decided in the end to just do the simplest thing that works. This means simply extending the AsioOut class to allow you to optionally record at the same time as playing with NAudio.

To initiate ASIO Recording, there is a new InitRecordAndPlayback method, which allows you to specify how many channels to record. If you are only recording, and not playing back as well, then you need to tell it what sample rate you would prefer in a third parameter (leaving the input IWaveProvider null).

this.asioOut.InitRecordAndPlayback(null, recordChannelCount, 44100);

I’ve also added an InputChannelOffset property, which means you can skip over some of the input channels to select just the input you want. Obviously in the future it would be better to let you pick explicitly which inputs you want to record from.

To start recording (and playback), you simply call the Play method, and you must explicitly stop recording yourself. You are alerted of new audio available via the AudioAvailable event. This gives you an AudioAvailableEventArgs which gives you direct access to the ASIO driver’s record buffers for maximum performance, along with AsioSampleType which tells you what format the audio is in:

public class AsioAudioAvailableEventArgs : EventArgs

    /// <summary>
    /// Pointer to a buffer per input channel
    /// </summary>
    public IntPtr[] InputBuffers { get; private set; }

    /// <summary>
    /// Number of samples in each buffer
    /// </summary>
    public int SamplesPerBuffer { get; private set; }

    /// <summary>
    /// Audio format within each buffer
    /// Most commonly this will be one of, Int32LSB, Int16LSB, Int24LSB or Float32LSB
    /// </summary>
    public AsioSampleType AsioSampleType { get; private set; }

    /// <summary>
    /// Converts all the recorded audio into a buffer of 32 bit floating point samples, interleaved by channel
    /// </summary>
    /// <returns>The samples as 32 bit floating point, interleaved</returns>
    public float[] GetAsInterleavedSamples() ...

The GetAsInterleavedSamples is a helper method to make working with the recorded audio easier. It creates a float array, and reads the recorded samples into that, converting from 16 or 24 bit if necessary (only the most common ASIO sample types are supported). This saves you from having to write your own unsafe C# code if you are not comfortable with that. Remember that this callback is happening from within the ASIO driver itself, so you must return as quickly as possible. If you crash while in this callback you could find your ASIO driver fails and you may need to reboot your computer to recover the audio device.

I’ve updated the NAudioDemo application to include a simple demonstration of ASIO recording in action, so if you can, test it out with your soundcard and let me know how you get on. I am aware that the public interface for this feature is a bit of a hack, but time does not permit me at the moment to polish it further. Hopefully a future version of NAudio will feature an improved ASIO API, but for NAudio 1.6 there will at least be a way to do recording with ASIO.

Tuesday, 11 September 2012

New Open Source Project–GraphPad

I’ve open sourced a simple utility I wrote earlier this year, when I was preparing to give a talk on DVCS. It’s called GraphPad, and it’s a simple tool for visualising Directed Acyclic Graphs, with the ability to define your own with a basic syntax, or import history from a Git or Mercurial repository.


The idea behind it was that I would be able to use it in my talk to show what the DAG would look like for various. The really tricky thing is coming up with a good node layout algorithm, and mine is extremely rudimentary.

What’s more there are some very nice ones being developed now, particularly for Git, such as SeeGit or the very impressive “dragon” tool that comes with the Git Source Control Provider for Visual Studio, both of which are also WPF applications. Mine does at least have the distinction of being the only one I know of that also works with Mercurial.

For now, I am not actively developing this project, but I thought I’d open source it in case anyone has a use for it and wants to take it a bit further (the next step was to make the nodes show the commit details in a nice looking tooltip for Git/Hg repositories, as well as showing which nodes the various branch and head/tip labels are pointing at).

You can find it here on bitbucket.

Monday, 10 September 2012

Windows 8–First Impressions

I thought I’d post a few first impressions having actually used a Windows 8 machine for a day after setting my laptop up to boot Win 8 from VHD.

I like the fact that you can search just by typing in the start screen, although it seems a little unintuitive, and sometimes it appears to have found nothing, when actually it has discovered some matching applications and you need to click again to actually see the matches.

The new task manager is very cool. I’m especially pleased that you can bring a processes child windows to the front with it, which is something I used to need SysInternals Process Explorer for. It helps you to recover applications that appear to have hung because they are showing a message box or save as dialog that is not visible.

I’ve only briefly played with the default applications. I like the fact that the Sports one lets you specify your favourite team. It still needs a bit of tweaking as there are confusing ‘results’ shown for future fixtures, but its a good start. The Weather application using my location to find where I am is also a nice touch. I also like that it comes with a SkyDrive app by default as well.

My biggest annoyance so far is that when you use Alt-tab or Windows-tab to cycle through open applications, it includes the Metro apps as well. This is a pain because you can’t close Metro apps. If you’ve clicked through a bunch of Metro apps, then they will forever clutter up your alt-tab experience.

The Windows Store is rather sparsely populated, and once again it was not obvious to me how to search. Hopefully it will have good search capabilities as I doubt any app I create will ever be one of the top apps in any category.

The lack of a start menu in desktop mode still feels weird to me, and I still would like the metro interface to be available as a floating window in desktop mode, but I’ll give myself a bit longer to see if I can learn to live with this new way of working.

Saturday, 8 September 2012

Installing and Booting Windows 8 RTM off a VHD (Virtual Hard Disk)

I discovered this week that simply having Visual Studio 2012 is not enough to write WinRT applications (Metro/Windows Store apps); you also need to have Windows 8 as your Operating System. Although the RTM Windows 8 is available now from MSDN, I’m not quite ready yet to switch my main laptop over to it, so a virtual machine is the way forward. Unfortunately though, Windows Virtual PC refuses to install Windows 8 and while Oracle VirtualBox can run it, performance isn’t exactly brilliant.

Fortunately, about a year ago, Scott Hanselman wrote a brilliant blog post describing how to use the boot to VHD functionality in Windows 7 to install Windows 8 as an alternative boot option, only running off a VHD you create. I was too nervous to try this out when he originally posted it, but I finally plucked up the courage and did my first Win 8 booting from VHD install today.

The instructions Scott gives are for the Windows 8 developer preview, so I thought I’d briefly review the steps, noting a couple of very small differences for the RTM and a few other things I noticed along the way:

Step 1 – Create a bootable USB stick
The instructions in Scott Hanselman’s post are very easy to follow if you have the Win 8 RTM ISO. Just download the utility and away you go. The space requirements are smaller – you should be able to get away with a 4GB USB stick, although I used a 16GB one.

Step 2 – Make a VHD
This is nice and easy using the “Disk Management” tool in Windows (or follow Scott’s instructions to use the DISKPART command-line). I went for an 80GB dynamically expanding VHD.

Step 3 – Write stuff down
Remember exactly where you put that VHD, and you might also want to have a Windows 8 product key handy. You’ll probably need a bunch of other passwords too, most notably your Windows Live one, and your wireless network.

Step 4 – Boot from the USB stick
Scott says F12 is the key you need to press to select a boot device, and it worked for me on my DELL XPS laptop.

Step 5 – Select new install
The Windows 8 RTM setup offers an upgrade option that the developer preview didn’t, so you need to say that you are doing a fresh install.

Step 6 – Attach the VHD within setup
Scott’s instructions are very good here. The keyboard shortcut you need to remember to get to a command prompt is Shift-F10. The other thing I noticed was that my Win 7 C drive was actually called the E drive, so if you have multiple disk partitions, find out which one your VHD is in before using DISKPART.

Step 7 – Select the VHD partition to install to
After you have refreshed the list of available partitions to install to, your VHD ought to be obvious (e.g. mine was the 80GB one), but take great care that you choose the right one. Scott’s instructions say that you will get a warning message before you proceed, but I didn’t, leaving me worried that I might have chosen the wrong one!

Step 8 – Let it reboot and finish setting up Windows 8
The process creates a very nice looking new boot menu when your computer starts up, which Scott shows screenshots of. This gives me the choice between Windows 8 and 7. Windows 8 is set the default, but you get to change the defaults if you like, so I set mine back to default to Win 7. Update: After a few reboots, my computer has switched back to using the boring Windows 7 text based boot menu. I'm not quite sure why this happened, but possibly was after I had a audio driver crash in Windows 7.

Step 9 – Accessing data between Win 7 and Win 8
One nice thing is that when you boot from the VHD, your Windows 7 drive containing the VHD gets mounted as a drive within Windows 8, making it easy to access any data (although you do need to let it change the folder permissions to look into your User folder). And vice-versa, on Win 7 you can easily use the Disk Management tool to mount the Windows 8 VHD and copy files on or off it as you wish.

Step 10 – Cleaning up
This is the step I haven’t done yet, but probably at some point I will decide I don’t want to use this VHD anymore. Obviously you wouldn’t want to just delete the VHD as you’d end up with an entry in your boot menu that won’t work anymore. Hopefully there is a way to take the Win 8 VHD out of the boot menu. I’ll update this blog post with a link if I find instructions somewhere.
From beginning to end, the process of setting up my laptop to boot to Win 8 from a VHD took me just over an hour, and I would recommend it to anyone wanting to get started with Win 8 development.

Monday, 3 September 2012

Screen-Scraping in C# using LINQPad and HTML Agility Pack

I am a big fan of LINQPad for prototyping small bits of code, but every now and then you find you need to reference another library. LINQPad, does allow this by editing the Query Properties and adding a reference to a dll in the GAC or at a specific path. But the new LINQPad beta release makes life even easier by allowing you to reference NuGet packages.

I recently wanted to do a simple bit of screen-scraping, to extract the results from a web page containing football scores. By examining the HTML with FireBug I was quickly able to see that each month’s fixtures were in a series of tables each with a header row and then one row per result. The HTML looked something like this:

<table class="fixtures" width="502" border="0">
    <tr class="fixture-header">
        <th class="first" scope="col" colspan="6">November</th>
        <th class="goals-for" title="Goals For" scope="col">F</th>
        <th class="goals-against" title="Goals Against" scope="col">A</th>
        <th class="tv-channel" scope="col">
        <th class="last" scope="col"> </th>
    <tr class="home">
        <td class="first ">04</td>
        <td class="month"> Wed </td>
        <td class="fixture-icon">
        <td class="competition">UEFA Champions League</td>
        <td class="home-away">H</td>
        <td class="bold opposition ">
        <td class="goals-for"> 4 </td>
        <td class="goals-against"> 1 </td>
        <td class="tv-channel"> </td>
        <td class="menu-button" valign="middle">

To be able to navigate around HTML in .NET, by far the best library I have found is the HTML Agility Pack. Adding this to your LINQPad Query is very simple with the new beta. Press F4 to bring up Query Properties, then click Add NuGet, find the Html Agility Pack in the list and click Add To Query.

Now we are ready to load the document and find all the tables with the class of “fixtures”. You can use a special XPath syntax to do this in one step:

var web = new HtmlAgilityPack.HtmlWeb();
var doc = web.Load("");
foreach(var fixturesTable in doc.DocumentNode.SelectNodes("//table[@class='fixtures']"))
    // ...

Having got each fixture table, I then ignore the top row (which has a class of “fixture-header”), and use the classes on each of the table columns to pull out the information I am interested in. Finally, I use the handy Dump extension method in LINQPad to output my information to the results window:

foreach(var fixture in fixturesTable.SelectNodes("tr"))
    var fixtureClass = fixture.Attributes["class"];
    // header rows have class of fixture-header
    if(fixtureClass == null || !fixtureClass.Value.Contains("fixture-header"))
        var day = fixture.SelectSingleNode("td[@class='first ']").InnerText.Trim();
        var month = fixture.SelectSingleNode("td[@class='month']").InnerText.Trim();
        var venue = fixture.SelectSingleNode("td[@class='home-away']").InnerText.Trim();
        var oppositionNode = fixture.SelectNodes("td").FirstOrDefault(n => n.Attributes["class"].Value.Contains("opposition"));
        var opposition = oppositionNode.SelectSingleNode("a").InnerText.Trim();
        var matchReportUrl = oppositionNode.SelectSingleNode("a").Attributes["href"].Value.Trim();
        var goalsFor = fixture.SelectSingleNode("td[@class='goals-for']").InnerText.Trim();
        var goalsAgainst = fixture.SelectSingleNode("td[@class='goals-against']").InnerText.Trim();
        string.Format("{0} {1} {2} {3} {4}-{5}", day, month, venue, opposition, goalsFor, goalsAgainst).Dump();

This gives me the data I am after, and from here it is easy to convert it into any other format I want such as XML or insert it into a database (something that LINQPad also makes very easy).

04 Sun H Blackburn Rovers 6-2
17 Sat H Birmingham City 3-1
20 Tue A AZ Alkmaar 1-1
25 Sun A West Ham United 2-2
28 Wed H Liverpool 2-1
31 Sat H Tottenham Hotspur 3-0
04 Wed H AZ Alkmaar 4-1
07 Sat A Wolverhampton W. 4-1

And the really nice thing about using LINQPad for this rather than creating a Visual Studio project is that the entire thing is stored in a single compact .linq file without all the extraneous noise of sln, csproj, AssemblyInfo files etc.