Welcome to MSDN Blogs Sign in | Join | Help

There's a bug that got away from me in Windows 7's HD Audio class driver (hdaudio.sys.)

Before I explain the bug, a few caveats:

If you're using Vista, there's no problem.  This only affects Windows 7.

If you use the third-party audio driver, there's no problem.  This only affects the Microsoft HD Audio class driver (hdaudio.sys.)

If your recording devices (mic and line in) are independent (can capture from both at the same time) or muxed (can capture from one or the other but not from their sum) then you're fine; this only affects mixed capture (can capture from one, from the other, or from their sum, but not from both sides of the mix independently.)

If either of your recording devices doesn't support jack presence detection then you're fine; this only affects mixed capture where both sides of the mix support jack presence detection.

Here's the bug.

  1. There are various ways to "reset" the system and bring the OS recording device state into synchronization with the hardware state
    • reboot
    • reset the HD Audio controller
    • reinstall the audio driver
    • restart the AudioEndpointBuilder service
    • unplug all recording devices in the mix
  2. The first state change (plug or unplug) after a reset works
  3. The second and further state changes are not recognized by the OS until a reset.

Here's a sample state diagram assuming a mixed Mic and Line In (click to view full-size:)

 

Workarounds:

Install the third-party audio driver instead of the HD Audio class driver.

Use audio extension cables to fool the jack presence detection hardware into thinking something is always plugged in.

Regular expressions are a tool for matching generic text.  XPath queries are a tool for matching chunks of XML.  Both are search technologies.

When using search technologies it is occasionally quite useful to have a query that will never match anything - for SQL, this would be something like "SELECT 1 WHERE 1 = 0".

My candidates for minimal unsatisfiable regular expression:

/a\bc/
\b is a zero-width assertion that matches a boundary at the beginning or end of a word - specifically, it is true between a word-ish character (\w) and a non-wordish character (\W).  Since literal "a" and "c" are both wordish characters, this will never match.

Or, if you allow Perl extensions:

/(?!)/
This is a negative lookahead for an empty string.  Since the empty string always matches everywhere, this will never match.

My candidate for minimal unsatisfiable XPath query:

/parent::*
This matches everything at or under the parent of the root element.  Since, by definition, the root element has no parent, this will never match.

In a recent post I sung the praises of square waves as a way to get a heckuva lot of power (3 dB more power than a sine wave) into a sample-range-limited signal.  It's time to take them down a notch now.

A problem with square waves is they're impossible to generate in the analog domain.  In fact, you can't even get close.

Signals generated in the analog domain are subject to physical laws regarding continuity (no teleportation) and whatnot. A common way to model these is to express (periodic) analog signals using a basis of sine waves with integral periods.  Suppose I want to generate the square wave:

f(x) = 1, -π < x < 0
-1, 0 < x < π

Graphed below are some approximations of this function using sine waves as a basis.  Note that only odd values of n are used in the sums of sin(nx).


 

The sums converge to +/-1 quite well, but there are definite "ears" at x near 0 where there's overshoot.  This doesn't appear to die down.  A closeup of one of the "ears":


 

If anything, rather than dying down, the "ears" converge to max(fn) → about 1.18, or about 9% of the "jump" from -1 to 1.  (Dym and McKean, in their 1972 book Fourier Series and Integrals, get the 9% right but incorrectly assert that the convergence is to 1.09.)

This mathematical phenomenon - Gibbs' phenomenon - is a good illustration of the difference between convergence of a series of functions and uniform convergence.

In this case, the series of partial sums pointwise converge to the square wave... for any given point x > 0 and ε > 0, the ear will eventually move to the left, and I can choose an N such that fn(x) is within of ε of 1 for all n > N...

... but the series does not uniformly converge to the square wave.  The following assertion is false: "for any given ε > 0, I can pick an N such that fn(x) is within of ε of 1 for all n > N and all x > 0."  This can be demonstrated by picking ε = 0.17, say.  For any n, even, say, n = 10100, there is an x close to 0 where f1e100(x) > 1.17.

Daylight Saving Time is a thorn in my side.

Politician: What time is it?
Scientist: What time would you like it to be?

It is my firm belief that

  1. Daylight Saving Time doesn't do any real good.
  2. Daylight Saving Time does real harm.
  3. Politicians love Daylight Saving Time because it's easy and at least it looks good.
