Tuesday, August 28, 2007

Lame

Not a hack, but when encoding MP3s I've found it's useful to dispense with some GUI and use LAME direcly. Download the LAME mp3 encoder someplace, and it will come with an executable. Then, you can write a batch file (.bat) like:

rem Adapted from a script by Robert Hegemann
@echo off
:processArgs
if %1=="" goto endmark

"C:\path\to\lame.exe" -h --abr 160 --mp3input %1
if errorlevel 1 goto errormark
shift
goto processArgs
:errormark
echo ERROR processing %1
pause
:endmark
pause
Put the batch file someplace accessible. To encode some songs, all you need to do is drag and drop them onto the batch file.

Friday, August 24, 2007

A n00b's RGB song

Written at around 3 am:
(Sung to the tune of "abc"/Twinkle Twinkle Little Star)

"0 1 2 3 4 5 6
7 8 9 A B-C-D-E-F
Sharp o o o o o Black
Red Green Blue White Web Safe hack
Now I know my RGBs
I can be a hacker please?"

Tuesday, August 21, 2007

Speed Reading

Sometime this year, I was discussing speed-reading with some friends at Olin. We were thinking of faster ways to read. I can't remember exactly why, but that night I wrote this very quickly.

Speed read

Change the number to change the delay between words. (I think the idea is to start at a comfortable level and gradually get faster).

I found it today and quickly renamed some of the variables and made it more browser compatible. (Also, in the old version the loop function would call setTimeout on itself with a parameter, but now I realized that it is better to use a global variable, g_nIndex). This is still definitely a thirty minute hack (low quality, questionable usefulness, and really fun).

Python Ostream

Here's a hack I read about in the Python Cookbook. This one's for everyone who dislikes C++ :) The idea is to allow syntax like "cout << x" in Python. I liked the idea so I put together a simpler and cleaner implementation.

This is Python code (!):
cout << "hello, world" << nl
cout << "Tuples:" << (5 , 6, 7) << nl
cout << "Lists:" << [5 , 6, 7] << nl
    
cout << "The number, " << 1+1 << ", bigger than " << 1.0/5.0 << nl
print "The number, %s, bigger than %s" % (1+1, 1.0/5.0)
print "The number," , 1+1 , ", bigger than" , 1.0/5.0
# The 2nd is inelegant in my opinion.
# I currently use the 3rd, but note that it inserts spaces, so it prints something different.
and here is the class (concise, and you can even pass it another stream object):
import sys
class OStream():
  def __init__(self, output=None):
    if output is None: output = sys.stdout
    self.output = output
  def __lshift__(self, obj):
    self.output.write(str(obj))
    return self
cout = OStream()
nl = '\n'
The recipe given in the Cookbook was longer, less clean (endl was an IoModifier object rather than '\n'), and used '%s' % obj instead of str(obj). I prefer my approach. I'm sure you could add all sorts of other "useful" features.

Have you ever thought it is weird that left-shift means print? Actually, cout << "hello" is a lot nicer to type than cout("hello"), to me at least, so I don't really mind. Coming up next: subclassing list so that mylist << 4 does mylist.append(4) ... and mylist >> 0 does mylist.pop(0). (Just kidding).

Friday, August 10, 2007

Change Desktop Pattern

Here's another example of how you can make something really nifty in under thirty minutes. A year ago, I wrote a WSH script to change my desktop pattern on startup. I found the right registry keys to change, but unfortunately, the changes wouldn't take effect until the desktop was refreshed. Then, I found an example of how to refresh the desktop here (I guess someone else had thought of this before me), and so I was ready. Note: I haven't tested this in Vista.
To set it up,
1) Create a folder, and a subfolder named "bgtemp".

