Matthew van Eerde's web log

  • Matthew van Eerde's web log

    Sample - WASAPI loopback capture (record what you hear)

    • 95 Comments

    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.

    EDIT 11/23/2009: Updated loopback-capture.exe to ignore the glitch flag on the first packet, since Windows 7 sets it.  Also improved the interaction between the capture thread bailing out and the user pressing Enter to finish.

  • Matthew van Eerde's web log

    How to install unsigned drivers

    • 4 Comments

    Most device drivers these days have signatures which allow the operating system to validate that the driver files have not been tampered with since the author sent them out into the world.

    It is still possible to run into an unsigned driver. If you try to install an unsigned driver, Windows will warn you at install time:

    IMPORTANT: if you see this warning, ask yourself this question - DO I TRUST THE SOURCE OF THIS DRIVER? If the answer is anything but a resounding YES, you should click "Don't install this software."

    Windows XP was, by and large, a 32-bit operating system. A 64-bit version of Windows XP was developed but it was not broadly released. At that time it was still fairly common to have unsigned drivers.

    Windows Vista was the first client release of Windows to have a widely deployed 64-bit release. This gave an opportunity to be more strict about enforcing driver signatures. For backwards compatibility, it was still possible to run unsigned drivers on the 32-bit version of Windows Vista (so the XP drivers would still work) but there was no such burden on Windows Vista 64-bit; so Windows Vista 64-bit was the first time that there was really a strong incentive to run with only signed drivers.

    Even so, there are still workarounds provided:

    • If you want to run an unsigned driver for one particular boot, you can hit F8 during boot1 and choose "Disable Driver Signature Enforcement". This allows unsigned drivers to load for this boot only.
    • If you're a device driver developer, it is expected that you will want to iterate on your particular driver; it is also expected that you will probably also have a kernel debugger attached. So driver signature enforcement is disabled if you have a kernel debugger attached.

    The most reliable method, however, and the one with the fewest side effects, is to... well... sign the driver!

    To sign a driver:

    1. Download the Windows Driver Kit.
    2. Make sure the driver .inf has a CatalogFile=MyCatalogFile.cat line (specify your own value for MyCatalogFile.cat). If one is missing you can add it to the [Version] section.
    3. Point inf2cat to the driver .inf file and it will make a .cat file for you. This .cat file will have an entry for every file pulled in by the .inf.
    4. Use SignTool to sign the .cat file.

    At this point you can see who says this driver is OK. Double-click the .cat file | View Signature | View Certificate | Certification Path and note the highest certificate in the chain:

    If you're a professional driver developer and you want your driver to ship on Windows systems, your next step would very likely be to use the Windows Logo Kit to run the Microsoft logo tests, and get an official "Microsoft says this driver has passed the logo tests" signature for your .cat file. (The difference is that the top certificate in the chain will be "Microsoft Root Authority" rather than "Microsoft Test Root Authority.") A full description of this process lies outside the scope of this blog post.

    But if you're just developing a driver for your personal use, you would probably skip this step. Instead of getting a "Microsoft says this driver is OK" signature, you can add the root certificate for your signing certificate to the "trusted root certificates" certificate store on the machine you want to load the driver on.

    If the root certificate in question is the "Microsoft Test Root Authority", you can run "bcdedit -set testsigning on" and reboot.

    If it's some other certificate (for example, if you generated a self-signed certificate) then you need to add it to the trusted certificate store by using certmgr.exe or by going to Internet Explorer | Internet Options | Content | Certificates.

    If you have everything right, then installing the driver should not pop up the "driver is unsigned" warning. If you still get the warning, looking at C:\windows\inf \setupapi.dev.log can give some clues as to what is wrong... whether there is a driver file that is not listed in the catalog file, or the catalog file's signature does not lead back to a trusted root certificate, or something else.

    1 In Windows 8 Developer Preview, the F8 experience has been reworked. Hit Shift-F8 instead.2
    2 Some BIOSes have an issue where Shift is not sent to Windows this early. If you have Windows 8 Developer Preview installed one of these machines there's another way - hit F10 during boot to get to the "Edit Boot Options" screen. Add /DISABLE_INTEGRITY_CHECKS to the boot flags.

  • Matthew van Eerde's web log

    Deriving the centripetal acceleration formula

    • 26 Comments

    A body that moves in a circular motion (of radius r) at constant speed (v) is always being accelerated.  The acceleration is at right angles to the direction of motion (towards the center of the circle) and of magnitude v2 / r.

    The direction of acceleration is deduced by symmetry arguments.  If the acceleration pointed out of the plane of the circle, then the body would leave the plane of the circle; it doesn't, so it isn't.  If the acceleration pointed in any direction other than perpendicular (left or right) then the body would speed up or slow down.  It doesn't.

    Now for the magnitude.  Consider the distance traveled by the body over a small time increment Δt:

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

    We can calculate the arc length s as both the distance traveled (distance = rate * time = v Δt) and using the definition of a radian (arc = radius * angle in radians = r Δθ:)

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

    The angular velocity of the object is thus v / r (in radians per unit of time.)

    The right half of the diagram is formed by putting the tails of the two v vectors together.  Note that Δθ is the same in both diagrams.

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

    Note the passing from sin to cos is via l'Hôpital's rule.

    QED.

  • Matthew van Eerde's web log

    Enumerating MIDI devices

    • 0 Comments

    In addition to audio playback and recording, Windows Multimedia (WinMM) provides a Musical Instrument Digital Interface (MIDI) API.

    Here's how to make a list of all the MIDI devices on the system, their capabilities, and the hardware device interface associated with each of them.

    Source and binaries attached.

    Pseudocode:

    midiInGetNumDevs or midiOutGetNumDevs
    for each device
        midiInGetDevCaps or midiOutGetDevCaps
        log device capabilities
        midiInMessage or midiOutMessage
            with DRV_QUERYDEVICEINTERFACESIZE
            and DRV_QUERYDEVICEINTERFACE
        log the device interface

    Output:

    >midienum.exe
    midiIn devices: 1
    -- 0: USB2.0 MIDI Device --
        Device ID: 0
        Manufacturer identifier: 65535
        Product identifier: 65535
        Driver version: 1.6
        Product name: USB2.0 MIDI Device
        Support: 0x0
        Device interface: "\\?\usb#vid_xxxx&pid_yyyy&..."
    midiOut devices: 2
    -- 0: Microsoft GS Wavetable Synth --
        Device ID: 0
        Manufacturer identifier: 1
        Product identifier: 27
        Driver version: 1.0
        Product name: Microsoft GS Wavetable Synth
        Technology: 7 (MOD_SWSYNTH)
        Voices: 32
        Notes: 32
        Channel mask: 0xffff
        Support: 0x1
            MIDICAPS_VOLUME
        Device interface: ""
    -- 1: USB2.0 MIDI Device --
        Device ID: 1
        Manufacturer identifier: 65535
        Product identifier: 65535
        Driver version: 1.6
        Product name: USB2.0 MIDI Device
        Technology: 1 (MOD_MIDIPORT)
        Voices: 0
        Notes: 0
        Channel mask: 0xffff
        Support: 0x0
        Device interface: "\\?\usb#vid_xxxx&pid_yyyy&..."

    (Actual device interface string suppressed.)

    Note the Microsoft GS Wavetable Synth device, which is always present.

    Why would you want to know the device interface? In our case, because we want to test all the audio-related interfaces of a particular device on the system.

  • Matthew van Eerde's web log

    Disney princesses: an attempt at a complete list

    • 24 Comments

    Last time we examined the "Disney princess" status of Belle from Beauty and the Beast. This time I attempt to give an exhaustive list of all Disney princesses together with a very short summary of their status.

    Snow White and the Seven Dwarfs (1937)

    Snow White
    Queen's Daughter; Queen? Prince's Girlfriend; Prince's Wife?
    Disney Princess

    Bambi (1942)

    Faline
    Prince's Wife
    Disney Princess
    Bambi's Mother
    Prince's Wife
    Disney Princess

    Cinderella (1950)

    Cinderella
    Prince's Wife
    Disney Princess

    Peter Pan (1953)

    Tiger Lily
    Chief's Daughter
    Disney Princess

    Sleeping Beauty (1959)

    Aurora
    King's Daughter; Prince's Girlfriend; Prince's Wife?
    Disney Princess

    Robin Hood (1973)

    Maid Marian
    Wealthy; Prince(?)'s Girlfriend
    Not a princess

    Nausicaä of the Valley of Wind (1984: Studio Ghibli1)

    Nausicaä
    King's Daughter; Queen?
    Disney(?) Princess
    Kushana
    Royalty's Daughter
    Disney(?) Princess
    Lastelle
    Queen's Daughter
    Disney(?) Princess

    The Black Cauldron (1985)

    Eilonwy
    Queen's Daughter; Prince(?)2's Girlfriend(?)
    Disney Princess

    Castle in the Sky (1986: Studio Ghibli)

    Sheeta
    Queen's Daughter; Queen?
    Disney(?) Princess

    Oliver & Company (1988)

    Jenny
    Wealthy
    Not a princess

    The Little Mermaid (1989)

    Ariel
    King's Daughter; Queen's Daughter?3 Prince's Wife
    Disney Princess
    Aquata, Andrina, Arista, Attina, Adella, Alana
    King's Daughter; Queen's Daughter?3 Queen?4
    Disney Princesses

    Beauty and the Beast (1991)

    Belle
    Prince's Girlfriend; Prince's Wife?5
    Disney Princess?

    Aladdin (1992)

    Jasmine
    Sultan's Daughter; Prince's Girlfriend?6 Sultan's Girlfriend?7
    Disney Princess

    The Lion King (1994)

    Nala
    King's Daughter8; Prince's Girlfriend; King's Girlfriend; King's Wife
    Disney Princess
    Kiara
    King's Daughter9
    Disney Princess?

    Pocahontas (1995)

    Pocahontas
    Chief's Daughter
    Disney Princess

    Princess Mononoke (1997: Studio Ghibli)

    San (also known as Princess Mononoke)
    Wolf God's Adopted Daughter
    Disney(?) Princess
    Kaya
    Prince's Sister
    Disney(?) Princess

    A Bug's Life (1998: Pixar)

    Dot
    Queen's Daughter
    Disney(?) Princess
    Atta
    Queen's Daughter; Queen
    Disney(?) Princess

    Atlantis: The Lost Empire (2001)

    Kida
    King's Daughter; Queen
    Disney Princess

    Tales from Earthsea (2006: Studio Ghibli)

    Therru (true name Tehanu)
    Prince's Girlfriend
    Not a princess

    Ponyo (2008: Studio Ghibli)

    Ponyo
    Queen's Daughter
    Disney(?) Princess

    The Princess and the Frog (2009)

    Tiana
    Prince's Wife
    Disney Princess
    Charlotte
    King's Daughter10
    Disney Princess

    Tangled (2010)

    Rapunzel
    King & Queen's Daughter
    Disney Princess

    Brave (2012)

    Merida
    King & Queen's Daughter
    Disney Princess

    Wreck-it Ralph (2012)

    Vanellope von Schweetz
    Princess
    Disney Princess

    Frozen (2013)

    Anna
    King & Queen's Daughter; Queen?11
    Disney Princess
    Elsa
    King & Queen's Daughter; Queen
    Disney Princess

    Methodology - I started with Disney's own "50 animated features" list as present on the Tangled DVD. To this I added the list of Pixar and (somewhat more controversially) Studio Ghibli movies released under the Disney name.

    1 Nausicaä of the Valley of Wind is technically not a Studio Ghibli film as the studio was not founded until after the film was released.
    2 In The Black Cauldron, Taran is not yet aware that he is a prince.
    3 In The Little Mermaid, Ursula briefly has a claim to being a queen. Her relationship to Triton is never made clear; if she is his ex-wife, Ariel and her sisters are potentially her daughters.
    4 In The Little Mermaid, Triton is briefly incapacitated. His eldest daughter thus has a brief claim to being Queen.
    5 In Beauty and the Beast, Belle's princess status hinges on whether she is married to the Beast.
    6 In Aladdin, Aladdin spends a fair amount of time transformed into a prince, though it is not clear where his principality lies.
    7 In Aladdin, Jafar is briefly Sultan and attempts to woo Jasmine, though there is no reciprocity in their relationship.
    8 In The Lion King it is perhaps best not to inquire too closely into the precise nature of the familial relationships between members of the pride in question.
    9 The Lion King ends with a shot of Simba and Nala's cub being presented, but it is not clear whether the cub is male or female; in sequels it is made clear that the cub is a girl and her name is Kiara.
    10 Charlotte's father is King of Mardi Gras, so Charlotte is princess for only one day. Still, Once a Princess, Always a Princess.
    11 When Elsa abandons Arendelle, there is a case to be made that Anna is de facto Queen. Anna herself would dispute this claim.

  • Matthew van Eerde's web log

    Sample - WASAPI exclusive-mode event-driven playback app, including the HD Audio alignment dance

    • 21 Comments

    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.

  • Matthew van Eerde's web log

    How to calculate a sine sweep - the wrong way

    • 3 Comments

    Suppose you want to generate a continuous sine-sweep from time tstart to time tend. You want the starting frequency to be ωstart, and the ending frequency to be ωend; you want the sweep to be logarithmic, so that octaves are swept out in equal times. (The alternative would be to sweep linearly, but usually logarithmic sweeping is what you want.) For today we're going to have continuous time and sample values, so sample rate and bit depth will not be part of this discussion; in particular, the units of t will be seconds, and our sample values will go from -1 to 1.

    Here's a picture of want we want to end up with:

    Without loss of generality, set tstart = 0. This simplifies some of the math.

    Let ω(t) be the "instantaneous frequency" at time t. Advanced Exercise: Define "instantaneous frequency" mathematically.

    Sweeping ω(t) logarithmically means that log ω(t) is swept linearly from log ωstart to log ωend. So

    Quick check: ω(0) = ωstart, ω(tend) = ωend. Yup.

    Let's define R (the rate of change of the frequency) as (ωend / ωstart)1 / tend. This formula reduces to

    ω(t) = ωstart Rt

    Next step is to look at phase. For a sine wave of constant frequency ω, phase progresses linearly with t on the circular interval [0, 2π):
    φ = k ω t
    for some constant k. If the frequency of a sine wave was 1 cycle per second, the phase would go from 0 to 2π in a single second - this reveals that the correct value of k is 2π:
    φ = 2π ω t

    What happens with a variable frequency? A little calculus provides the answer. Consider a small change Δt, small enough so that ω(t) is almost constant over the interval:
    Δφ ≈ 2π ω(t) Δt
    because over that interval the sine sweep function is well approximated by a sine wave of constant frequency ω(t).

    Starting from an initial phase φstart, let Δt → 0, and:

    Substitute:
    u = τ log R
    τ = u / log R
    = du / log R

    Define:
    B = 2π ωstart / log R
    A = φstart - B
    and we have:

    φ(t) = A + BRt

    Just for fun, here's the explicit solution without intermediate values:

     

    Now, the payoff: the signal is just the sine of the phase, so our sine sweep is:

     

    or, more explicitly:

     

    Ah, beautiful mathematically.

    But useless practically.

    Why useless?

    Well, note that our equation for phase is an exponential. That expression inside the sin(...) gets very big, very quickly. Any practical implementation of sin(...) is going to be utterly useless once its argument gets beyond a certain threshold - you'll get ugly chunky steps before you're halfway into the sweep.

    Nonetheless, there is a practical way to generate (pretty good) sine sweeps. More on that next time.

    EDIT: all logs are base e.

  • Matthew van Eerde's web log

    Sample - playing silence via WASAPI event-driven (pull) mode

    • 10 Comments

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

    EDIT: 7/30/2009 - fixed bug where I was treating the GetCurrentPadding value as the amount of free space in the buffer when in fact it's the amount of used space.

    While I was at it, added an icon and exited immediately on errors rather than waiting for the caller to hit Enter.

  • Matthew van Eerde's web log

    What is a perfect score in Yahtzee?

    • 11 Comments

    The dice game Yahtzee takes thirteen turns. Each turn involves rolling a set of five dice (with two possible rerolls, the player having the option to reroll only a subset of the dice), then marking one of thirteen boxes and scoring points according to whether the numbers on the dice meet the rules of the box. After each box is marked, the game is over.

    The thirteen boxes and their rules are:

    1. ONES: score is the sum of those dice showing a one (could be zero.)
    2. TWOS: score is the sum of those dice showing a two (could be zero.)
    3. THREES: score is the sum of those dice showing a three (could be zero.)
    4. FOURS: score is the sum of those dice showing a four (could be zero.)
    5. FIVES: score is the sum of those dice showing a five (could be zero.)
    6. SIXES: score is the sum of those dice showing a six (could be zero.)
    7. THREE OF A KIND: if three of the dice show the same number, the score is the sum of all five dice; otherwise, zero.
    8. FOUR OF A KIND: if four of the dice show the same number, the score is the sum of all five dice; otherwise, zero.
    9. FULL HOUSE: if three of the dice show the same number as each other, and the other two show the same number as each other, score 25; otherwise, zero.1
    10. SMALL STRAIGHT: if there are four dice which show (1 2 3 4), or (2 3 4 5), or (3 4 5 6), score 30; otherwise, zero. (There is no defined order for the rolled dice, so order is not important.)
    11. LARGE STRAIGHT: if the five dice show (1 2 3 4 5), or (2 3 4 5 6), score 40; otherwise, zero. (Order is not important.)
    12. CHANCE: score the total of the five dice (minimum possible score is 5.)2
    13. YAHTZEE (FIVE OF A KIND:) if all five dice show the same number, score 50; otherwise, zero.

    There are two bonuses possible:

    1. At the end of the game, if the point total for boxes 1-6 (ONES through SIXES) is at least 63, the player gets a bonus of 35. (63 corresponds to getting three ones + three twos + three threes + ... + three sixes, but there are other possibilities. I don't know where 35 came from.)
    2. At any time the player checks a box, if...
      • the five dice all have the same number as each other
      • AND the Yahtzee box is already checked
      • AND this was not a result of "taking a zero in Yahtzee"
      ... then the player gets a bonus 100 points. This bonus can take effect multiple times per game.

    So a perfect Yahtzee game looks like this. On the first turn...

    Roll Box Score
    (x x x x x) any Yahtzee YAHTZEE 50
    Subtotal 50

    ... then on the following turns, in no particular order (but see below:)

    Roll Box Score
    (1 1 1 1 1) ONES 5
    (2 2 2 2 2) TWOS 10
    (3 3 3 3 3) THREES 15
    (4 4 4 4 4) FOURS 20
    (5 5 5 5 5) FIVES 25
    (6 6 6 6 6) SIXES 30
    (6 6 6 6 6) THREE OF A KIND 30
    (6 6 6 6 6) FOUR OF A KIND 30
    (x x x x x) any Yahtzee FULL HOUSE 0 or 25 (see below)1
    (x x x x x) any Yahtzee SMALL STRAIGHT 0 (but see below)
    (x x x x x) any Yahtzee LARGE STRAIGHT 0 (but see below)
    (6 6 6 6 6) CHANCE 30
    Subtotal 195 or 220

    Now we look at the bonuses and add it all up:

    Category Score
    Base score 245 or 270
    Bonus for scoring >= 63 in ONES through SIXES 35
    Bonus for scoring twelve additional YAHTZEEs after scoring 50 in YAHTZEE 1200
    Total 1480 or 1505

    So a perfect score in Yahtzee is 1480 or 1505.

    Well...

    ... not quite. There's another rule about Yahtzees (the "Joker" rule) which I didn't tell you about yet, because it's kind of complicated. If...

    • You roll a Yahtzee
    • AND the YAHTZEE box is already checked (a zero score is OK; you don't get the 100 point bonus, but this rule still applies)
    • ... then you MUST score this in the ONES-through-SIXES box for the number you rolled (say, 3)...
    • ... UNLESS that box has also already been checked (a zero score is OK), in which case...

    ... you can score this roll in any unchecked box and it automatically meets the scoring criterion (wow!) So you would get 30 points in SMALL STRAIGHT or 40 points in LARGE STRAIGHT. (Or 25 points in FULL HOUSE.)1

    This gives an additional 70 or 95 points, bring the total to 1575. (It also creates additional order dependencies; the boxes for FULL HOUSE, SMALL STRAIGHT, and LARGE STRAIGHT must be checked after the ONES through SIXES box for the corresponding number is checked.)

    1 Does five-of-a-kind count as a full house? I'm not sure. Mathematically it would make sense. It turns out not to matter for this calculation because of the Joker rule, but it may matter in an actual game. The official rules say "Score in this box only if the dice show three of one number and two of another", so I guess not, but I would be comfortable adopting a "house rule" which allowed a Yahtzee to count as a "native" full house. Normally a house rule should be agreed upon by all players before the start of the game, but I'm pretty sure it would be possible to convince the other players to let you score your (3 3 3 3 3) in FULL HOUSE while YAHTZEE was still open. ("You want to throw away 25 points?  Really?  Um, go right ahead...")

    2 The lowest possible Yahtzee score is 5: get (1 1 1 1 1) and score it in Chance, then get zeros in all the other boxes.

  • Matthew van Eerde's web log

    How to calculate a sine sweep - the right way

    • 3 Comments

    Three quarters are better than a dollar because they make noise!
        -- Lilly, Lilly's Purple Plastic Purse

    Last time I talked about how to calculate a sine sweep mathematically.  There's a closed-form solution which is quite elegant but, alas, useless in a practical computer implementation due to the very big numbers that are being fed to the sin(...) function.

    A computer implementation will care only about the discrete samples that need to be generated.  The integral in the previous post turns out to be counterproductive - we're much more interested in the infinite sequence of φi, and more particularly in their residue mod 2π.

    Here are Matlab scripts that implement a sine sweep both ways:

    First, the naïve mathematical solution that just calculates the exponential:

    function signal = sinesweep_nostate( ...
        start, ...
        finish, ...
        seconds, ...
        sample_rate, ...
        amplitude, ...
        initial_phase, ...
        dc ...
    )
    % sinesweep_nostate returns a single-channel sine logarithmic sweep
    %     DO NOT USE THIS FUNCTION IN PRODUCTION
    %     THIS IS A PEDAGOGICAL EXERCISE ONLY
    %     THE SIGNAL THIS PRODUCES GRADUALLY DISTORTS
    %     INSTEAD USE sinesweep
    %    
    %     start: starting frequency in Hz
    %     finish: ending frequency in Hz
    %     seconds: duration of sweep in seconds
    %     samplerate: samples per second
    %     amplitude: amplitude
    %     initial_phase: starting phase
    %     dc: dc

    % echo these interesting intermediate values to the console
    R = (finish / start) ^ (1 / seconds)
    B = 2 * pi * start / log(R)
    A = initial_phase - B

    time = 0 : 1 / sample_rate : seconds;
    phase = A + B * R .^ time;
    signal = amplitude * sin(phase) + dc;

    end % sinesweep_nostate

    Now, the iterative version that adds up the little Δφs to calculate φi iteratively:

    function signal = sinesweep( ...
        start, ...
        finish, ...
        seconds, ...
        sample_rate, ...
        amplitude, ...
        initial_phase, ...
        dc ...
    )
    % sinesweep returns a single-channel sine logarithmic sweep
    %     start: starting frequency in Hz
    %     finish: ending frequency in Hz
    %     seconds: duration of sweep in seconds
    %     samplerate: samples per second
    %     amplitude: amplitude
    %     initial_phase: starting phase
    %     dc: dc

    time = 0 : 1 / sample_rate : seconds;
    frequency = exp( ...
        log(start) * (1 - time / seconds) + ...
        log(finish) * (time / seconds) ...
    );

    phase = 0 * time;
    phase(1) = initial_phase;
    for i = 2:length(phase)
        phase(i) = ...
            mod(phase(i - 1) + 2 * pi * frequency(i) / sample_rate, 2 * pi);
    end

    signal = amplitude * sin(phase) + dc;

    end % sinesweep

    If anything, one would expect the first to be more accurate, since it is mathematically precise, and the second is only an approximation.  But let's see how the signal produced by each of them holds up.

    I calculate, and plot, the same sweep both ways, with a slight dc offset to facilitate seeing the different sweeps on the same plot.  In particular this is a logarithmic sweep from 20 kHz to 21 kHz, over 30 seconds, using 44100 samples per second, an amplitude of 0.2, an initial phase of π, and a dc of 0.25 for one and 0.26 for the other:

    plot(...
        x, sinesweep(20000, 21000, 30, 44100, 0.2, pi, 0.25), 'd', ...
        x, sinesweep_nostate(20000, 21000, 30, 44100, 0.2, pi, 0.26), 's' ...
    )

    (x is just 1:30*44100 - necessary to allow a single plot statement.)

    Note that the approximate method is plotted in diamonds, and will be 0.01 lower on the resulting graph than the "exact" method, plotted in squares.

    (This takes a while to plot because there are a heckuva lot of points...)

    Ah, a nice solid mass of green and blue.  I won't show it here.

    OK, let's zoom in to a very early chunk of the signal - I expect these to match up closely, with only the dc offset separating the points:

    Yup.  Looks pretty good - give or take a pixel (quantization error in the display.)

    Now let's see what happens to the signal towards the end of the 30 seconds.

    Ick.  Something's rotten in the state of Denmark.  This distortion is so bad that it's visible - you don't even have to take an FFT to see it.

    Exercise: take an FFT and look at the distortion.

    Advanced Exercise: demonstrate that the distortion is, in fact, in the "exact" method and not in the iterative method... that is to say, show which clock is right.

    EDIT: On second glance it looks like the second picture is just showing a horizontal shift between the two signals, which is expected.  I'll need to dig deeper if I'm to prove my assertion that the iterative method produces less distortion than the "exact" method.

  • Matthew van Eerde's web log

    How to enumerate audio endpoint (IMMDevice) properties on your system

    • 12 Comments

    Source and binaries (amd64 and x86) attached.

    Pseudocode:

    CoCreateInstance(..., &pMMDeviceEnumerator);
    pMMDeviceEnumerator->EnumAudioEndpoints(..., &pMMDeviceCollection);
    for (each device in the collection) {
        pMMDevice->OpenPropertyStore(...,  &pPropertyStore);
        for (each property in the store) {
            log the property
        }
    }

    Output on my system:

    >audioendpoints.exe
    ID: {0.0.0.00000000}.{6d531641-b3e3-43c5-ba56-ba165b4a9bb6}
    State: 4 (DEVICE_STATE_NOTPRESENT)
    -- Properties (18) --
    {b3f8fa53-0004-438e-9003-51a46e139bfc},15:
        VT_BLOB of size 16
        db 07 06 00 03 00 01 00
        17 00 3a 00 2d 00 26 03
    DEVPKEY_Device_DeviceDesc:
        VT_LPWSTR Internal AUX Jack
    {b3f8fa53-0004-438e-9003-51a46e139bfc},6:
        VT_LPWSTR High Definition Audio Device
    {b3f8fa53-0004-438e-9003-51a46e139bfc},2:
        VT_LPWSTR {1}.HDAUDIO\FUNC_01&VEN_8384&DEV_7690&SUBSYS_102801BD&REV_1022\4&33D044FE&0&0001
    {83da6326-97a6-4088-9453-a1923f573b29},3:
        VT_LPWSTR hdaudio.inf:Microsoft.ntamd64:HdAudModel:6.1.7600.16385::hdaudio\func_01
    DEVPKEY_Device_BaseContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_ContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_EnumeratorName:
        VT_LPWSTR HDAUDIO
    {b3f8fa53-0004-438e-9003-51a46e139bfc},1:
        VT_BLOB of size 72
        a8 7f a4 d5 98 6d d1 11
        a2 1a 00 a0 c9 22 31 96
        9c ac 97 dc ec dd 59 4d
        b6 50 3b 8b a6 7b c2 a1
        00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00
        e0 cc 13 de 04 83 e9 4e
        ba ce 48 24 21 4e 3e a5
        00 00 02 00 01 00 00 00
    PKEY_AudioEndpoint_FormFactor:
        VT_UI4 10
    PKEY_AudioEndpoint_JackSubType:
        VT_LPWSTR {6994AD04-93EF-11D0-A3CC-00A0C9223196}
    DEVPKEY_DeviceClass_IconPath:
        VT_LPWSTR %windir%\system32\mmres.dll,-3018
    {840b8171-b0ad-410f-8581-cccc0382cfef},0:
        VT_BLOB of size 300
        01 00 00 00 28 01 00 00
        00 00 01 00 7b 00 32 00
        7d 00 2e 00 5c 00 5c 00
        3f 00 5c 00 68 00 64 00
        61 00 75 00 64 00 69 00
        6f 00 23 00 66 00 75 00
        6e 00 63 00 5f 00 30 00
        31 00 26 00 76 00 65 00
        6e 00 5f 00 38 00 33 00
        38 00 34 00 26 00 64 00
        65 00 76 00 5f 00 37 00
        36 00 39 00 30 00 26 00
        73 00 75 00 62 00 73 00
        79 00 73 00 5f 00 31 00
        30 00 32 00 38 00 30 00
        31 00 62 00 64 00 26 00
        72 00 65 00 76 00 5f 00
        31 00 30 00 32 00 32 00
        23 00 34 00 26 00 33 00
        33 00 64 00 30 00 34 00
        34 00 66 00 65 00 26 00
        30 00 26 00 30 00 30 00
        30 00 31 00 23 00 7b 00
        36 00 39 00 39 00 34 00
        61 00 64 00 30 00 34 00
        2d 00 39 00 33 00 65 00
        66 00 2d 00 31 00 31 00
        64 00 30 00 2d 00 61 00
        33 00 63 00 63 00 2d 00
        30 00 30 00 61 00 30 00
        63 00 39 00 32 00 32 00
        33 00 31 00 39 00 36 00
        7d 00 5c 00 65 00 6d 00
        69 00 63 00 69 00 6e 00
        74 00 6f 00 70 00 6f 00
        2f 00 30 00 30 00 30 00
        31 00 30 00 30 00 30 00
        30 00 00 00
    PKEY_AudioEndpoint_Association:
        VT_LPWSTR {00000000-0000-0000-0000-000000000000}
    PKEY_AudioEndpoint_Supports_EventDriven_Mode:
        VT_UI4 1
    DEVPKEY_Device_FriendlyName:
        VT_LPWSTR Internal AUX Jack (High Definition Audio Device)
    DEVPKEY_DeviceInterface_FriendlyName:
        VT_LPWSTR High Definition Audio Device
    PKEY_AudioEndpoint_GUID:
        VT_LPWSTR {6D531641-B3E3-43C5-BA56-BA165B4A9BB6}

    ID: {0.0.0.00000000}.{ca9fa848-1e60-401c-81e6-323546335d0a}
    State: 1 (DEVICE_STATE_ACTIVE)
    -- Properties (26) --
    {b3f8fa53-0004-438e-9003-51a46e139bfc},15:
        VT_BLOB of size 16
        da 07 0a 00 04 00 1c 00
        16 00 3a 00 3a 00 d1 00
    DEVPKEY_Device_DeviceDesc:
        VT_LPWSTR Speakers
    {b3f8fa53-0004-438e-9003-51a46e139bfc},6:
        VT_LPWSTR High Definition Audio Device
    {b3f8fa53-0004-438e-9003-51a46e139bfc},2:
        VT_LPWSTR {1}.HDAUDIO\FUNC_01&VEN_8384&DEV_7690&SUBSYS_102801BD&REV_1022\4&33D044FE&0&0001
    {83da6326-97a6-4088-9453-a1923f573b29},3:
        VT_LPWSTR hdaudio.inf:Microsoft.ntamd64:HdAudModel:6.1.7600.16385::hdaudio\func_01
    DEVPKEY_Device_BaseContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_ContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_EnumeratorName:
        VT_LPWSTR HDAUDIO
    {b3f8fa53-0004-438e-9003-51a46e139bfc},1:
        VT_BLOB of size 72
        a8 7f a4 d5 98 6d d1 11
        a2 1a 00 a0 c9 22 31 96
        9c ac 97 dc ec dd 59 4d
        b6 50 3b 8b a6 7b c2 a1
        00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00
        e0 cc 13 de 04 83 e9 4e
        ba ce 48 24 21 4e 3e a5
        00 00 02 00 01 00 00 00
    PKEY_AudioEndpoint_FormFactor:
        VT_UI4 1
    PKEY_AudioEndpoint_JackSubType:
        VT_LPWSTR {DFF21CE1-F70F-11D0-B917-00A0C9223196}
    DEVPKEY_DeviceClass_IconPath:
        VT_LPWSTR %windir%\system32\mmres.dll,-3010
    {840b8171-b0ad-410f-8581-cccc0382cfef},0:
        VT_BLOB of size 324
        01 00 00 00 40 01 00 00
        00 00 01 00 7b 00 32 00
        7d 00 2e 00 5c 00 5c 00
        3f 00 5c 00 68 00 64 00
        61 00 75 00 64 00 69 00
        6f 00 23 00 66 00 75 00
        6e 00 63 00 5f 00 30 00
        31 00 26 00 76 00 65 00
        6e 00 5f 00 38 00 33 00
        38 00 34 00 26 00 64 00
        65 00 76 00 5f 00 37 00
        36 00 39 00 30 00 26 00
        73 00 75 00 62 00 73 00
        79 00 73 00 5f 00 31 00
        30 00 32 00 38 00 30 00
        31 00 62 00 64 00 26 00
        72 00 65 00 76 00 5f 00
        31 00 30 00 32 00 32 00
        23 00 34 00 26 00 33 00
        33 00 64 00 30 00 34 00
        34 00 66 00 65 00 26 00
        30 00 26 00 30 00 30 00
        30 00 31 00 23 00 7b 00
        36 00 39 00 39 00 34 00
        61 00 64 00 30 00 34 00
        2d 00 39 00 33 00 65 00
        66 00 2d 00 31 00 31 00
        64 00 30 00 2d 00 61 00
        33 00 63 00 63 00 2d 00
        30 00 30 00 61 00 30 00
        63 00 39 00 32 00 32 00
        33 00 31 00 39 00 36 00
        7d 00 5c 00 65 00 73 00
        6c 00 61 00 76 00 65 00
        64 00 68 00 70 00 73 00
        70 00 65 00 61 00 6b 00
        65 00 72 00 74 00 6f 00
        70 00 6f 00 2f 00 30 00
        30 00 30 00 31 00 30 00
        30 00 30 00 31 00 00 00
        00 00 00 00
    PKEY_AudioEndpoint_Association:
        VT_LPWSTR {00000000-0000-0000-0000-000000000000}
    PKEY_AudioEndpoint_Supports_EventDriven_Mode:
        VT_UI4 1
    {9a82a7db-3ebb-41b4-83ba-18b7311718fc},1:
        VT_UI4 65536
    {233164c8-1b2c-4c7d-bc68-b671687a2567},1:
        VT_LPWSTR {2}.\\?\hdaudio#func_01&ven_8384&dev_7690&subsys_102801bd&rev_1022#4&33d044fe&0&0001#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\eslavedhpspeakerwave
    {5a9125b7-f367-4924-ace2-0803a4a3a471},0:
        VT_UI4 1610652916
    {5a9125b7-f367-4924-ace2-0803a4a3a471},2:
        VT_UI4 1610644836
    {b3f8fa53-0004-438e-9003-51a46e139bfc},0:
        VT_UI4 1
    PKEY_AudioEngine_DeviceFormat:
        VT_BLOB of size 40
        fe ff 02 00 44 ac 00 00
        10 b1 02 00 04 00 10 00
        16 00 10 00 03 00 00 00
        01 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0:
        VT_BLOB of size 40
        fe ff 02 00 44 ac 00 00
        20 62 05 00 08 00 20 00
        16 00 20 00 03 00 00 00
        03 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},1:
        VT_BLOB of size 8
        d3 8c 01 00 00 00 00 00
    DEVPKEY_Device_FriendlyName:
        VT_LPWSTR Speakers (High Definition Audio Device)
    DEVPKEY_DeviceInterface_FriendlyName:
        VT_LPWSTR High Definition Audio Device
    PKEY_AudioEndpoint_GUID:
        VT_LPWSTR {CA9FA848-1E60-401C-81E6-323546335D0A}

    ID: {0.0.1.00000000}.{4f5e42d9-227f-43ff-bf5b-a1ce5d0324cf}
    State: 8 (DEVICE_STATE_UNPLUGGED)
    -- Properties (28) --
    {b3f8fa53-0004-438e-9003-51a46e139bfc},15:
        VT_BLOB of size 16
        da 07 0a 00 04 00 1c 00
        16 00 3a 00 3a 00 0f 01
    DEVPKEY_Device_DeviceDesc:
        VT_LPWSTR Microphone
    {b3f8fa53-0004-438e-9003-51a46e139bfc},6:
        VT_LPWSTR High Definition Audio Device
    {b3f8fa53-0004-438e-9003-51a46e139bfc},2:
        VT_LPWSTR {1}.HDAUDIO\FUNC_01&VEN_8384&DEV_7690&SUBSYS_102801BD&REV_1022\4&33D044FE&0&0001
    {83da6326-97a6-4088-9453-a1923f573b29},3:
        VT_LPWSTR hdaudio.inf:Microsoft.ntamd64:HdAudModel:6.1.7600.16385::hdaudio\func_01
    DEVPKEY_Device_BaseContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_ContainerId:
        VT_CLSID {00000000-0000-0000-ffff-ffffffffffff}
    DEVPKEY_Device_EnumeratorName:
        VT_LPWSTR HDAUDIO
    {b3f8fa53-0004-438e-9003-51a46e139bfc},1:
        VT_BLOB of size 72
        a8 7f a4 d5 98 6d d1 11
        a2 1a 00 a0 c9 22 31 96
        9c ac 97 dc ec dd 59 4d
        b6 50 3b 8b a6 7b c2 a1
        00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00
        e0 cc 13 de 04 83 e9 4e
        ba ce 48 24 21 4e 3e a5
        00 00 02 00 01 00 00 00
    PKEY_AudioEndpoint_FormFactor:
        VT_UI4 4
    PKEY_AudioEndpoint_JackSubType:
        VT_LPWSTR {DFF21BE1-F70F-11D0-B917-00A0C9223196}
    DEVPKEY_DeviceClass_IconPath:
        VT_LPWSTR %windir%\system32\mmres.dll,-3014
    {840b8171-b0ad-410f-8581-cccc0382cfef},0:
        VT_BLOB of size 300
        01 00 00 00 28 01 00 00
        00 00 01 00 7b 00 32 00
        7d 00 2e 00 5c 00 5c 00
        3f 00 5c 00 68 00 64 00
        61 00 75 00 64 00 69 00
        6f 00 23 00 66 00 75 00
        6e 00 63 00 5f 00 30 00
        31 00 26 00 76 00 65 00
        6e 00 5f 00 38 00 33 00
        38 00 34 00 26 00 64 00
        65 00 76 00 5f 00 37 00
        36 00 39 00 30 00 26 00
        73 00 75 00 62 00 73 00
        79 00 73 00 5f 00 31 00
        30 00 32 00 38 00 30 00
        31 00 62 00 64 00 26 00
        72 00 65 00 76 00 5f 00
        31 00 30 00 32 00 32 00
        23 00 34 00 26 00 33 00
        33 00 64 00 30 00 34 00
        34 00 66 00 65 00 26 00
        30 00 26 00 30 00 30 00
        30 00 31 00 23 00 7b 00
        36 00 39 00 39 00 34 00
        61 00 64 00 30 00 34 00
        2d 00 39 00 33 00 65 00
        66 00 2d 00 31 00 31 00
        64 00 30 00 2d 00 61 00
        33 00 63 00 63 00 2d 00
        30 00 30 00 61 00 30 00
        63 00 39 00 32 00 32 00
        33 00 31 00 39 00 36 00
        7d 00 5c 00 65 00 6d 00
        69 00 63 00 69 00 6e 00
        74 00 6f 00 70 00 6f 00
        2f 00 30 00 30 00 30 00
        31 00 30 00 30 00 30 00
        31 00 00 00
    PKEY_AudioEndpoint_Association:
        VT_LPWSTR {00000000-0000-0000-0000-000000000000}
    PKEY_AudioEndpoint_Supports_EventDriven_Mode:
        VT_UI4 1
    {24dbb0fc-9311-4b3d-9cf0-18ff155639d4},3:
        VT_BOOL 0xffffffff
    {24dbb0fc-9311-4b3d-9cf0-18ff155639d4},4:
        VT_BOOL 0xffffffff
    {9a82a7db-3ebb-41b4-83ba-18b7311718fc},1:
        VT_UI4 65536
    {233164c8-1b2c-4c7d-bc68-b671687a2567},1:
        VT_LPWSTR {2}.\\?\hdaudio#func_01&ven_8384&dev_7690&subsys_102801bd&rev_1022#4&33d044fe&0&0001#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\emicinwave
    {5a9125b7-f367-4924-ace2-0803a4a3a471},0:
        VT_UI4 1610712932
    {5a9125b7-f367-4924-ace2-0803a4a3a471},2:
        VT_UI4 1610708836
    {b3f8fa53-0004-438e-9003-51a46e139bfc},0:
        VT_UI4 3
    PKEY_AudioEngine_DeviceFormat:
        VT_BLOB of size 40
        fe ff 02 00 44 ac 00 00
        10 b1 02 00 04 00 10 00
        16 00 10 00 03 00 00 00
        01 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0:
        VT_BLOB of size 40
        fe ff 02 00 44 ac 00 00
        20 62 05 00 08 00 20 00
        16 00 20 00 03 00 00 00
        03 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},1:
        VT_BLOB of size 8
        d3 8c 01 00 00 00 00 00
    DEVPKEY_Device_FriendlyName:
        VT_LPWSTR Microphone (High Definition Audio Device)
    DEVPKEY_DeviceInterface_FriendlyName:
        VT_LPWSTR High Definition Audio Device
    PKEY_AudioEndpoint_GUID:
        VT_LPWSTR {4F5E42D9-227F-43FF-BF5B-A1CE5D0324CF}

    ID: {0.0.1.00000000}.{6712184b-f8f0-474d-ad36-808cc90a7cfd}
    State: 4 (DEVICE_STATE_NOTPRESENT)
    -- Properties (29) --
    {b3f8fa53-0004-438e-9003-51a46e139bfc},15:
        VT_BLOB of size 16
        db 07 03 00 01 00 15 00
        10 00 3b 00 21 00 f4 00
    DEVPKEY_Device_DeviceDesc:
        VT_LPWSTR Microphone Array
    {b3f8fa53-0004-438e-9003-51a46e139bfc},6:
        VT_LPWSTR Microphone Array
    {b3f8fa53-0004-438e-9003-51a46e139bfc},2:
        VT_LPWSTR {1}.USB\VID_045E&PID_FFF0&MI_01\6&1474EDB6&0&0001
    {83da6326-97a6-4088-9453-a1923f573b29},3:
        VT_LPWSTR wdma_usb.inf:Microsoft.ntamd64:USBAudio:6.1.7600.16385:usb\class_01
    DEVPKEY_Device_BaseContainerId:
        VT_CLSID {9132f6ea-a152-5472-8685-61c0d297a30b}
    DEVPKEY_Device_ContainerId:
        VT_CLSID {9132f6ea-a152-5472-8685-61c0d297a30b}
    DEVPKEY_Device_EnumeratorName:
        VT_LPWSTR USB
    {b3f8fa53-0004-438e-9003-51a46e139bfc},1:
        VT_BLOB of size 72
        30 f1 1c 4e 79 16 3b 46
        a7 2f a5 bf 64 c8 6e ba
        4e 5a cd ab 63 c2 3b 46
        a7 2f a5 bf 64 c8 6e ba
        f0 75 12 8f e9 26 64 42
        ba 4d 39 ff f0 1d 94 aa
        a6 54 57 fc f8 2d 3b 46
        a7 2f a5 bf 64 c8 6e ba
        02 00 00 00 09 00 00 00
    PKEY_AudioEndpoint_FormFactor:
        VT_UI4 4
    PKEY_AudioEndpoint_JackSubType:
        VT_LPWSTR {DFF21BE5-F70F-11D0-B917-00A0C9223196}
    DEVPKEY_DeviceClass_IconPath:
        VT_LPWSTR %windir%\system32\mmres.dll,-3020
    {840b8171-b0ad-410f-8581-cccc0382cfef},0:
        VT_BLOB of size 236
        01 00 00 00 e8 00 00 00
        00 00 01 00 7b 00 32 00
        7d 00 2e 00 5c 00 5c 00
        3f 00 5c 00 75 00 73 00
        62 00 23 00 76 00 69 00
        64 00 5f 00 30 00 34 00
        35 00 65 00 26 00 70 00
        69 00 64 00 5f 00 66 00
        66 00 66 00 30 00 26 00
        6d 00 69 00 5f 00 30 00
        31 00 23 00 36 00 26 00
        31 00 34 00 37 00 34 00
        65 00 64 00 62 00 36 00
        26 00 30 00 26 00 30 00
        30 00 30 00 31 00 23 00
        7b 00 36 00 39 00 39 00
        34 00 61 00 64 00 30 00
        34 00 2d 00 39 00 33 00
        65 00 66 00 2d 00 31 00
        31 00 64 00 30 00 2d 00
        61 00 33 00 63 00 63 00
        2d 00 30 00 30 00 61 00
        30 00 63 00 39 00 32 00
        32 00 33 00 31 00 39 00
        36 00 7d 00 5c 00 67 00
        6c 00 6f 00 62 00 61 00
        6c 00 2f 00 30 00 30 00
        30 00 31 00 30 00 30 00
        30 00 31 00 00 00 00 00
        00 00 00 00
    PKEY_AudioEndpoint_Association:
        VT_LPWSTR {00000000-0000-0000-0000-000000000000}
    PKEY_AudioEndpoint_Supports_EventDriven_Mode:
        VT_UI4 1
    {24dbb0fc-9311-4b3d-9cf0-18ff155639d4},3:
        VT_BOOL 0x0
    {24dbb0fc-9311-4b3d-9cf0-18ff155639d4},4:
        VT_BOOL 0xffffffff
    {9a82a7db-3ebb-41b4-83ba-18b7311718fc},1:
        VT_UI4 65536
    {233164c8-1b2c-4c7d-bc68-b671687a2567},1:
        VT_LPWSTR {2}.\\?\usb#vid_045e&pid_fff0&mi_01#6&1474edb6&0&0001#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global
    {5a9125b7-f367-4924-ace2-0803a4a3a471},0:
        VT_UI4 1610713736
    {5a9125b7-f367-4924-ace2-0803a4a3a471},2:
        VT_UI4 1610709736
    {b3f8fa53-0004-438e-9003-51a46e139bfc},0:
        VT_UI4 3
    PKEY_AudioEngine_DeviceFormat:
        VT_BLOB of size 40
        fe ff 04 00 80 3e 00 00
        00 f4 01 00 08 00 10 00
        16 00 10 00 00 00 00 00
        01 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0:
        VT_BLOB of size 40
        fe ff 04 00 80 3e 00 00
        00 e8 03 00 10 00 20 00
        16 00 20 00 00 00 00 00
        03 00 00 00 00 00 10 00
        80 00 00 aa 00 38 9b 71
    {e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},1:
        VT_BLOB of size 8
        a0 86 01 00 00 00 00 00
    {9855c4cd-df8c-449c-a181-8191b68bd06c},0:
        VT_BLOB of size 16
        00 00 f0 41 00 00 f0 41
        00 00 f0 41 00 00 f0 41
    DEVPKEY_Device_FriendlyName:
        VT_LPWSTR Microphone Array (Microphone Array)
    DEVPKEY_DeviceInterface_FriendlyName:
        VT_LPWSTR Microphone Array
    PKEY_AudioEndpoint_GUID:
        VT_LPWSTR {6712184B-F8F0-474D-AD36-808CC90A7CFD}

    The unrecognized properties could be private properties used by the OS, or by the audio driver, or they might be defined in some public header that I didn't bother to scour.

    UPDATE 2011-09-09: added WAVEFORMATEX logging for properties known to be audio formats.

  • Matthew van Eerde's web log

    My unattend.xml file

    • 5 Comments

    As a tester on Windows 8 I install Windows on my dev machine very frequently.

    I use F12 to boot from the network into WinPE, then I run a .bat file which looks something like this:

    @echo off
    setlocal enabledelayedexpansion

    set PASSWORD_ADMIN=(redacted)
    set PASSWORD_MATEER=(redacted)
    set LICENSE_KEY=(redacted)
    set FANCYLANG=qps-plocm

    del unattend-processed.xml
    for /f "usebackq delims=" %%l in (`type unattend-template.xml`) do (
        set line=%%l
        set line=!line:#PASSWORD_ADMIN#=%PASSWORD_ADMIN%!
        set line=!line:#PASSWORD_MATEER#=%PASSWORD_MATEER%!
        set line=!line:#LICENSE_KEY#=%LICENSE_KEY%!
        set line=!line:#FANCY_LANG#=%FANCY_LANG%!
        echo !line! >> unattend-processed.xml
    )

    setup.exe /unattend:unattend-processed.xml

    This takes my template unattend.xml file, injects various parameters, and calls setup.exe.

    The template unattend.xml follows in all of its glory.  We'll take a closer look at some of the things in future posts.

    <?xml version="1.0" encoding="utf-8"?>
    <unattend xmlns="urn:schemas-microsoft-com:unattend">
        <settings pass="windowsPE">
            <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <UserData>
                    <ProductKey>
                        <WillShowUI>OnError</WillShowUI>
                        <Key>#LICENSE_KEY#</Key>
                    </ProductKey>
                    <AcceptEula>true</AcceptEula>
                    <FullName>Matthew van Eerde</FullName>
                    <Organization>Microsoft</Organization>
                </UserData>
                <DiskConfiguration>
                    <Disk wcm:action="add">
                        <CreatePartitions>
                            <CreatePartition wcm:action="add">
                                <Order>1</Order>
                                <Extend>true</Extend>
                                <Type>Primary</Type>
                            </CreatePartition>
                        </CreatePartitions>
                        <WillWipeDisk>true</WillWipeDisk>
                        <DiskID>0</DiskID>
                    </Disk>
                </DiskConfiguration>
                <ImageInstall>
                    <OSImage>
                        <WillShowUI>OnError</WillShowUI>
                        <InstallToAvailablePartition>true</InstallToAvailablePartition>
                    </OSImage>
                </ImageInstall>
            </component>
            <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <SetupUILanguage>
                    <UILanguage>#FANCY_LANG#</UILanguage>
                </SetupUILanguage>
                <InputLocale>0409:00000409</InputLocale>
                <UserLocale>en-US</UserLocale>
                <SystemLocale>en-US</SystemLocale>
                <UILanguage>#FANCY_LANG#</UILanguage>
            </component>
        </settings>
        <settings pass="specialize">
            <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <Identification>
                    <Credentials>
                        <Domain>redmond.corp.microsoft.com</Domain>
                        <Password>#PASSWORD_MATEER#</Password>
                        <Username>MatEer</Username>
                    </Credentials>
                    <JoinDomain>ntdev.corp.microsoft.com</JoinDomain>
                </Identification>
            </component>
            <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <ComputerName>MATEER-Q</ComputerName>
                <RegisteredOwner>Matthew van Eerde</RegisteredOwner>
                <RegisteredOrganization>Microsoft</RegisteredOrganization>
            </component>
            <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <fDenyTSConnections>false</fDenyTSConnections>
            </component>
            <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <FirewallGroups>
                    <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
                        <Active>true</Active>
                        <Profile>all</Profile>
                        <Group>@FirewallAPI.dll,-28752</Group>
                    </FirewallGroup>
                </FirewallGroups>
            </component>
        </settings>
        <settings pass="oobeSystem">
            <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <OOBE>
                    <HideEULAPage>true</HideEULAPage>
                    <NetworkLocation>Work</NetworkLocation>
                    <ProtectYourPC>3</ProtectYourPC>
                    <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
                    <HideLocalAccountScreen>true</HideLocalAccountScreen>
                    <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
                </OOBE>
                <UserAccounts>
                    <LocalAccounts>
                        <LocalAccount wcm:action="add">
                            <Password>
                                <Value>#PASSWORD_ADMIN#</Value>
                                <PlainText>true</PlainText>
                            </Password>
                            <Name>Admin</Name>
                            <Group>Administrators</Group>
                        </LocalAccount>
                    </LocalAccounts>
                    <DomainAccounts>
                        <DomainAccountList wcm:action="add">
                            <Domain>redmond.corp.microsoft.com</Domain>
                            <DomainAccount wcm:action="add">
                                <Group>RemoteDesktopUsers</Group>
                                <Name>MatEer</Name>
                            </DomainAccount>
                        </DomainAccountList>
                    </DomainAccounts>
                </UserAccounts>
                <TimeZone>Pacific Standard Time</TimeZone>
                <AutoLogon>
                    <Password>
                        <Value>#PASSWORD_MATEER#</Value>
                        <PlainText>true</PlainText>
                    </Password>
                    <Domain>redmond.corp.microsoft.com</Domain>
                    <Enabled>true</Enabled>
                    <LogonCount>1</LogonCount>
                    <Username>MatEer</Username>
                </AutoLogon>
            </component>
        </settings>
    </unattend>

     

  • Matthew van Eerde's web log

    Troubleshooting: how to install the Microsoft HD Audio class driver

    • 1 Comments

    Most on-the-motherboard audio devices support the Intel High Definition Audio standard.  Windows Vista (and later) includes a "class driver", hdaudio.sys, which should work with any such audio device.

    Usually systems come with a vendor-supplied driver installed.  This driver is designed specifically for the hardware it runs on (as opposed to being designed to the standard) and so it comes with additional functionality.

    Occasionally, for troubleshooting purposes, it is useful to switch from one driver to the other... either to get the additional functionality provided by the vendor-supplied driver, or to see what happens if the class driver is installed.

    Here's how to switch back and forth.

    Click "Start".

    Type "devmgmt.msc" (without the quotes) to launch Device Manager.

    Expand the "Sound, video and game controllers" node and note the list of audio devices.  In this case, I have one audio device, and by the "High Definition Audio Device" name I deduce that I have the class driver installed.  (If the device name included a company name, I would infer that I had a vendor driver installed.)

    Right-click the device you want to change the software on.

    Windows offers to automatically detect the driver that should be installed.  No thanks, we want to pick a particular driver:

    Windows asks where we want to look - do we have a set of driver files, or is the driver already in the list of installed drivers?  In this case, we want to look at the list of drivers for this hardware that are already installed.

    Windows shows us a list of drivers that are already installed and usable for this hardware.  At this point you would expect to see two drivers listed: the vendor driver, and "High Definition Audio Device."  (When I made this blog post, I was lazy, so I didn't bother to make a screenshot that showed two drivers.)

    To install the class driver, pick "High Definition Audio Device" and click "Next."

    "High Definition Audio Device" is a class driver, so you get this warning.  Click "Yes."

    Windows does its thing...

    ... and eventually tells you that it's done.  If your audio device was in use, you may get the "you have to restart" message.  Regardless, click "Close".

    If you got the "you will need to restart" message, Windows helpfully offers to restart right away.

    Make sure you're ready for a restart (no unsaved documents or anything) and restart either by clicking "Yes" or by using the Start menu.

    When you're done experimenting, you can go back to the vendor-supplied driver by going through the same steps and choosing the vendor-supplied driver in the list of drivers.

  • Matthew van Eerde's web log

    Sample: find out if your default audio playback and audio capture devices are on the same hardware

    • 5 Comments

    vbBretty on the audio forums asked how to tell if a given audio output and a given audio input were on the same physical audio card.

    I found the exercise interesting enough to share here:

    // main.cpp

    #define INITGUID

    #include <windows.h>
    #include <tchar.h>

    const GUID GUID_NULL = { 0 };

    #include <atlstr.h>
    #include <mmdeviceapi.h>
    #include <devicetopology.h>
    #include <functiondiscoverykeys.h>

    #define LOG(formatstring, ...) _tprintf(formatstring _T("\n"), __VA_ARGS__)

    // helper class to CoInitialize/CoUninitialize
    class CCoInitialize {
    private:
        HRESULT m_hr;
    public:
        CCoInitialize(PVOID pReserved, HRESULT &hr)
            : m_hr(E_UNEXPECTED) { hr = m_hr = CoInitialize(pReserved); }
        ~CCoInitialize() { if (SUCCEEDED(m_hr)) { CoUninitialize(); } }
    };

    // helper class to CoTaskMemFree
    class CCoTaskMemFreeOnExit {
    private:
        PVOID m_p;
    public:
        CCoTaskMemFreeOnExit(PVOID p) : m_p(p) {}
        ~CCoTaskMemFreeOnExit() { CoTaskMemFree(m_p); }
    };

    // helper class to PropVariantClear
    class CPropVariantClearOnExit {
    private:
        PROPVARIANT *m_p;
    public:
        CPropVariantClearOnExit(PROPVARIANT *p) : m_p(p) {}
        ~CPropVariantClearOnExit() { PropVariantClear(m_p); }
    };

    // find the default capture and render audio devices
    // determine whether they are on the same audio hardware
    int _tmain() {
        HRESULT hr = S_OK;

        // initialize COM
        CCoInitialize ci(NULL, hr);
        if (FAILED(hr)) {
            LOG(_T("CoInitialize failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        // get enumerator
        CComPtr<IMMDeviceEnumerator> pMMDeviceEnumerator;
        hr = pMMDeviceEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
        if (FAILED(hr)) {
            LOG(_T("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        // get default render/capture endpoints
        CComPtr<IMMDevice> pRenderEndpoint;
        hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pRenderEndpoint);
        if (FAILED(hr)) {
            LOG(_T("GetDefaultAudioEndpoint(eRender, eConsole) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }
        
        CComPtr<IMMDevice> pCaptureEndpoint;
        hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pCaptureEndpoint);
        if (FAILED(hr)) {
            LOG(_T("GetDefaultAudioEndpoint(eCapture, eConsole) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        // get endpoint device topologies
        CComPtr<IDeviceTopology> pRenderEndpointTopology;
        hr = pRenderEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pRenderEndpointTopology);
        if (FAILED(hr)) {
            LOG(_T("Render endpoint Activate(IDeviceTopology) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        CComPtr<IDeviceTopology> pCaptureEndpointTopology;
        hr = pCaptureEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pCaptureEndpointTopology);
        if (FAILED(hr)) {
            LOG(_T("Capture endpoint Activate(IDeviceTopology) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        // get KS filter device IDs
        LPWSTR szRenderFilterId;
        CComPtr<IConnector> pConnector;
        hr = pRenderEndpointTopology->GetConnector(0, &pConnector);
        if (FAILED(hr)) {
            LOG(_T("Render endpoint topology GetConnector(0) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        hr = pConnector->GetDeviceIdConnectedTo(&szRenderFilterId);
        if (FAILED(hr)) {
            LOG(_T("Render connector GetDeviceIdConnectedTo() failed: hr = 0x%08x"), hr);
            return __LINE__;
        }
        CCoTaskMemFreeOnExit freeRender(szRenderFilterId);
        LOG(_T("KS filter ID for render endpoint:\n\t%ls"), szRenderFilterId);

        LPWSTR szCaptureFilterId;
        pConnector = NULL;
        hr = pCaptureEndpointTopology->GetConnector(0, &pConnector);
        if (FAILED(hr)) {
            LOG(_T("Capture endpoint topology GetConnector(0) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        hr = pConnector->GetDeviceIdConnectedTo(&szCaptureFilterId);
        if (FAILED(hr)) {
            LOG(_T("Capture connector GetDeviceIdConnectedTo() failed: hr = 0x%08x"), hr);
            return __LINE__;
        }
        CCoTaskMemFreeOnExit freeCapture(szCaptureFilterId);
        LOG(_T("KS filter ID for capture endpoint:\n\t%ls"), szCaptureFilterId);

        // get IMMDevices for each associated devnode
        CComPtr<IMMDevice> pRenderDevnode;
        hr = pMMDeviceEnumerator->GetDevice(szRenderFilterId, &pRenderDevnode);
        if (FAILED(hr)) {
            LOG(_T("Getting render devnode via IMMDeviceEnumerator::GetDevice(\"%ls\") failed: hr = 0x%08x"), szRenderFilterId, hr);
            return __LINE__;
        }
        
        CComPtr<IMMDevice> pCaptureDevnode;
        hr = pMMDeviceEnumerator->GetDevice(szCaptureFilterId, &pCaptureDevnode);
        if (FAILED(hr)) {
            LOG(_T("Getting capture devnode via IMMDeviceEnumerator::GetDevice(\"%ls\") failed: hr = 0x%08x"), szCaptureFilterId, hr);
            return __LINE__;
        }
        LOG(_T(""));

        // open property set on each devnode
        CComPtr<IPropertyStore> pRenderDevnodePropertyStore;
        hr = pRenderDevnode->OpenPropertyStore(STGM_READ, &pRenderDevnodePropertyStore);
        if (FAILED(hr)) {
            LOG(_T("Getting render devnode property store failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        CComPtr<IPropertyStore> pCaptureDevnodePropertyStore;
        hr = pCaptureDevnode->OpenPropertyStore(STGM_READ, &pCaptureDevnodePropertyStore);
        if (FAILED(hr)) {
            LOG(_T("Getting capture devnode property store failed: hr = 0x%08x"), hr);
            return __LINE__;
        }

        // get PKEY_Device_InstanceId property
        PROPVARIANT varRenderInstanceId; PropVariantInit(&varRenderInstanceId);
        CPropVariantClearOnExit clearRender(&varRenderInstanceId);
        hr = pRenderDevnodePropertyStore->GetValue(PKEY_Device_InstanceId, &varRenderInstanceId);
        if (FAILED(hr)) {
            LOG(_T("Render devnode property store GetValue(PKEY_Device_InstanceId) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }
        if (VT_LPWSTR != varRenderInstanceId.vt) {
            LOG(_T("Render instance id variant type is %u - expected VT_LPWSTR"), varRenderInstanceId.vt);
            return __LINE__;
        }
        LOG(_T("Instance Id of default render device: %ls"), varRenderInstanceId.pwszVal);

        PROPVARIANT varCaptureInstanceId; PropVariantInit(&varCaptureInstanceId);
        CPropVariantClearOnExit clearCapture(&varCaptureInstanceId);
        hr = pCaptureDevnodePropertyStore->GetValue(PKEY_Device_InstanceId, &varCaptureInstanceId);
        if (FAILED(hr)) {
            LOG(_T("Capture devnode property store GetValue(PKEY_Device_InstanceId) failed: hr = 0x%08x"), hr);
            return __LINE__;
        }
        if (VT_LPWSTR != varCaptureInstanceId.vt) {
            LOG(_T("Capture instance id variant type is %u - expected VT_LPWSTR"), varCaptureInstanceId.vt);
            return __LINE__;
        }
        LOG(_T("Instance Id of default capture device: %ls"), varCaptureInstanceId.pwszVal);

        LOG(_T(""));

        // paydirt
        if (0 == _wcsicmp(varRenderInstanceId.pwszVal, varCaptureInstanceId.pwszVal)) {
            LOG(_T("Default render and capture audio endpoints ARE on the same hardware."));
        } else {
            LOG(_T("Default render and capture audio endpoints ARE NOT on the same hardware."));
        }

        return 0;
    }

  • Matthew van Eerde's web log

    How to enumerate DirectShow filters on your system

    • 3 Comments

    Last time I showed how to enumerate Media Foundation transforms.  This time, DirectShow filters.  Source and binaries attached.

    In pseudocode:

    GUID directshow_categories[] = { ... }; // static list
    ICreateDevEnum = CoCreate(...);
    for (each category in directshow_categories) {
        display the category name and GUID

        IEnumMoniker = ICreateDevEnum::CreateClassEnumerator(category);

        for (each IMoniker that IEnumMoniker finds) {
            IBindCtx = CreateBindCtx(...);
            IPropertyBag = IMoniker::BindToStorage(IBindCtx);

            pull the friendly name from the IPropertyBag and display it
        }
    }

    Output of the tool on my system:

    >devenum.exe
    -- Audio Capture Sources ({33D9A762-90C8-11D0-BD43-00A0C911CE86}) --
        Front Microphone (High Definiti
        Line In (High Definition Audio
        Rear Microphone (High Definitio

    -- Audio Compressors ({33D9A761-90C8-11D0-BD43-00A0C911CE86}) --
        WM Speech Encoder DMO
        WMAudio Encoder DMO
        IMA ADPCM
        PCM
        Microsoft ADPCM
        GSM 6.10
        CCITT A-Law
        CCITT u-Law
        MPEG Layer-3

    -- Audio Renderers ({E0F158E1-CB04-11D0-BD4E-00A0C911CE86}) --
        Speakers (High Definition Audio
        Default DirectSound Device
        Default WaveOut Device
        DirectSound: Speakers (High Definition Audio Device)

    -- Device Control Filters ({CC7BFB46-F175-11D1-A392-00E0291F3959}) --

    -- DirectShow Filters ({083863F1-70DE-11D0-BD40-00A0C911CE86}) --
        WMAudio Decoder DMO
        WMAPro over S/PDIF DMO
        WMSpeech Decoder DMO
        MP3 Decoder DMO
        Mpeg4s Decoder DMO
        WMV Screen decoder DMO
        WMVideo Decoder DMO
        Mpeg43 Decoder DMO
        Mpeg4 Decoder DMO
        DV Muxer
        Color Space Converter
        WM ASF Reader
        AVI Splitter
        VGA 16 Color Ditherer
        SBE2MediaTypeProfile
        Microsoft DTV-DVD Video Decoder
        AC3 Parser Filter
        StreamBufferSink
        Microsoft TV Captions Decoder
        MJPEG Decompressor
        CBVA DMO wrapper filter
        MPEG-I Stream Splitter
        SAMI (CC) Parser
        VBI Codec
        MPEG-2 Splitter
        Closed Captions Analysis Filter
        SBE2FileScan
        Microsoft MPEG-2 Video Encoder
        Internal Script Command Renderer
        MPEG Audio Decoder
        DV Splitter
        Video Mixing Renderer 9
        Microsoft MPEG-2 Encoder
        ACM Wrapper
        Video Renderer
        MPEG-2 Video Stream Analyzer
        Line 21 Decoder
        Video Port Manager
        Video Renderer
        VPS Decoder
        WM ASF Writer
        VBI Surface Allocator
        File writer
        iTV Data Sink
        iTV Data Capture filter
        DVD Navigator
        Microsoft TV Subtitles Decoder
        Overlay Mixer2
        AVI Draw
        RDP DShow Redirection Filter
        Microsoft MPEG-2 Audio Encoder
        WST Pager
        MPEG-2 Demultiplexer
        DV Video Decoder
        SampleGrabber
        Null Renderer
        MPEG-2 Sections and Tables
        Microsoft AC3 Encoder
        StreamBufferSource
        Smart Tee
        Overlay Mixer
        AVI Decompressor
        NetBridge
        AVI/WAV File Source
        Wave Parser
        MIDI Parser
        Multi-file Parser
        File stream renderer
        Microsoft DTV-DVD Audio Decoder
        StreamBufferSink2
        AVI Mux
        Line 21 Decoder 2
        File Source (Async.)
        File Source (URL)
        Media Center Extender Encryption Filter
        AudioRecorder WAV Dest
        AudioRecorder Wave Form
        SoundRecorder Null Renderer
        Infinite Pin Tee Filter
        Enhanced Video Renderer
        BDA MPEG2 Transport Information Filter
        MPEG Video Decoder

    -- External Renderers ({CC7BFB41-F175-11D1-A392-00E0291F3959}) --

    -- Midi Renderers ({4EFE2452-168A-11D1-BC76-00C04FB9453B}) --
        Default MidiOut Device
        Microsoft GS Wavetable Synth

    -- Video Capture Sources ({860BB310-5D01-11D0-BD3B-00A0C911CE86}) --

    -- Video Compressors ({33D9A760-90C8-11D0-BD43-00A0C911CE86}) --
        WMVideo8 Encoder DMO
        WMVideo9 Encoder DMO
        MSScreen 9 encoder DMO
        DV Video Encoder
        MJPEG Compressor
        Cinepak Codec by Radius
        Intel IYUV codec
        Intel IYUV codec
        Microsoft RLE
        Microsoft Video 1

    -- WDM Stream Decompression Devices ({2721AE20-7E70-11D0-A5D6-28DB04C10000}) --

    -- WDM Streaming Capture Devices ({65E8773D-8F56-11D0-A3B9-00A0C9223196}) --
        HD Audio Microphone
        HD Audio Muxed capture

    -- WDM Streaming Crossbar Devices ({A799A801-A46D-11D0-A18C-00A02401DCD4}) --

    -- WDM Streaming Rendering Devices ({65E8773E-8F56-11D0-A3B9-00A0C9223196}) --
        HD Audio Speaker

    -- WDM Streaming Tee/Splitter Devices ({0A4252A0-7E70-11D0-A5D6-28DB04C10000}) --
        Tee/Sink-to-Sink Converter

    -- WDM Streaming TV Audio Devices ({A799A802-A46D-11D0-A18C-00A02401DCD4}) --

    -- WDM Streaming TV Tuner Devices ({A799A800-A46D-11D0-A18C-00A02401DCD4}) --

    -- WDM Streaming VBI Codecs ({07DAD660-22F1-11D1-A9F4-00C04FBBDE8F}) --

    -- WDM Streaming Communication Transforms ({CF1DDA2C-9743-11D0-A3EE-00A0C9223196}) --
        Tee/Sink-to-Sink Converter

    -- WDM Streaming Data Transforms ({2EB07EA0-7E70-11D0-A5D6-28DB04C10000}) --

    -- WDM Streaming Interface Transforms ({CF1DDA2D-9743-11D0-A3EE-00A0C9223196}) --

    -- WDM Streaming Mixer Devices ({AD809C00-7B88-11D0-A5D6-28DB04C10000}) --

    -- BDA Network Providers ({71985F4B-1CA1-11D3-9CC8-00C04F7971E0}) --
        Microsoft ATSC Network Provider
        Microsoft DVBC Network Provider
        Microsoft DVBS Network Provider
        Microsoft DVBT Network Provider
        Microsoft Network Provider

    -- BDA Receiver Components ({FD0A5AF4-B41D-11D2-9C95-00C04F7971E0}) --

    -- BDA Rendering Filters ({71985F4A-1CA1-11D3-9CC8-00C04F7971E0}) --

    -- BDA Source Filters ({71985F48-1CA1-11D3-9CC8-00C04F7971E0}) --

    -- BDA Transport Information Renderers ({A2E3074F-6C3D-11D3-B653-00C04F79498E}) --
        BDA MPEG2 Transport Information Filter
        MPEG-2 Sections and Tables

    -- Video Effects (1 input) ({CC7BFB42-F175-11D1-A392-00E0291F3959}) --
        Fade
        BasicImage
        Convolution
        Chroma
        Matrix
        Pixelate
        DxtAlphaSetter Class
        Text Label Class
        Scale
        Blur
        Glow
        ICMFilter
        Alpha
        DropShadow
        Wave
        MotionBlur
        Shadow
        Emboss
        Engrave
        Light

    -- Video Effects (2 inputs) ({CC7BFB43-F175-11D1-A392-00E0291F3959}) --
        CrBlinds
        Iris
        RadialWipe
        Fade
        ZigZag
        RandomBars
        CrIris
        CrRadialWipe
        Spiral
        Pixelate
        Wheel
        Strips
        CrStretch
        Inset
        CrSlide
        CrInset
        Compositor
        Blinds
        CrSpiral
        Wipe
        CheckerBoard
        GradientWipe
        DxtCompositor Class
        CrBarn
        DxtKey Class
        Slide
        DxtJpeg Class
        CrZigzag
        Barn
        Stretch
        RandomDissolve

    -- EncAPI Encoders ({7D22E920-5CA9-4787-8C2B-A6779BD11781}) --
        Microsoft MPEG-2 Audio Encoder
        Microsoft MPEG-2 Video Encoder

    -- EncAPI Multiplexers ({236C9559-ADCE-4736-BF72-BAB34E392196}) --
        Microsoft MPEG-2 Encoder

     

  • Matthew van Eerde's web log

    How to write a leading apostrophe in Word

    • 4 Comments

    Disclaimer: I don't work on the Office team.

    Word has a smart quotes feature where it will automagically transform

    straight "double quotes," 'single quotes,' and greengrocer's apostrophes

    into

    curly “double quotes,” ‘single quotes,’ and greengrocer’s apostrophes

    as you type.  You send a Unicode Character 'APOSTROPHE' (U+0027) to Word, and Word turns it into a Unicode Character 'LEFT SINGLE QUOTATION MARK' (U+2018) or a Unicode Character 'RIGHT SINGLE QUOTATION MARK' (U+2019) as appropriate.  If you type it at the beginning of a word, it's an opening-single-quote; if you type it in the middle of a word, it's an apostrophe; if you type it at the end, it's either an apostrophe or a closing-single-quote, but both are the same character, so it doesn't matter.

    Right?

    Usually.

    The apostrophe is occasionally used at the beginning of a word, to mark elided letters.  Word has trouble with this.

    Twas brillig, and the slithy toves did gyre and gimbol in the wabe.

        -- Jabberwocky

    I want to give the Word folks credit here.  A common use of an apostrophe at the beginning of a word is to abbreviate a year.  Word gets this right (I'm using Word 2010 with default settings:)

    Check it out: a 57 Chevy!

    But Word also gets it wrong when you want to single-quote a clause that begins with a number:

    “They were singing 99 bottles of beer on the wall’.”

    Also, if you pluralize the date, Word gets suckered:

    10 models on sale! Check out the new 11s!

    Little nifties, from the 50s, innocent and sweet;
    Sexy ladies from the 80s, who are indiscreet
        -- 42nd Street

    For those times when Word gets it wrong, here's how to fix it.

    If you want to type a word that starts with an apostrophe:

    1. Type the apostrophe.  Word assumes you want an opening quote.  Fine
    2. Type the apostrophe again.  Word shrugs its shoulders and gives you a closing quote in a little soixante-neuf of punctuation.
    3. Type the magic sequence:
      1. Left arrow
      2. Backspace
      3. Right arrow
    4. Et voilà.  Continue typing your word.
      Twas 

    If you want to use an opening single quote with a sentence that starts with a number:

    1. Type the opening quote and the number in its entirety.  So far, so good.
      99
    2. Type the word breaker (usually a space.)  Word "helpfully" turns the opening single quote into an apostrophe.
      99 
    3. Invoke "Undo" via your favorite mechanism (I prefer Ctrl-Z since I'm already on the keyboard.)
      99 
    4. Et voilà.  Continue typing your quoted clause.
      99 bottles of beer on the wall

    That "Undo" tip is actually a fairly powerful curative against all sorts of Word voodoo.

  • Matthew van Eerde's web log

    Bug - jack detection on Windows 7 hdaudio.sys mixed capture

    • 5 Comments

    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.

  • Matthew van Eerde's web log

    Sample: how to enumerate waveIn and waveOut devices on your system

    • 4 Comments

    This shows how to call waveInGetNumDevs, waveInGetDevCaps, waveOutGetNumDevs, and waveOutGetDevCaps.

    // main.cpp

    #include <windows.h>
    #include <mmsystem.h>
    #include <stdio.h>

    #define LOG(format, ...) wprintf(format L"\n", __VA_ARGS__)

    int _cdecl wmain() {

        UINT devs = waveInGetNumDevs();
        LOG(L"waveIn devices: %u", devs);
        for (UINT dev = 0; dev < devs; dev++) {
            WAVEINCAPS caps = {};
            MMRESULT mmr = waveInGetDevCaps(dev, &caps, sizeof(caps));
           
            if (MMSYSERR_NOERROR != mmr) {
                 LOG(L"waveInGetDevCaps failed: mmr = 0x%08x", mmr);
                 return mmr;
            }
           
            LOG(
                L"-- waveIn device #%u --\n"
                L"Manufacturer ID: %u\n"
                L"Product ID: %u\n"
                L"Version: %u.%u\n"
                L"Product Name: %s\n"
                L"Formats: 0x%x\n"
                L"Channels: %u\n"
                L"Reserved: %u\n"
                ,
                dev,
                caps.wMid,
                caps.wPid,
                caps.vDriverVersion / 256, caps.vDriverVersion % 256,
                caps.szPname,
                caps.dwFormats,
                caps.wChannels,
                caps.wReserved1
            );
        }

        devs = waveOutGetNumDevs();
        LOG(L"waveOut devices: %u", devs);
        for (UINT dev = 0; dev < devs; dev++) {
            WAVEOUTCAPS caps = {};
            MMRESULT mmr = waveOutGetDevCaps(dev, &caps, sizeof(caps));
           
            if (MMSYSERR_NOERROR != mmr) {
                 LOG(L"waveOutGetDevCaps failed: mmr = 0x%08x", mmr);
                 return mmr;
            }
           
            LOG(
                L"-- waveOut device #%u --\n"
                L"Manufacturer ID: %u\n"
                L"Product ID: %u\n"
                L"Version: %u.%u\n"
                L"Product Name: %s\n"
                L"Formats: 0x%x\n"
                L"Channels: %u\n"
                L"Reserved: %u\n"
                L"Support: 0x%x\n"
                L"%s%s%s%s%s"
                ,
                dev,
                caps.wMid,
                caps.wPid,
                caps.vDriverVersion / 256, caps.vDriverVersion % 256,
                caps.szPname,
                caps.dwFormats,
                caps.wChannels,
                caps.wReserved1,
                caps.dwSupport,
                    ((caps.dwSupport & WAVECAPS_LRVOLUME) ?       L"\tWAVECAPS_LRVOLUME\n" :       L""),
                    ((caps.dwSupport & WAVECAPS_PITCH) ?          L"\tWAVECAPS_PITCH\n" :          L""),
                    ((caps.dwSupport & WAVECAPS_PLAYBACKRATE) ?   L"\tWAVECAPS_PLAYBACKRATE\n" :   L""),
                    ((caps.dwSupport & WAVECAPS_VOLUME) ?         L"\tWAVECAPS_VOLUME\n" :         L""),
                    ((caps.dwSupport & WAVECAPS_SAMPLEACCURATE) ? L"\tWAVECAPS_SAMPLEACCURATE\n" : L"")
            );
        }

        return 0;
    }

    On my system this outputs:

    waveIn devices: 3
    -- waveIn device #0 --
    Manufacturer ID: 1
    Product ID: 65535
    Version: 0.0
    Product Name: Microphone (High Definition Aud
    Formats: 0xfffff
    Channels: 2
    Reserved: 0

    -- waveIn device #1 --
    Manufacturer ID: 1
    Product ID: 65535
    Version: 0.0
    Product Name: Digital Audio (S/PDIF) (High De
    Formats: 0xfffff
    Channels: 2
    Reserved: 0

    -- waveIn device #2 --
    Manufacturer ID: 1
    Product ID: 65535
    Version: 0.0
    Product Name: CD Audio (High Definition Audio
    Formats: 0xfffff
    Channels: 2
    Reserved: 0

    waveOut devices: 2
    -- waveOut device #0 --
    Manufacturer ID: 1
    Product ID: 65535
    Version: 0.0
    Product Name: Headphones (High Definition Aud
    Formats: 0xfffff
    Channels: 2
    Reserved: 0
    Support: 0x2e
     WAVECAPS_LRVOLUME
     WAVECAPS_PLAYBACKRATE
     WAVECAPS_VOLUME
     WAVECAPS_SAMPLEACCURATE

    -- waveOut device #1 --
    Manufacturer ID: 1
    Product ID: 65535
    Version: 0.0
    Product Name: Digital Audio (S/PDIF) (High De
    Formats: 0xfffff
    Channels: 2
    Reserved: 0
    Support: 0x2e
     WAVECAPS_LRVOLUME
     WAVECAPS_PLAYBACKRATE
     WAVECAPS_VOLUME
     WAVECAPS_SAMPLEACCURATE

     

    Source and binaries attached.

  • Matthew van Eerde's web log

    The evolution of HRESULT_FROM_WIN32

    • 1 Comments

    We figured they were more actual guidelines. -- Pirates of the Caribbean

    Macros are wonderful things.  I love macros.  They're exciting and dangerous.  Every time I write a macro I salivate at the power I am wielding.  It's like using a blowtorch.

    It is perhaps no coincidence that many coding standards put big warning signs around macroland.  "Consider carefully before using macros", some say. "When considering using a macro, see if you can use an inline function instead," some say.  "Macros are frequent sources of bugs," some say.  "C++ has evolved to the point where, with inline functions and templates, macros are no longer necessary".

    Heresy.

    All absolutely true, and yet heresy nonetheless.  It is true that there are many bugs out there due to misuse of macros.  And yet only a few simple tips are necessary to prevent misuse of macros: (The problem is that the tips don't always get followed).

    She generally gave herself very good advice, (though she very seldom followed it) -- Alice in Wonderland

    1. By convention, name macros in ALL_CAPS.  This makes it easy to recognize them.
      Without this rule it is easy to think that the following macro is a function...
      #define SafeDelete(p) { delete (p); (p) = NULL; } void(0)
      ... and in fact it probably should be...
      template<typename T> SafeDelete(T *&p) { delete p; p = NULL; }
    2. Parenthesize the definition of the macro.
      This prevents precedence bugs.
      #define SQUARE(x) (x) * (x) // should be ((x) * (x))
      int four = 16 / SQUARE(2); // BUG: four == 16
    3. Parenthesize uses of the arguments of the macro unless this contravenes the purpose of the macro.
      This prevents precedence bugs in the other direction.
      #define SQUARE(x) (x * x) // should be ((x) * (x))
      int four = SQUARE(1 + 1); // BUG: four == 3
    4. Fill out optional clauses.
      #define ASSERT(x) if (!(x)) { exit(__LINE__); }
      // should be if (!(x)) { exit(__LINE__); } else {} (void)0
      if (IsTuesday())
          ASSERT(m_pGarbage);
      else { // BUG: will only vacuum and check mail on tuesday (and only if m_pGarbage is set)
          Vacuum();
          CheckMail();
      }
    5. UNDER NO CIRCUMSTANCES use an expression that has side effects as an argument when calling a macro.
      #define SafeDelete(p) { delete (p); (p) = NULL; } void(0) // reasonable, except for ALL_CAPS rule
      // p points to the first of a null-terminated array of pointers that need to be deleted
      while (p) SafeDelete(p++); // BUG: only odd-numbered pointers deleted, and AV if odd number of elements

      Exceptions I happen to agree with: VERIFY(...) cries out to be an exception to this rule since it commonly textifies the condition, and a popup that says "CloseHandle(m_file) failed" is infinitely more meaningful than "bOK failed".  As a corollary, VERIFY(...) macros should only evaluate their arguments once.
      Exceptions I've given up trying to convince other people are bad: SUCCEEDED(...), FAILED(...), and HRESULT_FROM_WIN32(GetLastError()) are common exceptions too.

    Let's talk about the "no expressions with side effects" rule a little more.  It is OK to do things like

    if (SUCCEEDED(CoInitialize(...))) { ... }

    if (FAILED(CoCreateInstance(...))) { ... }

    return HRESULT_FROM_WIN32(GetLastError());

    ... but for different reasons.  SUCCEEDED(...) and FAILED(...) only evaluate their arguments once, as can be verified from their definitions in winerror.h.  I'm using the Vista RTM version of winerror.h:

    //
    // Generic test for success on any status value (non-negative numbers
    // indicate success).
    //

    #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)

    //
    // and the inverse
    //

    #define FAILED(hr) (((HRESULT)(hr)) < 0)

    Note the heavy (and correct) use of parentheses.  I still personally don't think it's a good idea to rely on this, since macros have a habit of changing... but I won't ding anyone on a code review for it.

    HRESULT_FROM_WIN32(GetLastError()) is OK for a totally different reason.  HRESULT_FROM_WIN32(...), classically, does evaluate its argument multiple times... this time I'm using the Windows 2000 version of winerror.h:

    #define HRESULT_FROM_WIN32(x)   (x ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)) : 0 )

    ... so this looks like a bug... until you realize that GetLastError() is idempotent.  If you call it a bunch of times in a row on the same thread, it will (per its contract) always return the same value.

    Note that the macro is not completely parenthesized!  The bold x is missing its protective parentheses.  This was fixed in a Windows 2000 service pack.  For example, here's the Win2K SP3 version:

    #define HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))

    Much better.

    I'm still personally leery of using side effects in HRESULT_FROM_WIN32 though.  Why?  Because it sets a bad precedent.  It's easier to have a simple rule (no side effects in a macro call!) than to remember for an infinitude of macros and function calls which macros evaluate the arguments multiple times and which function calls really have side effects.  You also get legitimate bugs like this.

    Macros, macros, macros... you deviate just a little bit from the straight-and-narrow and what happens?   You get blown out of the water.  Wonderful things, macros.

    Nasty little bugs arising from harmless-looking violations of the safety tips are the cause of the dirty secret I am about to reveal...

    As of Vista RTM...

    HRESULT_FROM_WIN32 is NO LONGER a macro!

    This is one of those annoying little facts I try so hard to forget because they complicate life unnecessarily.  From my point of view, there is no bad consequence of treating HRESULT_FROM_WIN32 as the macro it looks like (and was... and who knows, may be again.)  The inline function it has become is uniformly safer.

    This has apparently been a long time in coming.  Here are some excerpts of winerror.h from various releases of Windows:

    Windows 2000 RTM:

    #define HRESULT_FROM_WIN32(x)   (x ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)) : 0 )

    Windows XP RTM:

    // __HRESULT_FROM_WIN32 will always be a macro.
    // The goal will be to enable INLINE_HRESULT_FROM_WIN32 all the time,
    // but there's too much code to change to do that at this time.

    #define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))

    #ifdef INLINE_HRESULT_FROM_WIN32
    #ifndef _HRESULT_DEFINED
    #define _HRESULT_DEFINED
    typedef long HRESULT;
    #endif
    #ifndef __midl
    __inline HRESULT HRESULT_FROM_WIN32(long x) { return x < 0 ? (HRESULT)x : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}
    #else
    #define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)
    #endif
    #else
    #define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)
    #endif

    Whew.  The net result is that HRESULT_FROM_WIN32 is a macro.  Oh, unless you decide to be avant-garde and #define INLINE_HRESULT_FROM_WIN32 before you #include <winerror.h>.  Which probably very few people did, except for build engineers running experiments.  So this is an "opt-in" inline function.

    Windows Vista RTM:

    //
    // HRESULT_FROM_WIN32(x) used to be a macro, however we now run it as an inline function
    // to prevent double evaluation of 'x'. If you still need the macro, you can use __HRESULT_FROM_WIN32(x)
    //
    #define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))

    #if !defined(_HRESULT_DEFINED) && !defined(__midl)
    #define _HRESULT_DEFINED
    typedef __success(return >= 0) long HRESULT;
    #endif

    #ifndef __midl
    FORCEINLINE HRESULT HRESULT_FROM_WIN32(unsigned long x) { return (HRESULT)(x) <= 0 ? (HRESULT)(x) : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}
    #else
    #define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)
    #endif

    The experiments appear to have been completed.   Apparently no imaginable consequence of changing to an inline function was worse than the known consequence of bugs.

    People who want the macro can still use __HRESULT_FROM_WIN32.

    Why would you want to?  Hmmm... maybe you're building a switch statement.  Something like:

    #define CASE_HRESULT(x) case x: return #x
    #define CASE_ERR(x) case HRESULT_FROM_WIN32(x): return #x // need __HRESULT_FROM_WIN32 here?

    LPCSTR FriendlyNameOf(HRESULT hr) {
        switch(hr) {
            CASE_HRESULT(S_OK);
            CASE_HRESULT(S_FALSE);
            ... many more...
            CASE_ERR(ERROR_SUCCESS);
            CASE_ERR(ERROR_INVALID_FUNCTION);
            ...
        }
        return "Unrecognized";
    }

    I haven't tried it out, but I suspect the compiler will reject the attempt to squeeze the result of an inline function into the value of a case, but will accept a complicated expression involving a bunch of constants (i.e., the macro will work.)

  • Matthew van Eerde's web log

    Unsolving the Rubik's Cube

    • 8 Comments

    Recently I was musing on Order vs. Chaos and toying with my Rubik's Cube.  I wondered, as a simple exercise, whether it would be possible to strongly unsolve the cube.  Obviously there is a single way to put the cube into a state of greatest possible order... and a multitude of ways to put it into a state of moderate to severe chaos... but is there a "most chaotic" state?

    Well, I mused, the "greatest possible order" state is achieved by bringing all the orange squares together... and all the blue squares together... and, in general, all the squares of any given color together.  How homogenous... or xenophobic.  Ew.

    Might it not be interesting to intermingle the colors to as great an extent as possible?  Can I put the cube in a state where no two orange squares are adjacent... and no two blue squares are adjacent... and, in general, no two adjacent squares are the same color?

    Of course, I responded.  In fact, I already know how to do that... I learned that trick before I even learned the Restore Order solution.

    Twelve turns later, I had a solution to the Adjacent Squares Are Different problem.

    Exercise: what are the twelve turns?

    Well, that was quick.

    But I wanted more.

    Yeah, OK, no two adjacent squares are the same color, by the usual definition of adjacency (the squares share a common border.)  But this is still a fairly homogenous solution... each face (of nine squares) still consists of only two colors, and there's a very high incidence of diagonally-touching squares of the same color.  Can't we diversify this even more?

    That took me a couple of days.  But here's the solution I came up with:

    Unsolved Rubik's cube

    Note that, as desired, no two adjacent squares are the same color... even if you consider squares that touch only at a corner to be adjacent... even if that corner lies on an edge, and the two squares in question lie on different faces of the cube.

    The method to achieving the solution was simple in the sense that it only requires two moves (starting from a solved cube) but is probably far from optimal in the "total number of turns" sense.

    There are two independent steps which can be done in either order:

    • Flip the orientation of all twelve of the "edge pieces" (the cubelets with two visible faces) 
    • Migrate all eight "corner pieces" (the cubelets with three visible faces) to the diametrically opposite position on the cube

    Each requires knowledge of a single move and a fair amount of courage.

    First, some syntax.

    Hold the cube facing you.  I will name the six faces of the cube:

    • Fore (the face closest to you - in the picture above, the face with the green center)
    • Hind (in the picture above, the face with the blue center)
    • Top (in the picture above, the face with the white center)
    • Bottom (in the picture above, the face with the yellow center)
    • Left (in the picture above, the face with the orange center)
    • Right (in the picture above, the face with the red center)

    Each face has a local definition of "clockwise"... this is the direction a clock painted on the face would turn.

    Cubelet syntax

    • A combination of two letters (FR) means the edge cubelet common to both faces - in this case the Fore and Right faces.  In the picture above, this is the green and white cubelet.
    • A combination of three letters (BHL) means the corner cubelet common to all three faces - in this case the Bottom, Hind, and Left faces.  In the picture above, this is the red-green-and-white cubelet.

    Move syntax: a letter means turn that face clockwise by 90 degrees.  A letter with a subscripted -1 means turn it counterclockwise by 90 degrees.  So a move "turn the Fore face clockwise, then turn the Left face counterclockwise, then turn the Top face counterclockwise" would be written:

    F L-1 T-1

    Start from a solved cube.

    Flip the orientation of all twelve of the "edge pieces"

    Pick your favorite color - say, red - and have that be the top face.

    The following move flips the orientation of the LT and TH edge pieces, and also disturbs the orientation of some corner pieces:

    L-1 T-1 L-1 L-1
    F-1 L-1 F-1 F-1
    T-1 F-1 T-1 T-1

    • Run the move once with red as the top face.
    • Position the cube so that red is still as the top face but the two remaining unflipped edge pieces with red on them are now in the LT and TH position.  That is, turn the whole cube 180 degrees with an axis of rotation that goes through the center of the top and bottom faces.
    • Run the move again.  All four edge cubelets with red on them should now be flipped.
    • Position the cube so that red is now the front face.
    • Run the move again four more times, keeping red as the front face but rotating the cube 90 degrees each time, so each execution of the move acts on two unflipped edge pieces.

    All twelve of the edge pieces are now flipped.  The corner pieces are still in their original positions, though they may be oriented incorrectly. That's OK.

    Reposition all eight corner pieces to the opposite corner

    The following move repositions FLT to TLH, TLH to TRH, and TRH to FLT:

    L-1 T R T-1
    L T R-1 T-1

    As a convenience, the following mirror-image move repositions FRT to TRH, TRH to TLH, and TLH to FRT:

    R T-1 L-1 T
    R-1 T-1 L T

    Strictly speaking, you can get away with only memorizing one of these moves - each move is equivalent to holding the cube in a different position and executing the other move twice.

    Pick three faces that share a common corner - say, the faces whose center cubes are red, white, and blue.  Position the cube so it is balancing on that corner.  Note that the corner cubelets now occupy four distinct strata:

    1. The "top" corner.
    2. The three corners that share a common edge with the "top" corner.
    3. The three corners that share a common edge with the "bottom" corner.
    4. The "bottom" corner. 

    This part of unsolving the cube is completed in four distinct phases:

    1. Unsolve the "top" corner - that is, find the red/white/blue cubelet (it's on the bottom corner) and move it to the top.  This will require two moves, which will to a certain degree randomize the positions of all of the rest of the corners.
    2. Unsolve the three corners that share a common edge with the "top" corner, without disturbing the cubelet in the "top" corner, or any already-unsolved cubelets in this stratum.
    3. Unsolve one of the three corners that share a common edge with the "bottom" corner, without disturbing any of the cubelets in the two strata above.
    4. Finally, there are three remaining un-unsolved cubelets.  Unsolve all three of these simultaneously - this will take precisely one execution of one of the two moves above.

    EDIT January 16 2012: Thanks to Dustin for pointing out that the diagram above shows some reds touching diagonally (near the topmost corner.)  After some analysis I believe this is just due to an error on my part in making the image; specifically, the top corner is green-yellow-red, but so is the corner in the top left.  Also, the green-yellow-orange corner is missing.

    Both of these can be explained by changing the red in the top corner to orange.  Updated image:

  • Matthew van Eerde's web log

    Anand vs. Gelfand world chess championship 2012 oldest pair of contenders since 1886

    • 2 Comments

    In 2012, World Chess Champion Viswanathan Anand will attempt to defend his title aqainst challenger Boris Gelfand. This is a very unusual match in that both players are fairly old by World Chess Champion contender standards. I decided to see just how unusual it was, and do so with some degree of rigor.

    One tricky bit is that chess championships (usually) have (at least) two players, so we have to define an age metric for pairs of people. Creating a well-ordering on tuples is sometimes controversial. I chose to have the comparison routine be:
        age(contenders) = min(age(contender) : contender ∈ contenders)
    which is to say, the age of the youngest contender.

    Another tricky bit was deciding which matches were definitively "world chess championship matches." I pulled the list of world chess championship matches from chessgames.com. For time periods where the organizational ownership of the title is in question, this includes matches sponsored by all contending organizations.

    As a naïve first pass, I looked up the birth years for all the contenders and subtracted that from the year of the championship to get an estimated age. This could be off by a year if the youngest contender's birthday comes after (or during?) the match. Nevertheless, this was accurate enough to give me a short list of matches to investigate further:.

    Year Player 1 Estimated Age Player 2 Estimated Age Minimum
    2012 Viswanathan Anand 43 Boris Gelfand 44 43
    2010 Viswanathan Anand 41 Veselin Topalov 35 35
    2008 Viswanathan Anand 39 Vladimir Kramnik 33 33
    2006 Vladimir Kramnik 31 Veselin Topalov 31 31
    2005 Veselin Topalov 30 Many N/A1 30
    2004 Vladimir Kramnik 29 Peter Leko 25 25
    2004 Rustam Kasimdzhanov 25 Michael Adams 33 25
    2001 Ruslan Ponomariov 18 Vassily Ivanchuk 32 18
    2000 Vladimir Kramnik 25 Garry Kasparov 37 25
    2000 Viswanathan Anand 31 Alexey Shirov 28 28
    1999 Alexander Khalifman 33 Vladimir Akopian 28 28
    1998 Anatoly Karpov 47 Viswanathan Anand 29 29
    1996 Anatoly Karpov 45 Gata Kamsky 22 22
    1995 Garry Kasparov 32 Viswanathan Anand 26 26
    1993 Garry Kasparov 30 Nigel Short 28 28
    1993 Anatoly Karpov 42 Jan Timman 42 42
    1990 Garry Kasparov 27 Anatoly Karpov 39 27
    1987 Garry Kasparov 24 Anatoly Karpov 36 24
    1986 Garry Kasparov 23 Anatoly Karpov 35 23
    1985 Garry Kasparov 22 Anatoly Karpov 34 22
    1984 Anatoly Karpov 33 Garry Kasparov 21 21
    1981 Anatoly Karpov 30 Viktor Korchnoi 50 30
    1978 Anatoly Karpov 27 Viktor Korchnoi 47 27
    1972 Bobby Fischer 29 Boris Spassky 35 29
    1969 Boris Spassky 32 Tigran Petrosian 40 32
    1966 Tigran Petrosian 37 Boris Spassky 29 29
    1963 Tigran Petrosian 34 Mikhail Botvinnik 52 34
    1961 Mikhail Botvinnik 50 Mikhail Tal 25 25
    1960 Mikhail Tal 24 Mikhail Botvinnik 49 24
    1958 Mikhail Botvinnik 47 Vasily Smyslov 37 37
    1957 Vasily Smyslov 36 Mikhail Botvinnik 46 36
    1954 Mikhail Botvinnik 43 Vasily Smyslov 33 33
    1951 Mikhail Botvinnik 40 David Bronstein 27 27
    1948 Mikhail Botvinnik 37 Vasily Smyslov 27 27
    1937 Alexander Alekhine 45 Max Euwe 36 36
    1935 Max Euwe 34 Alexander Alekhine 43 34
    1934 Alexander Alekhine 42 Efim Bogolyubov 45 42
    1929 Alexander Alekhine 37 Efim Bogolyubov 40 37
    1927 Alexander Alekhine 35 José Raúl Capablanca 39 35
    1921 José Raúl Capablanca 33 Emanuel Lasker 53 33
    1910 Emanuel Lasker 42 Dawid Janowski 42 42
    1910 Emanuel Lasker 42 Carl Schlecter 36 36
    1908 Emanuel Lasker 40 Siegbert Tarrasch 46 40
    1907 Emanuel Lasker 39 Frank Marshall 30 30
    1896 Emanuel Lasker 28 Wilhelm Steinitz 60 28
    1894 Emanuel Lasker 26 Wilhelm Steinitz 58 26
    1892 Wilhelm Steinitz 56 Mikhail Chigorin 42 42
    1890 Wilhelm Steinitz 54 Isidor Gunsberg 36 36
    1889 Wilhelm Steinitz 53 Mikhail Chigorin 39 39
    1886 Wilhelm Steinitz 50 Johannes Zukertort 44 44

    Closer investigation of each of the highlighted matches revealed that, astonishingly, in every case the youngest contender's birthday came after the match:

    • 2012 Viswanathan Anand (43?) vs. Boris Gelfand: Anand's birthday (December 11) comes after the match (starts in May) so he will still be 42.
    • 1993 Anatoly Karpov (42?) vs. Jan Timman (42?): Timman's birthday (December 14) came after the match (finished November 1) so he was still 41.
    • 1934 Alexander Alekhine (42?) vs. Efim Bogolyubov: Alekhine's birthday (October 31) came after the match (April to June) so he was still 41.
    • 1910 Emanuel Lasker (42?) vs. Dawid Janowski (42?): Janowski was born May 25; Lasker December 24. Lasker's birthday came after the match (finished December 8) so he was still 41.
    • 1892 Wilhelm Steinitz vs. Mikhail Chigorin (42?): Chigorin's birthday November 12 (October 31 old style) came after the match (finished February 28) so he was still 41.
    • 1886 Wilhelm Steinitz vs. Johannes Zukertort (44?): Zukertort's birthday (September 7) came after the match (finished March 29) so he was still 43.

    We conclude that Anand vs. Gelfand (2012) features the oldest contenders since the very first World Chess Championship Steinitz vs. Zukertort (1886) - and is within a year of even that! If the 2014 championship is a rematch, it will set the record.

    1 Topalov was the clear winner of the 2005 FIDE World Championship Tournament so there was no need for a runoff.

  • Matthew van Eerde's web log

    Windows audio render volume settings, from local to global

    • 2 Comments

    Windows has four different volume settings for any given piece of audio; they are all applied simultaneously. For example, if one of these settings is muted, you will not hear the audio, even if the others are turned up to "full". Here they are, from local to global:

    Stream volume

    The WASAPI API for this is IAudioStreamVolume.

    This most local volume control is used only in rather specialized situations. If a particular app wants to play multiple audio streams to the same audio device, and in the same session (see below), but wants to control the volumes for the streams independently, this is the volume control to use. For example:

    • A media playback app might want to cross fade songs n and n + 1 in a playlist; it could do this by playing the songs in different streams, gradually bringing the volume for song n down to zero, while simultaneously bringing the volume for song n + 1 up to full.
    • A game might offer distinct volume controls for:
      • The background music in the game
      • The sound effects (gunshots, explosions, and so on)
      • Voice chat in the game
      The first two would likely be in the same session and would thus use stream volume controls (exposed via the game UI - Windows does not offer UI for stream volume.) The third could very well be a different session, and even use a different device - the background music and sound effects might go to the default console device (e.g., speakers) while the voice chat might go to the default communications device (e.g., a headset.)

    Apps could also get these same effects by doing their own mixing and volume handling, and playing a single stream to the Windows audio engine, of course.

    Session volume

    By and large, this is the volume control that apps most commonly use. The WASAPI APIs for this are ISimpleAudioVolume and the more-rarely used IChannelAudioVolume.

    This is what people mean when they say "application volume" - a session is, by and large, an app (though there are exceptions in both directions.) This is the "S" in "WASAPI" - Windows Audio Session API.

    The session volume shows up in the Volume Mixer - each "Application" slider is a session volume control. This is also usually (but not always) tied to a volume control in the app.

    Endpoint volume

    The WASAPI API for this is IAudioEndpointVolume. Apps should generally prefer ISimpleAudioVolume to this because IAudioEndpointVolume will affect all apps on the system.

    This is what people mean when they say "system volume." It is exposed in the following three places in the UI. Note if you have multiple audio devices, each has its own setting.

    Ducking

    More about ducking

    When you get a phone call or start a voice chat, Windows (starting in Windows 7) will attenuate (or "duck") all of the audio on the system (except for phone calls and voice chats.) This is the "most global" because it affects all streams on all audio devices. Note that this volume control only has three settings (four if you count "mute".)

     

    Larry Osterman did a series of audio volume posts - one of them contains much of the information in this post, though the ducking feature was added after his post.

  • Matthew van Eerde's web log

    Generating formulae for polygonal numbers (triangular, square, pentagonal...)

    • 3 Comments

    The formula for square numbers is easy to remember:
    sn = n2
    There are also triangular numbers, pentagonal numbers, hexagonal numbers, and in general k-gonal numbers. These have formulae too... for example, triangular numbers:
    tn = n (n + 1) / 2
    ... but they can be hard to remember.

    If you see a fact, try to see it as intuitively as possible.

    Some formulae are not worth memorizing - it is more worthwhile to grasp the ideas behind the formulae and rederive them when you need them (or look them up.) This blog post shows how to derive the formulae for k-gonal numbers.

    Let's define a few terms. I've defined sn and tn above, and I'll define pn as the nth pentagonal number and hn as the nth hexagonal number, but let's generalize... let gk,n = g(k, n) be the nth k-gonal number. For example:
    g(3, n) = tn = n (n + 1) / 2
    g(4, n) = sn = n2
    The task, then, is to find a formula for g(k, n) for general k and n.

    Consider the nth k-gonal number. For simplicity of illustration, I will focus on a particular number, but the approach followed here should be followed while keeping the general case in mind. So, specifically, let's take a look at the 4th pentagonal number p4 = g(5, 4):

    The idea is to conceptually "cut" (n – 1) specific lines...

    ... and then "unroll" the structure to form a kind of "bar chart":

    The base of the chart is precisely one edge of the original diagram and so has length n = 4 (counting dots, not lines.) There are a couple of ways to find the height of the last bar - you can observe that each bar (not counting the bottom dot) adds (k – 2) = 3 over the bar before it (as marked below,) and there are (n – 1) = 3 nontrivial bars.
    Or you can note that each bar is the bottom dot, plus the sum of (k – 2) = 3 edges, each of which has (n – 1) = 3 unique dots, if we don't count the first dot as being part of the edge.
    Either way, you come up with the height being:
    h = 1 + (k – 2)(n – 1) = 1 + 3(3) = 10

    So we know the base and the height of the triangle. How do we find the area? Well, if we double the triangle, we get a rectangle:

    The base of the rectangle is n = 4, and the height is:
    h + 1 = 2 + (k – 2) (n – 1) = (k – 2) n + (4 – k) = (5 – 2) 4 + (4 – 5) = 11
    The area of the triangle is half of the area of the rectangle:
    g(k, n) = n ((k – 2) n + (4 – k)) / 2 = 4 ((5 – 2) 4 + (4 – 5)) / 2 = 22
    Counting dots confirms that there are, in fact, 22.

    So we have a general theorem - let's try it out on a few special cases. We can use the ones we know as a check.

    g(3, n) = n ((3 – 2) n + (4 – 3)) / 2 = n (n + 1) / 2 = tn
    g(4, n) = n ((4 – 2) n + (4 – 4)) / 2 = n2 = sn
    g(5, n) = n ((5 – 2) n + (4 – 5)) / 2 = n (3n – 1) / 2 = pn
    g(6, n) = n ((6 – 2) n + (4 – 6)) / 2 = n (2n – 1) = hn
    g(7, n) = n ((7 – 2) n + (4 – 7)) / 2 = n (5n – 3) / 2
    ...
    g(k, n) = n ((k – 2) n + (4 – k)) / 2
    ...

    Further checks: g(k, 1) should be 1 for all k, and g(k, 2) should be k for all k. These also check out.

  • Matthew van Eerde's web log

    How to enumerate Media Foundation transforms on your system

    • 0 Comments

    This mini-application (source and binaries attached) uses MFTEnumEx to get a list of all the registered IMFTransform classes on the system.  If you're writing a Media Foundation app, you're likely going to use different flags than I used... see the documentation for sets of flags that match three different scenarios.

    In my case I wanted as exhaustive a list of transforms as possible - so I include asynchronous MFTs, disabled MFTs, etc:

    DWORD dwFlags = 0
        // enumerate all three kinds of data flow
        | MFT_ENUM_FLAG_SYNCMFT
        | MFT_ENUM_FLAG_ASYNCMFT
        | MFT_ENUM_FLAG_HARDWARE
           
        // include not-usually-included kinds of MFTs
        | MFT_ENUM_FLAG_FIELDOFUSE
        | MFT_ENUM_FLAG_LOCALMFT
        | MFT_ENUM_FLAG_TRANSCODE_ONLY
       
        // DO NOT filter or sort the results
        // we want to include even blocked MFTs in this list
        // EXPLICITLY NOT SETTING MFT_ENUM_FLAG_SORTANDFILTER
    ;

    Opportunities for later improvement - I only print out the friendly name and the CLSID for each found transform.  I could add things like the MF_TRANSFORM_FLAGS_Attribute, the MF_SUPPORT_DYNAMIC_FORMAT_CHANGE attribute, the supported input types and output types, etc.  But this is good enough for now.

    Output of the tool on my machine:

    > MFTEnum.exe

    -- Audio decoders ({9EA73FB4-EF7A-4559-8D5D-719D8F0426C7}) --
    WMAudio Decoder MFT ({2EEB4ADF-4578-4D10-BCA7-BB955F56320A})
    Microsoft AAC Audio Decoder MFT ({32D186A7-218F-4C75-8876-DD77273A8999})
    GSM ACM Wrapper MFT ({4A76B469-7B66-4DD4-BA2D-DDF244C766DC})
    WMAPro over S/PDIF MFT ({5210F8E4-B0BB-47C3-A8D9-7B2282CC79ED})
    WMSpeech Decoder DMO ({874131CB-4ECC-443B-8948-746B89595D20})
    G711 Wrapper MFT ({92B66080-5E2D-449E-90C4-C41F268E5514})
    IMA ADPCM ACM Wrapper MFT ({A16E1BFF-A80D-48AD-AECD-A35C005685FE})
    MP3 Decoder MFT ({BBEEA841-0A63-4F52-A7AB-A9B3A84ED38A})
    ADPCM ACM Wrapper MFT ({CA34FE0A-5722-43AD-AF23-05F7650257DD})

    -- Audio effects ({11064C48-3648-4ED0-932E-05CE8AC811B7}) --
    AEC ({745057C7-F353-4F2D-A7EE-58434477730E})
    Resampler MFT ({F447B69E-1884-4A7E-8055-346F74D6EDB3})

    -- Audio encoders ({91C64BD0-F91E-4D8C-9276-DB248279D975}) --
    WM Speech Encoder DMO ({1F1F4E1A-2252-4063-84BB-EEE75F8856D5})
    WMAudio Encoder MFT ({70F598E9-F4AB-495A-99E2-A7C4D3D89ABF})
    Microsoft AAC Audio Encoder MFT ({93AF0C51-2275-45D2-A35B-F2BA21CAED00})

    -- Demultiplexers ({A8700A7A-939B-44C5-99D7-76226B23B3F1}) --
    No IMFTransforms found.

    -- Multiplexers ({059C561E-05AE-4B61-B69D-55B61EE54A7B}) --
    No IMFTransforms found.

    -- Miscellaneous ({90175D57-B7EA-4901-AEB3-933A8747756F}) --
    No IMFTransforms found.

    -- Video decoders ({D6C02D4B-6833-45B4-971A-05A4B04BAB91}) --
    DV Decoder MFT ({404A6DE5-D4D6-4260-9BC7-5A6CBD882432})
    Mpeg4s Decoder MFT ({5686A0D9-FE39-409F-9DFF-3FDBC849F9F5})
    Microsoft H264 Video Decoder MFT ({62CE7E72-4C71-4D20-B15D-452831A87D9D})
    WMV Screen decoder MFT ({7BAFB3B1-D8F4-4279-9253-27DA423108DE})
    WMVideo Decoder MFT ({82D353DF-90BD-4382-8BC2-3F6192B76E34})
    NVIDIA Video Decoder MFT ({ACAE6A3C-3DB0-4F71-B783-CDBD2AC6FDE8})
    MJPEG Decoder MFT ({CB17E772-E1CC-4633-8450-5617AF577905})
    Mpeg43 Decoder MFT ({CBA9E78B-49A3-49EA-93D4-6BCBA8C4DE07})
    Mpeg4 Decoder MFT ({F371728A-6052-4D47-827C-D039335DFE0A})

    -- Video effects ({12E17C21-532C-4A6E-8A1C-40825A736397}) --
    Frame Rate Converter ({01F36CE2-0907-4D8B-979D-F151BE91C883})
    Resizer MFT ({1EA1EA14-48F4-4054-AD1A-E8AEE10AC805})
    Color Control ({798059F0-89CA-4160-B325-AEB48EFE4F9A})
    Color Converter MFT ({98230571-0087-4204-B020-3282538E57D3})

    -- Video encoders ({F79EAC7D-E545-4387-BDEE-D647D7BDE42A}) --
    NVIDIA Video Encoder MFT ({305AFD76-ADD0-417E-AA99-3AC4FDB22B21})
    H264 Encoder MFT ({6CA50344-051A-4DED-9779-A43305165E35})
    WMVideo8 Encoder MFT ({7E320092-596A-41B2-BBEB-175D10504EB6})
    WMVideo9 Encoder MFT ({D23B90D0-144F-46BD-841D-59E4EB19DC59})

    -- Video processors ({302EA3FC-AA5F-47F9-9F7A-C2188BB16302}) --
    NVIDIA Video Processor MFT ({6A8C0482-4A25-48A1-AFD1-0A7C1629352A})

    -- KS data compressors ({1E84C900-7E70-11D0-A5D6-28DB04C10000}) --
    No IMFTransforms found.

    -- KS data decompressors ({2721AE20-7E70-11D0-A5D6-28DB04C10000}) --
    No IMFTransforms found.

    EDIT: the IMFAttributes::GetAllocatedString call in this app is incorrect.  I pass a NULL for the "length" output parameter.  As it turns out, this must not be NULL because the call may be marshaled out-of-proc.

  • Matthew van Eerde's web log

    Why a full-scale sine wave has an intensity of -3 dB FS

    • 3 Comments

    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?

Page 1 of 6 (139 items) 12345»