One of the points that my congresswoman brought up is that Daylight Saving Time was originally proposed by a founding father of the US, and that therefore it must be a good idea.  (Exercise: what logical fallacy is this?)

Today I decided to actually look up the original proposal and found it quite enlightening.

Benjamin Franklin's 1784 letter to the Journal de Paris (English translation)

The full article is well worth reading, but this quote will illustrate my point:

I believe all who have common sense, as soon as they have learnt... that it is daylight when the sun rises, will contrive to rise with him; and, to compel the rest, I would propose the following regulations...

let a tax be laid... on every window that is provided with shutters...

[let] no family be permitted to be supplied with more than one pound of candles per week...

Let guards also be posted to stop all the coaches, &c. that would pass the streets after sunset...

Every morning, as soon as the sun rises, let all the bells in every church be set ringing; and if that is not sufficient?, let cannon be fired in every street, to wake the sluggards effectually, and make them open their eyes to see their true interest. 

Yes, Benjamin Franklin was the first to propose a law encouraging people to get up early.

But he was joking.

Raymond Chen explains some common terms for blood relatives of varying distance across cultures in his blog post "What kind of uncle am I?"

He links to a diagram on genealogy.com that I felt was lacking something... so here's my version, with consanguinary colors.

Red means "marriage is almost certainly legally prohibited."

Yellow means "marriage may be legally prohibited - check your region's laws."

Green means "marriage is amost certainly legal."


Relationship by generations to common ancestor
# 0 1 2 3 4 ... m
0 self father/mother grand
(father/mother)
great‑
grand
(father/mother)
(great‑)2
grand
(father/mother)

(great-)m ‑ 2
grand
(father/mother)
1 son/daughter brother/sister aunt/uncle grand
(aunt/uncle)
great‑
grand
(aunt/uncle)

(great‑)m ‑ 3
grand
(aunt/uncle)
2 grand
(son/daughter)
niece/nephew (first) cousin first cousin,
once removed
first cousin,
twice removed

first cousin,
(m ‑ 2) times removed
3 great‑
grand
(son/daughter)
grand
(niece/nephew)
first cousin,
once removed
second cousin second cousin,
once removed

second cousin,
(m ‑ 3) times removed
4 (great‑)2
grand
(son/daughter)
great-
grand
(niece/nephew)
first cousin,
twice removed
second cousin,
once removed
third cousin
third cousin,
(m ‑ 4) times removed
...
n (great‑)n ‑ 2
grand
(son/daughter)
(great‑)n ‑ 3
grand
(niece/nephew)
first cousin,
(n ‑ 2) times removed
second cousin,
(n ‑ 3) times removed
third cousin,
(n ‑ 4) times removed

(n == m) ?
((n ‑ 1)th cousin) :
((min(n, m) ‑ 1)th cousin, |n ‑ m| times removed)

In his post "A chess problem begging for a solution", Michael Kaplan quotes Barbara Hambly's Star Trek novel Ishmael.  In the quoted scene, Spock (AKA Ishmael) plays a couple of chess games against a stranger - rather unusual chess games.  The problem alluded to is to determine the moves of the games given certain information.

Let's take the second game first.  There are two effective possibilities that meet the "Reverse Fool's mate" and "three moves" criteria:

Reverse Fool's mate, even material: $200 in 2½ moves

# Ishmael Stranger
1. e3 f6
2. a3 g5
3. Qh5#

 

... and...

Reverse Fool's mate plus a pawn: $220 in 2½ moves

# Ishmael Stranger
1. e4 f5
2. exf5 g5
3. Qh5#

 

The task for the first game, then, is to win either $400 or $380 in seven moves.  Assuming the game ends in mate (this is reasonable) gives us a difference of either $200 or $180.  The first move can not be a capture, so we really only have six moves to capture $200 worth of material - going after the queen is obvious, but the rooks are quite well tucked away, and it is hard to go after them and simultaneously set up the ending mate.