2) Enter the following and save it as "go.bat", within the folder:
echo "Change of Wallpaper - Ben Fisher"
@echo off
del bgtemp\*.bmp
cscript /NoLogo changed.wsf
3) Enter the following and save as "changed.wsf". Change the code so that strPath is set to any directory with JPG pictures, and strDestination is set to the "bgtemp" directory.
<job id="ChangeBG">
<script language="JScript">
fs = new ActiveXObject("Scripting.FileSystemObject");
var fso, e, file;
var strName; 
var aNames = [];
var strPath = "C:\\Program Files\\Ben's Fractal Screensaver\\Media";
var strDestination = "C:\\Program Files\\Ben's Fractal Screensaver\\Script\\bgtemp\\";
fso = new ActiveXObject("Scripting.FileSystemObject");
e = new Enumerator(fso.GetFolder(strPath).files);
for (e.moveFirst(); ! e.atEnd(); e.moveNext()) 
{
  file = e.item();
  strName = file.name.toString();
  if (strName.indexOf(".jpg") > 1) 
    aNames.push(strName);
}
var nWhich = Math.floor(Math.random() * aNames.length);
var fullpath = strPath + '\\' + aNames[nWhich];
strDestination += aNames[nWhich] + '.bmp';

//Convert to bitmap
var Converter = WScript.CreateObject("Gfx.Converter");
Converter.ToBitmap( fullpath, strDestination );

// Change registry
var objShell = WScript.CreateObject("Wscript.Shell");
objShell.RegWrite("HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Desktop\\General\\BackUpWallpaper", strDestination);
objShell.RegWrite("HKEY_CURRENT_USER\\Control Panel\\Desktop\\Wallpaper",  strDestination);
objShell.RegWrite("HKEY_CURRENT_USER\\Control Panel\\Desktop\\Wallpaper",  strDestination);

// Refresh, credit to http://www.oreilly.com/pub/h/5091
objShell.Run("%windir%\\System32\\RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters", 1, false);
</script>
</job>
4) You'll also need the "convert to bitmap" ActiveX control from http://dev.remotenetworktechnology.com/wsh/comwsh.htm. Unzip and register the dll with the command: Regsvr32 GfxConverter.ocx. It is kind of interesting to learn that desktop patterns must be .bmp files - if you choose another file type in the Desktop control panel it is silently converted into bmp.

5) Put a shortcut to "go.bat" in your startup folder. c:\Documents and Settings\(you)\Start Menu\Programs\Startup.
And that's it!

System Sounds!

Here's a great way to make your operating system more fun. First, get little clips from some of your favorite songs - the more distinctive, the better. Audacity is perfect for this. You'll have to export into WAV format, then, in the control panel, set them as your system sounds.
My computer is now Incredibly Awesome!
Start WindowsGloria
Windows LogonBerg - Violin Concerto Mv. III Allegro
Exit WindowsGrandmaster Flash - The Message
Windows LogoffEdgar Winter - Frankenstein
Device ConnectJavanese Bell Excerpt
Device DisconnectJavanese Bell Excerpt
Program ErrorJames Brown - Living in America
Critical StopYes - City of Love
AsteriskJohn Cage - Sonatas and Interludes for Prepared Piano
Default BeepLou Harrison - Concerto for Flute and Percussion Movement II
System NotificationLou Harrison - Concerto for Flute and Percussion Movement II
ExclamationToru Takemitsu - November Steps for Orchestra
I pulled these 2-second clips from songs in my music library, and I also got some good clips from an old copy of Encarta (I love the bells). Because the volumes aren't normalized, things are even more exciting. Feel free to use these!

Chat History Reader

