Matthew van Eerde's web log

  • Matthew van Eerde's web log

    shellproperty.exe - set/read string properties on a file from the command line

    • 2 Comments

    Yesterday Raymond Chen blogged a "Little Program" which could edit audio metadata. As it happens, I have a similar tool I threw together which accepts a property key and a string property value to update a property, or can read a string or string-vector property.

    Usage:

    >shellproperty
    shellproperty read <key> from <filename>
    shellproperty set <key> to <string> on <filename>

    Here's an example _fixup.bat script I use to set audio metadata on my copy of Giuseppe Sinopoli's recording of Madama Butterfly, to help distinguish it from other recordings of the same opera that I have.

    @echo off
    dir /s /b "I *.mp3" | xargs /addquotes shellproperty set PKEY_Music_AlbumTitle to "Madama Butterfly - Sinopoli / Freni: 1 of 3" on
    dir /s /b "II *.mp3" | xargs /addquotes shellproperty set PKEY_Music_AlbumTitle to "Madama Butterfly - Sinopoli / Freni: 2 of 3" on
    dir /s /b "III *.mp3" | xargs /addquotes shellproperty set PKEY_Music_AlbumTitle to "Madama Butterfly - Sinopoli / Freni: 3 of 3" on

    Source and amd64/x86 binaries attached, but in substance it's very similar to Raymond's "Little Program".

    Possible future improvements:

    1. When setting, allow specifying a vartype on the command line.
    2. Allow specifying a property key by fmtid and pid.
    3. Handle more vartypes for displaying properties.
    4. Allow dumping all properties on a given file.
  • Matthew van Eerde's web log

    shellproperty.exe v2: read all properties on a file; set properties of certain non-VT_LPWSTR types

    • 1 Comments

    I updated my toy app to set/read shell properties from the command line. New features:

    1. Read all the properties from a given file in one go.
    2. Recognize properties by their canonical name (if they have one.)
    3. Set a property to VT_EMPTY (removing it), or "VT_VECTOR | VT_LPWSTR", or VT_UI4, in addition to VT_LPWSTR.

    Usage:

    >shellproperty.exe
    shellproperty read [ <key> | all ] from <filename>
    shellproperty set <key> on <filename> to <vartype> <vartype-specific-arguments>

    <vartype>: VT_EMPTY | VT_LPWSTR | "VT_VECTOR | VT_LPWSTR" | VT_UI4

    Example of reading all properties from a file:

    >shellproperty read all from "I 01 Track 1.mp3" | sort
    {9E5E05AC-1936-4A75-94F7-4704B8B01923} 0: VT_BSTR I 01 Track 1.mp3
    {CFA31B45-525D-4998-BB44-3F7D81542FA4} 1: VT_LPWSTR MP3
    System.AppUserModel.ID:
    System.AppUserModel.ParentID:
    System.Audio.ChannelCount: 2 (stereo)
    System.Audio.EncodingBitrate: 320kbps
    System.Audio.Format: {00000055-0000-0010-8000-00AA00389B71}
    System.Audio.IsVariableBitRate: No
    System.Audio.PeakValue: 23841
    System.Audio.SampleRate: 44 kHz
    System.Audio.SampleSize: 16 bit
    System.Audio.StreamNumber: 0
    System.Author: Unknown artist
    System.ComputerName: MATEER-D (this PC)
    System.ContentType: audio/mpeg
    System.DateAccessed: 9/3/2013 5:55 PM
    System.DateCreated: 9/3/2013 5:55 PM
    System.DateImported: 9/3/2013 5:55 PM
    System.DateModified: 9/24/2013 3:21 PM
    System.Document.DateCreated: 9/3/2013 5:55 PM
    System.Document.DateSaved: 9/24/2013 3:21 PM
    System.DRM.IsProtected: No
    System.ExpandoProperties:
    System.FileAttributes: A
    System.FileAttributesDisplay:
    System.FileExtension: .mp3
    System.FileName: I 01 Track 1.mp3
    System.FileOwner: REDMOND\mateer
    System.FilePlaceholderStatus: 7
    System.IsFolder: Files
    System.IsShared: No
    System.ItemAuthors: Unknown artist
    System.ItemDate: 9/3/2013 5:55 PM
    System.ItemFolderNameDisplay: Les Misérables (concept album)
    System.ItemFolderPathDisplay: C:\music\Claude-Michel Schönberg & Alain Boublil\Les Misérables (concept album)
    System.ItemFolderPathDisplayNarrow: Les Misérables (concept album) (C:\music\Claude-Michel Schönberg & Alain Boublil)
    System.ItemName: I 01 Track 1.mp3
    System.ItemNameDisplay: I 01 Track 1.mp3
    System.ItemNameDisplayWithoutExtension: I 01 Track 1
    System.ItemParticipants: Unknown artist
    System.ItemPathDisplay: C:\music\Claude-Michel Schönberg & Alain Boublil\Les Misérables (concept album)\I 01 Track 1.mp3
    System.ItemPathDisplayNarrow: I 01 Track 1 (C:\music\Claude-Michel Schönberg & Alain Boublil\Les Misérables (concept album))
    System.ItemType: MP3 File
    System.ItemTypeText: MP3 File
    System.Kind: Music
    System.KindText: Music
    System.Link.TargetExtension:
    System.Link.TargetParsingPath:
    System.Link.TargetSFGAOFlags:
    System.Link.TargetSFGAOFlagsStrings:
    System.Media.AverageLevel: 4219
    System.Media.ClassPrimaryID: {D1607DBC-E323-4BE2-86A1-48A42A28441E}
    System.Media.ClassSecondaryID: {00000000-0000-0000-0000-000000000000}
    System.Media.CollectionGroupID: {3B02CC9D-BE3E-43A4-81AA-DC23DFD20083}
    System.Media.CollectionID: {3B02CC9D-BE3E-43A4-81AA-DC23DFD20083}
    System.Media.ContentID: {3780156C-B516-4897-B6AC-CB632A0CA4A5}
    System.Media.DlnaProfileID: MP3
    System.Media.Duration: 00:04:47
    System.Media.MCDI: E+96+54E9+98AD+A23C+DBD5+F62C+11889+15B50+170F9+1C1EC+1E01E+221A7+2916C+2C6EB+2F21A
    System.Media.MetadataContentProvider: AMG
    System.Media.Publisher: Colosseum
    System.Media.UniqueFileIdentifier: AMGt_id=T 987037;AMGp_id=P 1857378;AMGa_id=R 189777;X_id={9D0F0F00-0500-11DB-89CA-0019B92A3933};XA_id={51E50200-0400-11DB-89CA-0019B92A3933};XAP_id={6357088C-778C-11DC-9403-0019B9B20868}
    System.Media.Year: 1989
    System.MIMEType: audio/mpeg
    System.Music.AlbumArtist: Various Artists
    System.Music.AlbumID: Various Artists - Les Miserables - French Concept Album: 1 of 2
    System.Music.AlbumTitle: Les Miserables - French Concept Album: 1 of 2
    System.Music.Artist: Unknown artist
    System.Music.Composer: Alain Boublil; Claude-Michel Schönberg
    System.Music.DisplayArtist: Various Artists
    System.Music.Genre: Unknown genre
    System.Music.PartOfSet: 1/1
    System.Music.TrackNumber: 1
    System.NetworkLocation:
    System.NotUserContent: No
    System.OfflineAvailability: Available offline
    System.OfflineStatus:
    System.ParsingName: I 01 Track 1.mp3
    System.ParsingPath: C:\music\Claude-Michel Schönberg & Alain Boublil\Les Misérables (concept album)\I 01 Track 1.mp3
    System.PerceivedType: Audio
    System.SFGAOFlags: 1077936503
    System.SharedWith:
    System.ShareScope: music\Claude-Michel Schönberg & Alain Boublil\Les Misérables (concept album)
    System.SharingStatus: Not shared
    System.Shell.SFGAOFlagsStrings: filesys; stream
    System.Size: 10.9 MB
    System.ThumbnailCacheId: 16520045390528741485
    System.Title: Track 1
    System.VolumeId: {14FF6E9D-14F5-11E3-824C-806E6F6E6963}
    System.ZoneIdentifier: 0

    Example of updating a file:

    >type _fixup.bat
    @echo off

    for /f "usebackq delims=" %%f in (`dir /s /b "I *.mp3"`) do (
        shellproperty set System.Music.AlbumTitle on "%%f" to VT_LPWSTR "Madama Butterfly - Sinopoli / Freni: 1 of 3"
    )

    for /f "usebackq delims=" %%f in (`dir /s /b "II *.mp3"`) do (
        shellproperty set System.Music.AlbumTitle on "%%f" to VT_LPWSTR "Madama Butterfly - Sinopoli / Freni: 2 of 3"
    )

    for /f "usebackq delims=" %%f in (`dir /s /b "III *.mp3"`) do (
        shellproperty set System.Music.AlbumTitle on "%%f" to VT_LPWSTR "Madama Butterfly - Sinopoli / Freni: 3 of 3"
    )

    Source and binaries (x86 and amd64) attached.

  • Matthew van Eerde's web log

    Getting peak meters and volume settings for all apps and audio devices on the system

    • 3 Comments

    A few previous posts have touched on how to get peak meter readings on the device, and per-app

    Let's put it all together and write a single command-line tool which enumerates:
    1. All active audio devices (both playback and recording)
    2. Dumps the peak meter and volume levels for each device
    3. All active audio applications (audio sessions) per device
    4. Dumps the peak meter and volume levels for each audio session
    Note there is no API for enumerating individual streams within a session.
    Pseudocode:
    For each flow in (render, capture)
        For each device in IMMDeviceEnumerator::EnumAudioEndpoints(flow)
            Display the name of the device
            Get and display IAudioMeterInformation::GetPeakValue for the device
            Get and display IAudioEndpointVolume data for the device
            For each session in IAudioSessionManager2::GetSessionEnumerator
                Skip the session unless the state is "active"
                Get and display IAudioMeterInformation::GetPeakValue for the session
                Display session information
                Get and display ISimpleAudioVolume information
                Get and display IChannelAudioVolume information

    Sample output:

    >meters.exe
    -- Playback devices --
    Line out (High Definition Audio Device)
        Peak: 0.404736
        Mute: 0
        Volume range: 0% to 100% (-46.5 dB to 0 dB in steps of 1.5 dB)
        Master: 74% (-4.32831 dB)
        Channel 1 of 2: 74% (-4.32831 dB)
        Channel 2 of 2: 74% (-4.32831 dB)

        Active session #1
            Peak value: 0.240089
            Icon path:
            Display name:
            Grouping parameter: {98710e41-6535-4cf0-b9b3-4181a0b7103e}
            Process ID: 3496 (single-process)
            Session identifier: {0.0.0.00000000}.{b3d96927-adc1-4d0f-a83d-bda63ad41843}|\Device\HarddiskVolume1\Windows\System32\WWAHost.exe%b{00000000-0000-0000-0000-000000000000}
            Session instance identifier: {0.0.0.00000000}.{b3d96927-adc1-4d0f-a83d-bda63ad41843}|\Device\HarddiskVolume1\Windows\System32\WWAHost.exe%b{00000000-0000-0000-0000-000000000000}|1%b3496
            System sounds session: no
            Package full name: Microsoft.ZuneMusic_2.2.41.0_x64__8wekyb3d8bbwe
            Master volume: 1 (0 dB FS)
            Not muted
            Channel #1 volume: 1 (0 dB FS)
            Channel #2 volume: 1 (0 dB FS)

        Active session #2
            Peak value: 0.329753
            Icon path:
            Display name:
            Grouping parameter: {fc078096-d2fc-4883-8b0d-af4619266c02}
            Process ID: 6720 (multi-process)
            Session identifier: {0.0.0.00000000}.{b3d96927-adc1-4d0f-a83d-bda63ad41843}|#%b{726CF207-6167-47C4-A745-55691DBD84A1}
            Session instance identifier: {0.0.0.00000000}.{b3d96927-adc1-4d0f-a83d-bda63ad41843}|#%b{726CF207-6167-47C4-A745-55691DBD84A1}|1%b#
            System sounds session: no
            HWND: 0x00000000000D1390 Windows Media Player
            Master volume: 1 (0 dB FS)
            Not muted
            Channel #1 volume: 1 (0 dB FS)
            Channel #2 volume: 1 (0 dB FS)

    Internal speakers (High Definition Audio Device)
        Peak: 0
        Mute: 1
        Volume range: 0% to 100% (-46.5 dB to 0 dB in steps of 1.5 dB)
        Master: 65.7804% (-6 dB)
        Channel 1 of 1: 65.7804% (-6 dB)

    -- Recording devices --
    Microphone (High Definition Audio Device)
        Peak: 0.000274658
        Mute: 0
        Volume range: 0% to 100% (-34.5 dB to 12 dB in steps of 1.5 dB)
        Master: 84.7652% (6 dB)
        Channel 1 of 2: 84.7652% (6 dB)
        Channel 2 of 2: 84.7652% (6 dB)

        Active session #1
            Peak value: 0.000274658
            Icon path:
            Display name:
            Grouping parameter: {cee77f5a-d651-4392-8ffc-232c6eecdf51}
            Process ID: 8212 (single-process)
            Session identifier: {0.0.1.00000000}.{878a0979-89d6-43ec-9cff-e3f70dac2618}|\Device\HarddiskVolume1\Program Files\WindowsApps\Microsoft.WindowsSoundRecorder_6.3.9600.16384_x64__8wekyb3d8bbwe\soundrec.exe%b{00000000-0000-0000-0000-000000000000}
            Session instance identifier: {0.0.1.00000000}.{878a0979-89d6-43ec-9cff-e3f70dac2618}|\Device\HarddiskVolume1\Program Files\WindowsApps\Microsoft.WindowsSoundRecorder_6.3.9600.16384_x64__8wekyb3d8bbwe\soundrec.exe%b{00000000-0000-0000-0000-000000000000}|1%b8212
            System sounds session: no
            Package full name: Microsoft.WindowsSoundRecorder_6.3.9600.16384_x64__8wekyb3d8bbwe
            Master volume: 0.847652 (-1.43565 dB FS)
            Not muted
            Channel #1 volume: 1 (0 dB FS)
            Channel #2 volume: 1 (0 dB FS)

        Active session #2
            Peak value: 0.000274658
            Icon path:
            Display name:
            Grouping parameter: {c346e9e3-a37e-427b-a2be-1feb2c81b469}
            Process ID: 2608 (single-process)
            Session identifier: {0.0.1.00000000}.{878a0979-89d6-43ec-9cff-e3f70dac2618}|\Device\HarddiskVolume1\Windows\System32\SoundRecorder.exe%b{00000000-0000-0000-0000-000000000000}
            Session instance identifier: {0.0.1.00000000}.{878a0979-89d6-43ec-9cff-e3f70dac2618}|\Device\HarddiskVolume1\Windows\System32\SoundRecorder.exe%b{00000000-0000-0000-0000-000000000000}|1%b2608
            System sounds session: no
            HWND: 0x00000000004611FA Sound Recorder
            Master volume: 0.847652 (-1.43565 dB FS)
            Not muted
            Channel #1 volume: 1 (0 dB FS)
            Channel #2 volume: 1 (0 dB FS)

     Source and binaries attached.

  • Matthew van Eerde's web log

    Even if someone's signaling right, they still have the right of way

    • 0 Comments

    I was driving to work this morning and I had an experience which vindicated my paranoia, and may even have passed it on to someone else.

    I was heading East on NE 80th St approaching 140th Ave NE in Redmond. This is a two-way stop; drivers on 140th have the right of way and do not stop. Drivers on 80th (me) have to stop.

    I came to a full stop and signaled right (I wanted to head South on 140th). A driver (let's call him Sam) pulled up behind me, also signaling right. There were three cars heading South on 140th, all of them signaling right (they wanted to head West on 80th).

    At this point I had a conversation with myself that went something like this.

    Well, Matt, you could turn right now. All those cars are turning right, so they won't hit you.
    But wait, Matt. Those cars have the right of way. Sure they're signaling right. But that doesn't mean they'll actually turn right.
    Yup, you're right, Matt. Better to wait to see what actually happens.

    So I waited, and sure enough, all three cars actually turned right. So I suppose I could have gone. And more cars were feeding in to 140th from Redmond Way. And all of these cars were signaling right. And one was a school bus.

    At this point Sam (remember Sam?) got impatient and honked his horn. This shocked me a little.

    I imagine anyone who is from New York or Los Angeles is shaking their heads at me now. Not for waiting, but for being shocked. "He honked his horn? So what?"

    (This is a cultural difference. In New York or Los Angeles, if you're waiting at a red light, you will get honked at as soon as the other guy's light turns yellow. But in Washington, the guy behind you will calmly wait through two full greens, then politely knock on your window and ask if everything is OK.)

    I trust the school bus even less than the cars, so I let the school bus go.

    The car behind the school bus is a minivan. He's signaling right, too. But I let him go as well... and he goes straight!

    Behind the minivan, there's enough of a gap that I feel comfortable pulling out, so I do. And Sam pulls up to the line.

    As I'm cruising down 140th, I glance in my rear-view mirror. I see a line of cars coming down 140th to the intersection I just left, all signaling right...

    ... and I see my friend Sam...

    ... patiently waiting.

    May the Force be with you, Sam.

  • Matthew van Eerde's web log

    Sample app for RECT functions

    • 0 Comments

    Riffing on Raymond Chen's post today about SubtractRect I threw together a toy app which demonstrates three rectangle functions: UnionRect, IntersectRect, and SubtractRect.

    Usage:

    >rects.exe
    rects.exe
        union     (left1 top1 right1 bottom1) (left2 top2 right2 bottom2) |
        intersect (left1 top1 right1 bottom1) (left2 top2 right2 bottom2) |
        subtract  (left1 top1 right1 bottom1) (left2 top2 right2 bottom2)

    Sample output:

    >rects.exe union (2 2 6 6) (4 4 8 8)
          (left = 2; top = 2; right = 6; bottom = 6)
    union (left = 4; top = 4; right = 8; bottom = 8)
        = (left = 2; top = 2; right = 8; bottom = 8)

    Source and binaries (amd64 and x86) attached.

    Still no pictures though.

    Exercise: implement BOOL SymmetricDifferenceRect(_Out_ RECT *C, const RECT *A, const RECT *B).

Page 1 of 1 (5 items)

September, 2013