I believe that the solution that Barbara Hambly had in mind is the following variation of the "other" mate that every chess student learns (Scholar's Mate).  This particular setup is gated by White's need to move his Bishop out of the way, and this nicely satisfies the common convention that players alternate colors in successive games.  Naturally Spock, being a gentleman, would let the stranger take White first.

Mate, a queen, and four pawns: $380 in 7 moves

# Stranger
Ishmael
1. c4 e6
2. c5
Bxc5
3. d4 Bxd4
4. e3 Bxe3
5. Qf3 Qf6
6. Bc4 Qxf3
7. Kf1 Qxf2#

 

However, from a "chess problem" point of view, there's a cook.  It is, in fact, possible to get mate plus a queen plus four pawns in a mere five and a half moves, rather than the seven full moves above:

Mate, a queen, and four pawns: $380 in 5½ moves

# Ishmael
Stranger
1. e3
h5
2. Qxh5
d5
3. Qxd5
Bf5
4. Qxb7
Bh7
5. Qxc7
Qc8
6. Qxc8#

 

Even worse, there is a way to get $400 in a mere four and a half moves.

Mate, a queen, a bishop, and a knight: $400 in 4½ moves

# Ishmael
Stranger
1. d4
Nh6
2. Bxh6
b7
3. Qd3
Ba6
4. Qxa6
Qc8
5. Qxc8#

 

The problem, as it stands, is therefore underdetermined... Spock and the stranger had a full two and a half moves to play around with in the first game, either to exchange material or perhaps to allow the stranger to pick up a free pawn (and return it in the second game.)

There is a general pattern in professional people.  We start out idealistic and adventurous, and as disasters inevitably accumulate we become more circumspect and pessimistic.

(Pre-emptive snarky response: "you say that like it's a bad thing.")

While pessimism is invaluable to a tester, it should be tempered with a just sense of hubris.  Larry Wall's saw about the three virtues of any great programmer has a historical antecedent from one of the canonical American authors:

We should be careful to get out of an experience only the wisdom that is in it -- and stop there; lest we be like the cat that sits down on a hot stove-lid. She will never sit down on a hot stove-lid again -- and that is well; but also she will never sit down on a cold one any more.
    -- Mark Twain, Following the Equator

Keep your childlike optimism.

I was asked one day why this full-scale sine wave was being measured by our signal analysis tools as -3 dB FS, even though it hits the maximum and the minimum sample values:

 

The answer is "because it's a sine wave, not a square wave."  The intensity of a signal can be calculated from the following formula:

The inner integral does not depend on t - it's just the average sample value - so it's usually precalculated:

 

 

The importance of taking dc into account during analysis can be appreciated if you try to calculate the intensity of a signal with a high dc.

Exercise: calculate the intensity of x(t) ≡ -0.5 using the formulas above; calculate the "naïve intensity" by using the last formula above and omitting dc. Note the difference.

Now that we have the necessary formulas, let's analyze our full-scale sine wave signal.  Plugging in a full-scale sine wave and analyzing over a full period we get:

 

As expected, the average value of the signal over a single period is 0.

Evaluating the intensity requires finding the antiderivative of (sin(t))2. This can be ascertained most easily by plotting a few values and realizing that (sin(t))2 = (1 - cos(2t))/2:

 

 

 

This is a dimensionless number that ranges from 0 (signal is a flat line) to 1 (... we'll get to that later.)

We can convert this into a dB FS measurement using the formula IdB FS = 20 log10 IRMS:

Et voilà - that's where the -3 dB comes from.

In contrast to a sine wave, a full-scale square wave centered at 0 has an intensity of 0 dB FS:

To finish up, a couple of advanced exercises:

Advanced Exercise: prove that, provided t1 and t2 are sufficiently far apart, the intensity of a general sine wave x(t) = a sin(ωt + φ) + c depends only on a and not on ω, φ, or c.

Advanced Exercise: completely characterize the set of digital signals that achieve 0 dB FS intensity.  If you have, say, 1000 samples of mono 16-bit integer audio to play with, how many distinct signals achieve 0 dB FS intensity?

Attached to this post is a sample WASAPI exclusive-mode event-driven playback app, including amd64 and x86 binaries, source, and a modification of the ac3.wav Dolby Digital test tone to include a "fact" chunk.

>play-exclusive.exe -?
play-exclusive.exe -?
play-exclusive.exe --list-devices
play-exclusive.exe [--device "Device long name"] --file "WAV file name"

    -? prints this message.
    --list-devices displays the long names of all active playback devices.

Plays the given file to the given device in WASAPI exclusive mode.
If no device is specified, plays to the default console device.

On the particular system I used to test this, these are the devices I have:

>play-exclusive.exe --list-devices
Active render endpoints found: 3
    Digital Audio (S/PDIF) (2- High Definition Audio Device)
    Speakers (2- High Definition Audio Device)
    Sceptre (High Definition Audio Device)

And this is the output I get when I play the attached ac3.wav test tones to the Sceptre HDMI output:


>play-exclusive --device "Sceptre (High Definition Audio Device)" --file ac3.wav
Opening .wav file "ac3.wav"...
The default period for this device is 30000 hundred-nanoseconds, or 144 frames.
Buffer size not aligned - doing the alignment dance.
Trying again with periodicity of 33333 hundred-nanoseconds, or 160 frames.
We ended up with a period of 33333 hns or 160 frames.

Successfully played all 460800 frames.

A word on the "alignment dance" highlighted above... first, this scene from The Pacifier.  (Vin Diesel is so coordinated.)

The Pacifier: The Peter Panda dance

Here's the source for the dance (in play.cpp in the attached.)

// call IAudioClient::Initialize the first time
// this may very well fail
// if the device period is unaligned
hr = pAudioClient->Initialize(
    AUDCLNT_SHAREMODE_EXCLUSIVE,
    AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
    hnsPeriod, hnsPeriod, pWfx, NULL
);
// if you get a compilation error on AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED,
// uncomment the #define below
//#define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
if (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED == hr) {

    // if the buffer size was not aligned, need to do the alignment dance
    printf("Buffer size not aligned - doing the alignment dance.\n");
   
    // get the buffer size, which will be aligned
    hr = pAudioClient->GetBufferSize(&nFramesInBuffer);
    if (FAILED(hr)) {
        printf("IAudioClient::GetBufferSize failed: hr = 0x%08x\n", hr);
        return hr;
    }
   
    // throw away this IAudioClient
    pAudioClient->Release();

    // calculate the new aligned periodicity
    hnsPeriod = // hns =
        (REFERENCE_TIME)(
            10000.0 * // (hns / ms) *
            1000 * // (ms / s) *
            nFramesInBuffer / // frames /
            pWfx->nSamplesPerSec  // (frames / s)
            + 0.5 // rounding
        );

    // activate a new IAudioClient
    hr = pMMDevice->Activate(
        __uuidof(IAudioClient),
        CLSCTX_ALL, NULL,
        (void**)&pAudioClient
    );
    if (FAILED(hr)) {
        printf("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x\n", hr);
        return hr;
    }

    // try initialize again
    printf("Trying again with periodicity of %I64u hundred-nanoseconds, or %u frames.\n", hnsPeriod, nFramesInBuffer);
    hr = pAudioClient->Initialize(
        AUDCLNT_SHAREMODE_EXCLUSIVE,
        AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
        hnsPeriod, hnsPeriod, pWfx, NULL
    );

    if (FAILED(hr)) {
        printf("IAudioClient::Initialize failed, even with an aligned buffer: hr = 0x%08x\n", hr);
        pAudioClient->Release();
        return hr;
    }
} else if (FAILED(hr)) {
    printf("IAudioClient::Initialize failed: hr = 0x%08x\n", hr);
    pAudioClient->Release();
    return hr;
}

// OK, IAudioClient::Initialize succeeded
// let's see what buffer size we actually ended up with
hr = pAudioClient->GetBufferSize(&nFramesInBuffer);
if (FAILED(hr)) {
    printf("IAudioClient::GetBufferSize failed: hr = 0x%08x\n", hr);
    pAudioClient->Release();
    return hr;
}

// calculate the new period
hnsPeriod = // hns =
    (REFERENCE_TIME)(
        10000.0 * // (hns / ms) *
        1000 * // (ms / s) *
        nFramesInBuffer / // frames /
        pWfx->nSamplesPerSec  // (frames / s)
        + 0.5 // rounding
    );

 

 

Note the new HRESULT. 

HD Audio works on a 128-byte aligned buffer size.  This dance ensures that the HD Audio driver is being fed data in chunks of 128 bytes.  It is somewhat complicated by the fact that IAudioClient::Initialize takes a parameter of hundred-nano-seconds, but IAudioClient::GetBufferSize sets a parameter of frames.

In a previous post I showed how to play silence to a given audio device and hinted at a possible application.

Attached to this post is a sample WASAPI loopback capture app - amd64, x86 and source included.  This allows you to record the sound that is coming out of your speakers:

>loopback-capture -?
loopback-capture -?
loopback-capture --list-devices
loopback-capture [--device "Device long name"] [--file "file name"] [--int-16]

    -? prints this message.
    --list-devices displays the long names of all active playback devices.
    --device captures from the specified device (default if omitted)
    --file saves the output to a file (loopback-capture.wav if omitted))
    --int-16 attempts to coerce data to 16-bit integer format

There are a couple of oddities for WASAPI loopback capture.  One is that "event mode" doesn't work for loopback capture; you can call pAudioClient->Initialize(... AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ... ), you can call pAudioClient->SetEventHandle(...), and everything will succeed... but the "data is ready" event will never fire.  So this app creates its own waitable timer.

Another oddity is that WASAPI will only push data down to the render endpoint when there are active streams.  When nothing is playing, there is nothing to capture.

For example, play a song, and then run loopback-capture.  While loopback-capture is running, stop the song, and then start it again.  You'll get this output when you start it back up:

>loopback-capture
Press Enter to quit...
IAudioCaptureClient::GetBuffer set flags to 0x00000001 on pass 5381 after 1088829 frames

Thread HRESULT is 0x8000ffff

The flag in question is AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY.  When the song stopped, no more data was available to capture.  Eventually the song started up again, and WASAPI dutifully reported that there was a glitch detected.  This app stops on glitches.

There are a couple of other possible ways to handle this.  One way is to ignore glitches; then if you stop a song, wait a few seconds, and start it again, then the recorded signal will omit the wait and abut the two "audio is playing" portions.

But my particular favorite way of handling this is to run silence.exe.  That way there are never any "nothing is playing" glitches, because there's always something playing.

Be vewy vewy quiet - we'we hunting wabbits.
    -- Elmer Fudd

Attached is a mini-app I've written to play silence to any given playback device using WASAPI event-driven (pull) mode.  Source, x86 binary, and amd64 binary are attached.

Usage statement:

>silence -?
silence
silence -?
silence --list-devices
silence --device "Device long name"

    With no arguments, plays silence to the default audio device.
    -? prints this message.
    --list-devices displays the long names of all active playback devices.
    --device plays silence to the specified device.

>silence --list-devices
Active render endpoints found: 2
    Speakers (USB Audio Device)
    Headphones (High Definition Audio Device)

>silence --device "Headphones (High Definition Audio Device)"
Press Enter to quit...
Received stop event after 488 passes

While it's playing it shows up in the Volume Mixer:

 http://blogs.msdn.com/photos/matthew_van_eerde/images/9191979/original.aspx

Why would I write such a thing?

Well, there is the pedagogical exercise of writing a WASAPI event-driven render loop.

But there is also a practical application of an active silence stream, having to do with loopback capture.  More on this in a future post...

One of my favorite languages is Perl.  Perl has an ambivalent reputation; some people take to it, some accuse it of being a syntax-complete language.  (There's some truth to this.)

My view is that Perl gives you a very direct link into the mind of the programmer - much more so than other languages.  Perl is designed very much like a spoken language, perhaps because Larry Wall's background is linguistics.

There was a little girl
Who had a little curl
Right in the middle of her forehead.
And when she was good,
She was very, very, good;
But when she was bad
She was horrid.
   -- Henry Wadsworth Longfellow

(In an English accent, "forehead" and "horrid" actually rhyme.)

Two examples of my own Perl to illustrate my point.  This is in my email signature:

perl -e "print join er,reverse',','l hack',' P','Just anoth'"

And this little seasonal gem:

use strict;
use warnings;

sub receive($);

my @ordinals = qw(
zeroth
first second third fourth fifth sixth
seventh eighth ninth tenth eleventh twelfth
);

my @gifts = reverse split /\n/, <<END_OF_LIST;
Twelve drummers drumming;
Eleven pipers piping;
Ten lords a-leaping;
Nine ladies dancing;
Eight maids a-milking;
Seven swans a-swimming;
Six geese a-laying;
Five golden ringeds;
Four colly birds;
Three French hens;
Two turtle doves;
A partridge in a pear tree.
END_OF_LIST

for (my $day = 1; $day <= 12; $day++) {
receive($day);
}

sub receive($) {
my $day = shift;

print("On the ", $ordinals[$day], " day of Christmas, my true love sent to me:\n");

for (my $i = $day; $i > 0; $i--) {
my $gift = $gifts[$i - 1];

if ($i == 1 && $day != 1) {
$gift =~ s/^(\s*)A/$1And a/;
}

print $gift, "\n";
}

if ($day != 12) {
print "\n";
}
}

The latter kind of Perl I like to call "good Perl".  It's easy to read, I think.  There are a couple of idioms that take getting used to, just like with any new language, but well-written Perl is (I think) easier to read than any other language.

But flexibility has its dark sides as well.  Black Perl is the canonical example, but there are others such as Perl golf.  This kind of thing (the first sample above is an example) is responsible for at least part of Perl's reputation for opacity; its compatibility with shell scripting, and most particularly its embedded regular expression support, is responsible for much of the rest.

Exercise: duplicate the output of the second sample above using as short a Perl program as possible.

Like many programmers, I've messed around in a lot of development environments - including the UNIX/Linux family of operating systems.

One of the UNIX commands I like is xargs... this is very handy when writing one-off command lines or scripts.

I like it so much that I miss it terribly when I'm in a Windows OS.  So I wrote an xargs.bat and stuck it in my PATH.  Now instead of writing complicated for loops, I can just pipe to xargs.

The script is "powered by" the highlighted line:

C:\Users\MatEer\Desktop\custom-path>type xargs.bat
@echo off

setlocal

if /i (%1)==(/?) goto USAGE

if /i (%1)==() goto USAGE

if /i (%1)==(/addquotes) goto ADDQUOTES

goto NOQUOTES

:USAGE
echo usage: something-that-produces-output ^| %0 [/?] [/addquotes] thing-to-run
echo   xargs.bat by Matthew van Eerde 10/3/2005
echo.
echo   something-that-produces-output should write to its STDOUT
echo   thing-to-run will have each line of the output appended to it,
echo   then will be run successively
echo.
echo   If /addquotes is set, quotes will be added around the line
echo   before appending the line to thing-to-run
echo.
echo   If you call xargs without piping output to it, xargs will wait
echo   for you to type something in on STDIN.
echo   Ctrl-Z on its own line to finish.
goto END


:ADDQUOTES

rem eat /addquotes parameter
shift

rem Alas, shift doesn't affect %*
if (%1)==() goto USAGE
set basecommand=%1
shift

:BUILDBASECOMMAND
if (%1)==() goto DONEBASECOMMAND
set basecommand=%basecommand% %1
shift
goto BUILDBASECOMMAND
:DONEBASECOMMAND

rem run the program specified by %*
rem as many times as there are lines in STDIN
rem with one extra argument -- defined by each line of STDIN -- in quotes
rem
rem all that the find command does is intercept STDIN
rem
for /F "usebackq delims=" %%a in (`find /v ""`) do %basecommand% "%%a"

goto END


:NOQUOTES

rem run the program specified by %*
rem as many times as there are lines in STDIN
rem with extra arguments defined by each line of STDIN
rem
rem all that the find command does is intercept STDIN
rem
for /F "usebackq delims=" %%a in (`find /v ""`) do call %* %%a

goto END


:END

endlocal

This allows wonderfully anaOStic things like findstr /m IFoo * | xargs start

Stealing from XKCD again:

Terminology

I have a similar problem with North and South.  On the globe, there's a clearly marked "North Pole" and a clearly marked "South Pole."

Fine.

Magnets also have North and South poles.  These are typically labeled N and S respectively.  Fine.

But if you consider the Earth as a large magnet (which it is), then you have to stick the N where the penguins live (Antarctica-ish) and the S where the polar bears live (Canada-ish...)

 http://blogs.msdn.com/photos/matthew_van_eerde/images/9064687/original.aspx

That bugs me.

Hover the mouse over each picture (xkcd-style) in turn to get the text that tells the story.

An amusing behavior of Windows Vista recently came to my attention... sometimes the Sound control panel wants to go back... to the future!



So far, so good.



D'oh! Luckily, there's a way to recover...



Voilà tout.
More Posts Next page »
 
Page view tracker