I used Windows Live Messenger quite a bit at work this summer. You can save message logs in an xml format (enable this in Options). There is an xslt to view the logs in IE, but I wanted something cleaner.
So, I put together a quick app in Windows Forms. (You'll need the .NET 2.0 framework). It doesn't do much, but it does have search. The main interest for me was to use the XmlTextReader (a very nice lightweight xml reader built in to .NET, perfect for this task). I was also interested in RTF; instead of the methods for formatting, I set the raw RTF of the RichTextBox. The result is a useful little app, thanks to .NET.
Source and binary (68 k).

Visual Studio Shortcuts

Visual Studio Shortcuts Here are some of the Visual Studio shortcuts I use.

Commands

Visual Studio includes a basic command-line prompt that is very useful for opening files. One way to enter commands is to press control+/, and then type >. You can then start typing a command, like "open", and then part of a filename. The nice part is that this autocompletes, so this is a great way to open any file in your solution quickly. Press enter and the command is run. All of the commands you see in Tools-Options-Keyboard can be entered too, such as "File.CloseSolution" and so on.
There is a "command window" that is dedicated to this. I've mapped Ctrl+Space to View.CommandWindow, which is quicker than Ctrl+/, >. You can set up an alias to any command. For example, to set up an alias to a commonly used file, enter this:
alias m File.OpenFile CommonlyUsedFile.cs
Now, you can press Ctrl+Space,m,Enter to quickly open that file - very useful if you have a large solution. There are many built-in aliases such as "open" for "File.OpenFile". View this list by typing "alias" with no parameters, or remove an alias with the syntax "alias m /delete". I deleted most of the other aliases, as I'd rather type Ctrl+F4 then "close", and so on.
Useful aliases:
sh Tools.Shell
o File.OpenFile
cls Edit.ClearAll
g Debug.Start
bl Debug.Breakpoints
kb Debug.ListCallStack

Macros

Macros are a great idea, but I'm not too impressed with them in VS. VB is such an ugly language, and there is a distracting pause when running a macro. Anyways, I'd thought I'd try my hand. Here's something that will complete all nested parens and end the line. For example, if you type Console.WriteLine(a.ToString( and run the macro, it will add "));" and start the next line. I've mapped this to Control+Enter, and it can be useful if you are too lazy to count how many parens to close.
Sub CompleteLine()
Dim line As String
DTE.ActiveDocument.Selection.StartOfLine(0)
DTE.ActiveDocument.Selection.EndOfLine(True)
line = DTE.ActiveDocument.Selection.Text

Dim nparens As Integer, i As Long
nparens = 0
For i = 1 To Len(line) - 1 Step 1
    If StrComp("(", Mid$(line, i, 1), vbTextCompare) = 0 Then
  nparens = nparens + 1
    ElseIf StrComp(")", Mid$(line, i, 1), vbTextCompare) = 0 Then
  nparens = nparens - 1
    End If
Next

Dim strResult As String
strResult = ""
For i = 1 To nparens Step 1
    strResult &= ")"
Next
strResult &= ";"

DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.Text = strResult.ToString()
DTE.ActiveDocument.Selection.NewLine()
End Sub
I also have a macro that duplicates the current line, but I'm frustrated by the pause. My wish is to come up with something that could organize my Ctrl-Tab into visual, and not MRU order, but that's a story for another day.

Snippits

Snippits are a great feature of VS 2005. You can easily create your own snippits by creating xml files. There is a nice GUI for creating Snippits called Snippy (A Visual Studio 2005 PowerToy), but it seems to have dissapeared entirely along with gotdotnet.com. So, I've posted it here (378k).
I wrote snippits for creating unit tests, switch statements, and some XAML structures. Most often I used prop and propd for quickly creating properties.

Shortcuts

Finally, here are some keyboard bindings that I find useful:
Ctrl+Space View.CommandWindow
Ctrl+Q Edit.CommentSelection
Ctrl+Shift+Q Edit.UncommentSelection
Ctrl+Alt+, Toggle Bookmark
Ctrl+, Next Bookmark
Ctrl+Shift+, Prev Bookmark
Ctrl+Alt+W Watch Window
Ctrl+Alt+K Call stack
Ctrl+Alt+R Toggle Word wrap
Ctrl+Shift+Alt+W File.CloseSolution
If you happen to have MbUnit (which is cool) or VisualSVN,
Ctrl+T MbUnit – Run Tests
Ctrl+Shift+R VisualSVN-Revert Changes current item
Ctrl+Shift+H VisualSVN- Show current file changes
Ctrl+Shift+Alt+H VisualSVN – Show All Changes

Some of these override standard key bindings, but it’s easy to assign another. I have Ctrl+Shift+Space to be Edit.CompleteWord rather than Ctrl+Space. Anyways, those are some ways to make Visual Studio work for you. The Clipboard ring is nice also.

Tuesday, August 7, 2007

Adding Links to Web Album Generator

Web Album Generator is a great freeware program for creating online photo albums. It is sleek, easy to use, and does things the right way.
The only complaint I have is that it is not very customizeable. I like how in Facebook albums, clicking on the current photo takes you to the next photo, providing a quick way to skim through the pictures without having to find the "next" link. Web Album Generator has no option for this. But, since the output is just normal HTML, I can write a script to make changes to all of the HTML files.
Here's a Python script that will do the trick. If you haven't used Web Album Generator, you should give it a try. After exporting the album, specify the directory (strDirectory) and run the script. (You might want to make a backup copy first.)
# Designed for Web Album Generator 1.8.2 (ornj.net)
import os
import re

strDirectory = r"C:\outputdirectory"
astrFiles = os.listdir(strDirectory)

reNextPicture = re.compile('<a href="([^"]+.html)" title="Next Photograph">Next')
reEndlink = re.compile('(</div>)\s+(<p class=")')

def main():
  for strFilename in astrFiles:
    if strFilename.endswith("html"):
      f = open(st + '\\' + strFilename, 'r')
      strHtml = f.read() #Read entire file
      f.close()
      
      objmatch = re.search(reNextPicture, strHtml)
      if objmatch:
        filenext = objmatch.group(1)
        strHtml = strHtml.replace('<div id="photograph">', '<div id="photograph"><a href="'+filenext + '">',1)
        strHtml = reEndlink.sub('</a> </div> <p class="',strHtml)

        fout = open(strDirectory + '\\' + strFilename, 'w')
        fout.write(html)
        fout.close()
        print "Written:" + strFilename
main()

Monday, August 6, 2007

C#3 Extension Methods

I like some of the new C# 3.0 features. Extension methods are fun to play around with. Isn't this ridiculous:
(4).Square().ToString().Print(); // Prints 16
No performance drawbacks, and so much fun!

On a more useful note, I've written some extension methods that have been serving me well. (We've just upgraded to Orcas at work.)
  internal static class StringExtensions
  {
    public static void Print(this string str)
    {
      Console.WriteLine(str);
    }
    public static void Trace(this string str)
    {
      System.Diagnostics.Trace.WriteLine(str);
    }
    public static string Multiply(this string str, int n)
    {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < n; i++)
        sb.Append(str);
      return sb.ToString();
    }
    public static string[] SimpleSplit(this string str, string strDelim)
    {
      return str.Split(new string[] { strDelim }, StringSplitOptions.None);
    }
    public static string SimpleJoin(this string[] astr, string strDelim)
    {
      return String.Join(strDelim, astr);
    }
    public static Boolean IsNullOrEmpty(this string str)
    {
      return String.IsNullOrEmpty(str);
    }
    public static int ToInt(this string str)
    {
      return int.Parse(str);
    }
    public static Boolean Contains<T>(this T[] a, T val)
    {
      return a.IndexOf<T>(val) != -1;
    }
    public static int IndexOf<T>(this T[] a, T val)
    {
      for (int i = 0; i < a.Length; i++)
        if (a[i].Equals(val))
          return i;
      return -1;
    }
    // Just like Python!
    public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey keyTry, TValue defaultVal)
    {
      TValue ret;
      return dict.TryGetValue(keyTry, out ret) ? ret : defaultVal;
    }
  }
