Sunday, March 22, 2015

Snooze

When I was in high school, I used to set a sleep timer and an alarm on my stereo and fall asleep to music, then wake up to it again in the morning.  Once I got into college, I did the same thing with .BAT files that would stop and re-start WinAmp (those were the days).

A couple years ago, to my wife's dismay, I remembered how much I enjoyed this, and wrote some scripts for Linux to not only stop and start different music but also fade the volume out so the sleep timer didn't abruptly wake me up, and back in so that the wake-up experience was a little more pleasant.  Soon after, I re-did this all as a Windows HTA (HyperText Application), and finally I re-did it this month in JavaScript so that it would work with Windows 8 and have a gradient.  It looks like this:


I call it "fade" because it fades music in and out, in addition to being a nice sleep / wake timer.  You can download the code at https://github.com/strictlymike/fade and follow along if you're interested.  Or, just save fade.hta to your computer and run it.  If it doesn't work for some reason, feel free to contact me, or use your HTML/JScript skillz to figure it out.  Cheers.

Getting Faded

If you know HTML, it's reasonably simple to get started.  You might want to know about this little guy, though:

    <meta http-equiv="x-ua-compatible" content="IE=edge" /> <!-- Gradients -->

Without him, you can't have gradients.  Though, if you use him, you have to use JavaScript -- VBScript has been eliminated.

The UI is a designer's nightmare, but here it is:

The BODY tag has an onload function to initialize the UI, and an oncontextmenu function that disables right-click:

    <body onLoad="onLoad()" oncontextmenu="return false">

The CSS includes a gradient and disables the scroll bar with the overflow: hidden directive:

    <style>
        body { background: linear-gradient(#000044, black 100%);
                                    overflow:          hidden;        }
    </style>

Another SELECT element is pre-populated with times that don't fall within nice, easy increments of 15 minutes:

                <select style="width: 100%;" name="selOffTime">
                    <option value="-1">Never</option>
                    <option value="0">Immediately</option>
                    <option value="0.25">15 seconds</option>
                    <option value="1">1 minute</option>
                    <option value="5">5 minutes</option>
                    <option value="10">10 minutes</option>
                    <option value="15">15 minutes</option>
                    <option value="20">20 minutes</option>
                </select>

The rest are calculated within the body's onLoad() function:

        for (min=30; min<240; min+=15) {
            opt = document.createElement('OPTION');
            if (min == 60) {
                opt.textContent = '1 hour';
            } else if (min > 60) {
                opt.textContent = (min/60).toString() + ' hours';
            } else {
                opt.textContent = min.toString() + ' minutes';
            }
            opt.value = min.toString();
            selOffTime.add(opt);
        }

An INPUT element is pre-populated with a likely (or is it unlikely?) wakeup time, and given an onchange handler:

                    onChange="updateOnTimerLabel()" name="txtOnTime"/>

Another INPUT element, of type "file", is added to permit selection of MP3 files or playlists:

    <input type="file" style="width: 100%;" name="fileToPlay"></input>
    <br/>

A start and cancel button are present, and each has a respective onClick handler:

                <button style="width: 100%;" onClick="start()"
                    name="btnStart">Start</button>

Finally, a status bar is at the bottom, with a black background to match the ending color of the linear gradient defined in the CSS:

    <div id="divStatusBar"
        style="color: white; background-color: black; font-weight: bold;">
        Loading...</div>

If you're a designer or just enjoy attention to detail, take a moment to appreciate the fact that I not only included my CSS in a <style/> tag, but also took the time to mix it with inline CSS using the style=... attribute for each element.

In my onLoad() function, I take care of initialization: resizing the window, instantiating COM objects, updating the label for the sleep time, setting the progress bar update timeout, and adding options in increments of 15 minutes to the timer select box.

The rest is triggered largely through the start() function, wherein I check whether a valid playlist or MP3 was selected for waking, and schedule fade-out and fade-in of the music.

When the sleep timer expires, fadeOut() is called, which in turn schedules nudgeDownWrapper() to be called ever two seconds.  The role of nudgeDownWrapper() is to send a keystroke event for the media volume down button (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx) and count how many times this is done, finally sending a keystroke event for the media stop button.  The volume only needs to be decreased 50 times before it is certain to be muted, so that is how many times it is decreased.

If there is a wake timer, then after it expires, Windows Media Player is invoked with the playlist or MP3 as an argument, and nudgeUpWrapper() gets called to do its thing with volumeUp() several times.

I had another version that was media player-agnostic, but this simpler version is all that most people will need to get started building their own.

Rock_on.


No comments:

Post a Comment