I wish you could overload operators - I wanted to see "ab" * 3 (ababab) and "word"++ (WORD).

C#3 also has introduced object initializers, which are nice. Still no dictionary initializers, though - I'm disappointed. (See also the BidirectionalDictionary). Here's a workaround - I wish I could have used generics.
Dictionary<int, string> d = new Dictionary<int, string>();
d.AddMany( new object[] {4, "a", 5, "e", 6, "f"} );

public static void AddMany<TKey, TValue>(this Dictionary<TKey, TValue> dict, object[] aIn)
{
  if (aIn.Length % 2 != 0) throw new ArgumentOutOfRangeException();
  for (int i=0; i<aIn.Length; i+=2)
    dict[(TKey) aIn[i]] = (TValue) aIn[i + 1];
}

Friday, August 3, 2007

0xBAADF00D

I made a hexsp33k generator earlier this week. First, I got a large word list that contained inflections. I used agid-4 and wrote a quick python script to eliminate the unsure entries, proper nouns, and other characters. The result was a list of words separated by newline, with inflections delimited by space. Ready to go.

Because Hexsp33k doesn't need strict spelling, my thought was to use a "score" for each mapping of English to hex.

Looser mappings would have a negative score, hopefully eliminating entries that would be hard to read. I add to the score for each letter group. However, it turned out that I got best results when I stuck to only accepting A-F and 0 as O, and 5 as S. The other phonetic transcription ("for" into 4) would need a better algorithm.

Results:
0xCA5CADE5

0xBADA55E5

0xFACE0FF

0xFACADE5

0xDECEA5ED

0xC0FFEE (0xDECAF)

0xAD0BE

0xDEFACED

0xDEC0DED

0xAC1D, 0xD15EA5ED

0xDA7ABA5E 0xACCE55

0xC0C0A

0xBAD 0xF00D! 0xFEED 0xBOA!

0xACCEDED 0xDECADE

FADE, FACE, CAFE

DEAF, DEAD, DEED

0B0E C0DE BEADED B0BBED BAD

DEC0DE, DEFACE, D00D00, D00DAD

FOE, EBBED, FADED

Code:
Tweak the constants until you get something. It doesn't work that well - if I had had more time I would have explored some type of fuzzy matching, to allow creative misspellings. Also, in a 30 minute hack I tend to have inconsistent variable names in Python.
import operator
map = {
'a':['A', 0], 'b':['B', 0], 'c':['C', 0], 'd':['D', 0], 'e':['E', 0], 'f':['F', 0],
'o':['0', 0],  'ox':['0x', 0], 'ocks':['0x', 0], 'oks':['0x', 0], 's':['5', -.11],
#sounds like
#~ 'one':['1', -.4],
#~ 'won':['1', -.4],
#~ 'wun':['1', -.4],
}

def main():
  f = open("agid-4\\infl3np.txt", 'r')
  aResults = []
  for line in f:
    astrWords = line.lower().strip().split()
    for word in astrWords:
      nScore = 0
      strString = ''
      c = 0
      #higher score is better
      while c < len(word):
        if word[c:c+1] in map:
          data = map[word[c]]
          c+= 1
        elif word[c:c+2] in map:
          data = map[word[c:c+2]]
          c+=2
        elif word[c:c+3] in map:
          data = map[word[c:c+3]]
          c+=3
        elif word[c:c+4] in map:
          data = map[word[c:c+4]]
          c+=4
        else:
          nScore -= 200 #bad score
          c+= 1
          continue
          
        strString += data[0]
        nScore += 0.2 + data[1]*4
        
      if nScore > 0.1:
        aResults.append( (nScore, word, strString) )
  f.close()
  
  aResults.sort(key=operator.itemgetter(0))
  aResults.reverse()
  
  for res in aResults[0:5]:
    print res
  #For me, comes up with FACE0FF, EFFACED, DEFACED
  
main()

A Quicker Fox

Last week I gave myself a challenge. I wanted to write a phrase that used the letters from a-z, and to make it as short as possible (a pangram). As a challenge I did not use any help, the internet, or any program. After working for a while, I found some pretty good ones.

Why fold, just vex quick brazen gimp (29)
Why quick gimp just loved brazen FX (29)

Rhythm DJs fling vox, zap web quack (28)
why brazen DJ vex quick golf stamp (28)

I thought "rhythm" was very promising, but could never offset the repeated h. After much trial, I'm pretty sure you can't get shorter than 28 without using abbreviations like DJ.

After comparing with Wikipedia, I found that I had done pretty well. Using a Python program I wrote, I was able to find an even shorter one:
why klutz DJ FX vamp squib conger (27)

I see that I'll have to find a better way to get rid of my q. I'm done for now, but sometime I'll find shorter ones.

Wednesday, August 1, 2007

Drupal PHP Pages

Last year, I created a large Drupal website with many custom extensions. I thought of a good way to add custom pages. It wasn't finished in 30 minutes per se, but it's somewhat of a quick hack, and is very convenient and efficient (if you happen to use Drupal 5).

At the time, I loved Drupal, but slowly I became aware of its disadvantages. The whole system is built for ease of administration; efficiency is secondary. This means that even though it looks and works perfectly on a local test machine, in a real environment the number of database queries and heavy RAM usage will hit the server hard.

When a Drupal page loads it makes an extreme amount of database queries to retrieve configuration, layouts, and even the page you are viewing. To me, it doesn't make sense to hold all of this in a database - it is a needless query. Sure, this way makes it easier to edit your pages as an admin, but it introduces needless inefficiency querying the database and transferring data. Static pages should be files on the server.

My site had several custom pages that had a static structure. The pages had PHP that would look into the Drupal database information manually. At first, I made these Drupal "Pages," which can contain PHP code, but it is very inconvinient to edit PHP code in that little textarea. So, I told Drupal to redirect from certain urls to open a php file on the server. This way I would have the convenience of a nice URL, an easy-to-edit file, and one less database call.

I made the change to index.php, the page that serves all page requests. It should be clear where to merge this.
// Menu status constants are integers; page content is a string.
if (is_int($return)) {
  switch ($return) {
    case MENU_NOT_FOUND:
  
    if ( substr($_GET['q'],0,2) == 'p/')
    {
    //This is a modification by Ben.
    //The idea behind this was to simplify creating normal PHP pages. The URL looks more natural
    //Another benefit is that less database calls are required.
  global $pageargs,$user, $myuserrole;
  $pageurl = substr($_GET['q'], 2);
  $pageargs = explode('/', $pageurl);
  
  $myuserrole = theme_getuser_role($user->roles);
  $purl = '';
  switch ($pageargs[0])
  {
    case 'home': $purl = 'home.inc'; break;
    
    case '_page1': $purl = 'my/page1.inc'; break;
    case '_page2': $purl = 'my/page2.inc'; break;
  }
  if ($purl)
  {
    $content = file_get_contents('pages/'.$purl);
    print theme('page', drupal_eval($content));
   }
   else
   {
    drupal_not_found();
   }
   
   } 
    else
      drupal_not_found();
      break;
    case MENU_ACCESS_DENIED:
      drupal_access_denied();
      break;
    case MENU_SITE_OFFLINE:
      drupal_site_offline();
      break;
  }
}
I didn't have the pretty-urls option enabled, but this should work if is on. In this example, I'm mapping ?q=p/home to the file pages/home.inc. Note that I am not letting any file be opened - only the ones in the switch are accessible. Also, I used the .inc extension because I don't want the file to be accessed individually as a .php file. Nonetheless, any php code will be executed normally, thanks to the drupal_eval function. I store the $myuserrole and $pageargs variables in a global var because they will be useful in those pages.